From 21b7fbe14d9457400a90683a9bd3febe4b7f8840 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 26 Feb 2020 23:53:36 -0500 Subject: [PATCH] project: fix m/ pseudo ref handling with git worktrees Since most ref namespaces are shared among all worktrees, trying to set the pseudo m/ in the common git repo ends up clobbering each other when using shared checkouts. For example, in CrOS: Trying to set m/master in chromiumos/third_party/kernel.git/ will keep clobbering the other. Instead, when using git worktrees, lets set the m/ pseudo ref to point into the refs/worktree/ namespace which is unique to each git worktree. So we have in the common dir: chromiumos/third_party/kernel.git/: refs/remotes/m/master: ref: refs/worktree/m/master And then in each worktree we point refs/worktree/m/master to the respective manifest revision expression. Now people can use the m/master in each git worktree and have it resolve to the right commit for that worktree. Bug: https://crbug.com/gerrit/12404 Change-Id: I78814bdd5dd67bb13218c4c6ccd64f8a15dd0a52 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/256952 Tested-by: Mike Frysinger Reviewed-by: David Pursehouse --- git_refs.py | 2 ++ project.py | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/git_refs.py b/git_refs.py index 02b98cba..e2b62ab2 100644 --- a/git_refs.py +++ b/git_refs.py @@ -23,6 +23,8 @@ R_CHANGES = 'refs/changes/' R_HEADS = 'refs/heads/' R_TAGS = 'refs/tags/' R_PUB = 'refs/published/' +R_WORKTREE = 'refs/worktree/' +R_WORKTREE_M = R_WORKTREE + 'm/' R_M = 'refs/remotes/m/' diff --git a/project.py b/project.py index b93dcd58..fe55371e 100644 --- a/project.py +++ b/project.py @@ -42,7 +42,7 @@ import platform_utils import progress from repo_trace import IsTrace, Trace -from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M +from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M from pyversion import is_python3 if is_python3(): @@ -2741,10 +2741,19 @@ class Project(object): os.makedirs(self.objdir) self.bare_objdir.init() - # Enable per-worktree config file support if possible. This is more a - # nice-to-have feature for users rather than a hard requirement. - if self.use_git_worktrees and git_require((2, 19, 0)): - self.EnableRepositoryExtension('worktreeConfig') + if self.use_git_worktrees: + # Set up the m/ space to point to the worktree-specific ref space. + # We'll update the worktree-specific ref space on each checkout. + if self.manifest.branch: + self.bare_git.symbolic_ref( + '-m', 'redirecting to worktree scope', + R_M + self.manifest.branch, + R_WORKTREE_M + self.manifest.branch) + + # Enable per-worktree config file support if possible. This is more a + # nice-to-have feature for users rather than a hard requirement. + if git_require((2, 19, 0)): + self.EnableRepositoryExtension('worktreeConfig') # If we have a separate directory to hold refs, initialize it as well. if self.objdir != self.gitdir: @@ -2879,25 +2888,37 @@ class Project(object): def _InitMRef(self): if self.manifest.branch: - self._InitAnyMRef(R_M + self.manifest.branch) + if self.use_git_worktrees: + # We can't update this ref with git worktrees until it exists. + # We'll wait until the initial checkout to set it. + if not os.path.exists(self.worktree): + return + + base = R_WORKTREE_M + active_git = self.work_git + else: + base = R_M + active_git = self.bare_git + + self._InitAnyMRef(base + self.manifest.branch, active_git) def _InitMirrorHead(self): - self._InitAnyMRef(HEAD) + self._InitAnyMRef(HEAD, self.bare_git) - def _InitAnyMRef(self, ref): + def _InitAnyMRef(self, ref, active_git): cur = self.bare_ref.symref(ref) if self.revisionId: if cur != '' or self.bare_ref.get(ref) != self.revisionId: msg = 'manifest set to %s' % self.revisionId dst = self.revisionId + '^0' - self.bare_git.UpdateRef(ref, dst, message=msg, detach=True) + active_git.UpdateRef(ref, dst, message=msg, detach=True) else: remote = self.GetRemote(self.remote.name) dst = remote.ToLocal(self.revisionExpr) if cur != dst: msg = 'manifest set to %s' % self.revisionExpr - self.bare_git.symbolic_ref('-m', msg, ref, dst) + 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. @@ -3028,6 +3049,8 @@ class Project(object): with open(os.path.join(git_worktree_path, 'gitdir'), 'w') as fp: print(os.path.relpath(dotgit, git_worktree_path), file=fp) + self._InitMRef() + def _InitWorkTree(self, force_sync=False, submodules=False): realdotgit = os.path.join(self.worktree, '.git') tmpdotgit = realdotgit + '.tmp'