19
19
#include "blob.h"
20
20
#include "idxmap.h"
21
21
#include "diff.h"
22
+ #include "varint.h"
22
23
23
24
#include "git2/odb.h"
24
25
#include "git2/oid.h"
@@ -65,8 +66,11 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
65
66
static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ ;
66
67
static const size_t INDEX_HEADER_SIZE = 12 ;
67
68
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 ;
69
71
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 ;
70
74
71
75
static const unsigned int INDEX_HEADER_SIG = 0x44495243 ;
72
76
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)
434
438
index -> entries_search = git_index_entry_srch ;
435
439
index -> entries_search_path = index_entry_srch_path ;
436
440
index -> reuc_search = reuc_srch ;
441
+ index -> version = INDEX_VERSION_NUMBER_DEFAULT ;
437
442
438
443
if (index_path != NULL && (error = git_index_read (index , true)) < 0 )
439
444
goto fail ;
@@ -747,6 +752,28 @@ static int truncate_racily_clean(git_index *index)
747
752
return 0 ;
748
753
}
749
754
755
+ unsigned git_index_version (git_index * index )
756
+ {
757
+ assert (index );
758
+
759
+ return index -> version ;
760
+ }
761
+
762
+ int git_index_set_version (git_index * index , unsigned int version )
763
+ {
764
+ assert (index );
765
+
766
+ if (version < INDEX_VERSION_NUMBER_LB ||
767
+ version > INDEX_VERSION_NUMBER_UB ) {
768
+ giterr_set (GITERR_INDEX , "Invalid version number" );
769
+ return -1 ;
770
+ }
771
+
772
+ index -> version = version ;
773
+
774
+ return 0 ;
775
+ }
776
+
750
777
int git_index_write (git_index * index )
751
778
{
752
779
git_indexwriter writer = GIT_INDEXWRITER_INIT ;
@@ -2262,12 +2289,15 @@ static size_t read_entry(
2262
2289
git_index_entry * * out ,
2263
2290
git_index * index ,
2264
2291
const void * buffer ,
2265
- size_t buffer_size )
2292
+ size_t buffer_size ,
2293
+ const char * * last )
2266
2294
{
2267
2295
size_t path_length , entry_size ;
2268
2296
const char * path_ptr ;
2269
2297
struct entry_short source ;
2270
2298
git_index_entry entry = {{0 }};
2299
+ bool compressed = index -> version >= INDEX_VERSION_NUMBER_COMP ;
2300
+ char * tmp_path = NULL ;
2271
2301
2272
2302
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size )
2273
2303
return 0 ;
@@ -2302,33 +2332,56 @@ static size_t read_entry(
2302
2332
} else
2303
2333
path_ptr = (const char * ) buffer + offsetof(struct entry_short , path );
2304
2334
2305
- path_length = entry .flags & GIT_IDXENTRY_NAMEMASK ;
2306
-
2307
- /* if this is a very long string, we must find its
2308
- * real length without overflowing */
2309
- if (path_length == 0xFFF ) {
2310
- const char * path_end ;
2335
+ if (!compressed ) {
2336
+ path_length = entry .flags & GIT_IDXENTRY_NAMEMASK ;
2311
2337
2312
- path_end = memchr (path_ptr , '\0' , buffer_size );
2313
- if (path_end == NULL )
2314
- return 0 ;
2338
+ /* if this is a very long string, we must find its
2339
+ * real length without overflowing */
2340
+ if (path_length == 0xFFF ) {
2341
+ const char * path_end ;
2315
2342
2316
- path_length = path_end - path_ptr ;
2317
- }
2343
+ path_end = memchr (path_ptr , '\0' , buffer_size );
2344
+ if (path_end == NULL )
2345
+ return 0 ;
2318
2346
2319
- if (entry .flags & GIT_IDXENTRY_EXTENDED )
2320
- entry_size = long_entry_size (path_length );
2321
- else
2322
- entry_size = short_entry_size (path_length );
2347
+ path_length = path_end - path_ptr ;
2348
+ }
2323
2349
2324
- if (INDEX_FOOTER_SIZE + entry_size > buffer_size )
2325
- return 0 ;
2350
+ if (entry .flags & GIT_IDXENTRY_EXTENDED )
2351
+ entry_size = long_entry_size (path_length );
2352
+ else
2353
+ entry_size = short_entry_size (path_length );
2326
2354
2327
- entry .path = (char * )path_ptr ;
2355
+ if (INDEX_FOOTER_SIZE + entry_size > buffer_size )
2356
+ return 0 ;
2328
2357
2329
- if (index_entry_dup (out , index , & entry ) < 0 )
2358
+ entry .path = (char * )path_ptr ;
2359
+ } else {
2360
+ size_t varint_len ;
2361
+ size_t shared = git_decode_varint ((const unsigned char * )path_ptr ,
2362
+ & varint_len );
2363
+ size_t len = strlen (path_ptr + varint_len );
2364
+ size_t last_len = strlen (* last );
2365
+ size_t tmp_path_len ;
2366
+
2367
+ if (varint_len == 0 )
2368
+ return index_error_invalid ("incorrect prefix length" );
2369
+
2370
+ GITERR_CHECK_ALLOC_ADD (& tmp_path_len , shared , len + 1 );
2371
+ tmp_path = git__malloc (tmp_path_len );
2372
+ GITERR_CHECK_ALLOC (tmp_path );
2373
+ memcpy (tmp_path , last , last_len );
2374
+ memcpy (tmp_path + last_len , path_ptr + varint_len , len );
2375
+ entry_size = long_entry_size (shared + len );
2376
+ entry .path = tmp_path ;
2377
+ }
2378
+
2379
+ if (index_entry_dup (out , index , & entry ) < 0 ) {
2380
+ git__free (tmp_path );
2330
2381
return 0 ;
2382
+ }
2331
2383
2384
+ git__free (tmp_path );
2332
2385
return entry_size ;
2333
2386
}
2334
2387
@@ -2341,8 +2394,8 @@ static int read_header(struct index_header *dest, const void *buffer)
2341
2394
return index_error_invalid ("incorrect header signature" );
2342
2395
2343
2396
dest -> version = ntohl (source -> version );
2344
- if (dest -> version != INDEX_VERSION_NUMBER_EXT &&
2345
- dest -> version != INDEX_VERSION_NUMBER )
2397
+ if (dest -> version < INDEX_VERSION_NUMBER_LB ||
2398
+ dest -> version > INDEX_VERSION_NUMBER_UB )
2346
2399
return index_error_invalid ("incorrect header version" );
2347
2400
2348
2401
dest -> entry_count = ntohl (source -> entry_count );
@@ -2395,6 +2448,8 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
2395
2448
unsigned int i ;
2396
2449
struct index_header header = { 0 };
2397
2450
git_oid checksum_calculated , checksum_expected ;
2451
+ const char * * last = NULL ;
2452
+ const char * empty = "" ;
2398
2453
2399
2454
#define seek_forward (_increase ) { \
2400
2455
if (_increase >= buffer_size) { \
@@ -2415,6 +2470,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
2415
2470
if ((error = read_header (& header , buffer )) < 0 )
2416
2471
return error ;
2417
2472
2473
+ index -> version = header .version ;
2474
+ if (index -> version >= INDEX_VERSION_NUMBER_COMP )
2475
+ last = & empty ;
2476
+
2418
2477
seek_forward (INDEX_HEADER_SIZE );
2419
2478
2420
2479
assert (!index -> entries .length );
@@ -2427,7 +2486,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
2427
2486
/* Parse all the entries */
2428
2487
for (i = 0 ; i < header .entry_count && buffer_size > INDEX_FOOTER_SIZE ; ++ i ) {
2429
2488
git_index_entry * entry ;
2430
- size_t entry_size = read_entry (& entry , index , buffer , buffer_size );
2489
+ size_t entry_size = read_entry (& entry , index , buffer , buffer_size , last );
2431
2490
2432
2491
/* 0 bytes read means an object corruption */
2433
2492
if (entry_size == 0 ) {
@@ -2518,15 +2577,31 @@ static bool is_index_extended(git_index *index)
2518
2577
return (extended > 0 );
2519
2578
}
2520
2579
2521
- static int write_disk_entry (git_filebuf * file , git_index_entry * entry )
2580
+ static int write_disk_entry (git_filebuf * file , git_index_entry * entry , const char * * last )
2522
2581
{
2523
2582
void * mem = NULL ;
2524
2583
struct entry_short * ondisk ;
2525
2584
size_t path_len , disk_size ;
2526
2585
char * path ;
2586
+ const char * path_start = entry -> path ;
2587
+ size_t same_len = 0 ;
2527
2588
2528
2589
path_len = ((struct entry_internal * )entry )-> pathlen ;
2529
2590
2591
+ if (last ) {
2592
+ const char * last_c = * last ;
2593
+
2594
+ while (* path_start == * last_c ) {
2595
+ if (!* path_start || !* last_c )
2596
+ break ;
2597
+ ++ path_start ;
2598
+ ++ last_c ;
2599
+ ++ same_len ;
2600
+ }
2601
+ path_len -= same_len ;
2602
+ * last = entry -> path ;
2603
+ }
2604
+
2530
2605
if (entry -> flags & GIT_IDXENTRY_EXTENDED )
2531
2606
disk_size = long_entry_size (path_len );
2532
2607
else
@@ -2574,7 +2649,12 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
2574
2649
else
2575
2650
path = ondisk -> path ;
2576
2651
2577
- memcpy (path , entry -> path , path_len );
2652
+ if (last ) {
2653
+ path += git_encode_varint ((unsigned char * ) path ,
2654
+ disk_size ,
2655
+ path_len - same_len );
2656
+ }
2657
+ memcpy (path , path_start , path_len );
2578
2658
2579
2659
return 0 ;
2580
2660
}
@@ -2585,6 +2665,8 @@ static int write_entries(git_index *index, git_filebuf *file)
2585
2665
size_t i ;
2586
2666
git_vector case_sorted , * entries ;
2587
2667
git_index_entry * entry ;
2668
+ const char * * last = NULL ;
2669
+ const char * empty = "" ;
2588
2670
2589
2671
/* If index->entries is sorted case-insensitively, then we need
2590
2672
* to re-sort it case-sensitively before writing */
@@ -2596,8 +2678,11 @@ static int write_entries(git_index *index, git_filebuf *file)
2596
2678
entries = & index -> entries ;
2597
2679
}
2598
2680
2681
+ if (index -> version >= INDEX_VERSION_NUMBER_COMP )
2682
+ last = & empty ;
2683
+
2599
2684
git_vector_foreach (entries , i , entry )
2600
- if ((error = write_disk_entry (file , entry )) < 0 )
2685
+ if ((error = write_disk_entry (file , entry , last )) < 0 )
2601
2686
break ;
2602
2687
2603
2688
if (index -> ignore_case )
@@ -2762,8 +2847,12 @@ static int write_index(git_oid *checksum, git_index *index, git_filebuf *file)
2762
2847
2763
2848
assert (index && file );
2764
2849
2765
- is_extended = is_index_extended (index );
2766
- index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER ;
2850
+ if (index -> version <= INDEX_VERSION_NUMBER_EXT ) {
2851
+ is_extended = is_index_extended (index );
2852
+ index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB ;
2853
+ } else {
2854
+ index_version_number = index -> version ;
2855
+ }
2767
2856
2768
2857
header .signature = htonl (INDEX_HEADER_SIG );
2769
2858
header .version = htonl (index_version_number );
0 commit comments