project: detach HEAD in internal worktree checkout.

When checkout is done with Git worktrees then the HEAD in the
bare-git repositories point to the initialized default (e.g.
'refs/heads/master'). This default branch does not exist
locally and is not automatically created.
When a user now creates a branch in any git repository named
'master' then it is no longer possible to get rid of this branch,
neither is it possible to switch to another branch and switch
back to this master branch. Git concludes the 'master' branch is
already checked out (in the bare Git) and that results in a
lockdown of this master branch.

To repoduce this issue, run these commands in a repo tree
checked out with --worktree:
- git checkout master # assuming the remote repo has a master branch,
                      # a local tracking branch master is created here
- git checkout -b temp
- git checkout master # This one now fails
- git branch -d master # fails too
The failure is caused by Git assuming the master branch is checked out
by the bare git repository since HEAD is pointing towards it.

To workaround this, we always detach HEAD in the bare-git when
syncing.  We don't need it to point to a ref in general, but we
would like it to be valid so git tools "just work" if they're run
in here.

Signed-off-by: Remy Bohmer <oss@bohmer.net>
Change-Id: I15c96604363c41f0d01c42f533174393097daeb5
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/290985
Reviewed-by: Mike Frysinger <vapier@google.com>
This commit is contained in:
Remy Böhmer 2020-12-15 18:49:02 +01:00 committed by Mike Frysinger
parent 8add62325d
commit 1469c28ec3

View File

@ -2558,6 +2558,8 @@ class Project(object):
base = R_WORKTREE_M base = R_WORKTREE_M
active_git = self.work_git active_git = self.work_git
self._InitAnyMRef(HEAD, self.bare_git, detach=True)
else: else:
base = R_M base = R_M
active_git = self.bare_git active_git = self.bare_git
@ -2567,7 +2569,7 @@ class Project(object):
def _InitMirrorHead(self): def _InitMirrorHead(self):
self._InitAnyMRef(HEAD, self.bare_git) self._InitAnyMRef(HEAD, self.bare_git)
def _InitAnyMRef(self, ref, active_git): def _InitAnyMRef(self, ref, active_git, detach=False):
cur = self.bare_ref.symref(ref) cur = self.bare_ref.symref(ref)
if self.revisionId: if self.revisionId:
@ -2580,6 +2582,9 @@ class Project(object):
dst = remote.ToLocal(self.revisionExpr) dst = remote.ToLocal(self.revisionExpr)
if cur != dst: if cur != dst:
msg = 'manifest set to %s' % self.revisionExpr msg = 'manifest set to %s' % self.revisionExpr
if detach:
active_git.UpdateRef(ref, dst, message=msg, detach=True)
else:
active_git.symbolic_ref('-m', msg, ref, dst) active_git.symbolic_ref('-m', msg, ref, dst)
def _CheckDirReference(self, srcdir, destdir, share_refs): def _CheckDirReference(self, srcdir, destdir, share_refs):