1
0
mirror of https://github.com/git/git synced 2024-07-05 00:58:49 +00:00
git/t/t6120-describe.sh

483 lines
13 KiB
Bash
Raw Normal View History

#!/bin/sh
test_description='test describe'
# o---o-----o----o----o-------o----x
# \ D,R e /
# \---o-------------o-'
# \ B /
# `-o----o----o-'
# A c
#
# First parent of a merge commit is on the same line, second parent below.
. ./test-lib.sh
check_describe () {
expect="$1"
shift
describe_opts="$@"
test_expect_success "describe $describe_opts" '
R=$(git describe $describe_opts 2>err.actual) &&
case "$R" in
$expect) echo happy ;;
*) echo "Oops - $R is not $expect" &&
false ;;
esac
'
}
test_expect_success setup '
test_tick &&
echo one >file && git add file && git commit -m initial &&
one=$(git rev-parse HEAD) &&
git describe --always HEAD &&
test_tick &&
echo two >file && git add file && git commit -m second &&
two=$(git rev-parse HEAD) &&
test_tick &&
echo three >file && git add file && git commit -m third &&
test_tick &&
echo A >file && git add file && git commit -m A &&
test_tick &&
git tag -a -m A A &&
test_tick &&
echo c >file && git add file && git commit -m c &&
test_tick &&
git tag c &&
git reset --hard $two &&
test_tick &&
echo B >side && git add side && git commit -m B &&
test_tick &&
git tag -a -m B B &&
test_tick &&
git merge -m Merged c &&
merged=$(git rev-parse HEAD) &&
git reset --hard $two &&
test_tick &&
echo D >another && git add another && git commit -m D &&
test_tick &&
git tag -a -m D D &&
test_tick &&
git tag -a -m R R &&
test_tick &&
echo DD >another && git commit -a -m another &&
test_tick &&
git tag e &&
test_tick &&
echo DDD >another && git commit -a -m "yet another" &&
test_tick &&
git merge -m Merged $merged &&
test_tick &&
echo X >file && echo X >side && git add file side &&
git commit -m x
'
check_describe A-* HEAD
check_describe A-* HEAD^
check_describe R-* HEAD^^
check_describe A-* HEAD^^2
check_describe B HEAD^^2^
check_describe R-* HEAD^^^
check_describe c-* --tags HEAD
check_describe c-* --tags HEAD^
check_describe e-* --tags HEAD^^
check_describe c-* --tags HEAD^^2
check_describe B --tags HEAD^^2^
check_describe e --tags HEAD^^^
check_describe heads/master --all HEAD
check_describe tags/c-* --all HEAD^
check_describe tags/e --all HEAD^^^
check_describe B-0-* --long HEAD^^2^
check_describe A-3-* --long HEAD^^2
check_describe c-7-* --tags
check_describe e-3-* --first-parent --tags
test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
echo "A^0" >expect &&
git checkout A &&
test_when_finished "git checkout -" &&
git describe --contains >actual &&
test_cmp expect actual
'
check_describe tags/A --all A^0
test_expect_success 'no warning was displayed for A' '
tests: use 'test_must_be_empty' instead of 'test_cmp <empty> <out>' Using 'test_must_be_empty' is shorter and more idiomatic than >empty && test_cmp empty out as it saves the creation of an empty file. Furthermore, sometimes the expected empty file doesn't have such a descriptive name like 'empty', and its creation is far away from the place where it's finally used for comparison (e.g. in 't7600-merge.sh', where two expected empty files are created in the 'setup' test, but are used only about 500 lines later). These cases were found by instrumenting 'test_cmp' to error out the test script when it's used to compare empty files, and then converted manually. Note that even after this patch there still remain a lot of cases where we use 'test_cmp' to check empty files: - Sometimes the expected output is not hard-coded in the test, but 'test_cmp' is used to ensure that two similar git commands produce the same output, and that output happens to be empty, e.g. the test 'submodule update --merge - ignores --merge for new submodules' in 't7406-submodule-update.sh'. - Repetitive common tasks, including preparing the expected results and running 'test_cmp', are often extracted into a helper function, and some of this helper's callsites expect no output. - For the same reason as above, the whole 'test_expect_success' block is within a helper function, e.g. in 't3070-wildmatch.sh'. - Or 'test_cmp' is invoked in a loop, e.g. the test 'cvs update (-p)' in 't9400-git-cvsserver-server.sh'. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-19 21:57:25 +00:00
test_must_be_empty err.actual
'
test_expect_success 'rename tag A to Q locally' '
mv .git/refs/tags/A .git/refs/tags/Q
'
cat - >err.expect <<EOF
warning: tag 'A' is really 'Q' here
EOF
check_describe A-* HEAD
test_expect_success 'warning was displayed for Q' '
test_i18ncmp err.expect err.actual
'
test_expect_success 'rename tag Q back to A' '
mv .git/refs/tags/Q .git/refs/tags/A
'
test_expect_success 'pack tag refs' 'git pack-refs'
check_describe A-* HEAD
test_expect_success 'describe works from outside repo using --git-dir' '
git clone --bare "$TRASH_DIRECTORY" "$TRASH_DIRECTORY/bare" &&
git --git-dir "$TRASH_DIRECTORY/bare" describe >out &&
grep -E "^A-[1-9][0-9]?-g[0-9a-f]+$" out
'
Teach "git describe" --dirty option With the --dirty option, git describe works on HEAD but append s"-dirty" iff the contents of the work tree differs from HEAD. E.g. $ git describe --dirty v1.6.5-15-gc274db7 $ echo >> Makefile $ git describe --dirty v1.6.5-15-gc274db7-dirty The --dirty option can also be used to specify what is appended, instead of the default string "-dirty". $ git describe --dirty=.mod v1.6.5-15-gc274db7.mod Many build scripts use `git describe` to produce a version number based on the description of HEAD (on which the work tree is based) + saying that if the build contains uncommitted changes. This patch helps the writing of such scripts since `git describe --dirty` does directly the intended thing. Three possiblities were considered while discussing this new feature: 1. Describe the work tree by default and describe HEAD only if "HEAD" is explicitly specified Pro: does the right thing by default (both for users and for scripts) Pro: other git commands that works on the work tree by default Con: breaks existing scripts used by the Linux kernel and other projects 2. Use --worktree instead of --dirty Pro: does what it says: "git describe --worktree" describes the work tree Con: other commands do not require a --worktree option when working on the work tree (it often is the default mode for them) Con: unusable with an optional value: "git describe --worktree=.mod" is quite unintuitive. 3. Use --dirty as in this patch Pro: makes sense to specify an optional value (what the dirty mark is) Pro: does not have any of the big cons of previous alternatives * does not break scripts * is not inconsistent with other git commands This patch takes the third approach. Signed-off-by: Jean Privat <jean@pryen.org> Acked-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-10-21 13:35:22 +00:00
check_describe "A-*[0-9a-f]" --dirty
test_expect_success 'describe --dirty with --work-tree' '
(
cd "$TEST_DIRECTORY" &&
git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out"
) &&
grep -E "^A-[1-9][0-9]?-g[0-9a-f]+$" out
'
Teach "git describe" --dirty option With the --dirty option, git describe works on HEAD but append s"-dirty" iff the contents of the work tree differs from HEAD. E.g. $ git describe --dirty v1.6.5-15-gc274db7 $ echo >> Makefile $ git describe --dirty v1.6.5-15-gc274db7-dirty The --dirty option can also be used to specify what is appended, instead of the default string "-dirty". $ git describe --dirty=.mod v1.6.5-15-gc274db7.mod Many build scripts use `git describe` to produce a version number based on the description of HEAD (on which the work tree is based) + saying that if the build contains uncommitted changes. This patch helps the writing of such scripts since `git describe --dirty` does directly the intended thing. Three possiblities were considered while discussing this new feature: 1. Describe the work tree by default and describe HEAD only if "HEAD" is explicitly specified Pro: does the right thing by default (both for users and for scripts) Pro: other git commands that works on the work tree by default Con: breaks existing scripts used by the Linux kernel and other projects 2. Use --worktree instead of --dirty Pro: does what it says: "git describe --worktree" describes the work tree Con: other commands do not require a --worktree option when working on the work tree (it often is the default mode for them) Con: unusable with an optional value: "git describe --worktree=.mod" is quite unintuitive. 3. Use --dirty as in this patch Pro: makes sense to specify an optional value (what the dirty mark is) Pro: does not have any of the big cons of previous alternatives * does not break scripts * is not inconsistent with other git commands This patch takes the third approach. Signed-off-by: Jean Privat <jean@pryen.org> Acked-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-10-21 13:35:22 +00:00
test_expect_success 'set-up dirty work tree' '
echo >>file
'
check_describe "A-*[0-9a-f]-dirty" --dirty
test_expect_success 'describe --dirty with --work-tree (dirty)' '
(
cd "$TEST_DIRECTORY" &&
git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty >"$TRASH_DIRECTORY/out"
) &&
grep -E "^A-[1-9][0-9]?-g[0-9a-f]+-dirty$" out
'
Teach "git describe" --dirty option With the --dirty option, git describe works on HEAD but append s"-dirty" iff the contents of the work tree differs from HEAD. E.g. $ git describe --dirty v1.6.5-15-gc274db7 $ echo >> Makefile $ git describe --dirty v1.6.5-15-gc274db7-dirty The --dirty option can also be used to specify what is appended, instead of the default string "-dirty". $ git describe --dirty=.mod v1.6.5-15-gc274db7.mod Many build scripts use `git describe` to produce a version number based on the description of HEAD (on which the work tree is based) + saying that if the build contains uncommitted changes. This patch helps the writing of such scripts since `git describe --dirty` does directly the intended thing. Three possiblities were considered while discussing this new feature: 1. Describe the work tree by default and describe HEAD only if "HEAD" is explicitly specified Pro: does the right thing by default (both for users and for scripts) Pro: other git commands that works on the work tree by default Con: breaks existing scripts used by the Linux kernel and other projects 2. Use --worktree instead of --dirty Pro: does what it says: "git describe --worktree" describes the work tree Con: other commands do not require a --worktree option when working on the work tree (it often is the default mode for them) Con: unusable with an optional value: "git describe --worktree=.mod" is quite unintuitive. 3. Use --dirty as in this patch Pro: makes sense to specify an optional value (what the dirty mark is) Pro: does not have any of the big cons of previous alternatives * does not break scripts * is not inconsistent with other git commands This patch takes the third approach. Signed-off-by: Jean Privat <jean@pryen.org> Acked-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-10-21 13:35:22 +00:00
check_describe "A-*[0-9a-f].mod" --dirty=.mod
test_expect_success 'describe --dirty=.mod with --work-tree (dirty)' '
(
cd "$TEST_DIRECTORY" &&
git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --dirty=.mod >"$TRASH_DIRECTORY/out"
) &&
grep -E "^A-[1-9][0-9]?-g[0-9a-f]+.mod$" out
'
Teach "git describe" --dirty option With the --dirty option, git describe works on HEAD but append s"-dirty" iff the contents of the work tree differs from HEAD. E.g. $ git describe --dirty v1.6.5-15-gc274db7 $ echo >> Makefile $ git describe --dirty v1.6.5-15-gc274db7-dirty The --dirty option can also be used to specify what is appended, instead of the default string "-dirty". $ git describe --dirty=.mod v1.6.5-15-gc274db7.mod Many build scripts use `git describe` to produce a version number based on the description of HEAD (on which the work tree is based) + saying that if the build contains uncommitted changes. This patch helps the writing of such scripts since `git describe --dirty` does directly the intended thing. Three possiblities were considered while discussing this new feature: 1. Describe the work tree by default and describe HEAD only if "HEAD" is explicitly specified Pro: does the right thing by default (both for users and for scripts) Pro: other git commands that works on the work tree by default Con: breaks existing scripts used by the Linux kernel and other projects 2. Use --worktree instead of --dirty Pro: does what it says: "git describe --worktree" describes the work tree Con: other commands do not require a --worktree option when working on the work tree (it often is the default mode for them) Con: unusable with an optional value: "git describe --worktree=.mod" is quite unintuitive. 3. Use --dirty as in this patch Pro: makes sense to specify an optional value (what the dirty mark is) Pro: does not have any of the big cons of previous alternatives * does not break scripts * is not inconsistent with other git commands This patch takes the third approach. Signed-off-by: Jean Privat <jean@pryen.org> Acked-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-10-21 13:35:22 +00:00
test_expect_success 'describe --dirty HEAD' '
test_must_fail git describe --dirty HEAD
'
test_expect_success 'set-up matching pattern tests' '
git tag -a -m test-annotated test-annotated &&
echo >>file &&
test_tick &&
git commit -a -m "one more" &&
git tag test1-lightweight &&
echo >>file &&
test_tick &&
git commit -a -m "yet another" &&
git tag test2-lightweight &&
echo >>file &&
test_tick &&
git commit -a -m "even more"
'
check_describe "test-annotated-*" --match="test-*"
check_describe "test1-lightweight-*" --tags --match="test1-*"
check_describe "test2-lightweight-*" --tags --match="test2-*"
check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
check_describe "test2-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^
check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test3-*" HEAD
check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
test_expect_success 'set-up branches' '
git branch branch_A A &&
git branch branch_C c &&
git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" &&
git update-ref refs/remotes/origin/remote_branch_C "c^{commit}" &&
git update-ref refs/original/original_branch_A test-annotated~2
'
check_describe "heads/branch_A*" --all --match="branch_*" --exclude="branch_C" HEAD
check_describe "remotes/origin/remote_branch_A*" --all --match="origin/remote_branch_*" --exclude="origin/remote_branch_C" HEAD
check_describe "original/original_branch_A*" --all test-annotated~1
test_expect_success '--match does not work for other types' '
test_must_fail git describe --all --match="*original_branch_*" test-annotated~1
'
test_expect_success '--exclude does not work for other types' '
R=$(git describe --all --exclude="any_pattern_even_not_matching" test-annotated~1) &&
case "$R" in
*original_branch_A*) echo "fail: Found unknown reference $R with --exclude"
false;;
*) echo ok: Found some known type;;
esac
'
test_expect_success 'name-rev with exact tags' '
echo A >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
git name-rev --tags --name-only $tag_object >actual &&
test_cmp expect actual &&
echo "A^0" >expect &&
tagged_commit=$(git rev-parse "refs/tags/A^0") &&
git name-rev --tags --name-only $tagged_commit >actual &&
test_cmp expect actual
'
test_expect_success 'name-rev --all' '
>expect.unsorted &&
for rev in $(git rev-list --all)
do
git name-rev $rev >>expect.unsorted
done &&
sort <expect.unsorted >expect &&
git name-rev --all >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
test_expect_success 'name-rev --stdin' '
>expect.unsorted &&
for rev in $(git rev-list --all)
do
name=$(git name-rev --name-only $rev) &&
echo "$rev ($name)" >>expect.unsorted
done &&
sort <expect.unsorted >expect &&
git rev-list --all | git name-rev --stdin >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
git describe --contains $tag_object >actual &&
test_cmp expect actual &&
echo "A^0" >expect &&
tagged_commit=$(git rev-parse "refs/tags/A^0") &&
git describe --contains $tagged_commit >actual &&
test_cmp expect actual
'
test_expect_success 'describe --contains and --match' '
echo "A^0" >expect &&
tagged_commit=$(git rev-parse "refs/tags/A^0") &&
test_must_fail git describe --contains --match="B" $tagged_commit &&
git describe --contains --match="B" --match="A" $tagged_commit >actual &&
test_cmp expect actual
'
test_expect_success 'describe --exclude' '
echo "c~1" >expect &&
tagged_commit=$(git rev-parse "refs/tags/A^0") &&
test_must_fail git describe --contains --match="B" $tagged_commit &&
git describe --contains --match="?" --exclude="A" $tagged_commit >actual &&
test_cmp expect actual
'
test_expect_success 'describe --contains and --no-match' '
echo "A^0" >expect &&
tagged_commit=$(git rev-parse "refs/tags/A^0") &&
git describe --contains --match="B" --no-match $tagged_commit >actual &&
test_cmp expect actual
'
test_expect_success 'setup and absorb a submodule' '
test_create_repo sub1 &&
test_commit -C sub1 initial &&
git submodule add ./sub1 &&
git submodule absorbgitdirs &&
git commit -a -m "add submodule" &&
git describe --dirty >expect &&
git describe --broken >out &&
test_cmp expect out
'
test_expect_success 'describe chokes on severely broken submodules' '
mv .git/modules/sub1/ .git/modules/sub_moved &&
test_must_fail git describe --dirty
'
test_expect_success 'describe ignoring a broken submodule' '
git describe --broken >out &&
grep broken out
'
test_expect_success 'describe with --work-tree ignoring a broken submodule' '
(
cd "$TEST_DIRECTORY" &&
git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --broken >"$TRASH_DIRECTORY/out"
) &&
test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
grep broken out
'
builtin/describe.c: describe a blob Sometimes users are given a hash of an object and they want to identify it further (ex.: Use verify-pack to find the largest blobs, but what are these? or [1]) When describing commits, we try to anchor them to tags or refs, as these are conceptually on a higher level than the commit. And if there is no ref or tag that matches exactly, we're out of luck. So we employ a heuristic to make up a name for the commit. These names are ambiguous, there might be different tags or refs to anchor to, and there might be different path in the DAG to travel to arrive at the commit precisely. When describing a blob, we want to describe the blob from a higher layer as well, which is a tuple of (commit, deep/path) as the tree objects involved are rather uninteresting. The same blob can be referenced by multiple commits, so how we decide which commit to use? This patch implements a rather naive approach on this: As there are no back pointers from blobs to commits in which the blob occurs, we'll start walking from any tips available, listing the blobs in-order of the commit and once we found the blob, we'll take the first commit that listed the blob. For example git describe --tags v0.99:Makefile conversion-901-g7672db20c2:Makefile tells us the Makefile as it was in v0.99 was introduced in commit 7672db20. The walking is performed in reverse order to show the introduction of a blob rather than its last occurrence. [1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
test_expect_success 'describe a blob at a directly tagged commit' '
echo "make it a unique blob" >file &&
git add file && git commit -m "content in file" &&
git tag -a -m "latest annotated tag" unique-file &&
git describe HEAD:file >actual &&
echo "unique-file:file" >expect &&
test_cmp expect actual
'
test_expect_success 'describe a blob with its first introduction' '
git commit --allow-empty -m "empty commit" &&
git rm file &&
git commit -m "delete blob" &&
git revert HEAD &&
git commit --allow-empty -m "empty commit" &&
git describe HEAD:file >actual &&
echo "unique-file:file" >expect &&
test_cmp expect actual
'
test_expect_success 'describe directly tagged blob' '
git tag test-blob unique-file:file &&
git describe test-blob >actual &&
echo "unique-file:file" >expect &&
# suboptimal: we rather want to see "test-blob"
test_cmp expect actual
'
test_expect_success 'describe tag object' '
git tag test-blob-1 -a -m msg unique-file:file &&
test_must_fail git describe test-blob-1 2>actual &&
test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
'
test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
i=1 &&
while test $i -lt 8000
do
echo "commit refs/heads/master
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
data <<EOF
commit #$i
EOF"
test $i = 1 && echo "from refs/heads/master^0"
i=$(($i + 1))
done | git fast-import &&
git checkout master &&
git tag far-far-away HEAD^ &&
echo "HEAD~4000 tags/far-far-away~3999" >expect &&
git name-rev HEAD~4000 >actual &&
test_cmp expect actual &&
run_with_limited_stack git name-rev HEAD~4000 >actual &&
test_cmp expect actual
'
test_expect_success ULIMIT_STACK_SIZE 'describe works in a deep repo' '
git tag -f far-far-away HEAD~7999 &&
echo "far-far-away" >expect &&
git describe --tags --abbrev=0 HEAD~4000 >actual &&
test_cmp expect actual &&
run_with_limited_stack git describe --tags --abbrev=0 HEAD~4000 >actual &&
test_cmp expect actual
'
check_describe tags/A --all A
check_describe tags/c --all c
check_describe heads/branch_A --all --match='branch_*' branch_A
describe: confirm that blobs actually exist Prior to 644eb60bd0 (builtin/describe.c: describe a blob, 2017-11-15), we noticed and complained about missing objects, since they were not valid commits: $ git describe 0000000000000000000000000000000000000000 fatal: 0000000000000000000000000000000000000000 is not a valid 'commit' object After that commit, we feed any non-commit to lookup_blob(), and complain only if it returns NULL. But the lookup_* functions do not actually look at the on-disk object database at all. They return an entry from the in-memory object hash if present (and if it matches the requested type), and otherwise auto-create a "struct object" of the requested type. A missing object would hit that latter case: we create a bogus blob struct, walk all of history looking for it, and then exit successfully having produced no output. One reason nobody may have noticed this is that some related cases do still work OK: 1. If we ask for a tree by sha1, then the call to lookup_commit_referecne_gently() would have parsed it, and we would have its true type in the in-memory object hash. 2. If we ask for a name that doesn't exist but isn't a 40-hex sha1, then get_oid() would complain before we even look at the objects at all. We can fix this by replacing the lookup_blob() call with a check of the true type via sha1_object_info(). This is not quite as efficient as we could possibly make this check. We know in most cases that the object was already parsed in the earlier commit lookup, so we could call lookup_object(), which does auto-create, and check the resulting struct's type (or NULL). However it's not worth the fragility nor code complexity to save a single object lookup. The new tests cover this case, as well as that of a tree-by-sha1 (which does work as described above, but was not explicitly tested). Noticed-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Jeff King <peff@peff.net> Acked-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-12 17:23:06 +00:00
test_expect_success 'describe complains about tree object' '
test_must_fail git describe HEAD^{tree}
'
test_expect_success 'describe complains about missing object' '
test_must_fail git describe $ZERO_OID
describe: confirm that blobs actually exist Prior to 644eb60bd0 (builtin/describe.c: describe a blob, 2017-11-15), we noticed and complained about missing objects, since they were not valid commits: $ git describe 0000000000000000000000000000000000000000 fatal: 0000000000000000000000000000000000000000 is not a valid 'commit' object After that commit, we feed any non-commit to lookup_blob(), and complain only if it returns NULL. But the lookup_* functions do not actually look at the on-disk object database at all. They return an entry from the in-memory object hash if present (and if it matches the requested type), and otherwise auto-create a "struct object" of the requested type. A missing object would hit that latter case: we create a bogus blob struct, walk all of history looking for it, and then exit successfully having produced no output. One reason nobody may have noticed this is that some related cases do still work OK: 1. If we ask for a tree by sha1, then the call to lookup_commit_referecne_gently() would have parsed it, and we would have its true type in the in-memory object hash. 2. If we ask for a name that doesn't exist but isn't a 40-hex sha1, then get_oid() would complain before we even look at the objects at all. We can fix this by replacing the lookup_blob() call with a check of the true type via sha1_object_info(). This is not quite as efficient as we could possibly make this check. We know in most cases that the object was already parsed in the earlier commit lookup, so we could call lookup_object(), which does auto-create, and check the resulting struct's type (or NULL). However it's not worth the fragility nor code complexity to save a single object lookup. The new tests cover this case, as well as that of a tree-by-sha1 (which does work as described above, but was not explicitly tested). Noticed-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Jeff King <peff@peff.net> Acked-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-12 17:23:06 +00:00
'
test_expect_success 'name-rev a rev shortly after epoch' '
test_when_finished "git checkout master" &&
git checkout --orphan no-timestamp-underflow &&
# Any date closer to epoch than the CUTOFF_DATE_SLOP constant
# in builtin/name-rev.c.
GIT_COMMITTER_DATE="@1234 +0000" \
git commit -m "committer date shortly after epoch" &&
old_commit_oid=$(git rev-parse HEAD) &&
echo "$old_commit_oid no-timestamp-underflow" >expect &&
git name-rev $old_commit_oid >actual &&
test_cmp expect actual
'
t6120: add a test to cover inner conditions in 'git name-rev's name_rev() In 'builtin/name-rev.c' in the name_rev() function there is a loop iterating over all parents of the given commit, and the loop body looks like this: if (parent_number > 1) { if (generation > 0) // branch #1 new_name = ... else // branch #2 new_name = ... name_rev(parent, new_name, ...); } else { // branch #3 name_rev(...); } These conditions are not covered properly in the test suite. As far as purely test coverage goes, they are all executed several times over in 't6120-describe.sh'. However, they don't directly influence the command's output, because the repository used in that test script contains several branches and tags pointing somewhere into the middle of the commit DAG, and thus result in a better name for the to-be-named commit. This can hide bugs: e.g. by replacing the 'new_name' parameter of the first recursive name_rev() call with 'tip_name' (effectively making both branch #1 and #2 a noop) 'git name-rev --all' shows thousands of bogus names in the Git repository, but the whole test suite still passes successfully. In an early version of a later patch in this series I managed to mess up all three branches (at once!), but the test suite still passed. So add a new test case that operates on the following history: A--------------master \ / \----------M2 \ / \---M1-C \ / B and names the commit 'B' to make sure that all three branches are crucial to determine 'B's name: - There is only a single ref, so all names are based on 'master', without any undesired interference from other refs. - Each time name_rev() follows the second parent of a merge commit, it appends "^2" to the name. Following 'master's second parent right at the start ensures that all commits on the ancestry path from 'master' to 'B' have a different base name from the original 'tip_name' of the very first name_rev() invocation. Currently, while name_rev() is recursive, it doesn't matter, but it will be necessary to properly cover all three branches after the recursion is eliminated later in this series. - Following 'M2's second parent makes sure that branch #2 (i.e. when 'generation = 0') affects 'B's name. - Following the only parent of the non-merge commit 'C' ensures that branch #3 affects 'B's name, and that it increments 'generation'. - Coming from 'C' 'generation' is 1, thus following 'M1's second parent makes sure that branch #1 affects 'B's name. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-12 10:38:14 +00:00
# A--------------master
# \ /
# \----------M2
# \ /
# \---M1-C
# \ /
# B
test_expect_success 'name-rev covers all conditions while looking at parents' '
git init repo &&
(
cd repo &&
echo A >file &&
git add file &&
git commit -m A &&
A=$(git rev-parse HEAD) &&
git checkout --detach &&
echo B >file &&
git commit -m B file &&
B=$(git rev-parse HEAD) &&
git checkout $A &&
git merge --no-ff $B && # M1
echo C >file &&
git commit -m C file &&
git checkout $A &&
git merge --no-ff HEAD@{1} && # M2
git checkout master &&
git merge --no-ff HEAD@{1} &&
echo "$B master^2^2~1^2" >expect &&
git name-rev $B >actual &&
test_cmp expect actual
)
'
test_done