Skip to content

Commit 8b48981

Browse files
committed
Merge branch 'jx/bundle'
"git bundle" learns "--stdin" option to read its refs from the standard input. Also, it now does not lose refs whey they point at the same object. * jx/bundle: bundle: arguments can be read from stdin bundle: lost objects when removing duplicate pendings test: add helper functions for git-bundle
2 parents 42342b3 + 5bb0fd2 commit 8b48981

File tree

6 files changed

+576
-78
lines changed

6 files changed

+576
-78
lines changed

bundle.c

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -338,48 +338,6 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec *
338338
return 0;
339339
}
340340

341-
static int compute_and_write_prerequisites(int bundle_fd,
342-
struct rev_info *revs,
343-
int argc, const char **argv)
344-
{
345-
struct child_process rls = CHILD_PROCESS_INIT;
346-
struct strbuf buf = STRBUF_INIT;
347-
FILE *rls_fout;
348-
int i;
349-
350-
strvec_pushl(&rls.args,
351-
"rev-list", "--boundary", "--pretty=oneline",
352-
NULL);
353-
for (i = 1; i < argc; i++)
354-
strvec_push(&rls.args, argv[i]);
355-
rls.out = -1;
356-
rls.git_cmd = 1;
357-
if (start_command(&rls))
358-
return -1;
359-
rls_fout = xfdopen(rls.out, "r");
360-
while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) {
361-
struct object_id oid;
362-
if (buf.len > 0 && buf.buf[0] == '-') {
363-
write_or_die(bundle_fd, buf.buf, buf.len);
364-
if (!get_oid_hex(buf.buf + 1, &oid)) {
365-
struct object *object = parse_object_or_die(&oid,
366-
buf.buf);
367-
object->flags |= UNINTERESTING;
368-
add_pending_object(revs, object, buf.buf);
369-
}
370-
} else if (!get_oid_hex(buf.buf, &oid)) {
371-
struct object *object = parse_object_or_die(&oid,
372-
buf.buf);
373-
object->flags |= SHOWN;
374-
}
375-
}
376-
strbuf_release(&buf);
377-
fclose(rls_fout);
378-
if (finish_command(&rls))
379-
return error(_("rev-list died"));
380-
return 0;
381-
}
382-
383341
/*
384342
* Write out bundle refs based on the tips already
385343
* parsed into revs.pending. As a side effect, may
@@ -474,15 +432,49 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
474432
return ref_count;
475433
}
476434

435+
struct bundle_prerequisites_info {
436+
struct object_array *pending;
437+
int fd;
438+
};
439+
440+
static void write_bundle_prerequisites(struct commit *commit, void *data)
441+
{
442+
struct bundle_prerequisites_info *bpi = data;
443+
struct object *object;
444+
struct pretty_print_context ctx = { 0 };
445+
struct strbuf buf = STRBUF_INIT;
446+
447+
if (!(commit->object.flags & BOUNDARY))
448+
return;
449+
strbuf_addf(&buf, "-%s ", oid_to_hex(&commit->object.oid));
450+
write_or_die(bpi->fd, buf.buf, buf.len);
451+
452+
ctx.fmt = CMIT_FMT_ONELINE;
453+
ctx.output_encoding = get_log_output_encoding();
454+
strbuf_reset(&buf);
455+
pretty_print_commit(&ctx, commit, &buf);
456+
strbuf_trim(&buf);
457+
458+
object = (struct object *)commit;
459+
object->flags |= UNINTERESTING;
460+
add_object_array_with_path(object, buf.buf, bpi->pending, S_IFINVALID,
461+
NULL);
462+
strbuf_addch(&buf, '\n');
463+
write_or_die(bpi->fd, buf.buf, buf.len);
464+
strbuf_release(&buf);
465+
}
466+
477467
int create_bundle(struct repository *r, const char *path,
478468
int argc, const char **argv, struct strvec *pack_options, int version)
479469
{
480470
struct lock_file lock = LOCK_INIT;
481471
int bundle_fd = -1;
482472
int bundle_to_stdout;
483473
int ref_count = 0;
484-
struct rev_info revs;
474+
struct rev_info revs, revs_copy;
485475
int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
476+
struct bundle_prerequisites_info bpi;
477+
int i;
486478

487479
bundle_to_stdout = !strcmp(path, "-");
488480
if (bundle_to_stdout)
@@ -512,27 +504,44 @@ int create_bundle(struct repository *r, const char *path,
512504
save_commit_buffer = 0;
513505
repo_init_revisions(r, &revs, NULL);
514506

515-
/* write prerequisites */
516-
if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
517-
goto err;
518-
519507
argc = setup_revisions(argc, argv, &revs, NULL);
520508

521509
if (argc > 1) {
522510
error(_("unrecognized argument: %s"), argv[1]);
523511
goto err;
524512
}
525513

526-
object_array_remove_duplicates(&revs.pending);
514+
/* save revs.pending in revs_copy for later use */
515+
memcpy(&revs_copy, &revs, sizeof(revs));
516+
revs_copy.pending.nr = 0;
517+
revs_copy.pending.alloc = 0;
518+
revs_copy.pending.objects = NULL;
519+
for (i = 0; i < revs.pending.nr; i++) {
520+
struct object_array_entry *e = revs.pending.objects + i;
521+
if (e)
522+
add_object_array_with_path(e->item, e->name,
523+
&revs_copy.pending,
524+
e->mode, e->path);
525+
}
527526

528-
ref_count = write_bundle_refs(bundle_fd, &revs);
527+
/* write prerequisites */
528+
revs.boundary = 1;
529+
if (prepare_revision_walk(&revs))
530+
die("revision walk setup failed");
531+
bpi.fd = bundle_fd;
532+
bpi.pending = &revs_copy.pending;
533+
traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
534+
object_array_remove_duplicates(&revs_copy.pending);
535+
536+
/* write bundle refs */
537+
ref_count = write_bundle_refs(bundle_fd, &revs_copy);
529538
if (!ref_count)
530539
die(_("Refusing to create empty bundle."));
531540
else if (ref_count < 0)
532541
goto err;
533542

534543
/* write pack */
535-
if (write_pack_data(bundle_fd, &revs, pack_options))
544+
if (write_pack_data(bundle_fd, &revs_copy, pack_options))
536545
goto err;
537546

538547
if (!bundle_to_stdout) {

object.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,16 @@ void object_array_clear(struct object_array *array)
412412
}
413413

414414
/*
415-
* Return true iff array already contains an entry with name.
415+
* Return true if array already contains an entry.
416416
*/
417-
static int contains_name(struct object_array *array, const char *name)
417+
static int contains_object(struct object_array *array,
418+
const struct object *item, const char *name)
418419
{
419420
unsigned nr = array->nr, i;
420421
struct object_array_entry *object = array->objects;
421422

422423
for (i = 0; i < nr; i++, object++)
423-
if (!strcmp(object->name, name))
424+
if (item == object->item && !strcmp(object->name, name))
424425
return 1;
425426
return 0;
426427
}
@@ -432,7 +433,8 @@ void object_array_remove_duplicates(struct object_array *array)
432433

433434
array->nr = 0;
434435
for (src = 0; src < nr; src++) {
435-
if (!contains_name(array, objects[src].name)) {
436+
if (!contains_object(array, objects[src].item,
437+
objects[src].name)) {
436438
if (src != array->nr)
437439
objects[array->nr] = objects[src];
438440
array->nr++;

t/t5510-fetch.sh

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,10 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
99
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
1010

1111
. ./test-lib.sh
12+
. "$TEST_DIRECTORY"/test-bundle-functions.sh
1213

1314
D=$(pwd)
1415

15-
test_bundle_object_count () {
16-
git verify-pack -v "$1" >verify.out &&
17-
test "$2" = $(grep "^$OID_REGEX " verify.out | wc -l)
18-
}
19-
20-
convert_bundle_to_pack () {
21-
while read x && test -n "$x"
22-
do
23-
:;
24-
done
25-
cat
26-
}
27-
2816
test_expect_success setup '
2917
echo >file original &&
3018
git add file &&
@@ -483,9 +471,7 @@ test_expect_success 'unbundle 1' '
483471

484472
test_expect_success 'bundle 1 has only 3 files ' '
485473
cd "$D" &&
486-
convert_bundle_to_pack <bundle1 >bundle.pack &&
487-
git index-pack bundle.pack &&
488-
test_bundle_object_count bundle.pack 3
474+
test_bundle_object_count bundle1 3
489475
'
490476

491477
test_expect_success 'unbundle 2' '
@@ -500,9 +486,7 @@ test_expect_success 'bundle does not prerequisite objects' '
500486
git add file2 &&
501487
git commit -m add.file2 file2 &&
502488
git bundle create bundle3 -1 HEAD &&
503-
convert_bundle_to_pack <bundle3 >bundle.pack &&
504-
git index-pack bundle.pack &&
505-
test_bundle_object_count bundle.pack 3
489+
test_bundle_object_count bundle3 3
506490
'
507491

508492
test_expect_success 'bundle should be able to create a full history' '
@@ -1055,9 +1039,7 @@ test_expect_success 'all boundary commits are excluded' '
10551039
git merge otherside &&
10561040
ad=$(git log --no-walk --format=%ad HEAD) &&
10571041
git bundle create twoside-boundary.bdl main --since="$ad" &&
1058-
convert_bundle_to_pack <twoside-boundary.bdl >twoside-boundary.pack &&
1059-
pack=$(git index-pack --fix-thin --stdin <twoside-boundary.pack) &&
1060-
test_bundle_object_count .git/objects/pack/pack-${pack##pack }.pack 3
1042+
test_bundle_object_count --thin twoside-boundary.bdl 3
10611043
'
10621044

10631045
test_expect_success 'fetch --prune prints the remotes url' '

t/t5607-clone-bundle.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ test_expect_success 'die if bundle file cannot be created' '
4141
test_must_fail git bundle create adir --all
4242
'
4343

44-
test_expect_failure 'bundle --stdin' '
44+
test_expect_success 'bundle --stdin' '
4545
echo main | git bundle create stdin-bundle.bdl --stdin &&
4646
git ls-remote stdin-bundle.bdl >output &&
4747
grep main output
4848
'
4949

50-
test_expect_failure 'bundle --stdin <rev-list options>' '
50+
test_expect_success 'bundle --stdin <rev-list options>' '
5151
echo main | git bundle create hybrid-bundle.bdl --stdin tag &&
5252
git ls-remote hybrid-bundle.bdl >output &&
5353
grep main output

0 commit comments

Comments
 (0)