Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git worktree: two worktrees point in the same location, cannot be prune

When I do git worktree list, it display follows.

/path/to/workspace  c943d35 [master]
/path/to/workspace  ef4df56 (detached HEAD)

It is my working directory (not worktree directory). I do not know how it happened, and how to clean it. I've try git worktree prune, but it does not change anything. Any help will be appreciated. Many thanks.

like image 358
cityzz Avatar asked Mar 13 '23 04:03

cityzz


1 Answers

git worktree -- details mentions:

Each linked working tree has a private sub-directory in the repository’s $GIT_DIR/worktrees directory.

Check the content of your main repo/.git/worktrees and see if there is a sub-folder that you could manually remove.


Git 2.20 (Q4 2018) fixes a bug in which the same path could be registered under multiple worktree entries if the path was missing (for instance, was removed manually).
Also, as a convenience, expand the number of cases in which --force is applicable.

See commit 684e742 (30 Aug 2018) by Jeff King (peff).
See commit 3a54043, commit f414310, commit 68a6b3a, commit e19831c, commit cb56f55, commit 45059e6, commit 602aaed, commit e5353be, commit 4c5fa9e (28 Aug 2018) by Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 1c515bf, 17 Sep 2018)

worktree: delete .git/worktrees if empty after 'remove'

For cleanliness, "git worktree prune" deletes the .git/worktrees directory if it is empty after pruning is complete.

For consistency, make "git worktree remove <path>" likewise delete .git/worktrees if it is empty after the removal.


The same Git 2.20, when traversing objects for reachability and deciding what objects are unreferenced and expendable, have been taught to also consider per-worktree refs of other worktrees as starting points to prevent data loss.

See commit 14f74d5 (03 Nov 2018), commit c9ef0d9, commit b29759d, commit ab3e1f7, commit 061e420, commit 3a3b9d8 (21 Oct 2018), and commit 8aff1a9, commit 5c79f74 (29 Sep 2018) by Nguyễn Thái Ngọc Duy (pclouds).
See commit a8c754d (21 Oct 2018) by Elijah Newren (newren).
(Merged by Junio C Hamano -- gitster -- in commit e146cc9, 13 Nov 2018)

In particular (commit 3a3b9d8):

refs: new ref types to make per-worktree refs visible to all worktrees

One of the problems with multiple worktree is accessing per-worktree refs of one worktree from another worktree.
This was sort of solved by multiple ref store, where the code can open the ref store of another worktree and has access to the ref space of that worktree.

The problem with this is reporting.
"HEAD" in another ref space is also called "HEAD" like in the current ref space.
In order to differentiate them, all the code must somehow carry the ref store around and print something like "HEAD from this ref store".

Instead of entering a separate ref space, we make refs from other worktrees available in the current ref space.
So "HEAD" is always HEAD of the current worktree, but then we can have "worktrees/blah/HEAD" to denote HEAD from a worktree named "blah".
This syntax coincidentally matches the underlying directory structure which makes implementation a bit easier.

The main worktree has to be treated specially because well... it's special from the beginning.
So HEAD from the main worktree is acccessible via the name "main-worktree/HEAD" instead of "worktrees/main/HEAD" because "main" could be just another secondary worktree.

This patch also makes it possible to specify refs from one worktree in another one, e.g.

git log worktrees/foo/HEAD

That (the new refs "worktrees/<name>/HEAD") leads to Git 2.23 (Q2 2019), where the code now sanitizes the names given to worktrees, to make sure these refs are well-formed.

See commit 1de16ae (08 Mar 2019) by Nguyễn Thái Ngọc Duy (pclouds).
Helped-by: Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 0d107b1, 13 Jun 2019)

worktree add: sanitize worktree names

Worktree names are based on $(basename $GIT_WORK_TREE).
They aren't significant until 3a3b9d8 (refs: new ref types to make per-worktree refs visible to all worktrees - 2018-10-21, Git v2.20.0-rc0), where worktree name could be part of a refname and must follow refname rules.

Update 'worktree add' code to remove special characters to follow these rules. In the future the user will be able to specify the worktree name by themselves if they're not happy with this dumb character substitution.


And the same Git 2.22.1 (Q3 2019) mentions "git worktree add" used to fail when another worktree connected to the same repository was corrupt, which has been corrected.

See commit 105df73 (13 May 2019) by Nguyễn Thái Ngọc Duy (pclouds).
(Merged by Junio C Hamano -- gitster -- in commit 933f294, 25 Jul 2019)

worktree add: be tolerant of corrupt worktrees

find_worktree() can die() unexpectedly because it uses real_path() instead of the gentler version.

When it's used in 'git worktree add' (added in cb56f55 (worktree: disallow adding same path multiple times, 2018-08-28, Git v2.20.0-rc0), or since v2.20.0. Though the real bug in find_worktree() is much older) and there's a bad worktree, this die() could prevent people from adding new worktrees.

The "bad" condition to trigger this is when a parent of the worktree's location is deleted. Then real_path() will complain.

Use the other version so that bad worktrees won't affect 'worktree add'.
The bad ones will eventually be pruned, we just have to tolerate them for a bit.


Before Git 2.26 (Q1 2020), in rare cases "git worktree add <path>" could think that <path> was already a registered worktree even when it wasn't, and refuse to add the new worktree.
This has been corrected.

See commit bb69b3b, commit bb4995f, commit a80c4c2 (24 Feb 2020) by Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 49e5043, 05 Mar 2020)

worktree: don't allow "add" validation to be fooled by suffix matching

Reported-by: Cameron Gunnin
Signed-off-by: Eric Sunshine

"git worktree add <path>" performs various checks before approving <path> as a valid location for the new worktree.

Aside from ensuring that <path> does not already exist, one of the questions it asks is whether <path> is already a registered worktree.
To perform this check, it queries find_worktree() and disallows the "add" operation if find_worktree() finds a match for <path>.
As a convenience, however, find_worktree() casts an overly wide net to allow users to identify worktrees by shorthand in order to keep typing to a minimum.
For instance, it performs suffix matching which, given subtrees "foo/bar" and "foo/baz", can correctly select the latter when asked only for "baz".

"add" validation knows the exact path it is interrogating, so this sort of heuristic-based matching is, at best, questionable for this use-case and, at worst, may may accidentally interpret <path> as matching an existing worktree and incorrectly report it as already registered even when it isn't.
(In fact, validate_worktree_add() already contains a special case to avoid accidentally matching against the main worktree, precisely due to this problem.)

Avoid the problem of potential accidental matching against an existing worktree by instead taking advantage of find_worktree_by_path() which matches paths deterministically, without applying any sort of magic shorthand matching performed by find_worktree().

And:

worktree: improve find_worktree() documentation

Signed-off-by: Eric Sunshine

Do a better job of explaining that find_worktree()'s main purpose is to locate a worktree based upon input from a user which may be some sort of shorthand for identifying a worktree rather than an actual path.

For instance, one shorthand a user can use to identify a worktree is by unique path suffix (i.e. given worktrees at paths "foo/bar" and "foo/baz", the latter can be identified simply as "baz").
The actual heuristics find_worktree() uses to select a worktree may be expanded in the future (for instance, one day it may allow worktree selection by <id> of the .git/worktrees/<id>/ administrative directory), thus the documentation does not provide a precise description of how matching is performed, instead leaving it open-ended to allow for future enhancement.

While at it, drop mention of the non-NULL requirement of prefix since NULL has long been allowed.

For instance, prefix_filename() has explicitly allowed NULL since 116fb64e43 (prefix_filename: drop length parameter, 2017-03-20, Git v2.13.0-rc0), and find_worktree() itself since e4da43b1f0 (prefix_filename: return newly allocated string, 2017-03-20, Git v2.13.0-rc0).


Note that the same worktree directory must be registered only once, but "git worktree move" allowed this invariant to be violated, which has been corrected with Git 2.28 (Q3 2020).

See commit 810382e, commit d179af6, commit 916133e, commit 4a3ce47, commit dd9609a, commit 1b14d40 (10 Jun 2020), and commit c9b77f2 (08 Jun 2020) by Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 9740ef8, 22 Jun 2020)

With Git 2.28 (Q3 2020), The same worktree directory must be registered only once, but "git worktree move" allowed this invariant to be violated, which has been corrected.

See commit 810382e, commit d179af6, commit 916133e, commit 4a3ce47, commit dd9609a, commit 1b14d40 (10 Jun 2020), and commit c9b77f2 (08 Jun 2020) by Eric Sunshine (sunshineco).
(Merged by Junio C Hamano -- gitster -- in commit 9740ef8, 22 Jun 2020)

worktree: prune duplicate entries referencing same worktree path

Signed-off-by: Eric Sunshine

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/<id>/.

Worse, "git worktree move" is careless 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.

And:

worktree: prune linked worktree referencing main worktree path

Reported-by: Jonathan Müller
Signed-off-by: Eric Sunshine

"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/<id>/ 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.


Note that Git 2.30 does list locked worktrees:

See commit c57b336:

worktree: teach list to annotate locked worktree

The "git worktree list" shows the absolute path to the working tree, the commit that is checked out and the name of the branch. It is not immediately obvious which of the worktrees, if any, are locked.

"git worktree remove" refuses to remove a locked worktree with an error message. If "git worktree list" told which worktrees are locked in its output, the user would not even attempt to remove such a worktree, or would realize that "git worktree remove -f -f <path>" is required.

Teach "git worktree list" to append "locked" to its output.
The output from the command becomes like so:

$ git worktree list
/path/to/main             abc123 [master]
/path/to/worktree         456def (detached HEAD)
/path/to/locked-worktree  123abc (detached HEAD) locked

With Git 2.33 (Q3 2021), "git worktree add --lock"(man) learned to record why the worktree is locked with a custom message.

See commit 0db4961 (15 Jul 2021), and commit f7c35ea, commit f9365c0 (11 Jul 2021) by Stephen Manz (SRManz).
(Merged by Junio C Hamano -- gitster -- in commit 01369fd, 28 Jul 2021)

worktree: teach add to accept --reason <string> with --lock

Signed-off-by: Stephen Manz
Reviewed-by: Eric Sunshine

The default reason stored in the lock file, "added with --lock", is unlikely to be what the user would have given in a separate git worktree lock(man) command.
Allowing --reason to be specified along with --lock when adding a working tree gives the user control over the reason for locking without needing a second command.

git worktree now includes in its man page:

git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]

git worktree now includes in its man page:

With lock or with add --lock, an explanation why the working tree is locked.

like image 187
VonC Avatar answered Mar 16 '23 07:03

VonC