diff options
author | Mike Frysinger <vapier@google.com> | 2021-11-18 15:53:17 -0500 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2022-08-22 19:34:46 +0000 |
commit | 07d21e6bde9bd7efdfb8f25f2ed23f023daa5c1f (patch) | |
tree | 0302f2c7f2352c949329cc1764ad23899f4dc303 /project.py | |
parent | 076d54652e0025e1360f66e483926477b910b02e (diff) | |
download | git-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.py | 42 |
1 files changed, 37 insertions, 5 deletions
@@ -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: |