diff options
-rw-r--r-- | project.py | 44 | ||||
-rw-r--r-- | subcmds/sync.py | 13 |
2 files changed, 46 insertions, 11 deletions
@@ -32,7 +32,7 @@ import traceback | |||
32 | from color import Coloring | 32 | from color import Coloring |
33 | from git_command import GitCommand, git_require | 33 | from git_command import GitCommand, git_require |
34 | from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE | 34 | from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE |
35 | from error import GitError, HookError, UploadError | 35 | from error import GitError, HookError, UploadError, DownloadError |
36 | from error import ManifestInvalidRevisionError | 36 | from error import ManifestInvalidRevisionError |
37 | from error import NoManifestException | 37 | from error import NoManifestException |
38 | from trace import IsTrace, Trace | 38 | from trace import IsTrace, Trace |
@@ -1101,6 +1101,7 @@ class Project(object): | |||
1101 | quiet=False, | 1101 | quiet=False, |
1102 | is_new=None, | 1102 | is_new=None, |
1103 | current_branch_only=False, | 1103 | current_branch_only=False, |
1104 | force_sync=False, | ||
1104 | clone_bundle=True, | 1105 | clone_bundle=True, |
1105 | no_tags=False, | 1106 | no_tags=False, |
1106 | archive=False, | 1107 | archive=False, |
@@ -1140,7 +1141,7 @@ class Project(object): | |||
1140 | if is_new is None: | 1141 | if is_new is None: |
1141 | is_new = not self.Exists | 1142 | is_new = not self.Exists |
1142 | if is_new: | 1143 | if is_new: |
1143 | self._InitGitDir() | 1144 | self._InitGitDir(force_sync=force_sync) |
1144 | else: | 1145 | else: |
1145 | self._UpdateHooks() | 1146 | self._UpdateHooks() |
1146 | self._InitRemote() | 1147 | self._InitRemote() |
@@ -1233,11 +1234,11 @@ class Project(object): | |||
1233 | 'revision %s in %s not found' % (self.revisionExpr, | 1234 | 'revision %s in %s not found' % (self.revisionExpr, |
1234 | self.name)) | 1235 | self.name)) |
1235 | 1236 | ||
1236 | def Sync_LocalHalf(self, syncbuf): | 1237 | def Sync_LocalHalf(self, syncbuf, force_sync=False): |
1237 | """Perform only the local IO portion of the sync process. | 1238 | """Perform only the local IO portion of the sync process. |
1238 | Network access is not required. | 1239 | Network access is not required. |
1239 | """ | 1240 | """ |
1240 | self._InitWorkTree() | 1241 | self._InitWorkTree(force_sync=force_sync) |
1241 | all_refs = self.bare_ref.all | 1242 | all_refs = self.bare_ref.all |
1242 | self.CleanPublishedCache(all_refs) | 1243 | self.CleanPublishedCache(all_refs) |
1243 | revid = self.GetRevisionId(all_refs) | 1244 | revid = self.GetRevisionId(all_refs) |
@@ -2164,7 +2165,7 @@ class Project(object): | |||
2164 | if GitCommand(self, cmd).Wait() != 0: | 2165 | if GitCommand(self, cmd).Wait() != 0: |
2165 | raise GitError('%s merge %s ' % (self.name, head)) | 2166 | raise GitError('%s merge %s ' % (self.name, head)) |
2166 | 2167 | ||
2167 | def _InitGitDir(self, mirror_git=None): | 2168 | def _InitGitDir(self, mirror_git=None, force_sync=False): |
2168 | init_git_dir = not os.path.exists(self.gitdir) | 2169 | init_git_dir = not os.path.exists(self.gitdir) |
2169 | init_obj_dir = not os.path.exists(self.objdir) | 2170 | init_obj_dir = not os.path.exists(self.objdir) |
2170 | try: | 2171 | try: |
@@ -2181,7 +2182,20 @@ class Project(object): | |||
2181 | if init_obj_dir or init_git_dir: | 2182 | if init_obj_dir or init_git_dir: |
2182 | self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False, | 2183 | self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False, |
2183 | copy_all=True) | 2184 | copy_all=True) |
2184 | self._CheckDirReference(self.objdir, self.gitdir, share_refs=False) | 2185 | try: |
2186 | self._CheckDirReference(self.objdir, self.gitdir, share_refs=False) | ||
2187 | except GitError as e: | ||
2188 | print("Retrying clone after deleting %s" % force_sync, file=sys.stderr) | ||
2189 | if force_sync: | ||
2190 | try: | ||
2191 | shutil.rmtree(os.path.realpath(self.gitdir)) | ||
2192 | if self.worktree and os.path.exists( | ||
2193 | os.path.realpath(self.worktree)): | ||
2194 | shutil.rmtree(os.path.realpath(self.worktree)) | ||
2195 | return self._InitGitDir(mirror_git=mirror_git, force_sync=False) | ||
2196 | except: | ||
2197 | raise e | ||
2198 | raise e | ||
2185 | 2199 | ||
2186 | if init_git_dir: | 2200 | if init_git_dir: |
2187 | mp = self.manifest.manifestProject | 2201 | mp = self.manifest.manifestProject |
@@ -2308,7 +2322,8 @@ class Project(object): | |||
2308 | src = os.path.realpath(os.path.join(srcdir, name)) | 2322 | src = os.path.realpath(os.path.join(srcdir, name)) |
2309 | # Fail if the links are pointing to the wrong place | 2323 | # Fail if the links are pointing to the wrong place |
2310 | if src != dst: | 2324 | if src != dst: |
2311 | raise GitError('cannot overwrite a local work tree') | 2325 | raise GitError('--force-sync not enabled; cannot overwrite a local ' |
2326 | 'work tree') | ||
2312 | 2327 | ||
2313 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): | 2328 | def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all): |
2314 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. | 2329 | """Update |dotgit| to reference |gitdir|, using symlinks where possible. |
@@ -2361,11 +2376,11 @@ class Project(object): | |||
2361 | shutil.copy(src, dst) | 2376 | shutil.copy(src, dst) |
2362 | except OSError as e: | 2377 | except OSError as e: |
2363 | if e.errno == errno.EPERM: | 2378 | if e.errno == errno.EPERM: |
2364 | raise GitError('filesystem must support symlinks') | 2379 | raise DownloadError('filesystem must support symlinks') |
2365 | else: | 2380 | else: |
2366 | raise | 2381 | raise |
2367 | 2382 | ||
2368 | def _InitWorkTree(self): | 2383 | def _InitWorkTree(self, force_sync=False): |
2369 | dotgit = os.path.join(self.worktree, '.git') | 2384 | dotgit = os.path.join(self.worktree, '.git') |
2370 | init_dotgit = not os.path.exists(dotgit) | 2385 | init_dotgit = not os.path.exists(dotgit) |
2371 | try: | 2386 | try: |
@@ -2374,7 +2389,16 @@ class Project(object): | |||
2374 | self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True, | 2389 | self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True, |
2375 | copy_all=False) | 2390 | copy_all=False) |
2376 | 2391 | ||
2377 | self._CheckDirReference(self.gitdir, dotgit, share_refs=True) | 2392 | try: |
2393 | self._CheckDirReference(self.gitdir, dotgit, share_refs=True) | ||
2394 | except GitError as e: | ||
2395 | if force_sync: | ||
2396 | try: | ||
2397 | shutil.rmtree(dotgit) | ||
2398 | return self._InitWorkTree(force_sync=False) | ||
2399 | except: | ||
2400 | raise e | ||
2401 | raise e | ||
2378 | 2402 | ||
2379 | if init_dotgit: | 2403 | if init_dotgit: |
2380 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) | 2404 | _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId()) |
diff --git a/subcmds/sync.py b/subcmds/sync.py index ec333ae7..a8074a40 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -119,6 +119,11 @@ credentials. | |||
119 | The -f/--force-broken option can be used to proceed with syncing | 119 | The -f/--force-broken option can be used to proceed with syncing |
120 | other projects if a project sync fails. | 120 | other projects if a project sync fails. |
121 | 121 | ||
122 | The --force-sync option can be used to overwrite existing git | ||
123 | directories if they have previously been linked to a different | ||
124 | object direcotry. WARNING: This may cause data to be lost since | ||
125 | refs may be removed when overwriting. | ||
126 | |||
122 | The --no-clone-bundle option disables any attempt to use | 127 | The --no-clone-bundle option disables any attempt to use |
123 | $URL/clone.bundle to bootstrap a new Git repository from a | 128 | $URL/clone.bundle to bootstrap a new Git repository from a |
124 | resumeable bundle file on a content delivery network. This | 129 | resumeable bundle file on a content delivery network. This |
@@ -174,6 +179,11 @@ later is required to fix a server side protocol bug. | |||
174 | p.add_option('-f', '--force-broken', | 179 | p.add_option('-f', '--force-broken', |
175 | dest='force_broken', action='store_true', | 180 | dest='force_broken', action='store_true', |
176 | help="continue sync even if a project fails to sync") | 181 | help="continue sync even if a project fails to sync") |
182 | p.add_option('--force-sync', | ||
183 | dest='force_sync', action='store_true', | ||
184 | help="overwrite an existing git directory if it needs to " | ||
185 | "point to a different object directory. WARNING: this " | ||
186 | "may cause loss of data") | ||
177 | p.add_option('-l', '--local-only', | 187 | p.add_option('-l', '--local-only', |
178 | dest='local_only', action='store_true', | 188 | dest='local_only', action='store_true', |
179 | help="only update working tree, don't fetch") | 189 | help="only update working tree, don't fetch") |
@@ -281,6 +291,7 @@ later is required to fix a server side protocol bug. | |||
281 | success = project.Sync_NetworkHalf( | 291 | success = project.Sync_NetworkHalf( |
282 | quiet=opt.quiet, | 292 | quiet=opt.quiet, |
283 | current_branch_only=opt.current_branch_only, | 293 | current_branch_only=opt.current_branch_only, |
294 | force_sync=opt.force_sync, | ||
284 | clone_bundle=not opt.no_clone_bundle, | 295 | clone_bundle=not opt.no_clone_bundle, |
285 | no_tags=opt.no_tags, archive=self.manifest.IsArchive, | 296 | no_tags=opt.no_tags, archive=self.manifest.IsArchive, |
286 | optimized_fetch=opt.optimized_fetch) | 297 | optimized_fetch=opt.optimized_fetch) |
@@ -696,7 +707,7 @@ later is required to fix a server side protocol bug. | |||
696 | for project in all_projects: | 707 | for project in all_projects: |
697 | pm.update() | 708 | pm.update() |
698 | if project.worktree: | 709 | if project.worktree: |
699 | project.Sync_LocalHalf(syncbuf) | 710 | project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync) |
700 | pm.end() | 711 | pm.end() |
701 | print(file=sys.stderr) | 712 | print(file=sys.stderr) |
702 | if not syncbuf.Finish(): | 713 | if not syncbuf.Finish(): |