diff options
author | Gavin Mak <gavinmak@google.com> | 2025-07-23 15:23:10 -0700 |
---|---|---|
committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2025-07-23 15:57:49 -0700 |
commit | 720bd1e96b4e9c36d035987578fc01a9939d753f (patch) | |
tree | 27ea0e54c715dd02909bc9932f9c2a89b892a23f | |
parent | 25858c8b16264beca64bf22b91588fa6694b2b07 (diff) | |
download | git-repo-a0fd44cdb27751aec85ee426e0815340b1fc6aaf.tar.gz |
Interleaved sync should not try checkout out a project if it's a mirror.
Change-Id: I2549faab197a3202d79a10e44b449b68d53e3fe7
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/492942
Commit-Queue: Gavin Mak <gavinmak@google.com>
Reviewed-by: Scott Lee <ddoman@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
-rw-r--r-- | subcmds/sync.py | 92 | ||||
-rw-r--r-- | tests/test_subcmds_sync.py | 20 |
2 files changed, 69 insertions, 43 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index b02fdd02..c0310c56 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -2269,51 +2269,57 @@ later is required to fix a server side protocol bug. | |||
2269 | checkout_finish = None | 2269 | checkout_finish = None |
2270 | checkout_stderr = "" | 2270 | checkout_stderr = "" |
2271 | 2271 | ||
2272 | if fetch_success and not opt.network_only: | 2272 | if fetch_success: |
2273 | checkout_start = time.time() | 2273 | # We skip checkout if it's network-only or if the project has no |
2274 | stderr_capture = io.StringIO() | 2274 | # working tree (e.g., a mirror). |
2275 | try: | 2275 | if opt.network_only or not project.worktree: |
2276 | with contextlib.redirect_stderr(stderr_capture): | 2276 | checkout_success = True |
2277 | syncbuf = SyncBuffer( | 2277 | else: |
2278 | project.manifest.manifestProject.config, | 2278 | # This is a normal project that needs a checkout. |
2279 | detach_head=opt.detach_head, | 2279 | checkout_start = time.time() |
2280 | stderr_capture = io.StringIO() | ||
2281 | try: | ||
2282 | with contextlib.redirect_stderr(stderr_capture): | ||
2283 | syncbuf = SyncBuffer( | ||
2284 | project.manifest.manifestProject.config, | ||
2285 | detach_head=opt.detach_head, | ||
2286 | ) | ||
2287 | local_half_errors = [] | ||
2288 | project.Sync_LocalHalf( | ||
2289 | syncbuf, | ||
2290 | force_sync=opt.force_sync, | ||
2291 | force_checkout=opt.force_checkout, | ||
2292 | force_rebase=opt.rebase, | ||
2293 | errors=local_half_errors, | ||
2294 | verbose=opt.verbose, | ||
2295 | ) | ||
2296 | checkout_success = syncbuf.Finish() | ||
2297 | if local_half_errors: | ||
2298 | checkout_error = SyncError( | ||
2299 | aggregate_errors=local_half_errors | ||
2300 | ) | ||
2301 | except KeyboardInterrupt: | ||
2302 | logger.error( | ||
2303 | "Keyboard interrupt while processing %s", project.name | ||
2280 | ) | 2304 | ) |
2281 | local_half_errors = [] | 2305 | except GitError as e: |
2282 | project.Sync_LocalHalf( | 2306 | checkout_error = e |
2283 | syncbuf, | 2307 | logger.error( |
2284 | force_sync=opt.force_sync, | 2308 | "error.GitError: Cannot checkout %s: %s", |
2285 | force_checkout=opt.force_checkout, | 2309 | project.name, |
2286 | force_rebase=opt.rebase, | 2310 | e, |
2287 | errors=local_half_errors, | ||
2288 | verbose=opt.verbose, | ||
2289 | ) | 2311 | ) |
2290 | checkout_success = syncbuf.Finish() | 2312 | except Exception as e: |
2291 | if local_half_errors: | 2313 | checkout_error = e |
2292 | checkout_error = SyncError( | 2314 | logger.error( |
2293 | aggregate_errors=local_half_errors | 2315 | "error: Cannot checkout %s: %s: %s", |
2294 | ) | 2316 | project.name, |
2295 | except KeyboardInterrupt: | 2317 | type(e).__name__, |
2296 | logger.error( | 2318 | e, |
2297 | "Keyboard interrupt while processing %s", project.name | 2319 | ) |
2298 | ) | 2320 | finally: |
2299 | except GitError as e: | 2321 | checkout_finish = time.time() |
2300 | checkout_error = e | 2322 | checkout_stderr = stderr_capture.getvalue() |
2301 | logger.error( | ||
2302 | "error.GitError: Cannot checkout %s: %s", project.name, e | ||
2303 | ) | ||
2304 | except Exception as e: | ||
2305 | checkout_error = e | ||
2306 | logger.error( | ||
2307 | "error: Cannot checkout %s: %s: %s", | ||
2308 | project.name, | ||
2309 | type(e).__name__, | ||
2310 | e, | ||
2311 | ) | ||
2312 | finally: | ||
2313 | checkout_finish = time.time() | ||
2314 | checkout_stderr = stderr_capture.getvalue() | ||
2315 | elif fetch_success: | ||
2316 | checkout_success = True | ||
2317 | 2323 | ||
2318 | # Consolidate all captured output. | 2324 | # Consolidate all captured output. |
2319 | captured_parts = [] | 2325 | captured_parts = [] |
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py index 9cd19f10..5955e404 100644 --- a/tests/test_subcmds_sync.py +++ b/tests/test_subcmds_sync.py | |||
@@ -309,6 +309,7 @@ class FakeProject: | |||
309 | self.relpath = relpath | 309 | self.relpath = relpath |
310 | self.name = name or relpath | 310 | self.name = name or relpath |
311 | self.objdir = objdir or relpath | 311 | self.objdir = objdir or relpath |
312 | self.worktree = relpath | ||
312 | 313 | ||
313 | self.use_git_worktrees = False | 314 | self.use_git_worktrees = False |
314 | self.UseAlternates = False | 315 | self.UseAlternates = False |
@@ -836,6 +837,25 @@ class InterleavedSyncTest(unittest.TestCase): | |||
836 | project.Sync_NetworkHalf.assert_called_once() | 837 | project.Sync_NetworkHalf.assert_called_once() |
837 | project.Sync_LocalHalf.assert_not_called() | 838 | project.Sync_LocalHalf.assert_not_called() |
838 | 839 | ||
840 | def test_worker_no_worktree(self): | ||
841 | """Test interleaved sync does not checkout with no worktree.""" | ||
842 | opt = self._get_opts() | ||
843 | project = self.projA | ||
844 | project.worktree = None | ||
845 | project.Sync_NetworkHalf = mock.Mock( | ||
846 | return_value=SyncNetworkHalfResult(error=None, remote_fetched=True) | ||
847 | ) | ||
848 | project.Sync_LocalHalf = mock.Mock() | ||
849 | self.mock_context["projects"] = [project] | ||
850 | |||
851 | result_obj = self.cmd._SyncProjectList(opt, [0]) | ||
852 | result = result_obj.results[0] | ||
853 | |||
854 | self.assertTrue(result.fetch_success) | ||
855 | self.assertTrue(result.checkout_success) | ||
856 | project.Sync_NetworkHalf.assert_called_once() | ||
857 | project.Sync_LocalHalf.assert_not_called() | ||
858 | |||
839 | def test_worker_fetch_fails_exception(self): | 859 | def test_worker_fetch_fails_exception(self): |
840 | """Test _SyncProjectList with an exception during fetch.""" | 860 | """Test _SyncProjectList with an exception during fetch.""" |
841 | opt = self._get_opts() | 861 | opt = self._get_opts() |