Skip to content

Commit f18b512

Browse files
derrickstoleegitster
authored andcommitted
bundle: create filtered bundles
A previous change allowed Git to parse bundles with the 'filter' capability. Now, teach Git to create bundles with this option. Some rearranging of code is required to get the option parsing in the correct spot. There are now two reasons why we might need capabilities (a new hash algorithm or an object filter) so that is pulled out into a place where we can check both at the same time. The --filter option is parsed as part of setup_revisions(), but it expected the --objects flag, too. That flag is somewhat implied by 'git bundle' because it creates a pack-file walking objects, but there is also a walk that walks the revision range expecting only commits. Make this parsing work by setting 'revs.tree_objects' and 'revs.blob_objects' before the call to setup_revisions(). Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c4ea513 commit f18b512

File tree

2 files changed

+90
-11
lines changed

2 files changed

+90
-11
lines changed

bundle.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec *
332332
"--stdout", "--thin", "--delta-base-offset",
333333
NULL);
334334
strvec_pushv(&pack_objects.args, pack_options->v);
335+
if (revs->filter.choice)
336+
strvec_pushf(&pack_objects.args, "--filter=%s",
337+
list_objects_filter_spec(&revs->filter));
335338
pack_objects.in = -1;
336339
pack_objects.out = bundle_fd;
337340
pack_objects.git_cmd = 1;
@@ -499,10 +502,37 @@ int create_bundle(struct repository *r, const char *path,
499502
int bundle_to_stdout;
500503
int ref_count = 0;
501504
struct rev_info revs, revs_copy;
502-
int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
505+
int min_version = 2;
503506
struct bundle_prerequisites_info bpi;
504507
int i;
505508

509+
/* init revs to list objects for pack-objects later */
510+
save_commit_buffer = 0;
511+
repo_init_revisions(r, &revs, NULL);
512+
513+
/*
514+
* Pre-initialize the '--objects' flag so we can parse a
515+
* --filter option successfully.
516+
*/
517+
revs.tree_objects = revs.blob_objects = 1;
518+
519+
argc = setup_revisions(argc, argv, &revs, NULL);
520+
521+
/*
522+
* Reasons to require version 3:
523+
*
524+
* 1. @object-format is required because our hash algorithm is not
525+
* SHA1.
526+
* 2. @filter is required because we parsed an object filter.
527+
*/
528+
if (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)
529+
min_version = 3;
530+
531+
if (argc > 1) {
532+
error(_("unrecognized argument: %s"), argv[1]);
533+
goto err;
534+
}
535+
506536
bundle_to_stdout = !strcmp(path, "-");
507537
if (bundle_to_stdout)
508538
bundle_fd = 1;
@@ -525,17 +555,14 @@ int create_bundle(struct repository *r, const char *path,
525555
write_or_die(bundle_fd, capability, strlen(capability));
526556
write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name));
527557
write_or_die(bundle_fd, "\n", 1);
528-
}
529-
530-
/* init revs to list objects for pack-objects later */
531-
save_commit_buffer = 0;
532-
repo_init_revisions(r, &revs, NULL);
533558

534-
argc = setup_revisions(argc, argv, &revs, NULL);
535-
536-
if (argc > 1) {
537-
error(_("unrecognized argument: %s"), argv[1]);
538-
goto err;
559+
if (revs.filter.choice) {
560+
const char *value = expand_list_objects_filter_spec(&revs.filter);
561+
capability = "@filter=";
562+
write_or_die(bundle_fd, capability, strlen(capability));
563+
write_or_die(bundle_fd, value, strlen(value));
564+
write_or_die(bundle_fd, "\n", 1);
565+
}
539566
}
540567

541568
/* save revs.pending in revs_copy for later use */
@@ -558,6 +585,10 @@ int create_bundle(struct repository *r, const char *path,
558585
bpi.fd = bundle_fd;
559586
bpi.pending = &revs_copy.pending;
560587

588+
/*
589+
* Remove any object walking here. We only care about commits and
590+
* tags here. The revs_copy has the right instances of these values.
591+
*/
561592
revs.blob_objects = revs.tree_objects = 0;
562593
traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
563594
object_array_remove_duplicates(&revs_copy.pending);

t/t6020-bundle-misc.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,4 +487,52 @@ test_expect_success 'unfiltered bundle with --objects' '
487487
test_cmp expect actual
488488
'
489489

490+
for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100"
491+
do
492+
test_expect_success "filtered bundle: $filter" '
493+
test_when_finished rm -rf .git/objects/pack cloned unbundled &&
494+
git bundle create partial.bdl \
495+
--all \
496+
--filter=$filter &&
497+
498+
git bundle verify partial.bdl >unfiltered &&
499+
make_user_friendly_and_stable_output <unfiltered >actual &&
500+
501+
cat >expect <<-EOF &&
502+
The bundle contains these 10 refs:
503+
<COMMIT-P> refs/heads/main
504+
<COMMIT-N> refs/heads/release
505+
<COMMIT-D> refs/heads/topic/1
506+
<COMMIT-H> refs/heads/topic/2
507+
<COMMIT-D> refs/pull/1/head
508+
<COMMIT-G> refs/pull/2/head
509+
<TAG-1> refs/tags/v1
510+
<TAG-2> refs/tags/v2
511+
<TAG-3> refs/tags/v3
512+
<COMMIT-P> HEAD
513+
The bundle uses this filter: $filter
514+
The bundle records a complete history.
515+
EOF
516+
test_cmp expect actual &&
517+
518+
test_config uploadpack.allowfilter 1 &&
519+
test_config uploadpack.allowanysha1inwant 1 &&
520+
git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned &&
521+
522+
git init unbundled &&
523+
git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt &&
524+
525+
# Count the same number of reachable objects.
526+
reflist=$(git for-each-ref --format="%(objectname)") &&
527+
git rev-list --objects --filter=$filter --missing=allow-any \
528+
$reflist >expect &&
529+
for repo in cloned unbundled
530+
do
531+
git -C $repo rev-list --objects --missing=allow-any \
532+
$reflist >actual &&
533+
test_cmp expect actual || return 1
534+
done
535+
'
536+
done
537+
490538
test_done

0 commit comments

Comments
 (0)