diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc index 8a27aa19fd..65cd00c152 100644 --- a/Documentation/git-pack-objects.adoc +++ b/Documentation/git-pack-objects.adoc @@ -402,11 +402,13 @@ will be automatically changed to version `1`. of filenames that cause collisions in Git's default name-hash algorithm. + -Incompatible with `--delta-islands`. The `--use-bitmap-index` option is -ignored in the presence of `--path-walk`. The `--path-walk` option -supports the `--filter=` forms `blob:none`, `blob:limit=`, -`tree:0`, `object:type=`, and `sparse:`. These supported filter -types can be combined with the `combine:+` form. +When `--use-bitmap-index` is specified with `--path-walk`, a successful +bitmap traversal is used for object enumeration, with path-walk +remaining as the fallback traversal when the bitmap cannot satisfy the +request. The `--path-walk` option supports the `--filter=` forms +`blob:none`, `blob:limit=`, `tree:0`, `object:type=`, and +`sparse:`. These supported filter types can be combined with the +`combine:+` form. DELTA ISLANDS diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index bc5f9ef321..e3760b3492 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2744,6 +2744,22 @@ static inline void oe_set_tree_depth(struct packing_data *pack, pack->tree_depth[e - pack->objects] = tree_depth; } +static void record_tree_depth(const struct object_id *oid, const char *name) +{ + const char *p; + unsigned depth; + struct object_entry *ent; + + /* the empty string is a root tree, which is depth 0 */ + depth = *name ? 1 : 0; + for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) + depth++; + + ent = packlist_find(&to_pack, oid); + if (ent && depth > oe_tree_depth(&to_pack, ent)) + oe_set_tree_depth(&to_pack, ent, depth); +} + /* * Return the size of the object without doing any delta * reconstruction (so non-deltas are true object sizes, but deltas @@ -4406,20 +4422,8 @@ static void show_object(struct object *obj, const char *name, add_preferred_base_object(name); add_object_entry(&obj->oid, obj->type, name, 0); - if (use_delta_islands) { - const char *p; - unsigned depth; - struct object_entry *ent; - - /* the empty string is a root tree, which is depth 0 */ - depth = *name ? 1 : 0; - for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) - depth++; - - ent = packlist_find(&to_pack, &obj->oid); - if (ent && depth > oe_tree_depth(&to_pack, ent)) - oe_set_tree_depth(&to_pack, ent, depth); - } + if (use_delta_islands) + record_tree_depth(&obj->oid, name); } static void show_object__ma_allow_any(struct object *obj, const char *name, void *data) @@ -4763,6 +4767,31 @@ static int add_objects_by_path(const char *path, continue; add_object_entry(oid, type, path, exclude); + + if (type == OBJ_COMMIT) { + struct commit *commit; + + if (!write_bitmap_index && !use_delta_islands) + continue; + + commit = lookup_commit(the_repository, oid); + if (!commit) + die(_("could not find commit %s"), oid_to_hex(oid)); + if (write_bitmap_index) + index_commit_for_bitmap(commit); + /* + * Skip island propagation for boundary commits. + * The regular traversal's show_commit() is only + * called for interesting commits; matching that + * here keeps path-walk from doing extra work that + * would only be a no-op anyway (boundary commits + * are not in island_marks). + */ + if (use_delta_islands && !exclude) + propagate_island_marks(the_repository, commit); + } else if (type == OBJ_TREE && use_delta_islands) { + record_tree_depth(oid, path); + } } oe_end = to_pack.nr_objects; @@ -4795,6 +4824,13 @@ static int get_object_list_path_walk(struct rev_info *revs) info.path_fn = add_objects_by_path; info.path_fn_data = &processed; + /* + * Path-walk needs boundary commits to discover thin-pack bases, but + * bitmap traversal does not understand the boundary state. Set it + * here so any prior bitmap attempt sees the usual non-boundary walk. + */ + revs->boundary = 1; + /* * Allow the --[no-]sparse option to be interesting here, if only * for testing purposes. Paths with no interesting objects will not @@ -5218,8 +5254,6 @@ int cmd_pack_objects(int argc, const char *option = NULL; if (!path_walk_filter_compatible(&filter_options)) option = "--filter"; - else if (use_delta_islands) - option = "--delta-islands"; if (option) { warning(_("cannot use %s with %s"), @@ -5228,9 +5262,7 @@ int cmd_pack_objects(int argc, } } if (path_walk) { - strvec_push(&rp, "--boundary"); strvec_push(&rp, "--objects"); - use_bitmap_index = 0; } else if (thin) { use_internal_rev_list = 1; strvec_push(&rp, shallow diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh index 047efb995d..1b115d921a 100755 --- a/t/perf/p5311-pack-bitmaps-fetch.sh +++ b/t/perf/p5311-pack-bitmaps-fetch.sh @@ -4,16 +4,18 @@ test_description='performance of fetches from bitmapped packs' . ./perf-lib.sh test_fetch_bitmaps () { + argv=$1 + export argv + test_expect_success 'setup test directory' ' rm -fr * .git ' test_perf_default_repo - test_expect_success 'create bitmapped server repo' ' + test_expect_success "create bitmapped server repo ${argv:+($argv)}" ' git config pack.writebitmaps true && - git config pack.writeBitmapLookupTable '"$1"' && - git repack -ad + git repack -ad $argv ' # simulate a fetch from a repository that last fetched N days ago, for @@ -21,7 +23,7 @@ test_fetch_bitmaps () { # and assume the first entry in the chain that is N days older than the current # HEAD is where the HEAD would have been then. for days in 1 2 4 8 16 32 64 128; do - title=$(printf '%10s' "($days days)") + title=$(printf '%10s' "($days days${argv:+, $argv})") test_expect_success "setup revs from $days days ago" ' now=$(git log -1 --format=%ct HEAD) && then=$(($now - ($days * 86400))) && @@ -32,7 +34,7 @@ test_fetch_bitmaps () { } >revs ' - test_perf "server $title (lookup=$1)" ' + test_perf "server $title" ' git pack-objects --stdout --revs \ --thin --delta-base-offset \ tmp.pack @@ -42,13 +44,15 @@ test_fetch_bitmaps () { test_file_size tmp.pack ' - test_perf "client $title (lookup=$1)" ' + test_perf "client $title" ' git index-pack --stdin --fix-thin in && + + git rev-list --objects --no-object-names HEAD >expect.raw && + sort expect.raw >expect && + + for reuse in true false + do + : >trace.txt && + + GIT_TRACE2_EVENT="$(pwd)/trace.txt" \ + git -c pack.allowPackReuse=$reuse pack-objects \ + --stdout --revs --path-walk --use-bitmap-index \ + out.pack && + test_grep "\"category\":\"bitmap\",\"key\":\"bitmap/hits\"" trace.txt && + + git index-pack out.pack && + + list_packed_objects out.idx >actual.raw && + sort actual.raw >actual && + test_cmp expect actual || return 1 + done + ) +' + test_expect_success 'incremental repack fails when bitmaps are requested' ' test_commit more-1 && test_must_fail git repack -d 2>err && diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh index 2c961c7096..9b28344a0a 100755 --- a/t/t5320-delta-islands.sh +++ b/t/t5320-delta-islands.sh @@ -53,6 +53,35 @@ test_expect_success 'separate islands disallows delta' ' ! is_delta_base $two $one ' +test_expect_success 'path-walk island repack respects islands' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-islands" \ + git -c "pack.island=refs/heads/(.*)" repack -adfi \ + --path-walk 2>err && + test_region pack-objects path-walk trace.path-walk-islands && + test_grep ! "cannot use --delta-islands with --path-walk" err && + ! is_delta_base $one $two && + ! is_delta_base $two $one +' + +test_expect_success 'path-walk island bitmap repack respects islands' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-island-bitmap" \ + git -c "pack.island=refs/heads/(.*)" repack -a -d -f -i -b \ + --path-walk 2>err && + test_region pack-objects path-walk trace.path-walk-island-bitmap && + test_path_is_file .git/objects/pack/*.bitmap && + git rev-list --test-bitmap --use-bitmap-index one && + test_grep ! "cannot use --delta-islands with --path-walk" err && + ! is_delta_base $one $two && + ! is_delta_base $two $one +' + +test_expect_success 'path-walk same island allows delta' ' + GIT_TRACE2_EVENT="$(pwd)/trace.path-walk-same-island" \ + git -c "pack.island=refs/heads" repack -adfi --path-walk && + test_region pack-objects path-walk trace.path-walk-same-island && + is_delta_base $one $two +' + test_expect_success 'same island allows delta' ' git -c "pack.island=refs/heads" repack -adfi && is_delta_base $one $two