File Coverage

deps/libgit2/src/pack-objects.c
Criterion Covered Total %
statement 490 723 67.7
branch 251 500 50.2
condition n/a
subroutine n/a
pod n/a
total 741 1223 60.5


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "pack-objects.h"
9              
10             #include "zstream.h"
11             #include "delta.h"
12             #include "iterator.h"
13             #include "netops.h"
14             #include "pack.h"
15             #include "thread-utils.h"
16             #include "tree.h"
17             #include "util.h"
18             #include "revwalk.h"
19             #include "commit_list.h"
20              
21             #include "git2/pack.h"
22             #include "git2/commit.h"
23             #include "git2/tag.h"
24             #include "git2/indexer.h"
25             #include "git2/config.h"
26              
27             struct unpacked {
28             git_pobject *object;
29             void *data;
30             struct git_delta_index *index;
31             size_t depth;
32             };
33              
34             struct tree_walk_context {
35             git_packbuilder *pb;
36             git_buf buf;
37             };
38              
39             struct pack_write_context {
40             git_indexer *indexer;
41             git_indexer_progress *stats;
42             };
43              
44             struct walk_object {
45             git_oid id;
46             unsigned int uninteresting:1,
47             seen:1;
48             };
49              
50             #ifdef GIT_THREADS
51              
52             #define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \
53             int result = git_mutex_##op(&(pb)->mtx); \
54             assert(!result); \
55             GIT_UNUSED(result); \
56             } while (0)
57              
58             #else
59              
60             #define GIT_PACKBUILDER__MUTEX_OP(pb,mtx,op) GIT_UNUSED(pb)
61              
62             #endif /* GIT_THREADS */
63              
64             #define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock)
65             #define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock)
66             #define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
67             #define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
68              
69             /* The minimal interval between progress updates (in seconds). */
70             #define MIN_PROGRESS_UPDATE_INTERVAL 0.5
71              
72             /* Size of the buffer to feed to zlib */
73             #define COMPRESS_BUFLEN (1024 * 1024)
74              
75 53           static unsigned name_hash(const char *name)
76             {
77 53           unsigned c, hash = 0;
78              
79 53 100         if (!name)
80 34           return 0;
81              
82             /*
83             * This effectively just creates a sortable number from the
84             * last sixteen non-whitespace characters. Last characters
85             * count "most", so things that end in ".c" sort together.
86             */
87 127 100         while ((c = *name++) != 0) {
88 108 50         if (git__isspace(c))
89 0           continue;
90 108           hash = (hash >> 2) + (c << 24);
91             }
92 19           return hash;
93             }
94              
95 7           static int packbuilder_config(git_packbuilder *pb)
96             {
97             git_config *config;
98 7           int ret = 0;
99             int64_t val;
100              
101 7 50         if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0)
102 0           return ret;
103              
104             #define config_get(KEY,DST,DFLT) do { \
105             ret = git_config_get_int64(&val, config, KEY); \
106             if (!ret) { \
107             if (!git__is_sizet(val)) { \
108             git_error_set(GIT_ERROR_CONFIG, \
109             "configuration value '%s' is too large", KEY); \
110             ret = -1; \
111             goto out; \
112             } \
113             (DST) = (size_t)val; \
114             } else if (ret == GIT_ENOTFOUND) { \
115             (DST) = (DFLT); \
116             ret = 0; \
117             } else if (ret < 0) goto out; } while (0)
118              
119 7 50         config_get("pack.deltaCacheSize", pb->max_delta_cache_size,
    0          
    50          
    0          
120             GIT_PACK_DELTA_CACHE_SIZE);
121 7 50         config_get("pack.deltaCacheLimit", pb->cache_max_small_delta_size,
    0          
    50          
    0          
122             GIT_PACK_DELTA_CACHE_LIMIT);
123 7 50         config_get("pack.deltaCacheSize", pb->big_file_threshold,
    0          
    50          
    0          
124             GIT_PACK_BIG_FILE_THRESHOLD);
125 7 50         config_get("pack.windowMemory", pb->window_memory_limit, 0);
    0          
    50          
    0          
126              
127             #undef config_get
128              
129             out:
130 7           git_config_free(config);
131              
132 7           return ret;
133             }
134              
135 7           int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
136             {
137             git_packbuilder *pb;
138              
139 7           *out = NULL;
140              
141 7           pb = git__calloc(1, sizeof(*pb));
142 7 50         GIT_ERROR_CHECK_ALLOC(pb);
143              
144 7 50         if (git_oidmap_new(&pb->object_ix) < 0)
145 0           goto on_error;
146              
147 7 50         if (git_oidmap_new(&pb->walk_objects) < 0)
148 0           goto on_error;
149              
150 7           git_pool_init(&pb->object_pool, sizeof(struct walk_object));
151              
152 7           pb->repo = repo;
153 7           pb->nr_threads = 1; /* do not spawn any thread by default */
154              
155 14           if (git_hash_ctx_init(&pb->ctx) < 0 ||
156 14 50         git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 ||
157 14 50         git_repository_odb(&pb->odb, repo) < 0 ||
158 7           packbuilder_config(pb) < 0)
159             goto on_error;
160              
161             #ifdef GIT_THREADS
162              
163             if (git_mutex_init(&pb->cache_mutex) ||
164             git_mutex_init(&pb->progress_mutex) ||
165             git_cond_init(&pb->progress_cond))
166             {
167             git_error_set(GIT_ERROR_OS, "failed to initialize packbuilder mutex");
168             goto on_error;
169             }
170              
171             #endif
172              
173 7           *out = pb;
174 7           return 0;
175              
176             on_error:
177 0           git_packbuilder_free(pb);
178 0           return -1;
179             }
180              
181 3           unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n)
182             {
183 3 50         assert(pb);
184              
185             #ifdef GIT_THREADS
186             pb->nr_threads = n;
187             #else
188             GIT_UNUSED(n);
189 3 50         assert(1 == pb->nr_threads);
190             #endif
191              
192 3           return pb->nr_threads;
193             }
194              
195 6           static int rehash(git_packbuilder *pb)
196             {
197             git_pobject *po;
198             size_t i;
199              
200 6           git_oidmap_clear(pb->object_ix);
201              
202 6 50         for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) {
203 0 0         if (git_oidmap_set(pb->object_ix, &po->id, po) < 0)
204 0           return -1;
205             }
206              
207 6           return 0;
208             }
209              
210 64           int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
211             const char *name)
212             {
213             git_pobject *po;
214             size_t newsize;
215             int ret;
216              
217 64 50         assert(pb && oid);
    50          
218              
219             /* If the object already exists in the hash table, then we don't
220             * have any work to do */
221 64 100         if (git_oidmap_exists(pb->object_ix, oid))
222 11           return 0;
223              
224 53 100         if (pb->nr_objects >= pb->nr_alloc) {
225 6 50         GIT_ERROR_CHECK_ALLOC_ADD(&newsize, pb->nr_alloc, 1024);
    50          
226 6 50         GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newsize, newsize / 2, 3);
    50          
227              
228 6 50         if (!git__is_uint32(newsize)) {
229 0           git_error_set(GIT_ERROR_NOMEMORY, "packfile too large to fit in memory.");
230 0           return -1;
231             }
232              
233 6           pb->nr_alloc = newsize;
234              
235 6           pb->object_list = git__reallocarray(pb->object_list,
236             pb->nr_alloc, sizeof(*po));
237 6 50         GIT_ERROR_CHECK_ALLOC(pb->object_list);
238              
239 6 50         if (rehash(pb) < 0)
240 0           return -1;
241             }
242              
243 53           po = pb->object_list + pb->nr_objects;
244 53           memset(po, 0x0, sizeof(*po));
245              
246 53 50         if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0)
247 0           return ret;
248              
249 53           pb->nr_objects++;
250 53           git_oid_cpy(&po->id, oid);
251 53           po->hash = name_hash(name);
252              
253 53 50         if (git_oidmap_set(pb->object_ix, &po->id, po) < 0) {
254 0           git_error_set_oom();
255 0           return -1;
256             }
257              
258 53           pb->done = false;
259              
260 53 100         if (pb->progress_cb) {
261 32           double current_time = git__timer();
262 32           double elapsed = current_time - pb->last_progress_report_time;
263              
264 32 100         if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
265 2           pb->last_progress_report_time = current_time;
266              
267 2           ret = pb->progress_cb(
268             GIT_PACKBUILDER_ADDING_OBJECTS,
269             pb->nr_objects, 0, pb->progress_cb_payload);
270              
271 2 50         if (ret)
272 0           return git_error_set_after_callback(ret);
273             }
274             }
275              
276 64           return 0;
277             }
278              
279 0           static int get_delta(void **out, git_odb *odb, git_pobject *po)
280             {
281 0           git_odb_object *src = NULL, *trg = NULL;
282             size_t delta_size;
283             void *delta_buf;
284             int error;
285              
286 0           *out = NULL;
287              
288 0           if (git_odb_read(&src, odb, &po->delta->id) < 0 ||
289 0           git_odb_read(&trg, odb, &po->id) < 0)
290             goto on_error;
291              
292 0           error = git_delta(&delta_buf, &delta_size,
293             git_odb_object_data(src), git_odb_object_size(src),
294             git_odb_object_data(trg), git_odb_object_size(trg),
295             0);
296              
297 0 0         if (error < 0 && error != GIT_EBUFS)
    0          
298 0           goto on_error;
299              
300 0 0         if (error == GIT_EBUFS || delta_size != po->delta_size) {
    0          
301 0           git_error_set(GIT_ERROR_INVALID, "delta size changed");
302 0           goto on_error;
303             }
304              
305 0           *out = delta_buf;
306              
307 0           git_odb_object_free(src);
308 0           git_odb_object_free(trg);
309 0           return 0;
310              
311             on_error:
312 0           git_odb_object_free(src);
313 0           git_odb_object_free(trg);
314 0           return -1;
315             }
316              
317 53           static int write_object(
318             git_packbuilder *pb,
319             git_pobject *po,
320             int (*write_cb)(void *buf, size_t size, void *cb_data),
321             void *cb_data)
322             {
323 53           git_odb_object *obj = NULL;
324             git_object_t type;
325 53           unsigned char hdr[10], *zbuf = NULL;
326 53           void *data = NULL;
327 53           size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len;
328             int error;
329              
330             /*
331             * If we have a delta base, let's use the delta to save space.
332             * Otherwise load the whole object. 'data' ends up pointing to
333             * whatever data we want to put into the packfile.
334             */
335 53 100         if (po->delta) {
336 3 50         if (po->delta_data)
337 3           data = po->delta_data;
338 0 0         else if ((error = get_delta(&data, pb->odb, po)) < 0)
339 0           goto done;
340              
341 3           data_len = po->delta_size;
342 3           type = GIT_OBJECT_REF_DELTA;
343             } else {
344 50 50         if ((error = git_odb_read(&obj, pb->odb, &po->id)) < 0)
345 0           goto done;
346              
347 50           data = (void *)git_odb_object_data(obj);
348 50           data_len = git_odb_object_size(obj);
349 50           type = git_odb_object_type(obj);
350             }
351              
352             /* Write header */
353 53           hdr_len = git_packfile__object_header(hdr, data_len, type);
354              
355 53 50         if ((error = write_cb(hdr, hdr_len, cb_data)) < 0 ||
    50          
356 53           (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0)
357             goto done;
358              
359 53 100         if (type == GIT_OBJECT_REF_DELTA) {
360 3 50         if ((error = write_cb(po->delta->id.id, GIT_OID_RAWSZ, cb_data)) < 0 ||
    50          
361 3           (error = git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ)) < 0)
362             goto done;
363             }
364              
365             /* Write data */
366 53 100         if (po->z_delta_size) {
367 3           data_len = po->z_delta_size;
368              
369 3 50         if ((error = write_cb(data, data_len, cb_data)) < 0 ||
    50          
370 3           (error = git_hash_update(&pb->ctx, data, data_len)) < 0)
371             goto done;
372             } else {
373 50           zbuf = git__malloc(zbuf_len);
374 50 50         GIT_ERROR_CHECK_ALLOC(zbuf);
375              
376 50           git_zstream_reset(&pb->zstream);
377              
378 50 50         if ((error = git_zstream_set_input(&pb->zstream, data, data_len)) < 0)
379 0           goto done;
380              
381 100 100         while (!git_zstream_done(&pb->zstream)) {
382 50 50         if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 ||
    50          
383 50 50         (error = write_cb(zbuf, zbuf_len, cb_data)) < 0 ||
384 50           (error = git_hash_update(&pb->ctx, zbuf, zbuf_len)) < 0)
385             goto done;
386              
387 50           zbuf_len = COMPRESS_BUFLEN; /* reuse buffer */
388             }
389             }
390              
391             /*
392             * If po->delta is true, data is a delta and it is our
393             * responsibility to free it (otherwise it's a git_object's
394             * data). We set po->delta_data to NULL in case we got the
395             * data from there instead of get_delta(). If we didn't,
396             * there's no harm.
397             */
398 53 100         if (po->delta) {
399 3           git__free(data);
400 3           po->delta_data = NULL;
401             }
402              
403 53           pb->nr_written++;
404              
405             done:
406 53           git__free(zbuf);
407 53           git_odb_object_free(obj);
408 53           return error;
409             }
410              
411             enum write_one_status {
412             WRITE_ONE_SKIP = -1, /* already written */
413             WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
414             WRITE_ONE_WRITTEN = 1, /* normal */
415             WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
416             };
417              
418 56           static int write_one(
419             enum write_one_status *status,
420             git_packbuilder *pb,
421             git_pobject *po,
422             int (*write_cb)(void *buf, size_t size, void *cb_data),
423             void *cb_data)
424             {
425             int error;
426              
427 56 50         if (po->recursing) {
428 0           *status = WRITE_ONE_RECURSIVE;
429 0           return 0;
430 56 100         } else if (po->written) {
431 3           *status = WRITE_ONE_SKIP;
432 3           return 0;
433             }
434              
435 53 100         if (po->delta) {
436 3           po->recursing = 1;
437              
438 3 50         if ((error = write_one(status, pb, po->delta, write_cb, cb_data)) < 0)
439 0           return error;
440              
441             /* we cannot depend on this one */
442 3 50         if (*status == WRITE_ONE_RECURSIVE)
443 0           po->delta = NULL;
444             }
445              
446 53           *status = WRITE_ONE_WRITTEN;
447 53           po->written = 1;
448 53           po->recursing = 0;
449              
450 53           return write_object(pb, po, write_cb, cb_data);
451             }
452              
453 53           GIT_INLINE(void) add_to_write_order(git_pobject **wo, size_t *endp,
454             git_pobject *po)
455             {
456 53 50         if (po->filled)
457 0           return;
458 53           wo[(*endp)++] = po;
459 53           po->filled = 1;
460             }
461              
462 0           static void add_descendants_to_write_order(git_pobject **wo, size_t *endp,
463             git_pobject *po)
464             {
465 0           int add_to_order = 1;
466 0 0         while (po) {
467 0 0         if (add_to_order) {
468             git_pobject *s;
469             /* add this node... */
470 0           add_to_write_order(wo, endp, po);
471             /* all its siblings... */
472 0 0         for (s = po->delta_sibling; s; s = s->delta_sibling) {
473 0           add_to_write_order(wo, endp, s);
474             }
475             }
476             /* drop down a level to add left subtree nodes if possible */
477 0 0         if (po->delta_child) {
478 0           add_to_order = 1;
479 0           po = po->delta_child;
480             } else {
481 0           add_to_order = 0;
482             /* our sibling might have some children, it is next */
483 0 0         if (po->delta_sibling) {
484 0           po = po->delta_sibling;
485 0           continue;
486             }
487             /* go back to our parent node */
488 0           po = po->delta;
489 0 0         while (po && !po->delta_sibling) {
    0          
490             /* we're on the right side of a subtree, keep
491             * going up until we can go right again */
492 0           po = po->delta;
493             }
494 0 0         if (!po) {
495             /* done- we hit our original root node */
496 0           return;
497             }
498             /* pass it off to sibling at this level */
499 0           po = po->delta_sibling;
500             }
501             };
502             }
503              
504 0           static void add_family_to_write_order(git_pobject **wo, size_t *endp,
505             git_pobject *po)
506             {
507             git_pobject *root;
508              
509 0 0         for (root = po; root->delta; root = root->delta)
510             ; /* nothing */
511 0           add_descendants_to_write_order(wo, endp, root);
512 0           }
513              
514 0           static int cb_tag_foreach(const char *name, git_oid *oid, void *data)
515             {
516 0           git_packbuilder *pb = data;
517             git_pobject *po;
518              
519             GIT_UNUSED(name);
520              
521 0 0         if ((po = git_oidmap_get(pb->object_ix, oid)) == NULL)
522 0           return 0;
523              
524 0           po->tagged = 1;
525              
526             /* TODO: peel objects */
527              
528 0           return 0;
529             }
530              
531 7           static git_pobject **compute_write_order(git_packbuilder *pb)
532             {
533             size_t i, wo_end, last_untagged;
534             git_pobject **wo;
535              
536 7 50         if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL)
537 0           return NULL;
538              
539 60 100         for (i = 0; i < pb->nr_objects; i++) {
540 53           git_pobject *po = pb->object_list + i;
541 53           po->tagged = 0;
542 53           po->filled = 0;
543 53           po->delta_child = NULL;
544 53           po->delta_sibling = NULL;
545             }
546              
547             /*
548             * Fully connect delta_child/delta_sibling network.
549             * Make sure delta_sibling is sorted in the original
550             * recency order.
551             */
552 60 100         for (i = pb->nr_objects; i > 0;) {
553 53           git_pobject *po = &pb->object_list[--i];
554 53 100         if (!po->delta)
555 50           continue;
556             /* Mark me as the first child */
557 3           po->delta_sibling = po->delta->delta_child;
558 3           po->delta->delta_child = po;
559             }
560              
561             /*
562             * Mark objects that are at the tip of tags.
563             */
564 7 50         if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
565 0           git__free(wo);
566 0           return NULL;
567             }
568              
569             /*
570             * Give the objects in the original recency order until
571             * we see a tagged tip.
572             */
573 60 100         for (i = wo_end = 0; i < pb->nr_objects; i++) {
574 53           git_pobject *po = pb->object_list + i;
575 53 50         if (po->tagged)
576 0           break;
577 53           add_to_write_order(wo, &wo_end, po);
578             }
579 7           last_untagged = i;
580              
581             /*
582             * Then fill all the tagged tips.
583             */
584 7 50         for (; i < pb->nr_objects; i++) {
585 0           git_pobject *po = pb->object_list + i;
586 0 0         if (po->tagged)
587 0           add_to_write_order(wo, &wo_end, po);
588             }
589              
590             /*
591             * And then all remaining commits and tags.
592             */
593 7 50         for (i = last_untagged; i < pb->nr_objects; i++) {
594 0           git_pobject *po = pb->object_list + i;
595 0 0         if (po->type != GIT_OBJECT_COMMIT &&
    0          
596 0           po->type != GIT_OBJECT_TAG)
597 0           continue;
598 0           add_to_write_order(wo, &wo_end, po);
599             }
600              
601             /*
602             * And then all the trees.
603             */
604 7 50         for (i = last_untagged; i < pb->nr_objects; i++) {
605 0           git_pobject *po = pb->object_list + i;
606 0 0         if (po->type != GIT_OBJECT_TREE)
607 0           continue;
608 0           add_to_write_order(wo, &wo_end, po);
609             }
610              
611             /*
612             * Finally all the rest in really tight order
613             */
614 7 50         for (i = last_untagged; i < pb->nr_objects; i++) {
615 0           git_pobject *po = pb->object_list + i;
616 0 0         if (!po->filled)
617 0           add_family_to_write_order(wo, &wo_end, po);
618             }
619              
620 7 50         if (wo_end != pb->nr_objects) {
621 0           git__free(wo);
622 0           git_error_set(GIT_ERROR_INVALID, "invalid write order");
623 0           return NULL;
624             }
625              
626 7           return wo;
627             }
628              
629 7           static int write_pack(git_packbuilder *pb,
630             int (*write_cb)(void *buf, size_t size, void *cb_data),
631             void *cb_data)
632             {
633             git_pobject **write_order;
634             git_pobject *po;
635             enum write_one_status status;
636             struct git_pack_header ph;
637             git_oid entry_oid;
638 7           size_t i = 0;
639 7           int error = 0;
640              
641 7           write_order = compute_write_order(pb);
642 7 50         if (write_order == NULL)
643 0           return -1;
644              
645 7 50         if (!git__is_uint32(pb->nr_objects)) {
646 0           git_error_set(GIT_ERROR_INVALID, "too many objects");
647 0           return -1;
648             }
649              
650             /* Write pack header */
651 7           ph.hdr_signature = htonl(PACK_SIGNATURE);
652 7           ph.hdr_version = htonl(PACK_VERSION);
653 7           ph.hdr_entries = htonl(pb->nr_objects);
654              
655 7 50         if ((error = write_cb(&ph, sizeof(ph), cb_data)) < 0 ||
    50          
656 7           (error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
657             goto done;
658              
659 7           pb->nr_remaining = pb->nr_objects;
660             do {
661 7           pb->nr_written = 0;
662 60 100         for ( ; i < pb->nr_objects; ++i) {
663 53           po = write_order[i];
664              
665 53 50         if ((error = write_one(&status, pb, po, write_cb, cb_data)) < 0)
666 0           goto done;
667             }
668              
669 7           pb->nr_remaining -= pb->nr_written;
670 7 50         } while (pb->nr_remaining && i < pb->nr_objects);
    0          
671              
672 7 50         if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0)
673 0           goto done;
674              
675 7           error = write_cb(entry_oid.id, GIT_OID_RAWSZ, cb_data);
676              
677             done:
678             /* if callback cancelled writing, we must still free delta_data */
679 7 50         for ( ; i < pb->nr_objects; ++i) {
680 0           po = write_order[i];
681 0 0         if (po->delta_data) {
682 0           git__free(po->delta_data);
683 0           po->delta_data = NULL;
684             }
685             }
686              
687 7           git__free(write_order);
688 7           return error;
689             }
690              
691 33           static int write_pack_buf(void *buf, size_t size, void *data)
692             {
693 33           git_buf *b = (git_buf *)data;
694 33           return git_buf_put(b, buf, size);
695             }
696              
697 43           static int type_size_sort(const void *_a, const void *_b)
698             {
699 43           const git_pobject *a = (git_pobject *)_a;
700 43           const git_pobject *b = (git_pobject *)_b;
701              
702 43 100         if (a->type > b->type)
703 9           return -1;
704 34 100         if (a->type < b->type)
705 17           return 1;
706 17 50         if (a->hash > b->hash)
707 0           return -1;
708 17 50         if (a->hash < b->hash)
709 0           return 1;
710             /*
711             * TODO
712             *
713             if (a->preferred_base > b->preferred_base)
714             return -1;
715             if (a->preferred_base < b->preferred_base)
716             return 1;
717             */
718 17 100         if (a->size > b->size)
719 6           return -1;
720 11 100         if (a->size < b->size)
721 6           return 1;
722 5 100         return a < b ? -1 : (a > b); /* newest first */
723             }
724              
725 3           static int delta_cacheable(
726             git_packbuilder *pb,
727             size_t src_size,
728             size_t trg_size,
729             size_t delta_size)
730             {
731             size_t new_size;
732              
733 3 50         if (git__add_sizet_overflow(&new_size, pb->delta_cache_size, delta_size))
734 0           return 0;
735              
736 3 50         if (pb->max_delta_cache_size && new_size > pb->max_delta_cache_size)
    50          
737 0           return 0;
738              
739 3 50         if (delta_size < pb->cache_max_small_delta_size)
740 3           return 1;
741              
742             /* cache delta, if objects are large enough compared to delta size */
743 0 0         if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
744 0           return 1;
745              
746 3           return 0;
747             }
748              
749 33           static int try_delta(git_packbuilder *pb, struct unpacked *trg,
750             struct unpacked *src, size_t max_depth,
751             size_t *mem_usage, int *ret)
752             {
753 33           git_pobject *trg_object = trg->object;
754 33           git_pobject *src_object = src->object;
755             git_odb_object *obj;
756             size_t trg_size, src_size, delta_size, sizediff, max_size, sz;
757             size_t ref_depth;
758             void *delta_buf;
759              
760             /* Don't bother doing diffs between different types */
761 33 100         if (trg_object->type != src_object->type) {
762 13           *ret = -1;
763 13           return 0;
764             }
765              
766 20           *ret = 0;
767              
768             /* TODO: support reuse-delta */
769              
770             /* Let's not bust the allowed depth. */
771 20 50         if (src->depth >= max_depth)
772 0           return 0;
773              
774             /* Now some size filtering heuristics. */
775 20           trg_size = trg_object->size;
776 20 50         if (!trg_object->delta) {
777 20           max_size = trg_size/2 - 20;
778 20           ref_depth = 1;
779             } else {
780 0           max_size = trg_object->delta_size;
781 0           ref_depth = trg->depth;
782             }
783              
784 40           max_size = (uint64_t)max_size * (max_depth - src->depth) /
785 20           (max_depth - ref_depth + 1);
786 20 50         if (max_size == 0)
787 0           return 0;
788              
789 20           src_size = src_object->size;
790 20 50         sizediff = src_size < trg_size ? trg_size - src_size : 0;
791 20 50         if (sizediff >= max_size)
792 0           return 0;
793 20 50         if (trg_size < src_size / 32)
794 0           return 0;
795              
796             /* Load data if not already done */
797 20 100         if (!trg->data) {
798 12 50         if (git_odb_read(&obj, pb->odb, &trg_object->id) < 0)
799 0           return -1;
800              
801 12           sz = git_odb_object_size(obj);
802 12           trg->data = git__malloc(sz);
803 12 50         GIT_ERROR_CHECK_ALLOC(trg->data);
804 12           memcpy(trg->data, git_odb_object_data(obj), sz);
805              
806 12           git_odb_object_free(obj);
807              
808 12 50         if (sz != trg_size) {
809 0           git_error_set(GIT_ERROR_INVALID,
810             "inconsistent target object length");
811 0           return -1;
812             }
813              
814 12           *mem_usage += sz;
815             }
816 20 100         if (!src->data) {
817             size_t obj_sz;
818              
819 12 50         if (git_odb_read(&obj, pb->odb, &src_object->id) < 0 ||
    50          
820 6           !git__is_ulong(obj_sz = git_odb_object_size(obj)))
821 0           return -1;
822              
823 6           sz = obj_sz;
824 6           src->data = git__malloc(sz);
825 6 50         GIT_ERROR_CHECK_ALLOC(src->data);
826 6           memcpy(src->data, git_odb_object_data(obj), sz);
827              
828 6           git_odb_object_free(obj);
829              
830 6 50         if (sz != src_size) {
831 0           git_error_set(GIT_ERROR_INVALID,
832             "inconsistent source object length");
833 0           return -1;
834             }
835              
836 6           *mem_usage += sz;
837             }
838 20 100         if (!src->index) {
839 12 50         if (git_delta_index_init(&src->index, src->data, src_size) < 0)
840 0           return 0; /* suboptimal pack - out of memory */
841              
842 12           *mem_usage += git_delta_index_size(src->index);
843             }
844              
845 20 100         if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size,
846             max_size) < 0)
847 17           return 0;
848              
849 3 50         if (trg_object->delta) {
850             /* Prefer only shallower same-sized deltas. */
851 0 0         if (delta_size == trg_object->delta_size &&
    0          
852 0           src->depth + 1 >= trg->depth) {
853 0           git__free(delta_buf);
854 0           return 0;
855             }
856             }
857              
858             git_packbuilder__cache_lock(pb);
859 3 50         if (trg_object->delta_data) {
860 0           git__free(trg_object->delta_data);
861 0 0         assert(pb->delta_cache_size >= trg_object->delta_size);
862 0           pb->delta_cache_size -= trg_object->delta_size;
863 0           trg_object->delta_data = NULL;
864             }
865 3 50         if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
866 3           bool overflow = git__add_sizet_overflow(
867             &pb->delta_cache_size, pb->delta_cache_size, delta_size);
868              
869             git_packbuilder__cache_unlock(pb);
870              
871 3 50         if (overflow) {
872 0           git__free(delta_buf);
873 0           return -1;
874             }
875              
876 3           trg_object->delta_data = git__realloc(delta_buf, delta_size);
877 3 50         GIT_ERROR_CHECK_ALLOC(trg_object->delta_data);
878             } else {
879             /* create delta when writing the pack */
880             git_packbuilder__cache_unlock(pb);
881 0           git__free(delta_buf);
882             }
883              
884 3           trg_object->delta = src_object;
885 3           trg_object->delta_size = delta_size;
886 3           trg->depth = src->depth + 1;
887              
888 3           *ret = 1;
889 33           return 0;
890             }
891              
892 0           static size_t check_delta_limit(git_pobject *me, size_t n)
893             {
894 0           git_pobject *child = me->delta_child;
895 0           size_t m = n;
896              
897 0 0         while (child) {
898 0           size_t c = check_delta_limit(child, n + 1);
899 0 0         if (m < c)
900 0           m = c;
901 0           child = child->delta_sibling;
902             }
903 0           return m;
904             }
905              
906 24           static size_t free_unpacked(struct unpacked *n)
907             {
908 24           size_t freed_mem = 0;
909              
910 24 50         if (n->index) {
911 0           freed_mem += git_delta_index_size(n->index);
912 0           git_delta_index_free(n->index);
913             }
914 24           n->index = NULL;
915              
916 24 50         if (n->data) {
917 0           freed_mem += n->object->size;
918 0           git__free(n->data);
919 0           n->data = NULL;
920             }
921 24           n->object = NULL;
922 24           n->depth = 0;
923 24           return freed_mem;
924             }
925              
926 30           static int report_delta_progress(
927             git_packbuilder *pb, uint32_t count, bool force)
928             {
929             int ret;
930              
931 30 100         if (pb->progress_cb) {
932 19           double current_time = git__timer();
933 19           double elapsed = current_time - pb->last_progress_report_time;
934              
935 19 100         if (force || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
    100          
936 4           pb->last_progress_report_time = current_time;
937              
938 4           ret = pb->progress_cb(
939             GIT_PACKBUILDER_DELTAFICATION,
940             count, pb->nr_objects, pb->progress_cb_payload);
941              
942 4 50         if (ret)
943 0           return git_error_set_after_callback(ret);
944             }
945             }
946              
947 30           return 0;
948             }
949              
950 6           static int find_deltas(git_packbuilder *pb, git_pobject **list,
951             size_t *list_size, size_t window, size_t depth)
952             {
953             git_pobject *po;
954 6           git_buf zbuf = GIT_BUF_INIT;
955             struct unpacked *array;
956 6           size_t idx = 0, count = 0;
957 6           size_t mem_usage = 0;
958             size_t i;
959 6           int error = -1;
960              
961 6           array = git__calloc(window, sizeof(struct unpacked));
962 6 50         GIT_ERROR_CHECK_ALLOC(array);
963              
964             for (;;) {
965 30           struct unpacked *n = array + idx;
966 30           size_t max_depth, j, best_base = SIZE_MAX;
967              
968             git_packbuilder__progress_lock(pb);
969 30 100         if (!*list_size) {
970             git_packbuilder__progress_unlock(pb);
971 6           break;
972             }
973              
974 24           pb->nr_deltified += 1;
975 24           report_delta_progress(pb, pb->nr_deltified, false);
976              
977 24           po = *list++;
978 24           (*list_size)--;
979             git_packbuilder__progress_unlock(pb);
980              
981 24           mem_usage -= free_unpacked(n);
982 24           n->object = po;
983              
984 24 50         while (pb->window_memory_limit &&
    0          
985 0 0         mem_usage > pb->window_memory_limit &&
986             count > 1) {
987 0           size_t tail = (idx + window - count) % window;
988 0           mem_usage -= free_unpacked(array + tail);
989 0           count--;
990             }
991              
992             /*
993             * If the current object is at pack edge, take the depth the
994             * objects that depend on the current object into account
995             * otherwise they would become too deep.
996             */
997 24           max_depth = depth;
998 24 50         if (po->delta_child) {
999 0           size_t delta_limit = check_delta_limit(po, 0);
1000              
1001 0 0         if (delta_limit > max_depth)
1002 0           goto next;
1003              
1004 0           max_depth -= delta_limit;
1005             }
1006              
1007 24           j = window;
1008 44 50         while (--j > 0) {
1009             int ret;
1010 44           size_t other_idx = idx + j;
1011             struct unpacked *m;
1012              
1013 44 100         if (other_idx >= window)
1014 33           other_idx -= window;
1015              
1016 44           m = array + other_idx;
1017 44 100         if (!m->object)
1018 24           break;
1019              
1020 33 50         if (try_delta(pb, n, m, max_depth, &mem_usage, &ret) < 0)
1021 0           goto on_error;
1022 33 100         if (ret < 0)
1023 13           break;
1024 20 100         else if (ret > 0)
1025 20           best_base = other_idx;
1026             }
1027              
1028             /*
1029             * If we decided to cache the delta data, then it is best
1030             * to compress it right away. First because we have to do
1031             * it anyway, and doing it here while we're threaded will
1032             * save a lot of time in the non threaded write phase,
1033             * as well as allow for caching more deltas within
1034             * the same cache size limit.
1035             * ...
1036             * But only if not writing to stdout, since in that case
1037             * the network is most likely throttling writes anyway,
1038             * and therefore it is best to go to the write phase ASAP
1039             * instead, as we can afford spending more time compressing
1040             * between writes at that moment.
1041             */
1042 24 100         if (po->delta_data) {
1043 3 50         if (git_zstream_deflatebuf(&zbuf, po->delta_data, po->delta_size) < 0)
1044 0           goto on_error;
1045              
1046 3           git__free(po->delta_data);
1047 3           po->delta_data = git__malloc(zbuf.size);
1048 3 50         GIT_ERROR_CHECK_ALLOC(po->delta_data);
1049              
1050 3           memcpy(po->delta_data, zbuf.ptr, zbuf.size);
1051 3           po->z_delta_size = zbuf.size;
1052 3           git_buf_clear(&zbuf);
1053              
1054             git_packbuilder__cache_lock(pb);
1055 3           pb->delta_cache_size -= po->delta_size;
1056 3           pb->delta_cache_size += po->z_delta_size;
1057             git_packbuilder__cache_unlock(pb);
1058             }
1059              
1060             /*
1061             * If we made n a delta, and if n is already at max
1062             * depth, leaving it in the window is pointless. we
1063             * should evict it first.
1064             */
1065 24 100         if (po->delta && max_depth <= n->depth)
    50          
1066 0           continue;
1067              
1068             /*
1069             * Move the best delta base up in the window, after the
1070             * currently deltified object, to keep it longer. It will
1071             * be the first base object to be attempted next.
1072             */
1073 24 100         if (po->delta) {
1074 3           struct unpacked swap = array[best_base];
1075 3           size_t dist = (window + idx - best_base) % window;
1076 3           size_t dst = best_base;
1077 8 100         while (dist--) {
1078 5           size_t src = (dst + 1) % window;
1079 5           array[dst] = array[src];
1080 5           dst = src;
1081             }
1082 3           array[dst] = swap;
1083             }
1084              
1085             next:
1086 24           idx++;
1087 24 50         if (count + 1 < window)
1088 24           count++;
1089 24 50         if (idx >= window)
1090 0           idx = 0;
1091 24           }
1092 6           error = 0;
1093              
1094             on_error:
1095 72 100         for (i = 0; i < window; ++i) {
1096 66           git__free(array[i].index);
1097 66           git__free(array[i].data);
1098             }
1099 6           git__free(array);
1100 6           git_buf_dispose(&zbuf);
1101              
1102 6           return error;
1103             }
1104              
1105             #ifdef GIT_THREADS
1106              
1107             struct thread_params {
1108             git_thread thread;
1109             git_packbuilder *pb;
1110              
1111             git_pobject **list;
1112              
1113             git_cond cond;
1114             git_mutex mutex;
1115              
1116             size_t list_size;
1117             size_t remaining;
1118              
1119             size_t window;
1120             size_t depth;
1121             size_t working;
1122             size_t data_ready;
1123             };
1124              
1125             static void *threaded_find_deltas(void *arg)
1126             {
1127             struct thread_params *me = arg;
1128              
1129             while (me->remaining) {
1130             if (find_deltas(me->pb, me->list, &me->remaining,
1131             me->window, me->depth) < 0) {
1132             ; /* TODO */
1133             }
1134              
1135             git_packbuilder__progress_lock(me->pb);
1136             me->working = 0;
1137             git_cond_signal(&me->pb->progress_cond);
1138             git_packbuilder__progress_unlock(me->pb);
1139              
1140             if (git_mutex_lock(&me->mutex)) {
1141             git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
1142             return NULL;
1143             }
1144              
1145             while (!me->data_ready)
1146             git_cond_wait(&me->cond, &me->mutex);
1147              
1148             /*
1149             * We must not set ->data_ready before we wait on the
1150             * condition because the main thread may have set it to 1
1151             * before we get here. In order to be sure that new
1152             * work is available if we see 1 in ->data_ready, it
1153             * was initialized to 0 before this thread was spawned
1154             * and we reset it to 0 right away.
1155             */
1156             me->data_ready = 0;
1157             git_mutex_unlock(&me->mutex);
1158             }
1159             /* leave ->working 1 so that this doesn't get more work assigned */
1160             return NULL;
1161             }
1162              
1163             static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
1164             size_t list_size, size_t window, size_t depth)
1165             {
1166             struct thread_params *p;
1167             size_t i;
1168             int ret, active_threads = 0;
1169              
1170             if (!pb->nr_threads)
1171             pb->nr_threads = git_online_cpus();
1172              
1173             if (pb->nr_threads <= 1) {
1174             find_deltas(pb, list, &list_size, window, depth);
1175             return 0;
1176             }
1177              
1178             p = git__mallocarray(pb->nr_threads, sizeof(*p));
1179             GIT_ERROR_CHECK_ALLOC(p);
1180              
1181             /* Partition the work among the threads */
1182             for (i = 0; i < pb->nr_threads; ++i) {
1183             size_t sub_size = list_size / (pb->nr_threads - i);
1184              
1185             /* don't use too small segments or no deltas will be found */
1186             if (sub_size < 2*window && i+1 < pb->nr_threads)
1187             sub_size = 0;
1188              
1189             p[i].pb = pb;
1190             p[i].window = window;
1191             p[i].depth = depth;
1192             p[i].working = 1;
1193             p[i].data_ready = 0;
1194              
1195             /* try to split chunks on "path" boundaries */
1196             while (sub_size && sub_size < list_size &&
1197             list[sub_size]->hash &&
1198             list[sub_size]->hash == list[sub_size-1]->hash)
1199             sub_size++;
1200              
1201             p[i].list = list;
1202             p[i].list_size = sub_size;
1203             p[i].remaining = sub_size;
1204              
1205             list += sub_size;
1206             list_size -= sub_size;
1207             }
1208              
1209             /* Start work threads */
1210             for (i = 0; i < pb->nr_threads; ++i) {
1211             if (!p[i].list_size)
1212             continue;
1213              
1214             git_mutex_init(&p[i].mutex);
1215             git_cond_init(&p[i].cond);
1216              
1217             ret = git_thread_create(&p[i].thread,
1218             threaded_find_deltas, &p[i]);
1219             if (ret) {
1220             git_error_set(GIT_ERROR_THREAD, "unable to create thread");
1221             return -1;
1222             }
1223             active_threads++;
1224             }
1225              
1226             /*
1227             * Now let's wait for work completion. Each time a thread is done
1228             * with its work, we steal half of the remaining work from the
1229             * thread with the largest number of unprocessed objects and give
1230             * it to that newly idle thread. This ensure good load balancing
1231             * until the remaining object list segments are simply too short
1232             * to be worth splitting anymore.
1233             */
1234             while (active_threads) {
1235             struct thread_params *target = NULL;
1236             struct thread_params *victim = NULL;
1237             size_t sub_size = 0;
1238              
1239             /* Start by locating a thread that has transitioned its
1240             * 'working' flag from 1 -> 0. This indicates that it is
1241             * ready to receive more work using our work-stealing
1242             * algorithm. */
1243             git_packbuilder__progress_lock(pb);
1244             for (;;) {
1245             for (i = 0; !target && i < pb->nr_threads; i++)
1246             if (!p[i].working)
1247             target = &p[i];
1248             if (target)
1249             break;
1250             git_cond_wait(&pb->progress_cond, &pb->progress_mutex);
1251             }
1252              
1253             /* At this point we hold the progress lock and have located
1254             * a thread to receive more work. We still need to locate a
1255             * thread from which to steal work (the victim). */
1256             for (i = 0; i < pb->nr_threads; i++)
1257             if (p[i].remaining > 2*window &&
1258             (!victim || victim->remaining < p[i].remaining))
1259             victim = &p[i];
1260              
1261             if (victim) {
1262             sub_size = victim->remaining / 2;
1263             list = victim->list + victim->list_size - sub_size;
1264             while (sub_size && list[0]->hash &&
1265             list[0]->hash == list[-1]->hash) {
1266             list++;
1267             sub_size--;
1268             }
1269             if (!sub_size) {
1270             /*
1271             * It is possible for some "paths" to have
1272             * so many objects that no hash boundary
1273             * might be found. Let's just steal the
1274             * exact half in that case.
1275             */
1276             sub_size = victim->remaining / 2;
1277             list -= sub_size;
1278             }
1279             target->list = list;
1280             victim->list_size -= sub_size;
1281             victim->remaining -= sub_size;
1282             }
1283             target->list_size = sub_size;
1284             target->remaining = sub_size;
1285             target->working = 1;
1286             git_packbuilder__progress_unlock(pb);
1287              
1288             if (git_mutex_lock(&target->mutex)) {
1289             git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
1290             git__free(p);
1291             return -1;
1292             }
1293              
1294             target->data_ready = 1;
1295             git_cond_signal(&target->cond);
1296             git_mutex_unlock(&target->mutex);
1297              
1298             if (!sub_size) {
1299             git_thread_join(&target->thread, NULL);
1300             git_cond_free(&target->cond);
1301             git_mutex_free(&target->mutex);
1302             active_threads--;
1303             }
1304             }
1305              
1306             git__free(p);
1307             return 0;
1308             }
1309              
1310             #else
1311             #define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d)
1312             #endif
1313              
1314 11           static int prepare_pack(git_packbuilder *pb)
1315             {
1316             git_pobject **delta_list;
1317 11           size_t i, n = 0;
1318              
1319 11 100         if (pb->nr_objects == 0 || pb->done)
    100          
1320 5           return 0; /* nothing to do */
1321              
1322             /*
1323             * Although we do not report progress during deltafication, we
1324             * at least report that we are in the deltafication stage
1325             */
1326 6 100         if (pb->progress_cb)
1327 3           pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
1328              
1329 6           delta_list = git__mallocarray(pb->nr_objects, sizeof(*delta_list));
1330 6 50         GIT_ERROR_CHECK_ALLOC(delta_list);
1331              
1332 59 100         for (i = 0; i < pb->nr_objects; ++i) {
1333 53           git_pobject *po = pb->object_list + i;
1334              
1335             /* Make sure the item is within our size limits */
1336 53 100         if (po->size < 50 || po->size > pb->big_file_threshold)
    50          
1337 29           continue;
1338              
1339 24           delta_list[n++] = po;
1340             }
1341              
1342 6 50         if (n > 1) {
1343 6           git__tsort((void **)delta_list, n, type_size_sort);
1344 6 50         if (ll_find_deltas(pb, delta_list, n,
1345             GIT_PACK_WINDOW + 1,
1346             GIT_PACK_DEPTH) < 0) {
1347 0           git__free(delta_list);
1348 0           return -1;
1349             }
1350             }
1351              
1352 6           report_delta_progress(pb, pb->nr_objects, true);
1353              
1354 6           pb->done = true;
1355 6           git__free(delta_list);
1356 11           return 0;
1357             }
1358              
1359             #define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; }
1360              
1361 4           int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t size, void *payload), void *payload)
1362             {
1363 4 50         PREPARE_PACK;
1364 4           return write_pack(pb, cb, payload);
1365             }
1366              
1367 3           int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
1368             {
1369 3 50         PREPARE_PACK;
1370 3           git_buf_sanitize(buf);
1371 3           return write_pack(pb, &write_pack_buf, buf);
1372             }
1373              
1374 90           static int write_cb(void *buf, size_t len, void *payload)
1375             {
1376 90           struct pack_write_context *ctx = payload;
1377 90           return git_indexer_append(ctx->indexer, buf, len, ctx->stats);
1378             }
1379              
1380 4           int git_packbuilder_write(
1381             git_packbuilder *pb,
1382             const char *path,
1383             unsigned int mode,
1384             git_indexer_progress_cb progress_cb,
1385             void *progress_cb_payload)
1386             {
1387 4           git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
1388             git_indexer *indexer;
1389             git_indexer_progress stats;
1390             struct pack_write_context ctx;
1391             int t;
1392              
1393 4 50         PREPARE_PACK;
1394              
1395 4           opts.progress_cb = progress_cb;
1396 4           opts.progress_cb_payload = progress_cb_payload;
1397              
1398 4 50         if (git_indexer_new(
1399             &indexer, path, mode, pb->odb, &opts) < 0)
1400 0           return -1;
1401              
1402 4 50         if (!git_repository__configmap_lookup(&t, pb->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t)
    50          
1403 0           git_indexer__set_fsync(indexer, 1);
1404              
1405 4           ctx.indexer = indexer;
1406 4           ctx.stats = &stats;
1407              
1408 8           if (git_packbuilder_foreach(pb, write_cb, &ctx) < 0 ||
1409 4           git_indexer_commit(indexer, &stats) < 0) {
1410 0           git_indexer_free(indexer);
1411 0           return -1;
1412             }
1413              
1414 4           git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
1415              
1416 4           git_indexer_free(indexer);
1417 4           return 0;
1418             }
1419              
1420             #undef PREPARE_PACK
1421              
1422 2           const git_oid *git_packbuilder_hash(git_packbuilder *pb)
1423             {
1424 2           return &pb->pack_oid;
1425             }
1426              
1427              
1428 11           static int cb_tree_walk(
1429             const char *root, const git_tree_entry *entry, void *payload)
1430             {
1431             int error;
1432 11           struct tree_walk_context *ctx = payload;
1433              
1434             /* A commit inside a tree represents a submodule commit and should be skipped. */
1435 11 50         if (git_tree_entry_type(entry) == GIT_OBJECT_COMMIT)
1436 0           return 0;
1437              
1438 22 50         if (!(error = git_buf_sets(&ctx->buf, root)) &&
    50          
1439 11           !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry))))
1440 11           error = git_packbuilder_insert(
1441 11           ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf));
1442              
1443 11           return error;
1444             }
1445              
1446 4           int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid)
1447             {
1448             git_commit *commit;
1449              
1450 8           if (git_commit_lookup(&commit, pb->repo, oid) < 0 ||
1451 4           git_packbuilder_insert(pb, oid, NULL) < 0)
1452 0           return -1;
1453              
1454 4 50         if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0)
1455 0           return -1;
1456              
1457 4           git_commit_free(commit);
1458 4           return 0;
1459             }
1460              
1461 4           int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid)
1462             {
1463             int error;
1464 4           git_tree *tree = NULL;
1465 4           struct tree_walk_context context = { pb, GIT_BUF_INIT };
1466              
1467 4 50         if (!(error = git_tree_lookup(&tree, pb->repo, oid)) &&
    50          
1468             !(error = git_packbuilder_insert(pb, oid, NULL)))
1469 4           error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context);
1470              
1471 4           git_tree_free(tree);
1472 4           git_buf_dispose(&context.buf);
1473 4           return error;
1474             }
1475              
1476 1           int git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name)
1477             {
1478             git_object *obj;
1479             int error;
1480              
1481 1 50         assert(pb && id);
    50          
1482              
1483 1 50         if ((error = git_object_lookup(&obj, pb->repo, id, GIT_OBJECT_ANY)) < 0)
1484 0           return error;
1485              
1486 1           switch (git_object_type(obj)) {
1487             case GIT_OBJECT_BLOB:
1488 0           error = git_packbuilder_insert(pb, id, name);
1489 0           break;
1490             case GIT_OBJECT_TREE:
1491 0           error = git_packbuilder_insert_tree(pb, id);
1492 0           break;
1493             case GIT_OBJECT_COMMIT:
1494 1           error = git_packbuilder_insert_commit(pb, id);
1495 1           break;
1496             case GIT_OBJECT_TAG:
1497 0 0         if ((error = git_packbuilder_insert(pb, id, name)) < 0)
1498 0           goto cleanup;
1499 0           error = git_packbuilder_insert_recur(pb, git_tag_target_id((git_tag *) obj), NULL);
1500 0           break;
1501              
1502             default:
1503 0           git_error_set(GIT_ERROR_INVALID, "unknown object type");
1504 0           error = -1;
1505             }
1506              
1507             cleanup:
1508 1           git_object_free(obj);
1509 1           return error;
1510             }
1511              
1512 5           size_t git_packbuilder_object_count(git_packbuilder *pb)
1513             {
1514 5           return pb->nr_objects;
1515             }
1516              
1517 4           size_t git_packbuilder_written(git_packbuilder *pb)
1518             {
1519 4           return pb->nr_written;
1520             }
1521              
1522 36           static int lookup_walk_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id)
1523             {
1524             struct walk_object *obj;
1525              
1526 36           obj = git_pool_mallocz(&pb->object_pool, 1);
1527 36 50         if (!obj) {
1528 0           git_error_set_oom();
1529 0           return -1;
1530             }
1531              
1532 36           git_oid_cpy(&obj->id, id);
1533              
1534 36           *out = obj;
1535 36           return 0;
1536             }
1537              
1538 44           static int retrieve_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id)
1539             {
1540             struct walk_object *obj;
1541             int error;
1542              
1543 44 100         if ((obj = git_oidmap_get(pb->walk_objects, id)) == NULL) {
1544 36 50         if ((error = lookup_walk_object(&obj, pb, id)) < 0)
1545 0           return error;
1546              
1547 36 50         if ((error = git_oidmap_set(pb->walk_objects, &obj->id, obj)) < 0)
1548 0           return error;
1549             }
1550              
1551 44           *out = obj;
1552 44           return 0;
1553             }
1554              
1555 0           static int mark_blob_uninteresting(git_packbuilder *pb, const git_oid *id)
1556             {
1557             int error;
1558             struct walk_object *obj;
1559              
1560 0 0         if ((error = retrieve_object(&obj, pb, id)) < 0)
1561 0           return error;
1562              
1563 0           obj->uninteresting = 1;
1564              
1565 0           return 0;
1566             }
1567              
1568 0           static int mark_tree_uninteresting(git_packbuilder *pb, const git_oid *id)
1569             {
1570             struct walk_object *obj;
1571             git_tree *tree;
1572             int error;
1573             size_t i;
1574              
1575 0 0         if ((error = retrieve_object(&obj, pb, id)) < 0)
1576 0           return error;
1577              
1578 0 0         if (obj->uninteresting)
1579 0           return 0;
1580              
1581 0           obj->uninteresting = 1;
1582              
1583 0 0         if ((error = git_tree_lookup(&tree, pb->repo, id)) < 0)
1584 0           return error;
1585              
1586 0 0         for (i = 0; i < git_tree_entrycount(tree); i++) {
1587 0           const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
1588 0           const git_oid *entry_id = git_tree_entry_id(entry);
1589 0           switch (git_tree_entry_type(entry)) {
1590             case GIT_OBJECT_TREE:
1591 0 0         if ((error = mark_tree_uninteresting(pb, entry_id)) < 0)
1592 0           goto cleanup;
1593 0           break;
1594             case GIT_OBJECT_BLOB:
1595 0 0         if ((error = mark_blob_uninteresting(pb, entry_id)) < 0)
1596 0           goto cleanup;
1597 0           break;
1598             default:
1599             /* it's a submodule or something unknown, we don't want it */
1600             ;
1601             }
1602             }
1603              
1604             cleanup:
1605 0           git_tree_free(tree);
1606 0           return error;
1607             }
1608              
1609             /*
1610             * Mark the edges of the graph uninteresting. Since we start from a
1611             * git_revwalk, the commits are already uninteresting, but we need to
1612             * mark the trees and blobs.
1613             */
1614 3           static int mark_edges_uninteresting(git_packbuilder *pb, git_commit_list *commits)
1615             {
1616             int error;
1617             git_commit_list *list;
1618             git_commit *commit;
1619              
1620 6 100         for (list = commits; list; list = list->next) {
1621 3 50         if (!list->item->uninteresting)
1622 3           continue;
1623              
1624 0 0         if ((error = git_commit_lookup(&commit, pb->repo, &list->item->oid)) < 0)
1625 0           return error;
1626              
1627 0           error = mark_tree_uninteresting(pb, git_commit_tree_id(commit));
1628 0           git_commit_free(commit);
1629              
1630 0 0         if (error < 0)
1631 0           return error;
1632             }
1633              
1634 3           return 0;
1635             }
1636              
1637 17           int insert_tree(git_packbuilder *pb, git_tree *tree)
1638             {
1639             size_t i;
1640             int error;
1641             git_tree *subtree;
1642             struct walk_object *obj;
1643             const char *name;
1644              
1645 17 50         if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
1646 0           return error;
1647              
1648 17 50         if (obj->seen || obj->uninteresting)
    50          
1649 0           return 0;
1650              
1651 17           obj->seen = 1;
1652              
1653 17 50         if ((error = git_packbuilder_insert(pb, &obj->id, NULL)))
1654 0           return error;
1655              
1656 43 100         for (i = 0; i < git_tree_entrycount(tree); i++) {
1657 26           const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
1658 26           const git_oid *entry_id = git_tree_entry_id(entry);
1659 26           switch (git_tree_entry_type(entry)) {
1660             case GIT_OBJECT_TREE:
1661 8 50         if ((error = git_tree_lookup(&subtree, pb->repo, entry_id)) < 0)
1662 0           return error;
1663              
1664 8           error = insert_tree(pb, subtree);
1665 8           git_tree_free(subtree);
1666              
1667 8 50         if (error < 0)
1668 0           return error;
1669              
1670 8           break;
1671             case GIT_OBJECT_BLOB:
1672 18 50         if ((error = retrieve_object(&obj, pb, entry_id)) < 0)
1673 0           return error;
1674 18 50         if (obj->uninteresting)
1675 0           continue;
1676 18           name = git_tree_entry_name(entry);
1677 18 50         if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0)
1678 0           return error;
1679 18           break;
1680             default:
1681             /* it's a submodule or something unknown, we don't want it */
1682             ;
1683             }
1684             }
1685              
1686              
1687 17           return error;
1688             }
1689              
1690 9           int insert_commit(git_packbuilder *pb, struct walk_object *obj)
1691             {
1692             int error;
1693 9           git_commit *commit = NULL;
1694 9           git_tree *tree = NULL;
1695              
1696 9           obj->seen = 1;
1697              
1698 9 50         if ((error = git_packbuilder_insert(pb, &obj->id, NULL)) < 0)
1699 0           return error;
1700              
1701 9 50         if ((error = git_commit_lookup(&commit, pb->repo, &obj->id)) < 0)
1702 0           return error;
1703              
1704 9 50         if ((error = git_tree_lookup(&tree, pb->repo, git_commit_tree_id(commit))) < 0)
1705 0           goto cleanup;
1706              
1707 9 50         if ((error = insert_tree(pb, tree)) < 0)
1708 0           goto cleanup;
1709              
1710             cleanup:
1711 9           git_commit_free(commit);
1712 9           git_tree_free(tree);
1713 9           return error;
1714             }
1715              
1716 3           int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk)
1717             {
1718             int error;
1719             git_oid id;
1720             struct walk_object *obj;
1721              
1722 3 50         assert(pb && walk);
    50          
1723              
1724 3 50         if ((error = mark_edges_uninteresting(pb, walk->user_input)) < 0)
1725 0           return error;
1726              
1727             /*
1728             * TODO: git marks the parents of the edges
1729             * uninteresting. This may provide a speed advantage, but does
1730             * seem to assume the remote does not have a single-commit
1731             * history on the other end.
1732             */
1733              
1734             /* walk down each tree up to the blobs and insert them, stopping when uninteresting */
1735 12 100         while ((error = git_revwalk_next(&id, walk)) == 0) {
1736 9 50         if ((error = retrieve_object(&obj, pb, &id)) < 0)
1737 0           return error;
1738              
1739 9 50         if (obj->seen || obj->uninteresting)
    50          
1740 0           continue;
1741              
1742 9 50         if ((error = insert_commit(pb, obj)) < 0)
1743 0           return error;
1744             }
1745              
1746 3 50         if (error == GIT_ITEROVER)
1747 3           error = 0;
1748              
1749 3           return error;
1750             }
1751              
1752 3           int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
1753             {
1754 3 50         if (!pb)
1755 0           return -1;
1756              
1757 3           pb->progress_cb = progress_cb;
1758 3           pb->progress_cb_payload = progress_cb_payload;
1759              
1760 3           return 0;
1761             }
1762              
1763 7           void git_packbuilder_free(git_packbuilder *pb)
1764             {
1765 7 50         if (pb == NULL)
1766 0           return;
1767              
1768             #ifdef GIT_THREADS
1769              
1770             git_mutex_free(&pb->cache_mutex);
1771             git_mutex_free(&pb->progress_mutex);
1772             git_cond_free(&pb->progress_cond);
1773              
1774             #endif
1775              
1776 7 50         if (pb->odb)
1777 7           git_odb_free(pb->odb);
1778              
1779 7 50         if (pb->object_ix)
1780 7           git_oidmap_free(pb->object_ix);
1781              
1782 7 100         if (pb->object_list)
1783 6           git__free(pb->object_list);
1784              
1785 7           git_oidmap_free(pb->walk_objects);
1786 7           git_pool_clear(&pb->object_pool);
1787              
1788 7           git_hash_ctx_cleanup(&pb->ctx);
1789 7           git_zstream_free(&pb->zstream);
1790              
1791 7           git__free(pb);
1792             }