summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2021-11-18 15:53:17 -0500
committerMike Frysinger <vapier@google.com>2022-08-22 19:34:46 +0000
commit07d21e6bde9bd7efdfb8f25f2ed23f023daa5c1f (patch)
tree0302f2c7f2352c949329cc1764ad23899f4dc303 /project.py
parent076d54652e0025e1360f66e483926477b910b02e (diff)
downloadgit-repo-07d21e6bde9bd7efdfb8f25f2ed23f023daa5c1f.tar.gz
project: initialize new manifests in temp dirs
If initializing the manifest fails for any reason, don't leave it in a half complete state. This can cause problems if/when the user tries to reinit because different codepaths will be taken. For example, if we initialize manifests.git and don't finish probing the remote to see what default branch it uses, we end up always using "master" even if that isn't what the remote uses. To avoid all of this, use .tmp dirs when initializing, and rename to the final path only after we complete all the right steps. We should roll this out to all projects we clone, but start with the manifest project for now. Bug: https://crbug.com/gerrit/13526 Bug: https://crbug.com/gerrit/15805 Change-Id: I0214338de69ee11e090285c6b0b211052804af06 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/343539 Reviewed-by: LaMont Jones <lamontjones@google.com> Tested-by: Mike Frysinger <vapier@google.com>
Diffstat (limited to 'project.py')
-rw-r--r--project.py42
1 files changed, 37 insertions, 5 deletions
diff --git a/project.py b/project.py
index 2b57a5fb..ab449f00 100644
--- a/project.py
+++ b/project.py
@@ -2794,6 +2794,35 @@ class Project(object):
2794 else: 2794 else:
2795 raise 2795 raise
2796 2796
2797 def _InitialCheckoutStart(self):
2798 """Called when checking out a project for the first time.
2799
2800 This will use temporary non-visible paths so we can be safely interrupted
2801 without leaving incomplete state behind.
2802 """
2803 paths = [f'{x}.tmp' for x in (self.relpath, self.worktree, self.gitdir, self.objdir)]
2804 for p in paths:
2805 platform_utils.rmtree(p, ignore_errors=True)
2806 self.UpdatePaths(*paths)
2807
2808 def _InitialCheckoutFinalizeNetworkHalf(self):
2809 """Finalize the object dirs after network syncing works."""
2810 # Once the network half finishes, we can move the objects into the right
2811 # place by removing the ".tmp" suffix on the dirs.
2812 platform_utils.rmtree(self.gitdir[:-4], ignore_errors=True)
2813 os.rename(self.gitdir, self.gitdir[:-4])
2814 self.UpdatePaths(self.relpath, self.worktree, self.gitdir[:-4], self.objdir[:-4])
2815
2816 def _InitialCheckoutFinalizeLocalHalf(self):
2817 """Finalize the initial checkout and make it available."""
2818 assert self.gitdir == self.objdir
2819 # Once the local half finishes, we can move the manifest dir into the right
2820 # place by removing the ".tmp" suffix on the dirs.
2821 platform_utils.rmtree(self.worktree[:-4], ignore_errors=True)
2822 os.rename(self.worktree, self.worktree[:-4])
2823 self.UpdatePaths(
2824 self.relpath[:-4], self.worktree[:-4], self.gitdir, self.objdir)
2825
2797 def _InitGitWorktree(self): 2826 def _InitGitWorktree(self):
2798 """Init the project using git worktrees.""" 2827 """Init the project using git worktrees."""
2799 self.bare_git.worktree('prune') 2828 self.bare_git.worktree('prune')
@@ -3680,6 +3709,8 @@ class ManifestProject(MetaProject):
3680 (GitConfig.ForUser().UrlInsteadOf(manifest_url),), 3709 (GitConfig.ForUser().UrlInsteadOf(manifest_url),),
3681 file=sys.stderr) 3710 file=sys.stderr)
3682 3711
3712 self._InitialCheckoutStart()
3713
3683 # The manifest project object doesn't keep track of the path on the 3714 # The manifest project object doesn't keep track of the path on the
3684 # server where this git is located, so let's save that here. 3715 # server where this git is located, so let's save that here.
3685 mirrored_manifest_git = None 3716 mirrored_manifest_git = None
@@ -3839,16 +3870,14 @@ class ManifestProject(MetaProject):
3839 partial_clone_exclude=self.manifest.PartialCloneExclude): 3870 partial_clone_exclude=self.manifest.PartialCloneExclude):
3840 r = self.GetRemote() 3871 r = self.GetRemote()
3841 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) 3872 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
3842
3843 # Better delete the manifest git dir if we created it; otherwise next
3844 # time (when user fixes problems) we won't go through the "is_new" logic.
3845 if is_new:
3846 platform_utils.rmtree(self.gitdir)
3847 return False 3873 return False
3848 3874
3849 if manifest_branch: 3875 if manifest_branch:
3850 self.MetaBranchSwitch(submodules=submodules) 3876 self.MetaBranchSwitch(submodules=submodules)
3851 3877
3878 if is_new:
3879 self._InitialCheckoutFinalizeNetworkHalf()
3880
3852 syncbuf = SyncBuffer(self.config) 3881 syncbuf = SyncBuffer(self.config)
3853 self.Sync_LocalHalf(syncbuf, submodules=submodules) 3882 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3854 syncbuf.Finish() 3883 syncbuf.Finish()
@@ -3871,6 +3900,9 @@ class ManifestProject(MetaProject):
3871 with open(dest, 'wb') as f: 3900 with open(dest, 'wb') as f:
3872 f.write(manifest_data) 3901 f.write(manifest_data)
3873 3902
3903 if is_new:
3904 self._InitialCheckoutFinalizeLocalHalf()
3905
3874 try: 3906 try:
3875 self.manifest.Link(manifest_name) 3907 self.manifest.Link(manifest_name)
3876 except ManifestParseError as e: 3908 except ManifestParseError as e: