summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGavin Mak <gavinmak@google.com>2025-07-23 15:23:10 -0700
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2025-07-23 15:57:49 -0700
commit720bd1e96b4e9c36d035987578fc01a9939d753f (patch)
tree27ea0e54c715dd02909bc9932f9c2a89b892a23f
parent25858c8b16264beca64bf22b91588fa6694b2b07 (diff)
downloadgit-repo-a0fd44cdb27751aec85ee426e0815340b1fc6aaf.tar.gz
sync: Don't checkout if no worktreev2.57.1stable
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.py92
-rw-r--r--tests/test_subcmds_sync.py20
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()