diff options
-rw-r--r-- | subcmds/sync.py | 111 | ||||
-rw-r--r-- | tests/test_subcmds_sync.py | 20 |
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() |