28
28
#define PACK_CRUFT 4
29
29
30
30
#define DELETE_PACK 1
31
+ #define RETAIN_PACK 2
31
32
32
33
static int pack_everything ;
33
34
static int delta_base_offset = 1 ;
@@ -52,7 +53,7 @@ struct pack_objects_args {
52
53
const char * window_memory ;
53
54
const char * depth ;
54
55
const char * threads ;
55
- const char * max_pack_size ;
56
+ unsigned long max_pack_size ;
56
57
int no_reuse_delta ;
57
58
int no_reuse_object ;
58
59
int quiet ;
@@ -118,11 +119,26 @@ static void pack_mark_for_deletion(struct string_list_item *item)
118
119
item -> util = (void * )((uintptr_t )item -> util | DELETE_PACK );
119
120
}
120
121
122
+ static void pack_unmark_for_deletion (struct string_list_item * item )
123
+ {
124
+ item -> util = (void * )((uintptr_t )item -> util & ~DELETE_PACK );
125
+ }
126
+
121
127
static int pack_is_marked_for_deletion (struct string_list_item * item )
122
128
{
123
129
return (uintptr_t )item -> util & DELETE_PACK ;
124
130
}
125
131
132
+ static void pack_mark_retained (struct string_list_item * item )
133
+ {
134
+ item -> util = (void * )((uintptr_t )item -> util | RETAIN_PACK );
135
+ }
136
+
137
+ static int pack_is_retained (struct string_list_item * item )
138
+ {
139
+ return (uintptr_t )item -> util & RETAIN_PACK ;
140
+ }
141
+
126
142
static void mark_packs_for_deletion_1 (struct string_list * names ,
127
143
struct string_list * list )
128
144
{
@@ -135,17 +151,39 @@ static void mark_packs_for_deletion_1(struct string_list *names,
135
151
if (len < hexsz )
136
152
continue ;
137
153
sha1 = item -> string + len - hexsz ;
138
- /*
139
- * Mark this pack for deletion, which ensures that this
140
- * pack won't be included in a MIDX (if `--write-midx`
141
- * was given) and that we will actually delete this pack
142
- * (if `-d` was given).
143
- */
144
- if (!string_list_has_string (names , sha1 ))
154
+
155
+ if (pack_is_retained (item )) {
156
+ pack_unmark_for_deletion (item );
157
+ } else if (!string_list_has_string (names , sha1 )) {
158
+ /*
159
+ * Mark this pack for deletion, which ensures
160
+ * that this pack won't be included in a MIDX
161
+ * (if `--write-midx` was given) and that we
162
+ * will actually delete this pack (if `-d` was
163
+ * given).
164
+ */
145
165
pack_mark_for_deletion (item );
166
+ }
146
167
}
147
168
}
148
169
170
+ static void retain_cruft_pack (struct existing_packs * existing ,
171
+ struct packed_git * cruft )
172
+ {
173
+ struct strbuf buf = STRBUF_INIT ;
174
+ struct string_list_item * item ;
175
+
176
+ strbuf_addstr (& buf , pack_basename (cruft ));
177
+ strbuf_strip_suffix (& buf , ".pack" );
178
+
179
+ item = string_list_lookup (& existing -> cruft_packs , buf .buf );
180
+ if (!item )
181
+ BUG ("could not find cruft pack '%s'" , pack_basename (cruft ));
182
+
183
+ pack_mark_retained (item );
184
+ strbuf_release (& buf );
185
+ }
186
+
149
187
static void mark_packs_for_deletion (struct existing_packs * existing ,
150
188
struct string_list * names )
151
189
@@ -227,6 +265,8 @@ static void collect_pack_filenames(struct existing_packs *existing,
227
265
}
228
266
229
267
string_list_sort (& existing -> kept_packs );
268
+ string_list_sort (& existing -> non_kept_packs );
269
+ string_list_sort (& existing -> cruft_packs );
230
270
strbuf_release (& buf );
231
271
}
232
272
@@ -244,7 +284,7 @@ static void prepare_pack_objects(struct child_process *cmd,
244
284
if (args -> threads )
245
285
strvec_pushf (& cmd -> args , "--threads=%s" , args -> threads );
246
286
if (args -> max_pack_size )
247
- strvec_pushf (& cmd -> args , "--max-pack-size=%s " , args -> max_pack_size );
287
+ strvec_pushf (& cmd -> args , "--max-pack-size=%lu " , args -> max_pack_size );
248
288
if (args -> no_reuse_delta )
249
289
strvec_pushf (& cmd -> args , "--no-reuse-delta" );
250
290
if (args -> no_reuse_object )
@@ -317,6 +357,18 @@ static struct generated_pack_data *populate_pack_exts(const char *name)
317
357
return data ;
318
358
}
319
359
360
+ static int has_pack_ext (const struct generated_pack_data * data ,
361
+ const char * ext )
362
+ {
363
+ int i ;
364
+ for (i = 0 ; i < ARRAY_SIZE (exts ); i ++ ) {
365
+ if (strcmp (exts [i ].name , ext ))
366
+ continue ;
367
+ return !!data -> tempfiles [i ];
368
+ }
369
+ BUG ("unknown pack extension: '%s'" , ext );
370
+ }
371
+
320
372
static void repack_promisor_objects (const struct pack_objects_args * args ,
321
373
struct string_list * names )
322
374
{
@@ -734,6 +786,7 @@ static void midx_included_packs(struct string_list *include,
734
786
735
787
static int write_midx_included_packs (struct string_list * include ,
736
788
struct pack_geometry * geometry ,
789
+ struct string_list * names ,
737
790
const char * refs_snapshot ,
738
791
int show_progress , int write_bitmaps )
739
792
{
@@ -763,6 +816,38 @@ static int write_midx_included_packs(struct string_list *include,
763
816
if (preferred )
764
817
strvec_pushf (& cmd .args , "--preferred-pack=%s" ,
765
818
pack_basename (preferred ));
819
+ else if (names -> nr ) {
820
+ /* The largest pack was repacked, meaning that either
821
+ * one or two packs exist depending on whether the
822
+ * repository has a cruft pack or not.
823
+ *
824
+ * Select the non-cruft one as preferred to encourage
825
+ * pack-reuse among packs containing reachable objects
826
+ * over unreachable ones.
827
+ *
828
+ * (Note we could write multiple packs here if
829
+ * `--max-pack-size` was given, but any one of them
830
+ * will suffice, so pick the first one.)
831
+ */
832
+ for_each_string_list_item (item , names ) {
833
+ struct generated_pack_data * data = item -> util ;
834
+ if (has_pack_ext (data , ".mtimes" ))
835
+ continue ;
836
+
837
+ strvec_pushf (& cmd .args , "--preferred-pack=pack-%s.pack" ,
838
+ item -> string );
839
+ break ;
840
+ }
841
+ } else {
842
+ /*
843
+ * No packs were kept, and no packs were written. The
844
+ * only thing remaining are .keep packs (unless
845
+ * --pack-kept-objects was given).
846
+ *
847
+ * Set the `--preferred-pack` arbitrarily here.
848
+ */
849
+ ;
850
+ }
766
851
767
852
if (refs_snapshot )
768
853
strvec_pushf (& cmd .args , "--refs-snapshot=%s" , refs_snapshot );
@@ -888,6 +973,73 @@ static int write_filtered_pack(const struct pack_objects_args *args,
888
973
return finish_pack_objects_cmd (& cmd , names , local );
889
974
}
890
975
976
+ static int existing_cruft_pack_cmp (const void * va , const void * vb )
977
+ {
978
+ struct packed_git * a = * (struct packed_git * * )va ;
979
+ struct packed_git * b = * (struct packed_git * * )vb ;
980
+
981
+ if (a -> pack_size < b -> pack_size )
982
+ return -1 ;
983
+ if (a -> pack_size > b -> pack_size )
984
+ return 1 ;
985
+ return 0 ;
986
+ }
987
+
988
+ static void collapse_small_cruft_packs (FILE * in , size_t max_size ,
989
+ struct existing_packs * existing )
990
+ {
991
+ struct packed_git * * existing_cruft , * p ;
992
+ struct strbuf buf = STRBUF_INIT ;
993
+ size_t total_size = 0 ;
994
+ size_t existing_cruft_nr = 0 ;
995
+ size_t i ;
996
+
997
+ ALLOC_ARRAY (existing_cruft , existing -> cruft_packs .nr );
998
+
999
+ for (p = get_all_packs (the_repository ); p ; p = p -> next ) {
1000
+ if (!(p -> is_cruft && p -> pack_local ))
1001
+ continue ;
1002
+
1003
+ strbuf_reset (& buf );
1004
+ strbuf_addstr (& buf , pack_basename (p ));
1005
+ strbuf_strip_suffix (& buf , ".pack" );
1006
+
1007
+ if (!string_list_has_string (& existing -> cruft_packs , buf .buf ))
1008
+ continue ;
1009
+
1010
+ if (existing_cruft_nr >= existing -> cruft_packs .nr )
1011
+ BUG ("too many cruft packs (found %" PRIuMAX ", but knew "
1012
+ "of %" PRIuMAX ")" ,
1013
+ (uintmax_t )existing_cruft_nr + 1 ,
1014
+ (uintmax_t )existing -> cruft_packs .nr );
1015
+ existing_cruft [existing_cruft_nr ++ ] = p ;
1016
+ }
1017
+
1018
+ QSORT (existing_cruft , existing_cruft_nr , existing_cruft_pack_cmp );
1019
+
1020
+ for (i = 0 ; i < existing_cruft_nr ; i ++ ) {
1021
+ size_t proposed ;
1022
+
1023
+ p = existing_cruft [i ];
1024
+ proposed = st_add (total_size , p -> pack_size );
1025
+
1026
+ if (proposed <= max_size ) {
1027
+ total_size = proposed ;
1028
+ fprintf (in , "-%s\n" , pack_basename (p ));
1029
+ } else {
1030
+ retain_cruft_pack (existing , p );
1031
+ fprintf (in , "%s\n" , pack_basename (p ));
1032
+ }
1033
+ }
1034
+
1035
+ for (i = 0 ; i < existing -> non_kept_packs .nr ; i ++ )
1036
+ fprintf (in , "-%s.pack\n" ,
1037
+ existing -> non_kept_packs .items [i ].string );
1038
+
1039
+ strbuf_release (& buf );
1040
+ free (existing_cruft );
1041
+ }
1042
+
891
1043
static int write_cruft_pack (const struct pack_objects_args * args ,
892
1044
const char * destination ,
893
1045
const char * pack_prefix ,
@@ -934,10 +1086,14 @@ static int write_cruft_pack(const struct pack_objects_args *args,
934
1086
in = xfdopen (cmd .in , "w" );
935
1087
for_each_string_list_item (item , names )
936
1088
fprintf (in , "%s-%s.pack\n" , pack_prefix , item -> string );
937
- for_each_string_list_item (item , & existing -> non_kept_packs )
938
- fprintf (in , "-%s.pack\n" , item -> string );
939
- for_each_string_list_item (item , & existing -> cruft_packs )
940
- fprintf (in , "-%s.pack\n" , item -> string );
1089
+ if (args -> max_pack_size && !cruft_expiration ) {
1090
+ collapse_small_cruft_packs (in , args -> max_pack_size , existing );
1091
+ } else {
1092
+ for_each_string_list_item (item , & existing -> non_kept_packs )
1093
+ fprintf (in , "-%s.pack\n" , item -> string );
1094
+ for_each_string_list_item (item , & existing -> cruft_packs )
1095
+ fprintf (in , "-%s.pack\n" , item -> string );
1096
+ }
941
1097
for_each_string_list_item (item , & existing -> kept_packs )
942
1098
fprintf (in , "%s.pack\n" , item -> string );
943
1099
fclose (in );
@@ -990,6 +1146,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
990
1146
PACK_CRUFT ),
991
1147
OPT_STRING (0 , "cruft-expiration" , & cruft_expiration , N_ ("approxidate" ),
992
1148
N_ ("with --cruft, expire objects older than this" )),
1149
+ OPT_MAGNITUDE (0 , "max-cruft-size" , & cruft_po_args .max_pack_size ,
1150
+ N_ ("with --cruft, limit the size of new cruft packs" )),
993
1151
OPT_BOOL ('d' , NULL , & delete_redundant ,
994
1152
N_ ("remove redundant packs, and run git-prune-packed" )),
995
1153
OPT_BOOL ('f' , NULL , & po_args .no_reuse_delta ,
@@ -1017,7 +1175,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
1017
1175
N_ ("limits the maximum delta depth" )),
1018
1176
OPT_STRING (0 , "threads" , & po_args .threads , N_ ("n" ),
1019
1177
N_ ("limits the maximum number of threads" )),
1020
- OPT_STRING (0 , "max-pack-size" , & po_args .max_pack_size , N_ ( "bytes" ) ,
1178
+ OPT_MAGNITUDE (0 , "max-pack-size" , & po_args .max_pack_size ,
1021
1179
N_ ("maximum size of each packfile" )),
1022
1180
OPT_PARSE_LIST_OBJECTS_FILTER (& po_args .filter_options ),
1023
1181
OPT_BOOL (0 , "pack-kept-objects" , & pack_kept_objects ,
@@ -1327,7 +1485,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
1327
1485
struct string_list include = STRING_LIST_INIT_NODUP ;
1328
1486
midx_included_packs (& include , & existing , & names , & geometry );
1329
1487
1330
- ret = write_midx_included_packs (& include , & geometry ,
1488
+ ret = write_midx_included_packs (& include , & geometry , & names ,
1331
1489
refs_snapshot ? get_tempfile_path (refs_snapshot ) : NULL ,
1332
1490
show_progress , write_bitmaps > 0 );
1333
1491
0 commit comments