Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f5a4659

Browse files
committed
index: support index v4
Support reading and writing index v4. Index v4 uses a very simple compression scheme for pathnames, but is otherwise similar to index v3. Signed-off-by: David Turner <[email protected]>
1 parent 006f674 commit f5a4659

File tree

26 files changed

+234
-28
lines changed

26 files changed

+234
-28
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ v0.24 + 1
33

44
### Changes or improvements
55

6+
* Support for reading and writing git index v4 files
7+
68
### API additions
79

810
* `git_commit_create_buffer()` creates a commit and writes it into a
@@ -13,6 +15,9 @@ v0.24 + 1
1315
writing into a stream. Useful when you do not know the final size or
1416
want to copy the contents from another stream.
1517

18+
* `git_index_version()` and `git_set_index_version()` to get and set
19+
the index version
20+
1621
### API removals
1722

1823
* `git_blob_create_fromchunks()` has been removed in favour of

include/git2/index.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,31 @@ GIT_EXTERN(int) git_index_caps(const git_index *index);
251251
*/
252252
GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps);
253253

254+
/**
255+
* Get index on-disk version.
256+
*
257+
* Valid return values are 2, 3, or 4. If 3 is returned, an index
258+
* with version 2 may be written instead, if the extension data in
259+
* version 3 is not necessary.
260+
*
261+
* @param index An existing index object
262+
* @return the index version
263+
*/
264+
GIT_EXTERN(unsigned int) git_index_version(git_index *index);
265+
266+
/**
267+
* Set index on-disk version.
268+
*
269+
* Valid values are 2, 3, or 4. If 2 is given, an index with version
270+
* 3 may be written instead, if necessary to accurately represent the
271+
* index.
272+
*
273+
* @param index An existing index object
274+
* @param version The new version number
275+
* @return 0 on success, -1 on failure
276+
*/
277+
GIT_EXTERN(int) git_index_set_version(git_index *index, unsigned int version);
278+
254279
/**
255280
* Update the contents of an existing index object in memory by reading
256281
* from the hard disk.

src/index.c

Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "blob.h"
2020
#include "idxmap.h"
2121
#include "diff.h"
22+
#include "varint.h"
2223

2324
#include "git2/odb.h"
2425
#include "git2/oid.h"
@@ -65,8 +66,11 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
6566
static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
6667
static const size_t INDEX_HEADER_SIZE = 12;
6768

68-
static const unsigned int INDEX_VERSION_NUMBER = 2;
69+
static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2;
70+
static const unsigned int INDEX_VERSION_NUMBER_LB = 2;
6971
static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
72+
static const unsigned int INDEX_VERSION_NUMBER_COMP = 4;
73+
static const unsigned int INDEX_VERSION_NUMBER_UB = 4;
7074

7175
static const unsigned int INDEX_HEADER_SIG = 0x44495243;
7276
static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
@@ -434,6 +438,7 @@ int git_index_open(git_index **index_out, const char *index_path)
434438
index->entries_search = git_index_entry_srch;
435439
index->entries_search_path = index_entry_srch_path;
436440
index->reuc_search = reuc_srch;
441+
index->version = INDEX_VERSION_NUMBER_DEFAULT;
437442

438443
if (index_path != NULL && (error = git_index_read(index, true)) < 0)
439444
goto fail;
@@ -746,6 +751,26 @@ static int truncate_racily_clean(git_index *index)
746751
return 0;
747752
}
748753

754+
unsigned git_index_version(git_index *index)
755+
{
756+
assert(index);
757+
758+
return index->version;
759+
}
760+
761+
int git_index_set_version(git_index *index, unsigned int version)
762+
{
763+
assert(index);
764+
765+
if (version < INDEX_VERSION_NUMBER_LB ||
766+
version > INDEX_VERSION_NUMBER_UB)
767+
return -1;
768+
769+
index->version = version;
770+
771+
return 0;
772+
}
773+
749774
int git_index_write(git_index *index)
750775
{
751776
git_indexwriter writer = GIT_INDEXWRITER_INIT;
@@ -2261,12 +2286,15 @@ static size_t read_entry(
22612286
git_index_entry **out,
22622287
git_index *index,
22632288
const void *buffer,
2264-
size_t buffer_size)
2289+
size_t buffer_size,
2290+
const char **last)
22652291
{
22662292
size_t path_length, entry_size;
22672293
const char *path_ptr;
22682294
struct entry_short source;
22692295
git_index_entry entry = {{0}};
2296+
bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP;
2297+
char *tmp_path = NULL;
22702298

22712299
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
22722300
return 0;
@@ -2301,33 +2329,48 @@ static size_t read_entry(
23012329
} else
23022330
path_ptr = (const char *) buffer + offsetof(struct entry_short, path);
23032331

2304-
path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
2332+
if (!compressed) {
2333+
path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
23052334

2306-
/* if this is a very long string, we must find its
2307-
* real length without overflowing */
2308-
if (path_length == 0xFFF) {
2309-
const char *path_end;
2335+
/* if this is a very long string, we must find its
2336+
* real length without overflowing */
2337+
if (path_length == 0xFFF) {
2338+
const char *path_end;
23102339

2311-
path_end = memchr(path_ptr, '\0', buffer_size);
2312-
if (path_end == NULL)
2313-
return 0;
2340+
path_end = memchr(path_ptr, '\0', buffer_size);
2341+
if (path_end == NULL)
2342+
return 0;
23142343

2315-
path_length = path_end - path_ptr;
2316-
}
2344+
path_length = path_end - path_ptr;
2345+
}
23172346

2318-
if (entry.flags & GIT_IDXENTRY_EXTENDED)
2319-
entry_size = long_entry_size(path_length);
2320-
else
2321-
entry_size = short_entry_size(path_length);
2347+
if (entry.flags & GIT_IDXENTRY_EXTENDED)
2348+
entry_size = long_entry_size(path_length);
2349+
else
2350+
entry_size = short_entry_size(path_length);
23222351

2323-
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
2324-
return 0;
2352+
if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
2353+
return 0;
23252354

2326-
entry.path = (char *)path_ptr;
2355+
entry.path = (char *)path_ptr;
2356+
} else {
2357+
size_t shared = decode_varint((const unsigned char **) &path_ptr);
2358+
size_t len = strlen(path_ptr);
2359+
size_t last_len = strlen(*last);
23272360

2328-
if (index_entry_dup(out, index, &entry) < 0)
2361+
tmp_path = git__malloc(shared + len + 1);
2362+
memcpy(tmp_path, last, last_len);
2363+
strcpy(tmp_path + last_len, path_ptr);
2364+
entry_size = long_entry_size(shared + len);
2365+
entry.path = tmp_path;
2366+
}
2367+
2368+
if (index_entry_dup(out, index, &entry) < 0) {
2369+
git__free(tmp_path);
23292370
return 0;
2371+
}
23302372

2373+
git__free(tmp_path);
23312374
return entry_size;
23322375
}
23332376

@@ -2340,8 +2383,8 @@ static int read_header(struct index_header *dest, const void *buffer)
23402383
return index_error_invalid("incorrect header signature");
23412384

23422385
dest->version = ntohl(source->version);
2343-
if (dest->version != INDEX_VERSION_NUMBER_EXT &&
2344-
dest->version != INDEX_VERSION_NUMBER)
2386+
if (dest->version < INDEX_VERSION_NUMBER_LB ||
2387+
dest->version > INDEX_VERSION_NUMBER_UB)
23452388
return index_error_invalid("incorrect header version");
23462389

23472390
dest->entry_count = ntohl(source->entry_count);
@@ -2394,6 +2437,8 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
23942437
unsigned int i;
23952438
struct index_header header = { 0 };
23962439
git_oid checksum_calculated, checksum_expected;
2440+
const char **last = NULL;
2441+
const char *empty = "";
23972442

23982443
#define seek_forward(_increase) { \
23992444
if (_increase >= buffer_size) { \
@@ -2414,6 +2459,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
24142459
if ((error = read_header(&header, buffer)) < 0)
24152460
return error;
24162461

2462+
index->version = header.version;
2463+
if (index->version >= INDEX_VERSION_NUMBER_COMP)
2464+
last = &empty;
2465+
24172466
seek_forward(INDEX_HEADER_SIZE);
24182467

24192468
assert(!index->entries.length);
@@ -2426,7 +2475,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
24262475
/* Parse all the entries */
24272476
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
24282477
git_index_entry *entry;
2429-
size_t entry_size = read_entry(&entry, index, buffer, buffer_size);
2478+
size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
24302479

24312480
/* 0 bytes read means an object corruption */
24322481
if (entry_size == 0) {
@@ -2517,15 +2566,31 @@ static bool is_index_extended(git_index *index)
25172566
return (extended > 0);
25182567
}
25192568

2520-
static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
2569+
static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last)
25212570
{
25222571
void *mem = NULL;
25232572
struct entry_short *ondisk;
25242573
size_t path_len, disk_size;
25252574
char *path;
2575+
const char *path_start = entry->path;
2576+
size_t same_len = 0;
25262577

25272578
path_len = ((struct entry_internal *)entry)->pathlen;
25282579

2580+
if (last) {
2581+
const char *last_c = *last;
2582+
2583+
while (*path_start == *last_c) {
2584+
if (!*path_start || !*last_c)
2585+
break;
2586+
++path_start;
2587+
++last_c;
2588+
++same_len;
2589+
}
2590+
path_len -= same_len;
2591+
*last = entry->path;
2592+
}
2593+
25292594
if (entry->flags & GIT_IDXENTRY_EXTENDED)
25302595
disk_size = long_entry_size(path_len);
25312596
else
@@ -2573,7 +2638,11 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
25732638
else
25742639
path = ondisk->path;
25752640

2576-
memcpy(path, entry->path, path_len);
2641+
if (last) {
2642+
path += encode_varint(path_len - same_len,
2643+
(unsigned char*) path);
2644+
}
2645+
memcpy(path, path_start, path_len);
25772646

25782647
return 0;
25792648
}
@@ -2584,6 +2653,8 @@ static int write_entries(git_index *index, git_filebuf *file)
25842653
size_t i;
25852654
git_vector case_sorted, *entries;
25862655
git_index_entry *entry;
2656+
const char **last = NULL;
2657+
const char *empty = "";
25872658

25882659
/* If index->entries is sorted case-insensitively, then we need
25892660
* to re-sort it case-sensitively before writing */
@@ -2595,8 +2666,11 @@ static int write_entries(git_index *index, git_filebuf *file)
25952666
entries = &index->entries;
25962667
}
25972668

2669+
if (index->version >= INDEX_VERSION_NUMBER_COMP)
2670+
last = &empty;
2671+
25982672
git_vector_foreach(entries, i, entry)
2599-
if ((error = write_disk_entry(file, entry)) < 0)
2673+
if ((error = write_disk_entry(file, entry, last)) < 0)
26002674
break;
26012675

26022676
if (index->ignore_case)
@@ -2761,8 +2835,12 @@ static int write_index(git_oid *checksum, git_index *index, git_filebuf *file)
27612835

27622836
assert(index && file);
27632837

2764-
is_extended = is_index_extended(index);
2765-
index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER;
2838+
if (index->version <= INDEX_VERSION_NUMBER_EXT) {
2839+
is_extended = is_index_extended(index);
2840+
index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB;
2841+
} else {
2842+
index_version_number = index->version;
2843+
}
27662844

27672845
header.signature = htonl(INDEX_HEADER_SIG);
27682846
header.version = htonl(index_version_number);

src/index.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ struct git_index {
4646
git_vector_cmp entries_search;
4747
git_vector_cmp entries_search_path;
4848
git_vector_cmp reuc_search;
49+
50+
unsigned int version;
4951
};
5052

5153
struct git_index_conflict_iterator {

tests/index/version.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "clar_libgit2.h"
2+
#include "index.h"
3+
4+
static git_repository *g_repo = NULL;
5+
6+
void test_index_version__can_write_v4(void)
7+
{
8+
git_index *index;
9+
const git_index_entry *entry;
10+
11+
g_repo = cl_git_sandbox_init("version");
12+
cl_git_pass(git_repository_index(&index, g_repo));
13+
14+
cl_assert(index->on_disk);
15+
cl_assert(git_index_version(index) == 2);
16+
17+
cl_assert(git_index_entrycount(index) == 6);
18+
19+
cl_git_pass(git_index_set_version(index, 4));
20+
21+
cl_git_pass(git_index_write(index));
22+
git_index_free(index);
23+
24+
cl_git_pass(git_repository_index(&index, g_repo));
25+
cl_assert(git_index_version(index) == 4);
26+
27+
entry = git_index_get_bypath(index, "dir1/afile", 0);
28+
cl_assert(entry);
29+
entry = git_index_get_bypath(index, "long-dir-name-for-overflow-case-01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/long-file-name-for-overflow-case-012345678901234567890123456789012345678901234-alpha", 0);
30+
cl_assert(entry);
31+
32+
entry = git_index_get_bypath(index, "long-dir-name-for-overflow-case-01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/long-file-name-for-overflow-case-012345678901234567890123456789012345678901234-bravo", 0);
33+
cl_assert(entry);
34+
35+
git_index_free(index);
36+
}
37+
38+
void test_index_version__cleanup(void)
39+
{
40+
cl_git_sandbox_cleanup();
41+
g_repo = NULL;
42+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ref: refs/heads/master

tests/resources/version/.gitted/MERGE_RR

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[core]
2+
repositoryformatversion = 0
3+
filemode = true
4+
bare = false
5+
logallrefupdates = true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unnamed repository; edit this file 'description' to name the repository.
1.14 KB
Binary file not shown.

0 commit comments

Comments
 (0)