1
0
mirror of https://github.com/git/git.git synced 2024-09-29 16:41:53 +02:00
git/t/t2402-worktree-list.sh

204 lines
6.9 KiB
Bash
Raw Normal View History

#!/bin/sh
test_description='test git worktree list'
. ./test-lib.sh
test_expect_success 'setup' '
test_commit init
'
rev-parse: fix several options when running in a subdirectory In addition to making git_path() aware of certain file names that need to be handled differently e.g. when running in worktrees, the commit 557bd833bb (git_path(): be aware of file relocation in $GIT_DIR, 2014-11-30) also snuck in a new option for `git rev-parse`: `--git-path`. On the face of it, there is no obvious bug in that commit's diff: it faithfully calls git_path() on the argument and prints it out, i.e. `git rev-parse --git-path <filename>` has the same precise behavior as calling `git_path("<filename>")` in C. The problem lies deeper, much deeper. In hindsight (which is always unfair), implementing the .git/ directory discovery in `setup_git_directory()` by changing the working directory may have allowed us to avoid passing around a struct that contains information about the current repository, but it bought us many, many problems. In this case, when being called in a subdirectory, `git rev-parse` changes the working directory to the top-level directory before calling `git_path()`. In the new working directory, the result is correct. But in the working directory of the calling script, it is incorrect. Example: when calling `git rev-parse --git-path HEAD` in, say, the Documentation/ subdirectory of Git's own source code, the string `.git/HEAD` is printed. Side note: that bug is hidden when running in a subdirectory of a worktree that was added by the `git worktree` command: in that case, the (correct) absolute path of the `HEAD` file is printed. In the interest of time, this patch does not go the "correct" route to introduce a struct with repository information (and removing global state in the process), instead this patch chooses to detect when the command was called in a subdirectory and forces the result to be an absolute path. While at it, we are also fixing the output of --git-common-dir and --shared-index-path. Lastly, please note that we reuse the same strbuf for all of the relative_path() calls; this avoids frequent allocation (and duplicated code), and it does not risk memory leaks, for two reasons: 1) the cmd_rev_parse() function does not return anywhere between the use of the new strbuf instance and its final release, and 2) git-rev-parse is one of these "one-shot" programs in Git, i.e. it exits after running for a very short time, meaning that all allocated memory is released with the exit() call anyway. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-02-17 17:59:06 +01:00
test_expect_success 'rev-parse --git-common-dir on main worktree' '
git rev-parse --git-common-dir >actual &&
echo .git >expected &&
test_cmp expected actual &&
mkdir sub &&
git -C sub rev-parse --git-common-dir >actual2 &&
echo ../.git >expected2 &&
test_cmp expected2 actual2
'
rev-parse: fix several options when running in a subdirectory In addition to making git_path() aware of certain file names that need to be handled differently e.g. when running in worktrees, the commit 557bd833bb (git_path(): be aware of file relocation in $GIT_DIR, 2014-11-30) also snuck in a new option for `git rev-parse`: `--git-path`. On the face of it, there is no obvious bug in that commit's diff: it faithfully calls git_path() on the argument and prints it out, i.e. `git rev-parse --git-path <filename>` has the same precise behavior as calling `git_path("<filename>")` in C. The problem lies deeper, much deeper. In hindsight (which is always unfair), implementing the .git/ directory discovery in `setup_git_directory()` by changing the working directory may have allowed us to avoid passing around a struct that contains information about the current repository, but it bought us many, many problems. In this case, when being called in a subdirectory, `git rev-parse` changes the working directory to the top-level directory before calling `git_path()`. In the new working directory, the result is correct. But in the working directory of the calling script, it is incorrect. Example: when calling `git rev-parse --git-path HEAD` in, say, the Documentation/ subdirectory of Git's own source code, the string `.git/HEAD` is printed. Side note: that bug is hidden when running in a subdirectory of a worktree that was added by the `git worktree` command: in that case, the (correct) absolute path of the `HEAD` file is printed. In the interest of time, this patch does not go the "correct" route to introduce a struct with repository information (and removing global state in the process), instead this patch chooses to detect when the command was called in a subdirectory and forces the result to be an absolute path. While at it, we are also fixing the output of --git-common-dir and --shared-index-path. Lastly, please note that we reuse the same strbuf for all of the relative_path() calls; this avoids frequent allocation (and duplicated code), and it does not risk memory leaks, for two reasons: 1) the cmd_rev_parse() function does not return anywhere between the use of the new strbuf instance and its final release, and 2) git-rev-parse is one of these "one-shot" programs in Git, i.e. it exits after running for a very short time, meaning that all allocated memory is released with the exit() call anyway. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-02-17 17:59:06 +01:00
test_expect_success 'rev-parse --git-path objects linked worktree' '
echo "$(git rev-parse --show-toplevel)/.git/objects" >expect &&
test_when_finished "rm -rf linked-tree actual expect && git worktree prune" &&
git worktree add --detach linked-tree master &&
git -C linked-tree rev-parse --git-path objects >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees from main' '
echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
test_when_finished "rm -rf here out actual expect && git worktree prune" &&
git worktree add --detach here master &&
echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
git worktree list >out &&
sed "s/ */ /g" <out >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees from linked' '
echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
test_when_finished "rm -rf here out actual expect && git worktree prune" &&
git worktree add --detach here master &&
echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
git -C here worktree list >out &&
sed "s/ */ /g" <out >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees --porcelain' '
echo "worktree $(git rev-parse --show-toplevel)" >expect &&
echo "HEAD $(git rev-parse HEAD)" >>expect &&
echo "branch $(git symbolic-ref HEAD)" >>expect &&
echo >>expect &&
test_when_finished "rm -rf here actual expect && git worktree prune" &&
git worktree add --detach here master &&
echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
echo "HEAD $(git rev-parse HEAD)" >>expect &&
echo "detached" >>expect &&
echo >>expect &&
git worktree list --porcelain >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees with locked annotation' '
test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
git worktree add --detach locked master &&
git worktree add --detach unlocked master &&
git worktree lock locked &&
test_when_finished "git worktree unlock locked" &&
git worktree list >out &&
grep "/locked *[0-9a-f].* locked$" out &&
! grep "/unlocked *[0-9a-f].* locked$" out
'
worktree: teach `list --porcelain` to annotate locked worktree Commit c57b3367be (worktree: teach `list` to annotate locked worktree, 2020-10-11) taught "git worktree list" to annotate locked worktrees by appending "locked" text to its output, however, this is not listed in the --porcelain format. Teach "list --porcelain" to do the same and add a "locked" attribute followed by its reason, thus making both default and porcelain format consistent. If the locked reason is not available then only "locked" is shown. The output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain ... worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked worktree /path/to/locked-with-reason HEAD abc123abc123abc123abc123abc123abc123abc1 detached locked reason why it is locked ... In porcelain mode, if the lock reason contains special characters such as newlines, they are escaped with backslashes and the entire reason is enclosed in double quotes. For example: $ git worktree list --porcelain ... locked "worktree's path mounted in\nremovable device" ... Furthermore, let's update the documentation to state that some attributes in the porcelain format might be listed alone or together with its value depending whether the value is available or not. Thus documenting the case of the new "locked" attribute. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:08 +01:00
test_expect_success '"list" all worktrees --porcelain with locked' '
test_when_finished "rm -rf locked1 locked2 unlocked out actual expect && git worktree prune" &&
echo "locked" >expect &&
echo "locked with reason" >>expect &&
git worktree add --detach locked1 &&
git worktree add --detach locked2 &&
# unlocked worktree should not be annotated with "locked"
git worktree add --detach unlocked &&
git worktree lock locked1 &&
test_when_finished "git worktree unlock locked1" &&
git worktree lock locked2 --reason "with reason" &&
test_when_finished "git worktree unlock locked2" &&
git worktree list --porcelain >out &&
grep "^locked" out >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees --porcelain with locked reason newline escaped' '
test_when_finished "rm -rf locked_lf locked_crlf out actual expect && git worktree prune" &&
printf "locked \"locked\\\\r\\\\nreason\"\n" >expect &&
printf "locked \"locked\\\\nreason\"\n" >>expect &&
git worktree add --detach locked_lf &&
git worktree add --detach locked_crlf &&
git worktree lock locked_lf --reason "$(printf "locked\nreason")" &&
test_when_finished "git worktree unlock locked_lf" &&
git worktree lock locked_crlf --reason "$(printf "locked\r\nreason")" &&
test_when_finished "git worktree unlock locked_crlf" &&
git worktree list --porcelain >out &&
grep "^locked" out >actual &&
test_cmp expect actual
'
test_expect_success 'bare repo setup' '
git init --bare bare1 &&
echo "data" >file1 &&
git add file1 &&
git commit -m"File1: add data" &&
git push bare1 master &&
git reset --hard HEAD^
'
test_expect_success '"list" all worktrees from bare main' '
test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
git -C bare1 worktree add --detach ../there master &&
echo "$(pwd)/bare1 (bare)" >expect &&
echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
git -C bare1 worktree list >out &&
sed "s/ */ /g" <out >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees --porcelain from bare main' '
test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" &&
git -C bare1 worktree add --detach ../there master &&
echo "worktree $(pwd)/bare1" >expect &&
echo "bare" >>expect &&
echo >>expect &&
echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect &&
echo "HEAD $(git -C there rev-parse HEAD)" >>expect &&
echo "detached" >>expect &&
echo >>expect &&
git -C bare1 worktree list --porcelain >actual &&
test_cmp expect actual
'
test_expect_success '"list" all worktrees from linked with a bare main' '
test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
git -C bare1 worktree add --detach ../there master &&
echo "$(pwd)/bare1 (bare)" >expect &&
echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
git -C there worktree list >out &&
sed "s/ */ /g" <out >actual &&
test_cmp expect actual
'
test_expect_success 'bare repo cleanup' '
rm -rf bare1
'
test_expect_success 'broken main worktree still at the top' '
git init broken-main &&
(
cd broken-main &&
test_commit new &&
git worktree add linked &&
cat >expected <<-EOF &&
worktree $(pwd)
HEAD $ZERO_OID
EOF
cd linked &&
echo "worktree $(pwd)" >expected &&
echo "ref: .broken" >../.git/HEAD &&
git worktree list --porcelain >out &&
head -n 3 out >actual &&
test_cmp ../expected actual &&
git worktree list >out &&
head -n 1 out >actual.2 &&
grep -F "(error)" actual.2
)
'
test_expect_success 'linked worktrees are sorted' '
mkdir sorted &&
git init sorted/main &&
(
cd sorted/main &&
test_tick &&
test_commit new &&
git worktree add ../first &&
git worktree add ../second &&
git worktree list --porcelain >out &&
grep ^worktree out >actual
) &&
cat >expected <<-EOF &&
worktree $(pwd)/sorted/main
worktree $(pwd)/sorted/first
worktree $(pwd)/sorted/second
EOF
test_cmp expected sorted/main/actual
'
test_expect_success 'worktree path when called in .git directory' '
git worktree list >list1 &&
git -C .git worktree list >list2 &&
test_cmp list1 list2
'
test_done