diff options
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r-- | subcmds/sync.py | 274 |
1 files changed, 176 insertions, 98 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index 9a66e48b..0abe23d6 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -12,6 +12,7 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import collections | ||
15 | import functools | 16 | import functools |
16 | import http.cookiejar as cookielib | 17 | import http.cookiejar as cookielib |
17 | import io | 18 | import io |
@@ -66,7 +67,7 @@ _ONE_DAY_S = 24 * 60 * 60 | |||
66 | class Sync(Command, MirrorSafeCommand): | 67 | class Sync(Command, MirrorSafeCommand): |
67 | jobs = 1 | 68 | jobs = 1 |
68 | COMMON = True | 69 | COMMON = True |
69 | MULTI_MANIFEST_SUPPORT = False | 70 | MULTI_MANIFEST_SUPPORT = True |
70 | helpSummary = "Update working tree to the latest revision" | 71 | helpSummary = "Update working tree to the latest revision" |
71 | helpUsage = """ | 72 | helpUsage = """ |
72 | %prog [<project>...] | 73 | %prog [<project>...] |
@@ -295,52 +296,92 @@ later is required to fix a server side protocol bug. | |||
295 | """ | 296 | """ |
296 | return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only | 297 | return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only |
297 | 298 | ||
298 | def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data, manifest): | 299 | def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data, |
299 | """Update revisionId of every project with the SHA from superproject. | 300 | manifest): |
301 | """Update revisionId of projects with the commit hash from the superproject. | ||
300 | 302 | ||
301 | This function updates each project's revisionId with SHA from superproject. | 303 | This function updates each project's revisionId with the commit hash from |
302 | It writes the updated manifest into a file and reloads the manifest from it. | 304 | the superproject. It writes the updated manifest into a file and reloads |
305 | the manifest from it. When appropriate, sub manifests are also processed. | ||
303 | 306 | ||
304 | Args: | 307 | Args: |
305 | opt: Program options returned from optparse. See _Options(). | 308 | opt: Program options returned from optparse. See _Options(). |
306 | args: Arguments to pass to GetProjects. See the GetProjects | 309 | args: Arguments to pass to GetProjects. See the GetProjects |
307 | docstring for details. | 310 | docstring for details. |
308 | load_local_manifests: Whether to load local manifests. | 311 | superproject_logging_data: A dictionary of superproject data to log. |
309 | superproject_logging_data: A dictionary of superproject data that is to be logged. | ||
310 | manifest: The manifest to use. | 312 | manifest: The manifest to use. |
311 | |||
312 | Returns: | ||
313 | Returns path to the overriding manifest file instead of None. | ||
314 | """ | 313 | """ |
315 | superproject = self.manifest.superproject | 314 | have_superproject = manifest.superproject or any( |
316 | superproject.SetQuiet(opt.quiet) | 315 | m.superproject for m in manifest.all_children) |
317 | print_messages = git_superproject.PrintMessages(opt.use_superproject, | 316 | if not have_superproject: |
318 | self.manifest) | 317 | return |
319 | superproject.SetPrintMessages(print_messages) | 318 | |
320 | if opt.local_only: | 319 | if opt.local_only: |
321 | manifest_path = superproject.manifest_path | 320 | manifest_path = manifest.superproject.manifest_path |
322 | if manifest_path: | 321 | if manifest_path: |
323 | self._ReloadManifest(manifest_path, manifest, load_local_manifests) | 322 | self._ReloadManifest(manifest_path, manifest) |
324 | return manifest_path | 323 | return |
325 | 324 | ||
326 | all_projects = self.GetProjects(args, | 325 | all_projects = self.GetProjects(args, |
327 | missing_ok=True, | 326 | missing_ok=True, |
328 | submodules_ok=opt.fetch_submodules) | 327 | submodules_ok=opt.fetch_submodules, |
329 | update_result = superproject.UpdateProjectsRevisionId( | 328 | manifest=manifest, |
330 | all_projects, git_event_log=self.git_event_log) | 329 | all_manifests=not opt.this_manifest_only) |
331 | manifest_path = update_result.manifest_path | 330 | |
332 | superproject_logging_data['updatedrevisionid'] = bool(manifest_path) | 331 | per_manifest = collections.defaultdict(list) |
333 | if manifest_path: | 332 | manifest_paths = {} |
334 | self._ReloadManifest(manifest_path, manifest, load_local_manifests) | 333 | if opt.this_manifest_only: |
334 | per_manifest[manifest.path_prefix] = all_projects | ||
335 | else: | 335 | else: |
336 | if print_messages: | 336 | for p in all_projects: |
337 | print('warning: Update of revisionId from superproject has failed, ' | 337 | per_manifest[p.manifest.path_prefix].append(p) |
338 | 'repo sync will not use superproject to fetch the source. ', | 338 | |
339 | 'Please resync with the --no-use-superproject option to avoid this repo warning.', | 339 | superproject_logging_data = {} |
340 | file=sys.stderr) | 340 | need_unload = False |
341 | if update_result.fatal and opt.use_superproject is not None: | 341 | for m in self.ManifestList(opt): |
342 | sys.exit(1) | 342 | if not m.path_prefix in per_manifest: |
343 | return manifest_path | 343 | continue |
344 | use_super = git_superproject.UseSuperproject(opt.use_superproject, m) | ||
345 | if superproject_logging_data: | ||
346 | superproject_logging_data['multimanifest'] = True | ||
347 | superproject_logging_data.update( | ||
348 | superproject=use_super, | ||
349 | haslocalmanifests=bool(m.HasLocalManifests), | ||
350 | hassuperprojecttag=bool(m.superproject), | ||
351 | ) | ||
352 | if use_super and (m.IsMirror or m.IsArchive): | ||
353 | # Don't use superproject, because we have no working tree. | ||
354 | use_super = False | ||
355 | superproject_logging_data['superproject'] = False | ||
356 | superproject_logging_data['noworktree'] = True | ||
357 | if opt.use_superproject is not False: | ||
358 | print(f'{m.path_prefix}: not using superproject because there is no ' | ||
359 | 'working tree.') | ||
360 | |||
361 | if not use_super: | ||
362 | continue | ||
363 | m.superproject.SetQuiet(opt.quiet) | ||
364 | print_messages = git_superproject.PrintMessages(opt.use_superproject, m) | ||
365 | m.superproject.SetPrintMessages(print_messages) | ||
366 | update_result = m.superproject.UpdateProjectsRevisionId( | ||
367 | per_manifest[m.path_prefix], git_event_log=self.git_event_log) | ||
368 | manifest_path = update_result.manifest_path | ||
369 | superproject_logging_data['updatedrevisionid'] = bool(manifest_path) | ||
370 | if manifest_path: | ||
371 | m.SetManifestOverride(manifest_path) | ||
372 | need_unload = True | ||
373 | else: | ||
374 | if print_messages: | ||
375 | print(f'{m.path_prefix}: warning: Update of revisionId from ' | ||
376 | 'superproject has failed, repo sync will not use superproject ' | ||
377 | 'to fetch the source. ', | ||
378 | 'Please resync with the --no-use-superproject option to avoid ' | ||
379 | 'this repo warning.', | ||
380 | file=sys.stderr) | ||
381 | if update_result.fatal and opt.use_superproject is not None: | ||
382 | sys.exit(1) | ||
383 | if need_unload: | ||
384 | m.outer_client.manifest.Unload() | ||
344 | 385 | ||
345 | def _FetchProjectList(self, opt, projects): | 386 | def _FetchProjectList(self, opt, projects): |
346 | """Main function of the fetch worker. | 387 | """Main function of the fetch worker. |
@@ -485,8 +526,8 @@ later is required to fix a server side protocol bug. | |||
485 | 526 | ||
486 | return (ret, fetched) | 527 | return (ret, fetched) |
487 | 528 | ||
488 | def _FetchMain(self, opt, args, all_projects, err_event, manifest_name, | 529 | def _FetchMain(self, opt, args, all_projects, err_event, |
489 | load_local_manifests, ssh_proxy, manifest): | 530 | ssh_proxy, manifest): |
490 | """The main network fetch loop. | 531 | """The main network fetch loop. |
491 | 532 | ||
492 | Args: | 533 | Args: |
@@ -494,8 +535,6 @@ later is required to fix a server side protocol bug. | |||
494 | args: Command line args used to filter out projects. | 535 | args: Command line args used to filter out projects. |
495 | all_projects: List of all projects that should be fetched. | 536 | all_projects: List of all projects that should be fetched. |
496 | err_event: Whether an error was hit while processing. | 537 | err_event: Whether an error was hit while processing. |
497 | manifest_name: Manifest file to be reloaded. | ||
498 | load_local_manifests: Whether to load local manifests. | ||
499 | ssh_proxy: SSH manager for clients & masters. | 538 | ssh_proxy: SSH manager for clients & masters. |
500 | manifest: The manifest to use. | 539 | manifest: The manifest to use. |
501 | 540 | ||
@@ -526,10 +565,12 @@ later is required to fix a server side protocol bug. | |||
526 | # Iteratively fetch missing and/or nested unregistered submodules | 565 | # Iteratively fetch missing and/or nested unregistered submodules |
527 | previously_missing_set = set() | 566 | previously_missing_set = set() |
528 | while True: | 567 | while True: |
529 | self._ReloadManifest(manifest_name, self.manifest, load_local_manifests) | 568 | self._ReloadManifest(None, manifest) |
530 | all_projects = self.GetProjects(args, | 569 | all_projects = self.GetProjects(args, |
531 | missing_ok=True, | 570 | missing_ok=True, |
532 | submodules_ok=opt.fetch_submodules) | 571 | submodules_ok=opt.fetch_submodules, |
572 | manifest=manifest, | ||
573 | all_manifests=not opt.this_manifest_only) | ||
533 | missing = [] | 574 | missing = [] |
534 | for project in all_projects: | 575 | for project in all_projects: |
535 | if project.gitdir not in fetched: | 576 | if project.gitdir not in fetched: |
@@ -624,7 +665,7 @@ later is required to fix a server side protocol bug. | |||
624 | for project in projects: | 665 | for project in projects: |
625 | # Make sure pruning never kicks in with shared projects. | 666 | # Make sure pruning never kicks in with shared projects. |
626 | if (not project.use_git_worktrees and | 667 | if (not project.use_git_worktrees and |
627 | len(project.manifest.GetProjectsWithName(project.name)) > 1): | 668 | len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1): |
628 | if not opt.quiet: | 669 | if not opt.quiet: |
629 | print('\r%s: Shared project %s found, disabling pruning.' % | 670 | print('\r%s: Shared project %s found, disabling pruning.' % |
630 | (project.relpath, project.name)) | 671 | (project.relpath, project.name)) |
@@ -698,7 +739,7 @@ later is required to fix a server side protocol bug. | |||
698 | t.join() | 739 | t.join() |
699 | pm.end() | 740 | pm.end() |
700 | 741 | ||
701 | def _ReloadManifest(self, manifest_name, manifest, load_local_manifests=True): | 742 | def _ReloadManifest(self, manifest_name, manifest): |
702 | """Reload the manfiest from the file specified by the |manifest_name|. | 743 | """Reload the manfiest from the file specified by the |manifest_name|. |
703 | 744 | ||
704 | It unloads the manifest if |manifest_name| is None. | 745 | It unloads the manifest if |manifest_name| is None. |
@@ -706,17 +747,29 @@ later is required to fix a server side protocol bug. | |||
706 | Args: | 747 | Args: |
707 | manifest_name: Manifest file to be reloaded. | 748 | manifest_name: Manifest file to be reloaded. |
708 | manifest: The manifest to use. | 749 | manifest: The manifest to use. |
709 | load_local_manifests: Whether to load local manifests. | ||
710 | """ | 750 | """ |
711 | if manifest_name: | 751 | if manifest_name: |
712 | # Override calls Unload already | 752 | # Override calls Unload already |
713 | manifest.Override(manifest_name, load_local_manifests=load_local_manifests) | 753 | manifest.Override(manifest_name) |
714 | else: | 754 | else: |
715 | manifest.Unload() | 755 | manifest.Unload() |
716 | 756 | ||
717 | def UpdateProjectList(self, opt, manifest): | 757 | def UpdateProjectList(self, opt, manifest): |
758 | """Update the cached projects list for |manifest| | ||
759 | |||
760 | In a multi-manifest checkout, each manifest has its own project.list. | ||
761 | |||
762 | Args: | ||
763 | opt: Program options returned from optparse. See _Options(). | ||
764 | manifest: The manifest to use. | ||
765 | |||
766 | Returns: | ||
767 | 0: success | ||
768 | 1: failure | ||
769 | """ | ||
718 | new_project_paths = [] | 770 | new_project_paths = [] |
719 | for project in self.GetProjects(None, missing_ok=True): | 771 | for project in self.GetProjects(None, missing_ok=True, manifest=manifest, |
772 | all_manifests=False): | ||
720 | if project.relpath: | 773 | if project.relpath: |
721 | new_project_paths.append(project.relpath) | 774 | new_project_paths.append(project.relpath) |
722 | file_name = 'project.list' | 775 | file_name = 'project.list' |
@@ -766,7 +819,8 @@ later is required to fix a server side protocol bug. | |||
766 | new_paths = {} | 819 | new_paths = {} |
767 | new_linkfile_paths = [] | 820 | new_linkfile_paths = [] |
768 | new_copyfile_paths = [] | 821 | new_copyfile_paths = [] |
769 | for project in self.GetProjects(None, missing_ok=True): | 822 | for project in self.GetProjects(None, missing_ok=True, |
823 | manifest=manifest, all_manifests=False): | ||
770 | new_linkfile_paths.extend(x.dest for x in project.linkfiles) | 824 | new_linkfile_paths.extend(x.dest for x in project.linkfiles) |
771 | new_copyfile_paths.extend(x.dest for x in project.copyfiles) | 825 | new_copyfile_paths.extend(x.dest for x in project.copyfiles) |
772 | 826 | ||
@@ -897,8 +951,40 @@ later is required to fix a server side protocol bug. | |||
897 | 951 | ||
898 | return manifest_name | 952 | return manifest_name |
899 | 953 | ||
954 | def _UpdateAllManifestProjects(self, opt, mp, manifest_name): | ||
955 | """Fetch & update the local manifest project. | ||
956 | |||
957 | After syncing the manifest project, if the manifest has any sub manifests, | ||
958 | those are recursively processed. | ||
959 | |||
960 | Args: | ||
961 | opt: Program options returned from optparse. See _Options(). | ||
962 | mp: the manifestProject to query. | ||
963 | manifest_name: Manifest file to be reloaded. | ||
964 | """ | ||
965 | if not mp.standalone_manifest_url: | ||
966 | self._UpdateManifestProject(opt, mp, manifest_name) | ||
967 | |||
968 | if mp.manifest.submanifests: | ||
969 | for submanifest in mp.manifest.submanifests.values(): | ||
970 | child = submanifest.repo_client.manifest | ||
971 | child.manifestProject.SyncWithPossibleInit( | ||
972 | submanifest, | ||
973 | current_branch_only=self._GetCurrentBranchOnly(opt, child), | ||
974 | verbose=opt.verbose, | ||
975 | tags=opt.tags, | ||
976 | git_event_log=self.git_event_log, | ||
977 | ) | ||
978 | self._UpdateAllManifestProjects(opt, child.manifestProject, None) | ||
979 | |||
900 | def _UpdateManifestProject(self, opt, mp, manifest_name): | 980 | def _UpdateManifestProject(self, opt, mp, manifest_name): |
901 | """Fetch & update the local manifest project.""" | 981 | """Fetch & update the local manifest project. |
982 | |||
983 | Args: | ||
984 | opt: Program options returned from optparse. See _Options(). | ||
985 | mp: the manifestProject to query. | ||
986 | manifest_name: Manifest file to be reloaded. | ||
987 | """ | ||
902 | if not opt.local_only: | 988 | if not opt.local_only: |
903 | start = time.time() | 989 | start = time.time() |
904 | success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose, | 990 | success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose, |
@@ -924,6 +1010,7 @@ later is required to fix a server side protocol bug. | |||
924 | if not clean: | 1010 | if not clean: |
925 | sys.exit(1) | 1011 | sys.exit(1) |
926 | self._ReloadManifest(manifest_name, mp.manifest) | 1012 | self._ReloadManifest(manifest_name, mp.manifest) |
1013 | |||
927 | if opt.jobs is None: | 1014 | if opt.jobs is None: |
928 | self.jobs = mp.manifest.default.sync_j | 1015 | self.jobs = mp.manifest.default.sync_j |
929 | 1016 | ||
@@ -948,9 +1035,6 @@ later is required to fix a server side protocol bug. | |||
948 | if opt.prune is None: | 1035 | if opt.prune is None: |
949 | opt.prune = True | 1036 | opt.prune = True |
950 | 1037 | ||
951 | if self.outer_client.manifest.is_multimanifest and not opt.this_manifest_only and args: | ||
952 | self.OptionParser.error('partial syncs must use --this-manifest-only') | ||
953 | |||
954 | def Execute(self, opt, args): | 1038 | def Execute(self, opt, args): |
955 | if opt.jobs: | 1039 | if opt.jobs: |
956 | self.jobs = opt.jobs | 1040 | self.jobs = opt.jobs |
@@ -959,7 +1043,7 @@ later is required to fix a server side protocol bug. | |||
959 | self.jobs = min(self.jobs, (soft_limit - 5) // 3) | 1043 | self.jobs = min(self.jobs, (soft_limit - 5) // 3) |
960 | 1044 | ||
961 | manifest = self.outer_manifest | 1045 | manifest = self.outer_manifest |
962 | if opt.this_manifest_only or not opt.outer_manifest: | 1046 | if not opt.outer_manifest: |
963 | manifest = self.manifest | 1047 | manifest = self.manifest |
964 | 1048 | ||
965 | if opt.manifest_name: | 1049 | if opt.manifest_name: |
@@ -994,39 +1078,26 @@ later is required to fix a server side protocol bug. | |||
994 | 'receive updates; run `repo init --repo-rev=stable` to fix.', | 1078 | 'receive updates; run `repo init --repo-rev=stable` to fix.', |
995 | file=sys.stderr) | 1079 | file=sys.stderr) |
996 | 1080 | ||
997 | mp = manifest.manifestProject | 1081 | for m in self.ManifestList(opt): |
998 | is_standalone_manifest = bool(mp.standalone_manifest_url) | 1082 | mp = m.manifestProject |
999 | if not is_standalone_manifest: | 1083 | is_standalone_manifest = bool(mp.standalone_manifest_url) |
1000 | mp.PreSync() | 1084 | if not is_standalone_manifest: |
1085 | mp.PreSync() | ||
1001 | 1086 | ||
1002 | if opt.repo_upgraded: | 1087 | if opt.repo_upgraded: |
1003 | _PostRepoUpgrade(manifest, quiet=opt.quiet) | 1088 | _PostRepoUpgrade(m, quiet=opt.quiet) |
1004 | 1089 | ||
1005 | if not opt.mp_update: | 1090 | if opt.mp_update: |
1091 | self._UpdateAllManifestProjects(opt, mp, manifest_name) | ||
1092 | else: | ||
1006 | print('Skipping update of local manifest project.') | 1093 | print('Skipping update of local manifest project.') |
1007 | elif not is_standalone_manifest: | ||
1008 | self._UpdateManifestProject(opt, mp, manifest_name) | ||
1009 | 1094 | ||
1010 | load_local_manifests = not manifest.HasLocalManifests | 1095 | superproject_logging_data = {} |
1011 | use_superproject = git_superproject.UseSuperproject(opt.use_superproject, manifest) | 1096 | self._UpdateProjectsRevisionId(opt, args, superproject_logging_data, |
1012 | if use_superproject and (manifest.IsMirror or manifest.IsArchive): | 1097 | manifest) |
1013 | # Don't use superproject, because we have no working tree. | ||
1014 | use_superproject = False | ||
1015 | if opt.use_superproject is not None: | ||
1016 | print('Defaulting to no-use-superproject because there is no working tree.') | ||
1017 | superproject_logging_data = { | ||
1018 | 'superproject': use_superproject, | ||
1019 | 'haslocalmanifests': bool(manifest.HasLocalManifests), | ||
1020 | 'hassuperprojecttag': bool(manifest.superproject), | ||
1021 | } | ||
1022 | if use_superproject: | ||
1023 | manifest_name = self._UpdateProjectsRevisionId( | ||
1024 | opt, args, load_local_manifests, superproject_logging_data, | ||
1025 | manifest) or opt.manifest_name | ||
1026 | 1098 | ||
1027 | if self.gitc_manifest: | 1099 | if self.gitc_manifest: |
1028 | gitc_manifest_projects = self.GetProjects(args, | 1100 | gitc_manifest_projects = self.GetProjects(args, missing_ok=True) |
1029 | missing_ok=True) | ||
1030 | gitc_projects = [] | 1101 | gitc_projects = [] |
1031 | opened_projects = [] | 1102 | opened_projects = [] |
1032 | for project in gitc_manifest_projects: | 1103 | for project in gitc_manifest_projects: |
@@ -1059,9 +1130,12 @@ later is required to fix a server side protocol bug. | |||
1059 | for path in opened_projects] | 1130 | for path in opened_projects] |
1060 | if not args: | 1131 | if not args: |
1061 | return | 1132 | return |
1133 | |||
1062 | all_projects = self.GetProjects(args, | 1134 | all_projects = self.GetProjects(args, |
1063 | missing_ok=True, | 1135 | missing_ok=True, |
1064 | submodules_ok=opt.fetch_submodules) | 1136 | submodules_ok=opt.fetch_submodules, |
1137 | manifest=manifest, | ||
1138 | all_manifests=not opt.this_manifest_only) | ||
1065 | 1139 | ||
1066 | err_network_sync = False | 1140 | err_network_sync = False |
1067 | err_update_projects = False | 1141 | err_update_projects = False |
@@ -1073,7 +1147,6 @@ later is required to fix a server side protocol bug. | |||
1073 | # Initialize the socket dir once in the parent. | 1147 | # Initialize the socket dir once in the parent. |
1074 | ssh_proxy.sock() | 1148 | ssh_proxy.sock() |
1075 | all_projects = self._FetchMain(opt, args, all_projects, err_event, | 1149 | all_projects = self._FetchMain(opt, args, all_projects, err_event, |
1076 | manifest_name, load_local_manifests, | ||
1077 | ssh_proxy, manifest) | 1150 | ssh_proxy, manifest) |
1078 | 1151 | ||
1079 | if opt.network_only: | 1152 | if opt.network_only: |
@@ -1090,23 +1163,24 @@ later is required to fix a server side protocol bug. | |||
1090 | file=sys.stderr) | 1163 | file=sys.stderr) |
1091 | sys.exit(1) | 1164 | sys.exit(1) |
1092 | 1165 | ||
1093 | if manifest.IsMirror or manifest.IsArchive: | 1166 | for m in self.ManifestList(opt): |
1094 | # bail out now, we have no working tree | 1167 | if m.IsMirror or m.IsArchive: |
1095 | return | 1168 | # bail out now, we have no working tree |
1169 | continue | ||
1096 | 1170 | ||
1097 | if self.UpdateProjectList(opt, manifest): | 1171 | if self.UpdateProjectList(opt, m): |
1098 | err_event.set() | 1172 | err_event.set() |
1099 | err_update_projects = True | 1173 | err_update_projects = True |
1100 | if opt.fail_fast: | 1174 | if opt.fail_fast: |
1101 | print('\nerror: Local checkouts *not* updated.', file=sys.stderr) | 1175 | print('\nerror: Local checkouts *not* updated.', file=sys.stderr) |
1102 | sys.exit(1) | 1176 | sys.exit(1) |
1103 | 1177 | ||
1104 | err_update_linkfiles = not self.UpdateCopyLinkfileList(manifest) | 1178 | err_update_linkfiles = not self.UpdateCopyLinkfileList(m) |
1105 | if err_update_linkfiles: | 1179 | if err_update_linkfiles: |
1106 | err_event.set() | 1180 | err_event.set() |
1107 | if opt.fail_fast: | 1181 | if opt.fail_fast: |
1108 | print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr) | 1182 | print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr) |
1109 | sys.exit(1) | 1183 | sys.exit(1) |
1110 | 1184 | ||
1111 | err_results = [] | 1185 | err_results = [] |
1112 | # NB: We don't exit here because this is the last step. | 1186 | # NB: We don't exit here because this is the last step. |
@@ -1114,10 +1188,14 @@ later is required to fix a server side protocol bug. | |||
1114 | if err_checkout: | 1188 | if err_checkout: |
1115 | err_event.set() | 1189 | err_event.set() |
1116 | 1190 | ||
1117 | # If there's a notice that's supposed to print at the end of the sync, print | 1191 | printed_notices = set() |
1118 | # it now... | 1192 | # If there's a notice that's supposed to print at the end of the sync, |
1119 | if manifest.notice: | 1193 | # print it now... But avoid printing duplicate messages, and preserve |
1120 | print(manifest.notice) | 1194 | # order. |
1195 | for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix): | ||
1196 | if m.notice and m.notice not in printed_notices: | ||
1197 | print(m.notice) | ||
1198 | printed_notices.add(m.notice) | ||
1121 | 1199 | ||
1122 | # If we saw an error, exit with code 1 so that other scripts can check. | 1200 | # If we saw an error, exit with code 1 so that other scripts can check. |
1123 | if err_event.is_set(): | 1201 | if err_event.is_set(): |