diff --git a/bundle.c b/bundle.c index cb0e5931ac..693d619551 100644 --- a/bundle.c +++ b/bundle.c @@ -338,48 +338,6 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec * return 0; } -static int compute_and_write_prerequisites(int bundle_fd, - struct rev_info *revs, - int argc, const char **argv) -{ - struct child_process rls = CHILD_PROCESS_INIT; - struct strbuf buf = STRBUF_INIT; - FILE *rls_fout; - int i; - - strvec_pushl(&rls.args, - "rev-list", "--boundary", "--pretty=oneline", - NULL); - for (i = 1; i < argc; i++) - strvec_push(&rls.args, argv[i]); - rls.out = -1; - rls.git_cmd = 1; - if (start_command(&rls)) - return -1; - rls_fout = xfdopen(rls.out, "r"); - while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { - struct object_id oid; - if (buf.len > 0 && buf.buf[0] == '-') { - write_or_die(bundle_fd, buf.buf, buf.len); - if (!get_oid_hex(buf.buf + 1, &oid)) { - struct object *object = parse_object_or_die(&oid, - buf.buf); - object->flags |= UNINTERESTING; - add_pending_object(revs, object, buf.buf); - } - } else if (!get_oid_hex(buf.buf, &oid)) { - struct object *object = parse_object_or_die(&oid, - buf.buf); - object->flags |= SHOWN; - } - } - strbuf_release(&buf); - fclose(rls_fout); - if (finish_command(&rls)) - return error(_("rev-list died")); - return 0; -} - /* * Write out bundle refs based on the tips already * parsed into revs.pending. As a side effect, may @@ -474,6 +432,38 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) return ref_count; } +struct bundle_prerequisites_info { + struct object_array *pending; + int fd; +}; + +static void write_bundle_prerequisites(struct commit *commit, void *data) +{ + struct bundle_prerequisites_info *bpi = data; + struct object *object; + struct pretty_print_context ctx = { 0 }; + struct strbuf buf = STRBUF_INIT; + + if (!(commit->object.flags & BOUNDARY)) + return; + strbuf_addf(&buf, "-%s ", oid_to_hex(&commit->object.oid)); + write_or_die(bpi->fd, buf.buf, buf.len); + + ctx.fmt = CMIT_FMT_ONELINE; + ctx.output_encoding = get_log_output_encoding(); + strbuf_reset(&buf); + pretty_print_commit(&ctx, commit, &buf); + strbuf_trim(&buf); + + object = (struct object *)commit; + object->flags |= UNINTERESTING; + add_object_array_with_path(object, buf.buf, bpi->pending, S_IFINVALID, + NULL); + strbuf_addch(&buf, '\n'); + write_or_die(bpi->fd, buf.buf, buf.len); + strbuf_release(&buf); +} + int create_bundle(struct repository *r, const char *path, int argc, const char **argv, struct strvec *pack_options, int version) { @@ -481,8 +471,10 @@ int create_bundle(struct repository *r, const char *path, int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; - struct rev_info revs; + struct rev_info revs, revs_copy; int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3; + struct bundle_prerequisites_info bpi; + int i; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) @@ -512,10 +504,6 @@ int create_bundle(struct repository *r, const char *path, save_commit_buffer = 0; repo_init_revisions(r, &revs, NULL); - /* write prerequisites */ - if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv)) - goto err; - argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) { @@ -523,16 +511,37 @@ int create_bundle(struct repository *r, const char *path, goto err; } - object_array_remove_duplicates(&revs.pending); + /* save revs.pending in revs_copy for later use */ + memcpy(&revs_copy, &revs, sizeof(revs)); + revs_copy.pending.nr = 0; + revs_copy.pending.alloc = 0; + revs_copy.pending.objects = NULL; + for (i = 0; i < revs.pending.nr; i++) { + struct object_array_entry *e = revs.pending.objects + i; + if (e) + add_object_array_with_path(e->item, e->name, + &revs_copy.pending, + e->mode, e->path); + } - ref_count = write_bundle_refs(bundle_fd, &revs); + /* write prerequisites */ + revs.boundary = 1; + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + bpi.fd = bundle_fd; + bpi.pending = &revs_copy.pending; + traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi); + object_array_remove_duplicates(&revs_copy.pending); + + /* write bundle refs */ + ref_count = write_bundle_refs(bundle_fd, &revs_copy); if (!ref_count) die(_("Refusing to create empty bundle.")); else if (ref_count < 0) goto err; /* write pack */ - if (write_pack_data(bundle_fd, &revs, pack_options)) + if (write_pack_data(bundle_fd, &revs_copy, pack_options)) goto err; if (!bundle_to_stdout) { diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh index 26985f4b44..425258767d 100755 --- a/t/t5607-clone-bundle.sh +++ b/t/t5607-clone-bundle.sh @@ -38,13 +38,13 @@ test_expect_success 'die if bundle file cannot be created' ' test_must_fail git bundle create adir --all ' -test_expect_failure 'bundle --stdin' ' +test_expect_success 'bundle --stdin' ' echo master | git bundle create stdin-bundle.bdl --stdin && git ls-remote stdin-bundle.bdl >output && grep master output ' -test_expect_failure 'bundle --stdin ' ' +test_expect_success 'bundle --stdin ' ' echo master | git bundle create hybrid-bundle.bdl --stdin tag && git ls-remote hybrid-bundle.bdl >output && grep master output diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh index b554538e00..6249420a80 100755 --- a/t/t6020-bundle-misc.sh +++ b/t/t6020-bundle-misc.sh @@ -242,8 +242,16 @@ test_expect_success 'create bundle with --since option' ' ' test_expect_success 'create bundle 1 - no prerequisites' ' + # create bundle from args git bundle create 1.bdl topic/1 topic/2 && + # create bundle from stdin + cat >input <<-\EOF && + topic/1 + topic/2 + EOF + git bundle create stdin-1.bdl --stdin expect <<-\EOF && The bundle contains these 2 refs: refs/heads/topic/1 @@ -256,10 +264,16 @@ test_expect_success 'create bundle 1 - no prerequisites' ' make_user_friendly_and_stable_output >actual && test_i18ncmp expect actual && - test_bundle_object_count 1.bdl 24 + git bundle verify stdin-1.bdl | + make_user_friendly_and_stable_output >actual && + test_i18ncmp expect actual && + + test_bundle_object_count 1.bdl 24 && + test_bundle_object_count stdin-1.bdl 24 ' test_expect_success 'create bundle 2 - has prerequisites' ' + # create bundle from args git bundle create 2.bdl \ --ignore-missing \ ^topic/deleted \ @@ -267,6 +281,18 @@ test_expect_success 'create bundle 2 - has prerequisites' ' ^topic/2 \ release && + # create bundle from stdin + # input has a non-exist reference: "topic/deleted" + cat >input <<-EOF && + ^topic/deleted + ^$D + ^topic/2 + EOF + git bundle create stdin-2.bdl \ + --ignore-missing \ + --stdin \ + release expect <<-\EOF && The bundle contains this ref: refs/heads/release @@ -280,7 +306,12 @@ test_expect_success 'create bundle 2 - has prerequisites' ' make_user_friendly_and_stable_output >actual && test_i18ncmp expect actual && - test_bundle_object_count 2.bdl 16 + git bundle verify stdin-2.bdl | + make_user_friendly_and_stable_output >actual && + test_i18ncmp expect actual && + + test_bundle_object_count 2.bdl 16 && + test_bundle_object_count stdin-2.bdl 16 ' test_expect_success 'fail to verify bundle without prerequisites' ' @@ -295,10 +326,15 @@ test_expect_success 'fail to verify bundle without prerequisites' ' test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 | make_user_friendly_and_stable_output >actual && + test_i18ncmp expect actual && + + test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 | + make_user_friendly_and_stable_output >actual && test_i18ncmp expect actual ' test_expect_success 'create bundle 3 - two refs, same object' ' + # create bundle from args git bundle create --version=3 3.bdl \ ^release \ ^topic/1 \ @@ -306,6 +342,16 @@ test_expect_success 'create bundle 3 - two refs, same object' ' main \ HEAD && + # create bundle from stdin + cat >input <<-\EOF && + ^release + ^topic/1 + ^topic/2 + EOF + git bundle create --version=3 stdin-3.bdl \ + --stdin \ + main HEAD expect <<-\EOF && The bundle contains these 2 refs: refs/heads/main @@ -319,10 +365,16 @@ test_expect_success 'create bundle 3 - two refs, same object' ' make_user_friendly_and_stable_output >actual && test_i18ncmp expect actual && - test_bundle_object_count 3.bdl 4 + git bundle verify stdin-3.bdl | + make_user_friendly_and_stable_output >actual && + test_i18ncmp expect actual && + + test_bundle_object_count 3.bdl 4 && + test_bundle_object_count stdin-3.bdl 4 ' test_expect_success 'create bundle 4 - with tags' ' + # create bundle from args git bundle create 4.bdl \ ^main \ ^release \ @@ -330,6 +382,18 @@ test_expect_success 'create bundle 4 - with tags' ' ^topic/2 \ --all && + # create bundle from stdin + cat >input <<-\EOF && + ^main + ^release + ^topic/1 + ^topic/2 + EOF + git bundle create stdin-4.bdl \ + --ignore-missing \ + --stdin \ + --all expect <<-\EOF && The bundle contains these 3 refs: refs/tags/v1 @@ -342,7 +406,12 @@ test_expect_success 'create bundle 4 - with tags' ' make_user_friendly_and_stable_output >actual && test_i18ncmp expect actual && - test_bundle_object_count 4.bdl 3 + git bundle verify stdin-4.bdl | + make_user_friendly_and_stable_output >actual && + test_i18ncmp expect actual && + + test_bundle_object_count 4.bdl 3 && + test_bundle_object_count stdin-4.bdl 3 ' test_expect_success 'clone from bundle' '