From 4a3ce479ce7c8f268f42b725e471e35967da9b4f Mon Sep 17 00:00:00 2001 From: Eric Sunshine Date: Wed, 10 Jun 2020 02:30:46 -0400 Subject: worktree: prune duplicate entries referencing same worktree path A fundamental restriction of linked working trees is that there must only ever be a single worktree associated with a particular path, thus "git worktree add" explicitly disallows creation of a new worktree at the same location as an existing registered worktree. Nevertheless, users can still "shoot themselves in the foot" by mucking with administrative files in .git/worktree//. Worse, "git worktree move" is careless[1] and allows a worktree to be moved atop a registered but missing worktree (which can happen, for instance, if the worktree is on removable media). For instance: $ git clone foo.git $ cd foo $ git worktree add ../bar $ git worktree add ../baz $ rm -rf ../bar $ git worktree move ../baz ../bar $ git worktree list .../foo beefd00f [master] .../bar beefd00f [bar] .../bar beefd00f [baz] Help users recover from this form of corruption by teaching "git worktree prune" to detect when multiple worktrees are associated with the same path. [1]: A subsequent commit will fix "git worktree move" validation to be more strict. Signed-off-by: Eric Sunshine Signed-off-by: Junio C Hamano --- t/t2401-worktree-prune.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 't') diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh index b7d6d5d45a..fd3916fee0 100755 --- a/t/t2401-worktree-prune.sh +++ b/t/t2401-worktree-prune.sh @@ -92,4 +92,16 @@ test_expect_success 'not prune proper checkouts' ' test -d .git/worktrees/nop ' +test_expect_success 'prune duplicate (linked/linked)' ' + test_when_finished rm -fr .git/worktrees w1 w2 && + git worktree add --detach w1 && + git worktree add --detach w2 && + sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new && + mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir && + git worktree prune --verbose >actual && + test_i18ngrep "duplicate entry" actual && + test -d .git/worktrees/w1 && + ! test -d .git/worktrees/w2 +' + test_done -- cgit v1.2.3 From 916133ef8edcda54b0bff03a949c0858cd8d3280 Mon Sep 17 00:00:00 2001 From: Eric Sunshine Date: Wed, 10 Jun 2020 02:30:47 -0400 Subject: worktree: prune linked worktree referencing main worktree path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "git worktree prune" detects when multiple entries are associated with the same path and prunes the duplicates, however, it does not detect when a linked worktree points at the path of the main worktree. Although "git worktree add" disallows creating a new worktree with the same path as the main worktree, such a case can arise outside the control of Git even without the user mucking with .git/worktree// administrative files. For instance: $ git clone foo.git $ git -C foo worktree add ../bar $ rm -rf bar $ mv foo bar $ git -C bar worktree list .../bar deadfeeb [master] .../bar deadfeeb [bar] Help the user recover from such corruption by extending "git worktree prune" to also detect when a linked worktree is associated with the path of the main worktree. Reported-by: Jonathan Müller Signed-off-by: Eric Sunshine Signed-off-by: Junio C Hamano --- t/t2401-worktree-prune.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 't') diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh index fd3916fee0..a6ce7f590b 100755 --- a/t/t2401-worktree-prune.sh +++ b/t/t2401-worktree-prune.sh @@ -104,4 +104,16 @@ test_expect_success 'prune duplicate (linked/linked)' ' ! test -d .git/worktrees/w2 ' +test_expect_success 'prune duplicate (main/linked)' ' + test_when_finished rm -fr repo wt && + test_create_repo repo && + test_commit -C repo x && + git -C repo worktree add --detach ../wt && + rm -fr wt && + mv repo wt && + git -C wt worktree prune --verbose >actual && + test_i18ngrep "duplicate entry" actual && + ! test -d .git/worktrees/wt +' + test_done -- cgit v1.2.3 From 810382ed378731314c03627a8a2d8962b814ad17 Mon Sep 17 00:00:00 2001 From: Eric Sunshine Date: Wed, 10 Jun 2020 02:30:49 -0400 Subject: worktree: make "move" refuse to move atop missing registered worktree "git worktree add" takes special care to avoid creating a new worktree at a location already registered to an existing worktree even if that worktree is missing (which can happen, for instance, if the worktree resides on removable media). "git worktree move", however, is not so careful when validating the destination location and will happily move the source worktree atop the location of a missing worktree. This leads to the anomalous situation of multiple worktrees being associated with the same path, which is expressly forbidden by design. For example: $ git clone foo.git $ cd foo $ git worktree add ../bar $ git worktree add ../baz $ rm -rf ../bar $ git worktree move ../baz ../bar $ git worktree list .../foo beefd00f [master] .../bar beefd00f [bar] .../bar beefd00f [baz] $ git worktree remove ../bar fatal: validation failed, cannot remove working tree: '.../bar' does not point back to '.git/worktrees/bar' Fix this shortcoming by enhancing "git worktree move" to perform the same additional validation of the destination directory as done by "git worktree add". While at it, add a test to verify that "git worktree move" won't move a worktree atop an existing (non-worktree) path -- a restriction which has always been in place but was never tested. Signed-off-by: Eric Sunshine Signed-off-by: Junio C Hamano --- t/t2403-worktree-move.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 't') diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh index 939d18d728..a4e1a178e0 100755 --- a/t/t2403-worktree-move.sh +++ b/t/t2403-worktree-move.sh @@ -112,6 +112,27 @@ test_expect_success 'move locked worktree (force)' ' git worktree move --force --force flump ploof ' +test_expect_success 'refuse to move worktree atop existing path' ' + >bobble && + git worktree add --detach beeble && + test_must_fail git worktree move beeble bobble +' + +test_expect_success 'move atop existing but missing worktree' ' + git worktree add --detach gnoo && + git worktree add --detach pneu && + rm -fr pneu && + test_must_fail git worktree move gnoo pneu && + git worktree move --force gnoo pneu && + + git worktree add --detach nu && + git worktree lock nu && + rm -fr nu && + test_must_fail git worktree move pneu nu && + test_must_fail git worktree --force move pneu nu && + git worktree move --force --force pneu nu +' + test_expect_success 'move a repo with uninitialized submodule' ' git init withsub && ( -- cgit v1.2.3