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 ;
@@ -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+
749774int 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 );
0 commit comments