torek mentions in his answer:
We've noted that --separate-git-dir
doesn't make the repository bare—a bare repository is one with no work-tree — it just puts the work-tree and repository (.git/*
files) in two different locations in the file system.
To be even clearer, you could not use --bare
with --separate-git-dir
anyway, considering you want to get a working tree, that you then need to populate (git switch -C master
should be enough)
With Git 2.29 (Q4 2020), the purpose of "git init --separate-git-dir
(man)" is to initialize a new project with the repository separate from the working tree, or, in the case of an existing project, to move the repository (the .git/
directory) out of the working tree.
It does not make sense to use --separate-git-dir
with a bare repository for which there is no working tree, so disallow its use with bare repositories.
See commit ccf236a (09 Aug 2020) by Eric Sunshine (sunshineco
).
(Merged by Junio C Hamano -- gitster
-- in commit a654836, 24 Aug 2020)
init
: disallow --separate-git-dir
with bare repository
Signed-off-by: Eric Sunshine
The purpose of "git init --separate-git-dir
(man)" is to separate the repository from the worktree.
This is true even when --separate-git-dir
is used on an existing worktree, in which case, it moves the .git/ subdirectory
to a new location outside the worktree.
However, an outright bare repository (such as one created by "git init --bare
"(man)), has no worktree, so using --separate-git-dir
to separate it from its non-existent worktree is nonsensical.
Therefore, make it an error to use --separate-git-dir
on a bare repository.
Implementation note: "git init
"(man) considers a repository bare if told so explicitly via --bare
or if it guesses it to be so based upon heuristics.
- In the explicit
--bare
case, a conflict with --separate-git-dir
is easy to detect early.
- In the guessed case, however, the conflict can only be detected once "bareness" is guessed, which happens after "
git init
"(man) has begun creating the repository.
Technically, we can get by with a single late check which would cover both cases, however, erroring out early, when possible, without leaving detritus provides a better user experience.
With Git 2.29 (Q4 2020), "git worktree
"(man) gained a "repair
" subcommand to help users recover after moving the worktrees or repository manually without telling Git.
Also, "git init --separate-work-dir=<path>
"(man) no longer corrupts administrative data related to linked worktrees.
See commit 59d876c, commit 42264bc, commit b214ab5, commit bdd1f3e (31 Aug 2020), and commit e8e1ff2 (27 Aug 2020) by Eric Sunshine (sunshineco
).
(Merged by Junio C Hamano -- gitster
-- in commit eb7460f, 09 Sep 2020)
init
: make --separate-git-dir work from within linked worktree
Reported-by: Henré Botha
Signed-off-by: Eric Sunshine
The intention of git init --separate-work-dir=<path>
(man) is to move the .git/
directory to a location outside of the main worktree.
When used within a linked worktree, however, rather than moving the .git/
directory as intended, it instead incorrectly moves the worktree's .git/worktrees/<id>
directory to <path>
, thus disconnecting the linked worktree from its parent repository and breaking the worktree in the process since its local .
git
(man) file no longer points at a location at which it can find the object database.
Fix this broken behavior.
An intentional side-effect of this change is that it also closes a loophole not caught by ccf236a23a ("init
: disallow --separate-git-dir
with bare repository", 2020-08-09, Git v2.29.0 -- merge) in which the check to prevent --separate-git-dir
being used in conjunction with a bare repository was unable to detect the invalid combination when invoked from within a linked worktree.
Therefore, add a test to verify that this loophole is closed, as well.
With Git 2.31 (Q1 2021), "git worktree repair
"(man) learned to deal with the case where both the repository and the worktree moved.
See commit cf76bae (21 Dec 2020) by Eric Sunshine (sunshineco
).
(Merged by Junio C Hamano -- gitster
-- in commit 8664fcb, 06 Jan 2021)
worktree
: teach repair
to fix multi-directional breakage
Signed-off-by: Eric Sunshine
git worktree repair
(man) knows how to repair the two-way links between the repository and a worktree as long as a link in one or the other direction is sound.
For instance, if a linked worktree is moved (without using git worktree move
(man)), repair is possible because the worktree still knows the location of the repository even though the repository no longer knows where the worktree is.
Similarly, if the repository is moved, repair is possible since the repository still knows the locations of the worktrees even though the worktrees no longer know where the repository is.
However, if both the repository and the worktrees are moved, then links are severed in both directions, and no repair is possible.
This is the case even when the new worktree locations are specified as arguments to git worktree repair
.
The reason for this limitation is twofold.
- First, when
repair
consults the worktree's gitfile
(/path/to/worktree/.git
) to determine the corresponding <repo>/worktrees/<id>/gitdir
file to fix, <repo>
is the old path to the repository, thus it is unable to fix the gitdir
file at its new location since it doesn't know where it is.
- Second, when
repair
consults <repo>/worktrees/<id>/gitdir
to find the location of the worktree's gitfile
(/path/to/worktree/.git
), the path recorded in gitdir
is the old location of the worktree's gitfile
, thus it is unable to repair the gitfile
since it doesn't know where it is.
Fix these shortcomings by teaching repair
to attempt to infer the new location of the <repo>/worktrees/<id>/gitdir
file when the location recorded in the worktree's gitfile
has become stale but the file is otherwise well-formed.
The inference is intentionally simple-minded.
For each worktree path specified as an argument, git worktree repair
manually reads the ".git
" gitfile
at that location and, if it is well-formed, extracts the <id>
.
It then searches for a corresponding <id>
in <repo>/worktrees/
and, if found, concludes that there is a reasonable match and updates <repo>/worktrees/<id>/gitdir
to point at the specified worktree path.
In order for <repo>
to be known, git worktree repair
must be run in the main worktree or bare repository.
git worktree repair
first attempts to repair each incoming /path/to/worktree/.git
gitfile
to point at the repository, and then attempts to repair outgoing <repo>/worktrees/<id>/gitdir
files to point at the worktrees.
This sequence was chosen arbitrarily when originally implemented since the order of fixes is immaterial as long as one side of the two-way link between the repository and a worktree is sound.
However, for this new repair technique to work, the order must be reversed. This is because the new inference mechanism, when it is successful, allows the outgoing <repo>/worktrees/<id>/gitdir
file to be repaired, thus fixing one side of the two-way link.
Once that side is fixed, the other side can be fixed by the existing repair mechanism, hence the order of repairs is now significant.
Two safeguards are employed to avoid hijacking a worktree from a different repository if the user accidentally specifies a foreign worktree as an argument.
- The first, as described above, is that it requires an
<id>
match between the repository and the worktree. That itself is not foolproof for preventing hijack, so:
- the second safeguard is that the inference will only kick in if the worktree's
/path/to/worktree/.git
gitfile
does not point at a repository.
git worktree
now includes in its man page:
If both the main working tree and linked working trees have been moved
manually, then running repair
in the main working tree and specifying the
new <path>
of each linked working tree will reestablish all connections
in both directions.