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
6566static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ ;
6667static 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 ;
6971static 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
7175static const unsigned int INDEX_HEADER_SIG = 0x44495243 ;
7276static 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 ;
@@ -747,6 +752,28 @@ static int truncate_racily_clean(git_index *index)
747752 return 0 ;
748753}
749754
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+
750777int git_index_write (git_index * index )
751778{
752779 git_indexwriter writer = GIT_INDEXWRITER_INIT ;
@@ -2262,12 +2289,15 @@ static size_t read_entry(
22622289 git_index_entry * * out ,
22632290 git_index * index ,
22642291 const void * buffer ,
2265- size_t buffer_size )
2292+ size_t buffer_size ,
2293+ const char * * last )
22662294{
22672295 size_t path_length , entry_size ;
22682296 const char * path_ptr ;
22692297 struct entry_short source ;
22702298 git_index_entry entry = {{0 }};
2299+ bool compressed = index -> version >= INDEX_VERSION_NUMBER_COMP ;
2300+ char * tmp_path = NULL ;
22712301
22722302 if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size )
22732303 return 0 ;
@@ -2302,33 +2332,56 @@ static size_t read_entry(
23022332 } else
23032333 path_ptr = (const char * ) buffer + offsetof(struct entry_short , path );
23042334
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 ;
23112337
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 ;
23152342
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 ;
23182346
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+ }
23232349
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 );
23262354
2327- entry .path = (char * )path_ptr ;
2355+ if (INDEX_FOOTER_SIZE + entry_size > buffer_size )
2356+ return 0 ;
23282357
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 );
23302381 return 0 ;
2382+ }
23312383
2384+ git__free (tmp_path );
23322385 return entry_size ;
23332386}
23342387
@@ -2341,8 +2394,8 @@ static int read_header(struct index_header *dest, const void *buffer)
23412394 return index_error_invalid ("incorrect header signature" );
23422395
23432396 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 )
23462399 return index_error_invalid ("incorrect header version" );
23472400
23482401 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)
23952448 unsigned int i ;
23962449 struct index_header header = { 0 };
23972450 git_oid checksum_calculated , checksum_expected ;
2451+ const char * * last = NULL ;
2452+ const char * empty = "" ;
23982453
23992454#define seek_forward (_increase ) { \
24002455 if (_increase >= buffer_size) { \
@@ -2415,6 +2470,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
24152470 if ((error = read_header (& header , buffer )) < 0 )
24162471 return error ;
24172472
2473+ index -> version = header .version ;
2474+ if (index -> version >= INDEX_VERSION_NUMBER_COMP )
2475+ last = & empty ;
2476+
24182477 seek_forward (INDEX_HEADER_SIZE );
24192478
24202479 assert (!index -> entries .length );
@@ -2427,7 +2486,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
24272486 /* Parse all the entries */
24282487 for (i = 0 ; i < header .entry_count && buffer_size > INDEX_FOOTER_SIZE ; ++ i ) {
24292488 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 );
24312490
24322491 /* 0 bytes read means an object corruption */
24332492 if (entry_size == 0 ) {
@@ -2518,15 +2577,31 @@ static bool is_index_extended(git_index *index)
25182577 return (extended > 0 );
25192578}
25202579
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 )
25222581{
25232582 void * mem = NULL ;
25242583 struct entry_short * ondisk ;
25252584 size_t path_len , disk_size ;
25262585 char * path ;
2586+ const char * path_start = entry -> path ;
2587+ size_t same_len = 0 ;
25272588
25282589 path_len = ((struct entry_internal * )entry )-> pathlen ;
25292590
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+
25302605 if (entry -> flags & GIT_IDXENTRY_EXTENDED )
25312606 disk_size = long_entry_size (path_len );
25322607 else
@@ -2574,7 +2649,12 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
25742649 else
25752650 path = ondisk -> path ;
25762651
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 );
25782658
25792659 return 0 ;
25802660}
@@ -2585,6 +2665,8 @@ static int write_entries(git_index *index, git_filebuf *file)
25852665 size_t i ;
25862666 git_vector case_sorted , * entries ;
25872667 git_index_entry * entry ;
2668+ const char * * last = NULL ;
2669+ const char * empty = "" ;
25882670
25892671 /* If index->entries is sorted case-insensitively, then we need
25902672 * to re-sort it case-sensitively before writing */
@@ -2596,8 +2678,11 @@ static int write_entries(git_index *index, git_filebuf *file)
25962678 entries = & index -> entries ;
25972679 }
25982680
2681+ if (index -> version >= INDEX_VERSION_NUMBER_COMP )
2682+ last = & empty ;
2683+
25992684 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 )
26012686 break ;
26022687
26032688 if (index -> ignore_case )
@@ -2762,8 +2847,12 @@ static int write_index(git_oid *checksum, git_index *index, git_filebuf *file)
27622847
27632848 assert (index && file );
27642849
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+ }
27672856
27682857 header .signature = htonl (INDEX_HEADER_SIG );
27692858 header .version = htonl (index_version_number );
0 commit comments