diff options
author | Mike Frysinger <vapier@google.com> | 2020-02-26 23:53:36 -0500 |
---|---|---|
committer | David Pursehouse <dpursehouse@collab.net> | 2020-02-29 07:22:08 +0000 |
commit | 21b7fbe14d9457400a90683a9bd3febe4b7f8840 (patch) | |
tree | 658712eafc8754a4b7e2382d299c8dd207baaf75 | |
parent | b967f5c17a04d6a01c234427b3b5c9b49dff6ca5 (diff) | |
download | git-repo-21b7fbe14d9457400a90683a9bd3febe4b7f8840.tar.gz |
project: fix m/ pseudo ref handling with git worktrees
Since most ref namespaces are shared among all worktrees, trying to
set the pseudo m/<branch> in the common git repo ends up clobbering
each other when using shared checkouts. For example, in CrOS:
<project path="src/third_party/kernel/v3.8"
name="chromiumos/third_party/kernel"
revision="refs/heads/chromeos-3.8" />
<project path="src/third_party/kernel/v3.10"
name="chromiumos/third_party/kernel"
revision="refs/heads/chromeos-3.10" />
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 <vapier@google.com>
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
-rw-r--r-- | git_refs.py | 2 | ||||
-rw-r--r-- | 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/' | |||
23 | R_HEADS = 'refs/heads/' | 23 | R_HEADS = 'refs/heads/' |
24 | R_TAGS = 'refs/tags/' | 24 | R_TAGS = 'refs/tags/' |
25 | R_PUB = 'refs/published/' | 25 | R_PUB = 'refs/published/' |
26 | R_WORKTREE = 'refs/worktree/' | ||
27 | R_WORKTREE_M = R_WORKTREE + 'm/' | ||
26 | R_M = 'refs/remotes/m/' | 28 | R_M = 'refs/remotes/m/' |
27 | 29 | ||
28 | 30 | ||
@@ -42,7 +42,7 @@ import platform_utils | |||
42 | import progress | 42 | import progress |
43 | from repo_trace import IsTrace, Trace | 43 | from repo_trace import IsTrace, Trace |
44 | 44 | ||
45 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M | 45 | from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M |
46 | 46 | ||
47 | from pyversion import is_python3 | 47 | from pyversion import is_python3 |
48 | if is_python3(): | 48 | if is_python3(): |
@@ -2741,10 +2741,19 @@ class Project(object): | |||
2741 | os.makedirs(self.objdir) | 2741 | os.makedirs(self.objdir) |
2742 | self.bare_objdir.init() | 2742 | self.bare_objdir.init() |
2743 | 2743 | ||
2744 | # Enable per-worktree config file support if possible. This is more a | 2744 | if self.use_git_worktrees: |
2745 | # nice-to-have feature for users rather than a hard requirement. | 2745 | # Set up the m/ space to point to the worktree-specific ref space. |
2746 | if self.use_git_worktrees and git_require((2, 19, 0)): | 2746 | # We'll update the worktree-specific ref space on each checkout. |
2747 | self.EnableRepositoryExtension('worktreeConfig') | 2747 | if self.manifest.branch: |
2748 | self.bare_git.symbolic_ref( | ||
2749 | '-m', 'redirecting to worktree scope', | ||
2750 | R_M + self.manifest.branch, | ||
2751 | R_WORKTREE_M + self.manifest.branch) | ||
2752 | |||
2753 | # Enable per-worktree config file support if possible. This is more a | ||
2754 | # nice-to-have feature for users rather than a hard requirement. | ||
2755 | if git_require((2, 19, 0)): | ||
2756 | self.EnableRepositoryExtension('worktreeConfig') | ||
2748 | 2757 | ||
2749 | # If we have a separate directory to hold refs, initialize it as well. | 2758 | # If we have a separate directory to hold refs, initialize it as well. |
2750 | if self.objdir != self.gitdir: | 2759 | if self.objdir != self.gitdir: |
@@ -2879,25 +2888,37 @@ class Project(object): | |||
2879 | 2888 | ||
2880 | def _InitMRef(self): | 2889 | def _InitMRef(self): |
2881 | if self.manifest.branch: | 2890 | if self.manifest.branch: |
2882 | self._InitAnyMRef(R_M + self.manifest.branch) | 2891 | if self.use_git_worktrees: |
2892 | # We can't update this ref with git worktrees until it exists. | ||
2893 | # We'll wait until the initial checkout to set it. | ||
2894 | if not os.path.exists(self.worktree): | ||
2895 | return | ||
2896 | |||
2897 | base = R_WORKTREE_M | ||
2898 | active_git = self.work_git | ||
2899 | else: | ||
2900 | base = R_M | ||
2901 | active_git = self.bare_git | ||
2902 | |||
2903 | self._InitAnyMRef(base + self.manifest.branch, active_git) | ||
2883 | 2904 | ||
2884 | def _InitMirrorHead(self): | 2905 | def _InitMirrorHead(self): |
2885 | self._InitAnyMRef(HEAD) | 2906 | self._InitAnyMRef(HEAD, self.bare_git) |
2886 | 2907 | ||
2887 | def _InitAnyMRef(self, ref): | 2908 | def _InitAnyMRef(self, ref, active_git): |
2888 | cur = self.bare_ref.symref(ref) | 2909 | cur = self.bare_ref.symref(ref) |
2889 | 2910 | ||
2890 | if self.revisionId: | 2911 | if self.revisionId: |
2891 | if cur != '' or self.bare_ref.get(ref) != self.revisionId: | 2912 | if cur != '' or self.bare_ref.get(ref) != self.revisionId: |
2892 | msg = 'manifest set to %s' % self.revisionId | 2913 | msg = 'manifest set to %s' % self.revisionId |
2893 | dst = self.revisionId + '^0' | 2914 | dst = self.revisionId + '^0' |
2894 | self.bare_git.UpdateRef(ref, dst, message=msg, detach=True) | 2915 | active_git.UpdateRef(ref, dst, message=msg, detach=True) |
2895 | else: | 2916 | else: |
2896 | remote = self.GetRemote(self.remote.name) | 2917 | remote = self.GetRemote(self.remote.name) |
2897 | dst = remote.ToLocal(self.revisionExpr) | 2918 | dst = remote.ToLocal(self.revisionExpr) |
2898 | if cur != dst: | 2919 | if cur != dst: |
2899 | msg = 'manifest set to %s' % self.revisionExpr | 2920 | msg = 'manifest set to %s' % self.revisionExpr |
2900 | self.bare_git.symbolic_ref('-m', msg, ref, dst) | 2921 | active_git.symbolic_ref('-m', msg, ref, dst) |
2901 | 2922 | ||
2902 | def _CheckDirReference(self, srcdir, destdir, share_refs): | 2923 | def _CheckDirReference(self, srcdir, destdir, share_refs): |
2903 | # Git worktrees don't use symlinks to share at all. | 2924 | # Git worktrees don't use symlinks to share at all. |
@@ -3028,6 +3049,8 @@ class Project(object): | |||
3028 | with open(os.path.join(git_worktree_path, 'gitdir'), 'w') as fp: | 3049 | with open(os.path.join(git_worktree_path, 'gitdir'), 'w') as fp: |
3029 | print(os.path.relpath(dotgit, git_worktree_path), file=fp) | 3050 | print(os.path.relpath(dotgit, git_worktree_path), file=fp) |
3030 | 3051 | ||
3052 | self._InitMRef() | ||
3053 | |||
3031 | def _InitWorkTree(self, force_sync=False, submodules=False): | 3054 | def _InitWorkTree(self, force_sync=False, submodules=False): |
3032 | realdotgit = os.path.join(self.worktree, '.git') | 3055 | realdotgit = os.path.join(self.worktree, '.git') |
3033 | tmpdotgit = realdotgit + '.tmp' | 3056 | tmpdotgit = realdotgit + '.tmp' |