summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--subcmds/sync.py111
-rw-r--r--tests/test_subcmds_sync.py20
2 files changed, 83 insertions, 48 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 13a322bc..c0310c56 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -412,16 +412,18 @@ later is required to fix a server side protocol bug.
412 type=int, 412 type=int,
413 metavar="JOBS", 413 metavar="JOBS",
414 help="number of network jobs to run in parallel (defaults to " 414 help="number of network jobs to run in parallel (defaults to "
415 "--jobs or 1). Ignored when --interleaved is set", 415 "--jobs or 1). Ignored unless --no-interleaved is set",
416 ) 416 )
417 p.add_option( 417 p.add_option(
418 "--jobs-checkout", 418 "--jobs-checkout",
419 default=None, 419 default=None,
420 type=int, 420 type=int,
421 metavar="JOBS", 421 metavar="JOBS",
422 help="number of local checkout jobs to run in parallel (defaults " 422 help=(
423 f"to --jobs or {DEFAULT_LOCAL_JOBS}). Ignored when --interleaved " 423 "number of local checkout jobs to run in parallel (defaults "
424 "is set", 424 f"to --jobs or {DEFAULT_LOCAL_JOBS}). Ignored unless "
425 "--no-interleaved is set"
426 ),
425 ) 427 )
426 428
427 p.add_option( 429 p.add_option(
@@ -480,7 +482,14 @@ later is required to fix a server side protocol bug.
480 p.add_option( 482 p.add_option(
481 "--interleaved", 483 "--interleaved",
482 action="store_true", 484 action="store_true",
483 help="fetch and checkout projects in parallel (experimental)", 485 default=True,
486 help="fetch and checkout projects in parallel (default)",
487 )
488 p.add_option(
489 "--no-interleaved",
490 dest="interleaved",
491 action="store_false",
492 help="fetch and checkout projects in phases",
484 ) 493 )
485 p.add_option( 494 p.add_option(
486 "-n", 495 "-n",
@@ -2260,51 +2269,57 @@ later is required to fix a server side protocol bug.
2260 checkout_finish = None 2269 checkout_finish = None
2261 checkout_stderr = "" 2270 checkout_stderr = ""
2262 2271
2263 if fetch_success and not opt.network_only: 2272 if fetch_success:
2264 checkout_start = time.time() 2273 # We skip checkout if it's network-only or if the project has no
2265 stderr_capture = io.StringIO() 2274 # working tree (e.g., a mirror).
2266 try: 2275 if opt.network_only or not project.worktree:
2267 with contextlib.redirect_stderr(stderr_capture): 2276 checkout_success = True
2268 syncbuf = SyncBuffer( 2277 else:
2269 project.manifest.manifestProject.config, 2278 # This is a normal project that needs a checkout.
2270 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
2271 ) 2304 )
2272 local_half_errors = [] 2305 except GitError as e:
2273 project.Sync_LocalHalf( 2306 checkout_error = e
2274 syncbuf, 2307 logger.error(
2275 force_sync=opt.force_sync, 2308 "error.GitError: Cannot checkout %s: %s",
2276 force_checkout=opt.force_checkout, 2309 project.name,
2277 force_rebase=opt.rebase, 2310 e,
2278 errors=local_half_errors,
2279 verbose=opt.verbose,
2280 ) 2311 )
2281 checkout_success = syncbuf.Finish() 2312 except Exception as e:
2282 if local_half_errors: 2313 checkout_error = e
2283 checkout_error = SyncError( 2314 logger.error(
2284 aggregate_errors=local_half_errors 2315 "error: Cannot checkout %s: %s: %s",
2285 ) 2316 project.name,
2286 except KeyboardInterrupt: 2317 type(e).__name__,
2287 logger.error( 2318 e,
2288 "Keyboard interrupt while processing %s", project.name 2319 )
2289 ) 2320 finally:
2290 except GitError as e: 2321 checkout_finish = time.time()
2291 checkout_error = e 2322 checkout_stderr = stderr_capture.getvalue()
2292 logger.error(
2293 "error.GitError: Cannot checkout %s: %s", project.name, e
2294 )
2295 except Exception as e:
2296 checkout_error = e
2297 logger.error(
2298 "error: Cannot checkout %s: %s: %s",
2299 project.name,
2300 type(e).__name__,
2301 e,
2302 )
2303 finally:
2304 checkout_finish = time.time()
2305 checkout_stderr = stderr_capture.getvalue()
2306 elif fetch_success:
2307 checkout_success = True
2308 2323
2309 # Consolidate all captured output. 2324 # Consolidate all captured output.
2310 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()