
The path-walk API currently uses a stack-based approach to recursing through the list of paths within the repository. This guarantees that after a tree path is explored, all paths contained within that tree path will be explored before continuing to explore siblings of that tree path. The initial motivation of this depth-first approach was to minimize memory pressure while exploring the repository. A breadth-first approach would have too many "active" paths being stored in the paths_to_lists map. We can take this approach one step further by making sure that blob paths are visited before tree paths. This allows the API to free the memory for these blob objects before continuing to perform the depth-first search. This modifies the order in which we visit siblings, but does not change the fact that we are performing depth-first search. To achieve this goal, use a priority queue with a custom sorting method. The sort needs to handle tags, blobs, and trees (commits are handled slightly differently). When objects share a type, we can sort by path name. This will keep children of the latest path to leave the stack be preferred over the rest of the paths in the stack, since they agree in prefix up to and including a directory separator. When the types are different, we can prefer tags over other types and blobs over trees. This causes significant adjustments to t6601-path-walk.sh to rearrange the order of the visited paths. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
369 lines
10 KiB
Bash
Executable File
369 lines
10 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
|
|
|
test_description='direct path-walk API tests'
|
|
|
|
. ./test-lib.sh
|
|
|
|
test_expect_success 'setup test repository' '
|
|
git checkout -b base &&
|
|
|
|
# Make some objects that will only be reachable
|
|
# via non-commit tags.
|
|
mkdir child &&
|
|
echo file >child/file &&
|
|
git add child &&
|
|
git commit -m "will abandon" &&
|
|
git tag -a -m "tree" tree-tag HEAD^{tree} &&
|
|
echo file2 >file2 &&
|
|
git add file2 &&
|
|
git commit --amend -m "will abandon" &&
|
|
git tag tree-tag2 HEAD^{tree} &&
|
|
|
|
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 child file file2 &&
|
|
|
|
mkdir left &&
|
|
mkdir right &&
|
|
echo a >a &&
|
|
echo b >left/b &&
|
|
echo c >right/c &&
|
|
git add . &&
|
|
git commit --amend -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})
|
|
3:tree::$(git rev-parse refs/tags/tree-tag^{})
|
|
3:tree::$(git rev-parse refs/tags/tree-tag2^{})
|
|
4:blob:a:$(git rev-parse base~2:a)
|
|
5:blob:file2:$(git rev-parse refs/tags/tree-tag2^{}:file2)
|
|
6:tree:a/:$(git rev-parse base:a)
|
|
7:tree:child/:$(git rev-parse refs/tags/tree-tag:child)
|
|
8:blob:child/file:$(git rev-parse refs/tags/tree-tag:child/file)
|
|
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)
|
|
12:blob:right/c:$(git rev-parse base~2:right/c)
|
|
12:blob:right/c:$(git rev-parse topic:right/c)
|
|
13: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 '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 '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_done
|