git/t/t3203-branch-output.sh

462 lines
11 KiB
Bash
Raw Normal View History

#!/bin/sh
test_description='git branch display tests'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'make commits' '
echo content >file &&
git add file &&
git commit -m one &&
git branch -M main &&
echo content >>file &&
git commit -a -m two
'
test_expect_success 'make branches' '
git branch branch-one &&
git branch branch-two HEAD^
'
test_expect_success 'make remote branches' '
git update-ref refs/remotes/origin/branch-one branch-one &&
git update-ref refs/remotes/origin/branch-two branch-two &&
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
'
cat >expect <<'EOF'
branch-one
branch-two
* main
EOF
test_expect_success 'git branch shows local branches' '
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch --list shows local branches' '
git branch --list >actual &&
test_cmp expect actual
'
cat >expect <<'EOF'
branch-one
branch-two
EOF
test_expect_success 'git branch --list pattern shows matching local branches' '
git branch --list branch* >actual &&
test_cmp expect actual
'
cat >expect <<'EOF'
origin/HEAD -> origin/branch-one
origin/branch-one
origin/branch-two
EOF
test_expect_success 'git branch -r shows remote branches' '
git branch -r >actual &&
test_cmp expect actual &&
git branch --remotes >actual &&
test_cmp expect actual
'
test_expect_success 'git branch --no-remotes is rejected' '
test_must_fail git branch --no-remotes 2>err &&
grep "unknown option .no-remotes." err
'
cat >expect <<'EOF'
branch-one
branch-two
* main
remotes/origin/HEAD -> origin/branch-one
remotes/origin/branch-one
remotes/origin/branch-two
EOF
test_expect_success 'git branch -a shows local and remote branches' '
git branch -a >actual &&
test_cmp expect actual &&
git branch --all >actual &&
test_cmp expect actual
'
test_expect_success 'git branch --no-all is rejected' '
test_must_fail git branch --no-all 2>err &&
grep "unknown option .no-all." err
'
cat >expect <<'EOF'
two
one
two
EOF
test_expect_success 'git branch -v shows branch summaries' '
git branch -v >tmp &&
awk "{print \$NF}" <tmp >actual &&
test_cmp expect actual
'
cat >expect <<'EOF'
two
one
EOF
test_expect_success 'git branch --list -v pattern shows branch summaries' '
git branch --list -v branch* >tmp &&
awk "{print \$NF}" <tmp >actual &&
test_cmp expect actual
'
test_expect_success 'git branch --ignore-case --list -v pattern shows branch summaries' '
git branch --list --ignore-case -v BRANCH* >tmp &&
awk "{print \$NF}" <tmp >actual &&
test_cmp expect actual
'
test_expect_success 'git branch -v pattern does not show branch summaries' '
test_must_fail git branch -v branch*
'
test_expect_success 'git branch `--show-current` shows current branch' '
cat >expect <<-\EOF &&
branch-two
EOF
git checkout branch-two &&
git branch --show-current >actual &&
test_cmp expect actual
'
test_expect_success 'git branch `--show-current` is silent when detached HEAD' '
git checkout HEAD^0 &&
git branch --show-current >actual &&
test_must_be_empty actual
'
test_expect_success 'git branch `--show-current` works properly when tag exists' '
cat >expect <<-\EOF &&
branch-and-tag-name
EOF
test_when_finished "
git checkout branch-one
git branch -D branch-and-tag-name
" &&
git checkout -b branch-and-tag-name &&
test_when_finished "git tag -d branch-and-tag-name" &&
git tag branch-and-tag-name &&
git branch --show-current >actual &&
test_cmp expect actual
'
test_expect_success 'git branch `--show-current` works properly with worktrees' '
cat >expect <<-\EOF &&
branch-one
branch-two
EOF
git checkout branch-one &&
test_when_finished "
git worktree remove worktree_dir
" &&
git worktree add worktree_dir branch-two &&
{
git branch --show-current &&
git -C worktree_dir branch --show-current
} >actual &&
test_cmp expect actual
'
test_expect_success 'git branch shows detached HEAD properly' '
cat >expect <<EOF &&
* (HEAD detached at $(git rev-parse --short HEAD^0))
branch-one
branch-two
main
EOF
git checkout HEAD^0 &&
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch shows detached HEAD properly after checkout --detach' '
git checkout main &&
cat >expect <<EOF &&
* (HEAD detached at $(git rev-parse --short HEAD^0))
branch-one
branch-two
main
EOF
git checkout --detach &&
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch shows detached HEAD properly after moving' '
cat >expect <<EOF &&
* (HEAD detached from $(git rev-parse --short HEAD))
branch-one
branch-two
main
EOF
git reset --hard HEAD^1 &&
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch shows detached HEAD properly from tag' '
cat >expect <<EOF &&
* (HEAD detached at fromtag)
branch-one
branch-two
main
EOF
git tag fromtag main &&
git checkout fromtag &&
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch shows detached HEAD properly after moving from tag' '
cat >expect <<EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git reset --hard HEAD^1 &&
git branch >actual &&
test_cmp expect actual
'
test_expect_success 'git branch `--sort=[-]objectsize` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-two
branch-one
main
EOF
git branch --sort=objectsize >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
main
branch-two
EOF
git branch --sort=-objectsize >actual &&
test_cmp expect actual
'
test_expect_success 'git branch `--sort=[-]type` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=type >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=-type >actual &&
test_cmp expect actual
'
test_expect_success 'git branch `--sort=[-]version:refname` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=version:refname >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
main
branch-two
branch-one
EOF
git branch --sort=-version:refname >actual &&
test_cmp expect actual
'
test_expect_success 'git branch --points-at option' '
cat >expect <<-\EOF &&
branch-one
main
EOF
git branch --points-at=branch-one >actual &&
test_cmp expect actual
'
tag: do not show ambiguous tag names as "tags/foo" Since b7cc53e9 (tag.c: use 'ref-filter' APIs, 2015-07-11), git-tag has started showing tags with ambiguous names (i.e., when both "heads/foo" and "tags/foo" exists) as "tags/foo" instead of just "foo". This is both: - pointless; the output of "git tag" includes only refs/tags, so we know that "foo" means the one in "refs/tags". and - ambiguous; in the original output, we know that the line "foo" means that "refs/tags/foo" exists. In the new output, it is unclear whether we mean "refs/tags/foo" or "refs/tags/tags/foo". The reason this happens is that commit b7cc53e9 switched git-tag to use ref-filter's "%(refname:short)" output formatting, which was adapted from for-each-ref. This more general code does not know that we care only about tags, and uses shorten_unambiguous_ref to get the short-name. We need to tell it that we care only about "refs/tags/", and it should shorten with respect to that value. In theory, the ref-filter code could figure this out by us passing FILTER_REFS_TAGS. But there are two complications there: 1. The handling of refname:short is deep in formatting code that does not even have our ref_filter struct, let alone the arguments to the filter_ref struct. 2. In git v2.7.0, we expose the formatting language to the user. If we follow this path, it will mean that "%(refname:short)" behaves differently for "tag" versus "for-each-ref" (including "for-each-ref refs/tags/"), which can lead to confusion. Instead, let's add a new modifier to the formatting language, "strip", to remove a specific set of prefix components. This fixes "git tag", and lets users invoke the same behavior from their own custom formats (for "tag" or "for-each-ref") while leaving ":short" with its same consistent meaning in all places. We introduce a test in t7004 for "git tag", which fails without this patch. We also add a similar test in t3203 for "git branch", which does not actually fail. But since it is likely that "branch" will eventually use the same formatting code, the test helps defend against future regressions. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-01-26 11:00:05 +08:00
test_expect_success 'ambiguous branch/tag not marked' '
git tag ambiguous &&
git branch ambiguous &&
echo " ambiguous" >expect &&
git branch --list ambiguous >actual &&
test_cmp expect actual
'
test_expect_success 'local-branch symrefs shortened properly' '
git symbolic-ref refs/heads/ref-to-branch refs/heads/branch-one &&
git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
cat >expect <<-\EOF &&
ref-to-branch -> branch-one
2017-01-10 16:49:52 +08:00
ref-to-remote -> origin/branch-one
EOF
git branch >actual.raw &&
grep ref-to <actual.raw >actual &&
test_cmp expect actual
'
test_expect_success 'sort branches, ignore case' '
(
git init -b main sort-icase &&
cd sort-icase &&
test_commit initial &&
git branch branch-one &&
git branch BRANCH-two &&
git branch --list | awk "{print \$NF}" >actual &&
cat >expected <<-\EOF &&
BRANCH-two
branch-one
main
EOF
test_cmp expected actual &&
git branch --list -i | awk "{print \$NF}" >actual &&
cat >expected <<-\EOF &&
branch-one
BRANCH-two
main
EOF
test_cmp expected actual
)
'
test_expect_success 'git branch --format option' '
cat >expect <<-\EOF &&
Refname is (HEAD detached from fromtag)
Refname is refs/heads/ambiguous
Refname is refs/heads/branch-one
Refname is refs/heads/branch-two
Refname is refs/heads/main
Refname is refs/heads/ref-to-branch
Refname is refs/heads/ref-to-remote
EOF
git branch --format="Refname is %(refname)" >actual &&
test_cmp expect actual
'
for-each-ref: add ahead-behind format atom The previous change implemented the ahead_behind() method, including an algorithm to compute the ahead/behind values for a number of commit tips relative to a number of commit bases. Now, integrate that algorithm as part of 'git for-each-ref' hidden behind a new format atom, ahead-behind. This naturally extends to 'git branch' and 'git tag' builtins, as well. This format allows specifying multiple bases, if so desired, and all matching references are compared against all of those bases. For this reason, failing to read a reference provided from these atoms results in an error. In order to translate the ahead_behind() method information to the format output code in ref-filter.c, we must populate arrays of ahead_behind_count structs. In struct ref_array, we store the full array that will be passed to ahead_behind(). In struct ref_array_item, we store an array of pointers that point to the relvant items within the full array. In this way, we can pull all relevant ahead/behind values directly when formatting output for a specific item. It also ensures the lifetime of the ahead_behind_count structs matches the time that the array is being used. Add specific tests of the ahead/behind counts in t6600-test-reach.sh, as it has an interesting repository shape. In particular, its merging strategy and its use of different commit-graphs would demonstrate over- counting if the ahead_behind() method did not already account for that possibility. Also add tests for the specific for-each-ref, branch, and tag builtins. In the case of 'git tag', there are intersting cases that happen when some of the selected tips are not commits. This requires careful logic around commits_nr in the second loop of filter_ahead_behind(). Also, the test in t7004 is carefully located to avoid being dependent on the GPG prereq. It also avoids using the test_commit helper, as that will add ticks to the time and disrupt the expected timestamps in later tag tests. Also add performance tests in a new p1300-graph-walks.sh script. This will be useful for more uses in the future, but for now compare the ahead-behind counting algorithm in 'git for-each-ref' to the naive implementation by running 'git rev-list --count' processes for each input. For the Git source code repository, the improvement is already obvious: Test this tree --------------------------------------------------------------- 1500.2: ahead-behind counts: git for-each-ref 0.07(0.07+0.00) 1500.3: ahead-behind counts: git branch 0.07(0.06+0.00) 1500.4: ahead-behind counts: git tag 0.07(0.06+0.00) 1500.5: ahead-behind counts: git rev-list 1.32(1.04+0.27) But the standard performance benchmark is the Linux kernel repository, which demosntrates a significant improvement: Test this tree --------------------------------------------------------------- 1500.2: ahead-behind counts: git for-each-ref 0.27(0.24+0.02) 1500.3: ahead-behind counts: git branch 0.27(0.24+0.03) 1500.4: ahead-behind counts: git tag 0.28(0.27+0.01) 1500.5: ahead-behind counts: git rev-list 4.57(4.03+0.54) The 'git rev-list' test exists in this change as a demonstration, but it will be removed in the next change to avoid wasting time on this comparison. Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-20 19:26:54 +08:00
test_expect_success 'git branch --format with ahead-behind' '
cat >expect <<-\EOF &&
(HEAD detached from fromtag) 0 0
refs/heads/ambiguous 0 0
refs/heads/branch-one 1 0
refs/heads/branch-two 0 0
refs/heads/main 1 0
refs/heads/ref-to-branch 1 0
refs/heads/ref-to-remote 1 0
EOF
git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
test_cmp expect actual
'
test_expect_success 'git branch with --format=%(rest) must fail' '
test_must_fail git branch --format="%(rest)" >actual
'
test_expect_success 'git branch --format --omit-empty' '
cat >expect <<-\EOF &&
Refname is (HEAD detached from fromtag)
Refname is refs/heads/ambiguous
Refname is refs/heads/branch-one
Refname is refs/heads/branch-two
Refname is refs/heads/ref-to-branch
Refname is refs/heads/ref-to-remote
EOF
git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
Refname is (HEAD detached from fromtag)
Refname is refs/heads/ambiguous
Refname is refs/heads/branch-one
Refname is refs/heads/branch-two
Refname is refs/heads/ref-to-branch
Refname is refs/heads/ref-to-remote
EOF
git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
test_cmp expect actual
'
test_expect_success 'worktree colors correct' '
cat >expect <<-EOF &&
* <GREEN>(HEAD detached from fromtag)<RESET>
ambiguous<RESET>
branch-one<RESET>
+ <CYAN>branch-two<RESET>
main<RESET>
ref-to-branch<RESET> -> branch-one
ref-to-remote<RESET> -> origin/branch-one
EOF
git worktree add worktree_dir branch-two &&
git branch --color >actual.raw &&
rm -r worktree_dir &&
git worktree prune &&
test_decode_color <actual.raw >actual &&
test_cmp expect actual
'
test_expect_success "set up color tests" '
echo "<RED>main<RESET>" >expect.color &&
echo "main" >expect.bare &&
color_args="--format=%(color:red)%(refname:short) --list main"
'
test_expect_success '%(color) omitted without tty' '
TERM=vt100 git branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.bare actual
'
test_expect_success TTY '%(color) present with tty' '
test_terminal git branch $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success '--color overrides auto-color' '
git branch --color $color_args >actual.raw &&
test_decode_color <actual.raw >actual &&
test_cmp expect.color actual
'
test_expect_success 'verbose output lists worktree path' '
one=$(git rev-parse --short HEAD) &&
two=$(git rev-parse --short main) &&
cat >expect <<-EOF &&
* (HEAD detached from fromtag) $one one
ambiguous $one one
branch-one $two two
+ branch-two $one ($(pwd)/worktree_dir) one
main $two two
ref-to-branch $two two
ref-to-remote $two two
EOF
git worktree add worktree_dir branch-two &&
git branch -vv >actual &&
rm -r worktree_dir &&
git worktree prune &&
test_cmp expect actual
'
test_done