Raw
1 /*
2 *
3 * Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se>
4 *
5 * This file is licensed under the GPL v2.
6 *
7 */
8
9 #define USE_THE_REPOSITORY_VARIABLE
10
11 #include "builtin.h"
12 #include "gettext.h"
13 #include "hex.h"
14
15 #include "packfile.h"
16 #include "odb.h"
17 #include "strbuf.h"
18
19 #define BLKSIZE 512
20
21 static const char pack_redundant_usage[] =
22 "git pack-redundant [--verbose] [--alt-odb] (--all | <pack-filename>...)";
23
24 static int load_all_packs, verbose, alt_odb;
25
26 struct llist_item {
27 struct llist_item *next;
28 struct object_id oid;
29 };
30 static struct llist {
31 struct llist_item *front;
32 struct llist_item *back;
33 size_t size;
34 } *all_objects; /* all objects which must be present in local packfiles */
35
36 static struct pack_list {
37 struct pack_list *next;
38 struct packed_git *pack;
39 struct llist *unique_objects;
40 struct llist *remaining_objects;
41 size_t all_objects_size;
42 } *local_packs = NULL, *altodb_packs = NULL;
43
44 static struct llist_item *free_nodes;
45
46 static inline void llist_item_put(struct llist_item *item)
47 {
48 item->next = free_nodes;
49 free_nodes = item;
50 }
51
52 static inline struct llist_item *llist_item_get(void)
53 {
54 struct llist_item *new_item;
55 if ( free_nodes ) {
56 new_item = free_nodes;
57 free_nodes = free_nodes->next;
58 } else {
59 int i = 1;
60 ALLOC_ARRAY(new_item, BLKSIZE);
61 for (; i < BLKSIZE; i++)
62 llist_item_put(&new_item[i]);
63 }
64 return new_item;
65 }
66
67 static inline void llist_init(struct llist **list)
68 {
69 *list = xmalloc(sizeof(struct llist));
70 (*list)->front = (*list)->back = NULL;
71 (*list)->size = 0;
72 }
73
74 static void llist_free(struct llist *list)
75 {
76 for (struct llist_item *i = list->front, *next; i; i = next) {
77 next = i->next;
78 llist_item_put(i);
79 }
80 free(list);
81 }
82
83 static struct llist * llist_copy(struct llist *list)
84 {
85 struct llist *ret;
86 struct llist_item *new_item, *old_item, *prev;
87
88 llist_init(&ret);
89
90 if ((ret->size = list->size) == 0)
91 return ret;
92
93 new_item = ret->front = llist_item_get();
94 new_item->oid = list->front->oid;
95
96 old_item = list->front->next;
97 while (old_item) {
98 prev = new_item;
99 new_item = llist_item_get();
100 prev->next = new_item;
101 new_item->oid = old_item->oid;
102 old_item = old_item->next;
103 }
104 new_item->next = NULL;
105 ret->back = new_item;
106
107 return ret;
108 }
109
110 static inline struct llist_item *llist_insert(struct llist *list,
111 struct llist_item *after,
112 const unsigned char *oid)
113 {
114 struct llist_item *new_item = llist_item_get();
115 oidread(&new_item->oid, oid, the_repository->hash_algo);
116 new_item->next = NULL;
117
118 if (after) {
119 new_item->next = after->next;
120 after->next = new_item;
121 if (after == list->back)
122 list->back = new_item;
123 } else {/* insert in front */
124 if (list->size == 0)
125 list->back = new_item;
126 else
127 new_item->next = list->front;
128 list->front = new_item;
129 }
130 list->size++;
131 return new_item;
132 }
133
134 static inline struct llist_item *llist_insert_back(struct llist *list,
135 const unsigned char *oid)
136 {
137 return llist_insert(list, list->back, oid);
138 }
139
140 static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
141 const struct object_id *oid, struct llist_item *hint)
142 {
143 struct llist_item *prev = NULL, *l;
144
145 l = (hint == NULL) ? list->front : hint;
146 while (l) {
147 int cmp = oidcmp(&l->oid, oid);
148 if (cmp > 0) { /* we insert before this entry */
149 return llist_insert(list, prev, oid->hash);
150 }
151 if (!cmp) { /* already exists */
152 return l;
153 }
154 prev = l;
155 l = l->next;
156 }
157 /* insert at the end */
158 return llist_insert_back(list, oid->hash);
159 }
160
161 /* returns a pointer to an item in front of sha1 */
162 static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *oid, struct llist_item *hint)
163 {
164 struct llist_item *prev, *l;
165
166 redo_from_start:
167 l = (hint == NULL) ? list->front : hint;
168 prev = NULL;
169 while (l) {
170 const int cmp = hashcmp(l->oid.hash, oid, the_repository->hash_algo);
171 if (cmp > 0) /* not in list, since sorted */
172 return prev;
173 if (!cmp) { /* found */
174 if (!prev) {
175 if (hint != NULL && hint != list->front) {
176 /* we don't know the previous element */
177 hint = NULL;
178 goto redo_from_start;
179 }
180 list->front = l->next;
181 } else
182 prev->next = l->next;
183 if (l == list->back)
184 list->back = prev;
185 llist_item_put(l);
186 list->size--;
187 return prev;
188 }
189 prev = l;
190 l = l->next;
191 }
192 return prev;
193 }
194
195 /* computes A\B */
196 static void llist_sorted_difference_inplace(struct llist *A,
197 struct llist *B)
198 {
199 struct llist_item *hint, *b;
200
201 hint = NULL;
202 b = B->front;
203
204 while (b) {
205 hint = llist_sorted_remove(A, b->oid.hash, hint);
206 b = b->next;
207 }
208 }
209
210 static inline struct pack_list * pack_list_insert(struct pack_list **pl,
211 struct pack_list *entry)
212 {
213 struct pack_list *p = xmalloc(sizeof(struct pack_list));
214 memcpy(p, entry, sizeof(struct pack_list));
215 p->next = *pl;
216 *pl = p;
217 return p;
218 }
219
220 static void pack_list_free(struct pack_list *pl)
221 {
222 for (struct pack_list *next; pl; pl = next) {
223 next = pl->next;
224 free(pl);
225 }
226 }
227
228 static inline size_t pack_list_size(struct pack_list *pl)
229 {
230 size_t ret = 0;
231 while (pl) {
232 ret++;
233 pl = pl->next;
234 }
235 return ret;
236 }
237
238 static struct pack_list * pack_list_difference(const struct pack_list *A,
239 const struct pack_list *B)
240 {
241 struct pack_list *ret;
242 const struct pack_list *pl;
243
244 if (!A)
245 return NULL;
246
247 pl = B;
248 while (pl != NULL) {
249 if (A->pack == pl->pack)
250 return pack_list_difference(A->next, B);
251 pl = pl->next;
252 }
253 ret = xmalloc(sizeof(struct pack_list));
254 memcpy(ret, A, sizeof(struct pack_list));
255 ret->next = pack_list_difference(A->next, B);
256 return ret;
257 }
258
259 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
260 {
261 size_t p1_off = 0, p2_off = 0, p1_step, p2_step;
262 const unsigned char *p1_base, *p2_base;
263 struct llist_item *p1_hint = NULL, *p2_hint = NULL;
264 const unsigned int hashsz = the_hash_algo->rawsz;
265
266 if (!p1->unique_objects)
267 p1->unique_objects = llist_copy(p1->remaining_objects);
268 if (!p2->unique_objects)
269 p2->unique_objects = llist_copy(p2->remaining_objects);
270
271 p1_base = p1->pack->index_data;
272 p2_base = p2->pack->index_data;
273 p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
274 p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
275 p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0);
276 p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0);
277
278 while (p1_off < p1->pack->num_objects * p1_step &&
279 p2_off < p2->pack->num_objects * p2_step)
280 {
281 const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
282 the_repository->hash_algo);
283 /* cmp ~ p1 - p2 */
284 if (cmp == 0) {
285 p1_hint = llist_sorted_remove(p1->unique_objects,
286 p1_base + p1_off,
287 p1_hint);
288 p2_hint = llist_sorted_remove(p2->unique_objects,
289 p1_base + p1_off,
290 p2_hint);
291 p1_off += p1_step;
292 p2_off += p2_step;
293 continue;
294 }
295 if (cmp < 0) { /* p1 has the object, p2 doesn't */
296 p1_off += p1_step;
297 } else { /* p2 has the object, p1 doesn't */
298 p2_off += p2_step;
299 }
300 }
301 }
302
303 static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
304 {
305 size_t ret = 0;
306 size_t p1_off = 0, p2_off = 0, p1_step, p2_step;
307 const unsigned char *p1_base, *p2_base;
308 const unsigned int hashsz = the_hash_algo->rawsz;
309
310 p1_base = p1->index_data;
311 p2_base = p2->index_data;
312 p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
313 p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
314 p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0);
315 p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0);
316
317 while (p1_off < p1->num_objects * p1_step &&
318 p2_off < p2->num_objects * p2_step)
319 {
320 int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off,
321 the_repository->hash_algo);
322 /* cmp ~ p1 - p2 */
323 if (cmp == 0) {
324 ret++;
325 p1_off += p1_step;
326 p2_off += p2_step;
327 continue;
328 }
329 if (cmp < 0) { /* p1 has the object, p2 doesn't */
330 p1_off += p1_step;
331 } else { /* p2 has the object, p1 doesn't */
332 p2_off += p2_step;
333 }
334 }
335 return ret;
336 }
337
338 /* another O(n^2) function ... */
339 static size_t get_pack_redundancy(struct pack_list *pl)
340 {
341 struct pack_list *subset;
342 size_t ret = 0;
343
344 if (!pl)
345 return 0;
346
347 while ((subset = pl->next)) {
348 while (subset) {
349 ret += sizeof_union(pl->pack, subset->pack);
350 subset = subset->next;
351 }
352 pl = pl->next;
353 }
354 return ret;
355 }
356
357 static inline off_t pack_set_bytecount(struct pack_list *pl)
358 {
359 off_t ret = 0;
360 while (pl) {
361 ret += pl->pack->pack_size;
362 ret += pl->pack->index_size;
363 pl = pl->next;
364 }
365 return ret;
366 }
367
368 static int cmp_remaining_objects(const void *a, const void *b)
369 {
370 struct pack_list *pl_a = *((struct pack_list **)a);
371 struct pack_list *pl_b = *((struct pack_list **)b);
372
373 if (pl_a->remaining_objects->size == pl_b->remaining_objects->size) {
374 /* have the same remaining_objects, big pack first */
375 if (pl_a->all_objects_size == pl_b->all_objects_size)
376 return 0;
377 else if (pl_a->all_objects_size < pl_b->all_objects_size)
378 return 1;
379 else
380 return -1;
381 } else if (pl_a->remaining_objects->size < pl_b->remaining_objects->size) {
382 /* sort by remaining objects, more objects first */
383 return 1;
384 } else {
385 return -1;
386 }
387 }
388
389 /* Sort pack_list, greater size of remaining_objects first */
390 static void sort_pack_list(struct pack_list **pl)
391 {
392 struct pack_list **ary, *p;
393 size_t n = pack_list_size(*pl);
394
395 if (n < 2)
396 return;
397
398 /* prepare an array of packed_list for easier sorting */
399 CALLOC_ARRAY(ary, n);
400 for (n = 0, p = *pl; p; p = p->next)
401 ary[n++] = p;
402
403 QSORT(ary, n, cmp_remaining_objects);
404
405 /* link them back again */
406 for (size_t i = 0; i < n - 1; i++)
407 ary[i]->next = ary[i + 1];
408 ary[n - 1]->next = NULL;
409 *pl = ary[0];
410
411 free(ary);
412 }
413
414
415 static void minimize(struct pack_list **min)
416 {
417 struct pack_list *pl, *unique = NULL, *non_unique = NULL;
418 struct llist *missing, *unique_pack_objects;
419
420 pl = local_packs;
421 while (pl) {
422 if (pl->unique_objects->size)
423 pack_list_insert(&unique, pl);
424 else
425 pack_list_insert(&non_unique, pl);
426 pl = pl->next;
427 }
428 /* find out which objects are missing from the set of unique packs */
429 missing = llist_copy(all_objects);
430 pl = unique;
431 while (pl) {
432 llist_sorted_difference_inplace(missing, pl->remaining_objects);
433 pl = pl->next;
434 }
435
436 *min = unique;
437
438 /* return if there are no objects missing from the unique set */
439 if (missing->size == 0) {
440 llist_free(missing);
441 pack_list_free(non_unique);
442 return;
443 }
444
445 unique_pack_objects = llist_copy(all_objects);
446 llist_sorted_difference_inplace(unique_pack_objects, missing);
447
448 /* remove unique pack objects from the non_unique packs */
449 pl = non_unique;
450 while (pl) {
451 llist_sorted_difference_inplace(pl->remaining_objects, unique_pack_objects);
452 pl = pl->next;
453 }
454
455 while (non_unique) {
456 struct pack_list *next;
457
458 /* sort the non_unique packs, greater size of remaining_objects first */
459 sort_pack_list(&non_unique);
460 if (non_unique->remaining_objects->size == 0)
461 break;
462
463 pack_list_insert(min, non_unique);
464
465 for (pl = non_unique->next; pl && pl->remaining_objects->size > 0; pl = pl->next)
466 llist_sorted_difference_inplace(pl->remaining_objects, non_unique->remaining_objects);
467
468 next = non_unique->next;
469 free(non_unique);
470 non_unique = next;
471 }
472
473 pack_list_free(non_unique);
474 llist_free(unique_pack_objects);
475 llist_free(missing);
476 }
477
478 static void load_all_objects(void)
479 {
480 struct pack_list *pl = local_packs;
481 struct llist_item *hint, *l;
482
483 llist_init(&all_objects);
484
485 while (pl) {
486 hint = NULL;
487 l = pl->remaining_objects->front;
488 while (l) {
489 hint = llist_insert_sorted_unique(all_objects,
490 &l->oid, hint);
491 l = l->next;
492 }
493 pl = pl->next;
494 }
495 /* remove objects present in remote packs */
496 pl = altodb_packs;
497 while (pl) {
498 llist_sorted_difference_inplace(all_objects, pl->remaining_objects);
499 pl = pl->next;
500 }
501 }
502
503 /* this scales like O(n^2) */
504 static void cmp_local_packs(void)
505 {
506 struct pack_list *subset, *pl = local_packs;
507
508 /* only one packfile */
509 if (!pl->next) {
510 llist_init(&pl->unique_objects);
511 return;
512 }
513
514 while ((subset = pl)) {
515 while ((subset = subset->next))
516 cmp_two_packs(pl, subset);
517 pl = pl->next;
518 }
519 }
520
521 static void scan_alt_odb_packs(void)
522 {
523 struct pack_list *local, *alt;
524
525 alt = altodb_packs;
526 while (alt) {
527 local = local_packs;
528 while (local) {
529 llist_sorted_difference_inplace(local->remaining_objects,
530 alt->remaining_objects);
531 local = local->next;
532 }
533 alt = alt->next;
534 }
535 }
536
537 static struct pack_list * add_pack(struct packed_git *p)
538 {
539 struct pack_list l;
540 size_t off = 0, step;
541 const unsigned char *base;
542
543 if (!p->pack_local && !(alt_odb || verbose))
544 return NULL;
545
546 l.pack = p;
547 llist_init(&l.remaining_objects);
548
549 if (open_pack_index(p)) {
550 llist_free(l.remaining_objects);
551 return NULL;
552 }
553
554 base = p->index_data;
555 base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
556 step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0);
557 while (off < p->num_objects * step) {
558 llist_insert_back(l.remaining_objects, base + off);
559 off += step;
560 }
561 l.all_objects_size = l.remaining_objects->size;
562 l.unique_objects = NULL;
563 if (p->pack_local)
564 return pack_list_insert(&local_packs, &l);
565 else
566 return pack_list_insert(&altodb_packs, &l);
567 }
568
569 static struct pack_list * add_pack_file(const char *filename)
570 {
571 struct packed_git *p;
572
573 if (strlen(filename) < 40)
574 die("Bad pack filename: %s", filename);
575
576 repo_for_each_pack(the_repository, p)
577 if (strstr(p->pack_name, filename))
578 return add_pack(p);
579 die("Filename %s not found in packed_git", filename);
580 }
581
582 static void load_all(void)
583 {
584 struct packed_git *p;
585
586 repo_for_each_pack(the_repository, p)
587 add_pack(p);
588 }
589
590 int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, struct repository *repo UNUSED) {
591 int i; int i_still_use_this = 0; struct pack_list *min = NULL, *red, *pl;
592 struct llist *ignore;
593 struct strbuf idx_name = STRBUF_INIT;
594 char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */
595
596 show_usage_if_asked(argc, argv, pack_redundant_usage);
597
598 for (i = 1; i < argc; i++) {
599 const char *arg = argv[i];
600 if (!strcmp(arg, "--")) {
601 i++;
602 break;
603 }
604 if (!strcmp(arg, "--all")) {
605 load_all_packs = 1;
606 continue;
607 }
608 if (!strcmp(arg, "--verbose")) {
609 verbose = 1;
610 continue;
611 }
612 if (!strcmp(arg, "--alt-odb")) {
613 alt_odb = 1;
614 continue;
615 }
616 if (!strcmp(arg, "--i-still-use-this")) {
617 i_still_use_this = 1;
618 continue;
619 }
620 if (*arg == '-')
621 usage(pack_redundant_usage);
622 else
623 break;
624 }
625
626 if (!i_still_use_this)
627 you_still_use_that("git pack-redundant", NULL);
628
629 if (load_all_packs)
630 load_all();
631 else
632 while (*(argv + i) != NULL)
633 add_pack_file(*(argv + i++));
634
635 if (!local_packs)
636 die("Zero packs found!");
637
638 load_all_objects();
639
640 if (alt_odb)
641 scan_alt_odb_packs();
642
643 /* ignore objects given on stdin */
644 llist_init(&ignore);
645 if (!isatty(0)) {
646 struct object_id oid;
647 while (fgets(buf, sizeof(buf), stdin)) {
648 if (get_oid_hex(buf, &oid))
649 die("Bad object ID on stdin: %s", buf);
650 llist_insert_sorted_unique(ignore, &oid, NULL);
651 }
652 }
653 llist_sorted_difference_inplace(all_objects, ignore);
654 pl = local_packs;
655 while (pl) {
656 llist_sorted_difference_inplace(pl->remaining_objects, ignore);
657 pl = pl->next;
658 }
659
660 cmp_local_packs();
661
662 minimize(&min);
663
664 if (verbose) {
665 fprintf(stderr, "There are %lu packs available in alt-odbs.\n",
666 (unsigned long)pack_list_size(altodb_packs));
667 fprintf(stderr, "The smallest (bytewise) set of packs is:\n");
668 pl = min;
669 while (pl) {
670 fprintf(stderr, "\t%s\n", pl->pack->pack_name);
671 pl = pl->next;
672 }
673 fprintf(stderr, "containing %lu duplicate objects "
674 "with a total size of %lukb.\n",
675 (unsigned long)get_pack_redundancy(min),
676 (unsigned long)pack_set_bytecount(min)/1024);
677 fprintf(stderr, "A total of %lu unique objects were considered.\n",
678 (unsigned long)all_objects->size);
679 fprintf(stderr, "Redundant packs (with indexes):\n");
680 }
681 pl = red = pack_list_difference(local_packs, min);
682 while (pl) {
683 printf("%s\n%s\n",
684 odb_pack_name(pl->pack->repo, &idx_name, pl->pack->hash, "idx"),
685 pl->pack->pack_name);
686 pl = pl->next;
687 }
688 if (verbose)
689 fprintf(stderr, "%luMB of redundant packs in total.\n",
690 (unsigned long)pack_set_bytecount(red)/(1024*1024));
691
692 pack_list_free(red);
693 pack_list_free(min);
694 llist_free(ignore);
695 strbuf_release(&idx_name);
696 return 0;
697 }