Skip to content

Commit 5bb0fd2

Browse files
jiangxingitster
authored andcommitted
bundle: arguments can be read from stdin
In order to create an incremental bundle, we need to pass many arguments to let git-bundle ignore some already packed commits. It will be more convenient to pass args via stdin. But the current implementation does not allow us to do this. This is because args are parsed twice when creating bundle. The first time for parsing args is in `compute_and_write_prerequisites()` by running `git-rev-list` command to write prerequisites in bundle file, and stdin is consumed in this step if "--stdin" option is provided for `git-bundle`. Later nothing can be read from stdin when running `setup_revisions()` in `create_bundle()`. The solution is to parse args once by removing the entire function `compute_and_write_prerequisites()` and then calling function `setup_revisions()`. In order to write prerequisites for bundle, will call `prepare_revision_walk()` and `traverse_commit_list()`. But after calling `prepare_revision_walk()`, the object array `revs.pending` is left empty, and the following steps could not work properly with the empty object array (`revs.pending`). Therefore, make a copy of `revs` to `revs_copy` for later use right after calling `setup_revisions()`. The copy of `revs_copy` is not a deep copy, it shares the same objects with `revs`. The object array of `revs` has been cleared, but objects themselves are still kept. Flags of objects may change after calling `prepare_revision_walk()`, we can use these changed flags without calling the `git rev-list` command and parsing its output like the former implementation. Also add testcases for git bundle in t6020, which read args from stdin. Signed-off-by: Jiang Xin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ce1d6d9 commit 5bb0fd2

File tree

3 files changed

+134
-56
lines changed

3 files changed

+134
-56
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) {

t/t5607-clone-bundle.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ test_expect_success 'die if bundle file cannot be created' '
3838
test_must_fail git bundle create adir --all
3939
'
4040

41-
test_expect_failure 'bundle --stdin' '
41+
test_expect_success 'bundle --stdin' '
4242
echo master | git bundle create stdin-bundle.bdl --stdin &&
4343
git ls-remote stdin-bundle.bdl >output &&
4444
grep master output
4545
'
4646

47-
test_expect_failure 'bundle --stdin <rev-list options>' '
47+
test_expect_success 'bundle --stdin <rev-list options>' '
4848
echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
4949
git ls-remote hybrid-bundle.bdl >output &&
5050
grep master output

t/t6020-bundle-misc.sh

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,16 @@ test_expect_success 'create bundle with --since option' '
242242
'
243243

244244
test_expect_success 'create bundle 1 - no prerequisites' '
245+
# create bundle from args
245246
git bundle create 1.bdl topic/1 topic/2 &&
246247
248+
# create bundle from stdin
249+
cat >input <<-\EOF &&
250+
topic/1
251+
topic/2
252+
EOF
253+
git bundle create stdin-1.bdl --stdin <input &&
254+
247255
cat >expect <<-\EOF &&
248256
The bundle contains these 2 refs:
249257
<COMMIT-D> refs/heads/topic/1
@@ -256,17 +264,35 @@ test_expect_success 'create bundle 1 - no prerequisites' '
256264
make_user_friendly_and_stable_output >actual &&
257265
test_i18ncmp expect actual &&
258266
259-
test_bundle_object_count 1.bdl 24
267+
git bundle verify stdin-1.bdl |
268+
make_user_friendly_and_stable_output >actual &&
269+
test_i18ncmp expect actual &&
270+
271+
test_bundle_object_count 1.bdl 24 &&
272+
test_bundle_object_count stdin-1.bdl 24
260273
'
261274

262275
test_expect_success 'create bundle 2 - has prerequisites' '
276+
# create bundle from args
263277
git bundle create 2.bdl \
264278
--ignore-missing \
265279
^topic/deleted \
266280
^$D \
267281
^topic/2 \
268282
release &&
269283
284+
# create bundle from stdin
285+
# input has a non-exist reference: "topic/deleted"
286+
cat >input <<-EOF &&
287+
^topic/deleted
288+
^$D
289+
^topic/2
290+
EOF
291+
git bundle create stdin-2.bdl \
292+
--ignore-missing \
293+
--stdin \
294+
release <input &&
295+
270296
cat >expect <<-\EOF &&
271297
The bundle contains this ref:
272298
<COMMIT-N> refs/heads/release
@@ -280,7 +306,12 @@ test_expect_success 'create bundle 2 - has prerequisites' '
280306
make_user_friendly_and_stable_output >actual &&
281307
test_i18ncmp expect actual &&
282308
283-
test_bundle_object_count 2.bdl 16
309+
git bundle verify stdin-2.bdl |
310+
make_user_friendly_and_stable_output >actual &&
311+
test_i18ncmp expect actual &&
312+
313+
test_bundle_object_count 2.bdl 16 &&
314+
test_bundle_object_count stdin-2.bdl 16
284315
'
285316

286317
test_expect_success 'fail to verify bundle without prerequisites' '
@@ -295,17 +326,32 @@ test_expect_success 'fail to verify bundle without prerequisites' '
295326
296327
test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 |
297328
make_user_friendly_and_stable_output >actual &&
329+
test_i18ncmp expect actual &&
330+
331+
test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 |
332+
make_user_friendly_and_stable_output >actual &&
298333
test_i18ncmp expect actual
299334
'
300335

301336
test_expect_success 'create bundle 3 - two refs, same object' '
337+
# create bundle from args
302338
git bundle create --version=3 3.bdl \
303339
^release \
304340
^topic/1 \
305341
^topic/2 \
306342
main \
307343
HEAD &&
308344
345+
# create bundle from stdin
346+
cat >input <<-\EOF &&
347+
^release
348+
^topic/1
349+
^topic/2
350+
EOF
351+
git bundle create --version=3 stdin-3.bdl \
352+
--stdin \
353+
main HEAD <input &&
354+
309355
cat >expect <<-\EOF &&
310356
The bundle contains these 2 refs:
311357
<COMMIT-P> refs/heads/main
@@ -319,17 +365,35 @@ test_expect_success 'create bundle 3 - two refs, same object' '
319365
make_user_friendly_and_stable_output >actual &&
320366
test_i18ncmp expect actual &&
321367
322-
test_bundle_object_count 3.bdl 4
368+
git bundle verify stdin-3.bdl |
369+
make_user_friendly_and_stable_output >actual &&
370+
test_i18ncmp expect actual &&
371+
372+
test_bundle_object_count 3.bdl 4 &&
373+
test_bundle_object_count stdin-3.bdl 4
323374
'
324375

325376
test_expect_success 'create bundle 4 - with tags' '
377+
# create bundle from args
326378
git bundle create 4.bdl \
327379
^main \
328380
^release \
329381
^topic/1 \
330382
^topic/2 \
331383
--all &&
332384
385+
# create bundle from stdin
386+
cat >input <<-\EOF &&
387+
^main
388+
^release
389+
^topic/1
390+
^topic/2
391+
EOF
392+
git bundle create stdin-4.bdl \
393+
--ignore-missing \
394+
--stdin \
395+
--all <input &&
396+
333397
cat >expect <<-\EOF &&
334398
The bundle contains these 3 refs:
335399
<TAG-1> refs/tags/v1
@@ -342,7 +406,12 @@ test_expect_success 'create bundle 4 - with tags' '
342406
make_user_friendly_and_stable_output >actual &&
343407
test_i18ncmp expect actual &&
344408
345-
test_bundle_object_count 4.bdl 3
409+
git bundle verify stdin-4.bdl |
410+
make_user_friendly_and_stable_output >actual &&
411+
test_i18ncmp expect actual &&
412+
413+
test_bundle_object_count 4.bdl 3 &&
414+
test_bundle_object_count stdin-4.bdl 3
346415
'
347416

348417
test_expect_success 'clone from bundle' '

0 commit comments

Comments
 (0)