From 1469c28ec3e221cf08a9f83a415945443eac0be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remy=20B=C3=B6hmer?= Date: Tue, 15 Dec 2020 18:49:02 +0100 Subject: [PATCH] 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 Change-Id: I15c96604363c41f0d01c42f533174393097daeb5 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/290985 Reviewed-by: Mike Frysinger --- project.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/project.py b/project.py index cb78ff72..343452ad 100644 --- a/project.py +++ b/project.py @@ -2558,6 +2558,8 @@ class Project(object): base = R_WORKTREE_M active_git = self.work_git + + self._InitAnyMRef(HEAD, self.bare_git, detach=True) else: base = R_M active_git = self.bare_git @@ -2567,7 +2569,7 @@ class Project(object): def _InitMirrorHead(self): 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) if self.revisionId: @@ -2580,7 +2582,10 @@ class Project(object): dst = remote.ToLocal(self.revisionExpr) if cur != dst: msg = 'manifest set to %s' % self.revisionExpr - active_git.symbolic_ref('-m', msg, ref, dst) + if detach: + active_git.UpdateRef(ref, dst, message=msg, detach=True) + else: + active_git.symbolic_ref('-m', msg, ref, dst) def _CheckDirReference(self, srcdir, destdir, share_refs): # Git worktrees don't use symlinks to share at all.