mirror of
https://github.com/git/git.git
synced 2026-10-05 22:35:45 +02:00
The `combine` filter takes the intersection of its children, that is:
objects are shown only when all child filters would admit the object.
The preceding patches added support for many individual filter types.
Enable users to compose these filters by implementing support for the
`combine` filter type.
Mapping intersection onto path_walk_info works because every supported
child filter is a monotonic restriction:
- `blob:none`, `tree:0` unconditionally clear `info->blobs` and (for
`tree:0`) `info->trees`; clearing an already-cleared flag is a
no-op.
- `object:type=X` is now expressed as an AND of each type flag with the
filtered type, so applying multiple such filters only refines the
existing set rather than overwrites it.
- `blob:limit=N` has to compose too: the intersection of "size < L1"
and "size < L2" is "size < min(L1, L2)".
Update the `LOFC_BLOB_LIMIT` handler to take the running minimum when
`info->blob_limit` is already set, so a combined filter with, e.g.,
both "blob:limit=10" and "blob:limit=5" produces a limit of 5
regardless of ordering.
- `sparse:oid` is left unchanged. A `combine` filter that includes a
`sparse:oid` is allowed at most once, since the existing handler
refuses to overwrite `info->pl`. Two `sparse:oid` filters in a single
`combine` would be unusual and are rejected with a warning, matching
the standalone `sparse:oid` behavior.
Implementation-wise, the existing `prepare_filters()` called
`list_objects_filter_release()` inside each case branch. That works fine for
top-level filters, but `combine` filters need to recurse over its child
filters without releasing each one in turn (since the parent's release
iterates the sub array). Split `prepare_filters()` into a recursive helper
that performs only the mutation, plus a thin wrapper that calls the helper
and then releases the top-level filter once.
The `LOFC_COMBINE` case in the helper just walks `sub_nr` and recurses;
child filters are released by the wrapper's single
`list_objects_filter_release()` call on the parent (which itself recursively
releases each sub-filter, the same way it always has).
If any sub-filter is unsupported (e.g. "tree:1", "sparse:<path>", or a
not-yet-supported choice), the recursion bubbles a failure up and the
existing pack-objects/backfill fallback paths kick in.
Add coverage in t6601:
- "combine:blob:none+tree:0" collapses to "tree:0"
- "combine:object:type=blob+blob:limit=3" yields only the blobs
smaller than three bytes
- "combine:object:type=blob+object:type=tree" intersects to empty
- "combine:tree:1+blob:none" reports the "tree:1" error.
Update Documentation/git-pack-objects.adoc to add combine to the
list of supported --filter forms.
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
933 lines
27 KiB
Bash
Executable File
933 lines
27 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
test_description='direct path-walk API tests'
|
|
|
|
. ./test-lib.sh
|
|
|
|
test_expect_success 'setup test repository' '
|
|
git checkout -b base &&
|
|
|
|
# Create tree objects that are only reachable via tags,
|
|
# not from any commit in the history.
|
|
child_blob_oid=$(echo "child blob content" | git hash-object -t blob -w --stdin) &&
|
|
child_tree_oid=$(printf "100644 blob %s\tfile\n" "$child_blob_oid" | git mktree) &&
|
|
tree_tag_oid=$(printf "040000 tree %s\tchild\n" "$child_tree_oid" | git mktree) &&
|
|
git tag -a -m "tree" tree-tag "$tree_tag_oid" &&
|
|
file2_blob_oid=$(echo "tagged tree file2" | git hash-object -t blob -w --stdin) &&
|
|
tree_tag2_oid=$(printf "040000 tree %s\tchild\n100644 blob %s\tfile2\n" "$child_tree_oid" "$file2_blob_oid" | git mktree) &&
|
|
git tag tree-tag2 "$tree_tag2_oid" &&
|
|
|
|
echo blob >file &&
|
|
blob_oid=$(git hash-object -t blob -w --stdin <file) &&
|
|
git tag -a -m "blob" blob-tag "$blob_oid" &&
|
|
echo blob2 >file2 &&
|
|
blob2_oid=$(git hash-object -t blob -w --stdin <file2) &&
|
|
git tag blob-tag2 "$blob2_oid" &&
|
|
|
|
rm -fr file file2 &&
|
|
|
|
mkdir left &&
|
|
mkdir right &&
|
|
echo a >a &&
|
|
echo b >left/b &&
|
|
echo c >right/c &&
|
|
git add . &&
|
|
git commit -m "first" &&
|
|
git tag -m "first" first HEAD &&
|
|
|
|
echo d >right/d &&
|
|
git add right &&
|
|
git commit -m "second" &&
|
|
git tag -a -m "second (under)" second.1 HEAD &&
|
|
git tag -a -m "second (top)" second.2 second.1 &&
|
|
|
|
# Set up file/dir collision in history.
|
|
rm a &&
|
|
mkdir a &&
|
|
echo a >a/a &&
|
|
echo bb >left/b &&
|
|
git add a left &&
|
|
git commit -m "third" &&
|
|
git tag -a -m "third" third &&
|
|
|
|
git checkout -b topic HEAD~1 &&
|
|
echo cc >right/c &&
|
|
git commit -a -m "topic" &&
|
|
git tag -a -m "fourth" fourth
|
|
'
|
|
|
|
test_expect_success 'all' '
|
|
test-tool path-walk -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
3:tree::$(git rev-parse base^{tree})
|
|
3:tree::$(git rev-parse base~1^{tree})
|
|
3:tree::$(git rev-parse base~2^{tree})
|
|
4:blob:a:$(git rev-parse base~2:a)
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
|
|
7:tree:a/:$(git rev-parse base:a)
|
|
8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
9:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
|
|
10:tree:left/:$(git rev-parse base:left)
|
|
10:tree:left/:$(git rev-parse base~2:left)
|
|
11:blob:left/b:$(git rev-parse base~2:left/b)
|
|
11:blob:left/b:$(git rev-parse base:left/b)
|
|
12:tree:right/:$(git rev-parse topic:right)
|
|
12:tree:right/:$(git rev-parse base~1:right)
|
|
12:tree:right/:$(git rev-parse base~2:right)
|
|
13:blob:right/c:$(git rev-parse base~2:right/c)
|
|
13:blob:right/c:$(git rev-parse topic:right/c)
|
|
14:blob:right/d:$(git rev-parse base~1:right/d)
|
|
blobs:10
|
|
commits:4
|
|
tags:7
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'indexed objects' '
|
|
test_when_finished git reset --hard &&
|
|
|
|
# stage change into index, adding a blob but
|
|
# also invalidating the cache-tree for the root
|
|
# and the "left" directory.
|
|
echo bogus >left/c &&
|
|
git add left &&
|
|
|
|
test-tool path-walk -- --indexed-objects >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:blob:a:$(git rev-parse HEAD:a)
|
|
1:blob:left/b:$(git rev-parse HEAD:left/b)
|
|
2:blob:left/c:$(git rev-parse :left/c)
|
|
3:blob:right/c:$(git rev-parse HEAD:right/c)
|
|
4:blob:right/d:$(git rev-parse HEAD:right/d)
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
blobs:5
|
|
commits:0
|
|
tags:0
|
|
trees:1
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'branches and indexed objects mix well' '
|
|
test_when_finished git reset --hard &&
|
|
|
|
# stage change into index, adding a blob but
|
|
# also invalidating the cache-tree for the root
|
|
# and the "right" directory.
|
|
echo fake >right/d &&
|
|
git add right &&
|
|
|
|
test-tool path-walk -- --indexed-objects --branches >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:tree:a/:$(git rev-parse refs/tags/third:a)
|
|
3:tree:left/:$(git rev-parse base:left)
|
|
3:tree:left/:$(git rev-parse base~2:left)
|
|
4:blob:left/b:$(git rev-parse base:left/b)
|
|
4:blob:left/b:$(git rev-parse base~2:left/b)
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
5:tree:right/:$(git rev-parse base~1:right)
|
|
5:tree:right/:$(git rev-parse base~2:right)
|
|
6:blob:right/c:$(git rev-parse base~2:right/c)
|
|
6:blob:right/c:$(git rev-parse topic:right/c)
|
|
7:blob:right/d:$(git rev-parse base~1:right/d)
|
|
7:blob:right/d:$(git rev-parse :right/d)
|
|
8:blob:a:$(git rev-parse base~2:a)
|
|
blobs:7
|
|
commits:4
|
|
tags:0
|
|
trees:10
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'base & topic, sparse' '
|
|
cat >patterns <<-EOF &&
|
|
/*
|
|
!/*/
|
|
/left/
|
|
EOF
|
|
|
|
test-tool path-walk --stdin-pl -- base topic <patterns >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:blob:a:$(git rev-parse base~2:a)
|
|
3:tree:left/:$(git rev-parse base:left)
|
|
3:tree:left/:$(git rev-parse base~2:left)
|
|
4:blob:left/b:$(git rev-parse base~2:left/b)
|
|
4:blob:left/b:$(git rev-parse base:left/b)
|
|
blobs:3
|
|
commits:4
|
|
tags:0
|
|
trees:6
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'base & topic, sparse, no tree pruning' '
|
|
cat >patterns <<-EOF &&
|
|
/*
|
|
!/*/
|
|
/left/
|
|
EOF
|
|
|
|
test-tool path-walk --stdin-pl --no-pl-sparse-trees \
|
|
-- base topic <patterns >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:blob:a:$(git rev-parse base~2:a)
|
|
3:tree:a/:$(git rev-parse base:a)
|
|
4:tree:left/:$(git rev-parse base:left)
|
|
4:tree:left/:$(git rev-parse base~2:left)
|
|
5:blob:left/b:$(git rev-parse base~2:left/b)
|
|
5:blob:left/b:$(git rev-parse base:left/b)
|
|
6:tree:right/:$(git rev-parse topic:right)
|
|
6:tree:right/:$(git rev-parse base~1:right)
|
|
6:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:3
|
|
commits:4
|
|
tags:0
|
|
trees:10
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic only' '
|
|
test-tool path-walk -- topic >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:blob:a:$(git rev-parse base~2:a)
|
|
3:tree:left/:$(git rev-parse base~2:left)
|
|
4:blob:left/b:$(git rev-parse base~2:left/b)
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
5:tree:right/:$(git rev-parse base~1:right)
|
|
5:tree:right/:$(git rev-parse base~2:right)
|
|
6:blob:right/c:$(git rev-parse base~2:right/c)
|
|
6:blob:right/c:$(git rev-parse topic:right/c)
|
|
7:blob:right/d:$(git rev-parse base~1:right/d)
|
|
blobs:5
|
|
commits:3
|
|
tags:0
|
|
trees:7
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base' '
|
|
test-tool path-walk -- topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
2:blob:a:$(git rev-parse topic:a):UNINTERESTING
|
|
3:tree:left/:$(git rev-parse topic:left):UNINTERESTING
|
|
4:blob:left/b:$(git rev-parse topic:left/b):UNINTERESTING
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
6:blob:right/c:$(git rev-parse topic:right/c)
|
|
7:blob:right/d:$(git rev-parse topic:right/d):UNINTERESTING
|
|
blobs:4
|
|
commits:1
|
|
tags:0
|
|
trees:3
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'fourth, blob-tag2, not base' '
|
|
test-tool path-walk -- fourth blob-tag2 --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
1:tag:/tags:$(git rev-parse fourth)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
4:blob:a:$(git rev-parse base~1:a):UNINTERESTING
|
|
5:tree:left/:$(git rev-parse base~1:left):UNINTERESTING
|
|
6:blob:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
|
|
7:tree:right/:$(git rev-parse topic:right)
|
|
8:blob:right/c:$(git rev-parse topic:right/c)
|
|
9:blob:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
|
|
blobs:5
|
|
commits:1
|
|
tags:1
|
|
trees:3
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base, only blobs' '
|
|
test-tool path-walk --no-trees --no-commits \
|
|
-- topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:blob:a:$(git rev-parse topic:a):UNINTERESTING
|
|
1:blob:left/b:$(git rev-parse topic:left/b):UNINTERESTING
|
|
2:blob:right/c:$(git rev-parse topic:right/c)
|
|
3:blob:right/d:$(git rev-parse topic:right/d):UNINTERESTING
|
|
blobs:4
|
|
commits:0
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
# No, this doesn't make a lot of sense for the path-walk API,
|
|
# but it is possible to do.
|
|
test_expect_success 'topic, not base, only commits' '
|
|
test-tool path-walk --no-blobs --no-trees \
|
|
-- topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
commits:1
|
|
blobs:0
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base, only trees' '
|
|
test-tool path-walk --no-blobs --no-commits \
|
|
-- topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:tree::$(git rev-parse topic^{tree})
|
|
1:tree:left/:$(git rev-parse topic:left):UNINTERESTING
|
|
2:tree:right/:$(git rev-parse topic:right)
|
|
commits:0
|
|
blobs:0
|
|
tags:0
|
|
trees:3
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base, boundary' '
|
|
test-tool path-walk -- --boundary topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1):UNINTERESTING
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree}):UNINTERESTING
|
|
2:blob:a:$(git rev-parse base~1:a):UNINTERESTING
|
|
3:tree:left/:$(git rev-parse base~1:left):UNINTERESTING
|
|
4:blob:left/b:$(git rev-parse base~1:left/b):UNINTERESTING
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
5:tree:right/:$(git rev-parse base~1:right):UNINTERESTING
|
|
6:blob:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
|
|
6:blob:right/c:$(git rev-parse topic:right/c)
|
|
7:blob:right/d:$(git rev-parse base~1:right/d):UNINTERESTING
|
|
blobs:5
|
|
commits:2
|
|
tags:0
|
|
trees:5
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base, boundary with pruning' '
|
|
test-tool path-walk --prune -- --boundary topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1):UNINTERESTING
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree}):UNINTERESTING
|
|
2:tree:right/:$(git rev-parse topic:right)
|
|
2:tree:right/:$(git rev-parse base~1:right):UNINTERESTING
|
|
3:blob:right/c:$(git rev-parse base~1:right/c):UNINTERESTING
|
|
3:blob:right/c:$(git rev-parse topic:right/c)
|
|
blobs:2
|
|
commits:2
|
|
tags:0
|
|
trees:4
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic, not base, --edge-aggressive with pruning' '
|
|
test-tool path-walk --prune --edge-aggressive -- topic --not base >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base^{tree}):UNINTERESTING
|
|
2:tree:right/:$(git rev-parse topic:right)
|
|
2:tree:right/:$(git rev-parse base:right):UNINTERESTING
|
|
3:blob:right/c:$(git rev-parse base:right/c):UNINTERESTING
|
|
3:blob:right/c:$(git rev-parse topic:right/c)
|
|
blobs:2
|
|
commits:1
|
|
tags:0
|
|
trees:4
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'trees are reported exactly once' '
|
|
test_when_finished "rm -rf unique-trees" &&
|
|
test_create_repo unique-trees &&
|
|
(
|
|
cd unique-trees &&
|
|
mkdir initial &&
|
|
test_commit initial/file &&
|
|
git switch -c move-to-top &&
|
|
git mv initial/file.t ./ &&
|
|
test_tick &&
|
|
git commit -m moved &&
|
|
git update-ref refs/heads/other HEAD
|
|
) &&
|
|
test-tool -C unique-trees path-walk -- --all >out &&
|
|
tree=$(git -C unique-trees rev-parse HEAD:) &&
|
|
grep "$tree" out >out-filtered &&
|
|
test_line_count = 1 out-filtered
|
|
'
|
|
|
|
test_expect_success 'all, blob:none filter' '
|
|
test-tool path-walk --filter=blob:none -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
3:tree::$(git rev-parse base^{tree})
|
|
3:tree::$(git rev-parse base~1^{tree})
|
|
3:tree::$(git rev-parse base~2^{tree})
|
|
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
5:tree:a/:$(git rev-parse base:a)
|
|
6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
7:tree:left/:$(git rev-parse base:left)
|
|
7:tree:left/:$(git rev-parse base~2:left)
|
|
8:tree:right/:$(git rev-parse topic:right)
|
|
8:tree:right/:$(git rev-parse base~1:right)
|
|
8:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:2
|
|
commits:4
|
|
tags:7
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic only, blob:none filter' '
|
|
test-tool path-walk --filter=blob:none -- topic >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:tree:left/:$(git rev-parse base~2:left)
|
|
3:tree:right/:$(git rev-parse topic:right)
|
|
3:tree:right/:$(git rev-parse base~1:right)
|
|
3:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:0
|
|
commits:3
|
|
tags:0
|
|
trees:7
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, blob:limit=0 filter' '
|
|
test-tool path-walk --filter=blob:limit=0 -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
3:tree::$(git rev-parse base^{tree})
|
|
3:tree::$(git rev-parse base~1^{tree})
|
|
3:tree::$(git rev-parse base~2^{tree})
|
|
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
4:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
5:tree:a/:$(git rev-parse base:a)
|
|
6:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
7:tree:left/:$(git rev-parse base:left)
|
|
7:tree:left/:$(git rev-parse base~2:left)
|
|
8:tree:right/:$(git rev-parse topic:right)
|
|
8:tree:right/:$(git rev-parse base~1:right)
|
|
8:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:2
|
|
commits:4
|
|
tags:7
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, blob:limit=3 filter' '
|
|
test-tool path-walk --filter=blob:limit=3 -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
3:tree::$(git rev-parse base^{tree})
|
|
3:tree::$(git rev-parse base~1^{tree})
|
|
3:tree::$(git rev-parse base~2^{tree})
|
|
4:blob:a:$(git rev-parse base~2:a)
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
6:tree:a/:$(git rev-parse base:a)
|
|
7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
8:tree:left/:$(git rev-parse base:left)
|
|
8:tree:left/:$(git rev-parse base~2:left)
|
|
9:blob:left/b:$(git rev-parse base~2:left/b)
|
|
10:tree:right/:$(git rev-parse topic:right)
|
|
10:tree:right/:$(git rev-parse base~1:right)
|
|
10:tree:right/:$(git rev-parse base~2:right)
|
|
11:blob:right/c:$(git rev-parse base~2:right/c)
|
|
12:blob:right/d:$(git rev-parse base~1:right/d)
|
|
blobs:6
|
|
commits:4
|
|
tags:7
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, tree:0 filter' '
|
|
test-tool path-walk --filter=tree:0 -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
|
|
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
|
|
blobs:2
|
|
commits:4
|
|
tags:7
|
|
trees:2
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic only, tree:0 filter' '
|
|
test-tool path-walk --filter=tree:0 -- topic >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
blobs:0
|
|
commits:3
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'tree:1 filter is rejected' '
|
|
test_must_fail test-tool path-walk --filter=tree:1 -- --all 2>err &&
|
|
test_grep "tree:1 filter not supported by the path-walk API" err
|
|
'
|
|
|
|
test_expect_success 'all, object:type=commit filter' '
|
|
test-tool path-walk --filter=object:type=commit -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
blobs:0
|
|
commits:4
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, object:type=tag filter' '
|
|
test-tool path-walk --filter=object:type=tag -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:tag:/tags:$(git rev-parse refs/tags/first)
|
|
0:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
0:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
0:tag:/tags:$(git rev-parse refs/tags/third)
|
|
0:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
0:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
0:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
blobs:0
|
|
commits:0
|
|
tags:7
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, object:type=tree filter' '
|
|
test-tool path-walk --filter=object:type=tree -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:tree::$(git rev-parse topic^{tree})
|
|
0:tree::$(git rev-parse base^{tree})
|
|
0:tree::$(git rev-parse base~1^{tree})
|
|
0:tree::$(git rev-parse base~2^{tree})
|
|
1:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
1:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
2:tree:a/:$(git rev-parse base:a)
|
|
3:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
4:tree:left/:$(git rev-parse base:left)
|
|
4:tree:left/:$(git rev-parse base~2:left)
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
5:tree:right/:$(git rev-parse base~1:right)
|
|
5:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:0
|
|
commits:0
|
|
tags:0
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, object:type=blob filter' '
|
|
test-tool path-walk --filter=object:type=blob -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
1:blob:a:$(git rev-parse base~2:a)
|
|
2:blob:left/b:$(git rev-parse base:left/b)
|
|
2:blob:left/b:$(git rev-parse base~2:left/b)
|
|
3:blob:right/c:$(git rev-parse base~2:right/c)
|
|
3:blob:right/c:$(git rev-parse topic:right/c)
|
|
4:blob:right/d:$(git rev-parse base~1:right/d)
|
|
blobs:8
|
|
commits:0
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, combine:blob:none+tree:0 filter' '
|
|
test-tool path-walk \
|
|
--filter=combine:blob:none+tree:0 -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{tree})
|
|
3:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2)
|
|
blobs:2
|
|
commits:4
|
|
tags:7
|
|
trees:2
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, combine:object:type=blob+blob:limit=3 filter' '
|
|
test-tool path-walk \
|
|
--filter=combine:object:type=blob+blob:limit=3 \
|
|
-- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
0:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
1:blob:a:$(git rev-parse base~2:a)
|
|
2:blob:left/b:$(git rev-parse base~2:left/b)
|
|
3:blob:right/c:$(git rev-parse base~2:right/c)
|
|
4:blob:right/d:$(git rev-parse base~1:right/d)
|
|
blobs:6
|
|
commits:0
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'all, combine of disjoint object:types is empty' '
|
|
test-tool path-walk \
|
|
--filter=combine:object:type=blob+object:type=tree \
|
|
-- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
blobs:0
|
|
commits:0
|
|
tags:0
|
|
trees:0
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'combine: rejects unsupported subfilters' '
|
|
test_must_fail test-tool path-walk \
|
|
--filter=combine:tree:1+blob:none -- --all 2>err &&
|
|
test_grep "tree:1 filter not supported by the path-walk API" err
|
|
'
|
|
|
|
test_expect_success 'setup sparse filter blob' '
|
|
# Cone-mode patterns: include root, exclude all dirs, include left/
|
|
cat >patterns <<-\EOF &&
|
|
/*
|
|
!/*/
|
|
/left/
|
|
EOF
|
|
sparse_oid=$(git hash-object -w -t blob patterns)
|
|
'
|
|
|
|
test_expect_success 'all, sparse:oid filter' '
|
|
test-tool path-walk --filter=sparse:oid=$sparse_oid -- --all >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/first)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.1)
|
|
1:tag:/tags:$(git rev-parse refs/tags/second.2)
|
|
1:tag:/tags:$(git rev-parse refs/tags/third)
|
|
1:tag:/tags:$(git rev-parse refs/tags/fourth)
|
|
1:tag:/tags:$(git rev-parse refs/tags/tree-tag)
|
|
1:tag:/tags:$(git rev-parse refs/tags/blob-tag)
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag^{})
|
|
2:blob:/tagged-blobs:$(git rev-parse refs/tags/blob-tag2^{})
|
|
3:tree::$(git rev-parse topic^{tree})
|
|
3:tree::$(git rev-parse base^{tree})
|
|
3:tree::$(git rev-parse base~1^{tree})
|
|
3:tree::$(git rev-parse base~2^{tree})
|
|
4:blob:a:$(git rev-parse base~2:a)
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag^{})
|
|
5:tree:/tagged-trees:$(git rev-parse refs/tags/tree-tag2^{})
|
|
6:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
|
|
7:tree:a/:$(git rev-parse base:a)
|
|
8:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
9:tree:left/:$(git rev-parse base:left)
|
|
9:tree:left/:$(git rev-parse base~2:left)
|
|
10:blob:left/b:$(git rev-parse base~2:left/b)
|
|
10:blob:left/b:$(git rev-parse base:left/b)
|
|
11:tree:right/:$(git rev-parse topic:right)
|
|
11:tree:right/:$(git rev-parse base~1:right)
|
|
11:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:6
|
|
commits:4
|
|
tags:7
|
|
trees:13
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_expect_success 'topic only, sparse:oid filter' '
|
|
test-tool path-walk --filter=sparse:oid=$sparse_oid -- topic >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse topic)
|
|
0:commit::$(git rev-parse base~1)
|
|
0:commit::$(git rev-parse base~2)
|
|
1:tree::$(git rev-parse topic^{tree})
|
|
1:tree::$(git rev-parse base~1^{tree})
|
|
1:tree::$(git rev-parse base~2^{tree})
|
|
2:blob:a:$(git rev-parse base~2:a)
|
|
3:tree:left/:$(git rev-parse base~2:left)
|
|
4:blob:left/b:$(git rev-parse base~2:left/b)
|
|
5:tree:right/:$(git rev-parse topic:right)
|
|
5:tree:right/:$(git rev-parse base~1:right)
|
|
5:tree:right/:$(git rev-parse base~2:right)
|
|
blobs:2
|
|
commits:3
|
|
tags:0
|
|
trees:7
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
# Demonstrate the SEEN flag ordering issue: when the same tree/blob OID
|
|
# appears at two sibling paths where one is in-cone and the other is
|
|
# out-of-cone, the path-walk must still discover blobs at the in-cone
|
|
# path even when the shared tree OID was first encountered out-of-cone.
|
|
# Since sparse:oid includes all trees, the out-of-cone tree (aaa/) is
|
|
# walked first, and its blob is skipped. The path-walk then re-walks
|
|
# the same tree OID at the in-cone path (zzz/) to find the blob there.
|
|
|
|
test_expect_success 'setup shared tree OID across cone boundary' '
|
|
git checkout --orphan shared-tree &&
|
|
git rm -rf . &&
|
|
mkdir aaa zzz &&
|
|
echo "shared content" >aaa/file &&
|
|
echo "shared content" >zzz/file &&
|
|
echo "root file" >rootfile &&
|
|
git add aaa zzz rootfile &&
|
|
git commit -m "aaa and zzz have same tree OID" &&
|
|
|
|
# Verify they really share a tree OID
|
|
aaa_tree=$(git rev-parse HEAD:aaa) &&
|
|
zzz_tree=$(git rev-parse HEAD:zzz) &&
|
|
test "$aaa_tree" = "$zzz_tree" &&
|
|
|
|
# Cone pattern: include root + zzz/ (not aaa/)
|
|
cat >shared-patterns <<-\EOF &&
|
|
/*
|
|
!/*/
|
|
/zzz/
|
|
EOF
|
|
shared_sparse_oid=$(git hash-object -w -t blob shared-patterns)
|
|
'
|
|
|
|
test_expect_success 'sparse:oid with shared tree OID across cone boundary' '
|
|
test-tool path-walk \
|
|
--filter=sparse:oid=$shared_sparse_oid \
|
|
-- shared-tree >out &&
|
|
|
|
cat >expect <<-EOF &&
|
|
0:commit::$(git rev-parse shared-tree)
|
|
1:tree::$(git rev-parse shared-tree^{tree})
|
|
2:blob:rootfile:$(git rev-parse shared-tree:rootfile)
|
|
3:tree:aaa/:$(git rev-parse shared-tree:aaa)
|
|
4:tree:zzz/:$(git rev-parse shared-tree:zzz)
|
|
5:blob:zzz/file:$(git rev-parse shared-tree:zzz/file)
|
|
blobs:2
|
|
commits:1
|
|
tags:0
|
|
trees:3
|
|
EOF
|
|
|
|
test_cmp_sorted expect out
|
|
'
|
|
|
|
test_done
|