diff options
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r-- | subcmds/sync.py | 109 |
1 files changed, 29 insertions, 80 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index 83c9ad36..fe63b484 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -60,7 +60,7 @@ from error import RepoChangedException, GitError, ManifestParseError | |||
60 | import platform_utils | 60 | import platform_utils |
61 | from project import SyncBuffer | 61 | from project import SyncBuffer |
62 | from progress import Progress | 62 | from progress import Progress |
63 | from repo_trace import Trace | 63 | from repo_trace import IsTrace, Trace |
64 | import ssh | 64 | import ssh |
65 | from wrapper import Wrapper | 65 | from wrapper import Wrapper |
66 | from manifest_xml import GitcManifest | 66 | from manifest_xml import GitcManifest |
@@ -739,6 +739,7 @@ later is required to fix a server side protocol bug. | |||
739 | bak_dir = os.path.join(objdir, '.repo', 'pack.bak') | 739 | bak_dir = os.path.join(objdir, '.repo', 'pack.bak') |
740 | if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir): | 740 | if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir): |
741 | return | 741 | return |
742 | saved = [] | ||
742 | files = set(platform_utils.listdir(pack_dir)) | 743 | files = set(platform_utils.listdir(pack_dir)) |
743 | to_backup = [] | 744 | to_backup = [] |
744 | for f in files: | 745 | for f in files: |
@@ -750,83 +751,12 @@ later is required to fix a server side protocol bug. | |||
750 | for fname in to_backup: | 751 | for fname in to_backup: |
751 | bak_fname = os.path.join(bak_dir, fname) | 752 | bak_fname = os.path.join(bak_dir, fname) |
752 | if not os.path.exists(bak_fname): | 753 | if not os.path.exists(bak_fname): |
753 | with Trace('%s saved %s', bare_git._project.name, fname): | 754 | saved.append(fname) |
754 | # Use a tmp file so that we are sure of a complete copy. | 755 | # Use a tmp file so that we are sure of a complete copy. |
755 | shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp') | 756 | shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp') |
756 | shutil.move(bak_fname + '.tmp', bak_fname) | 757 | shutil.move(bak_fname + '.tmp', bak_fname) |
757 | 758 | if saved: | |
758 | @staticmethod | 759 | Trace('%s saved %s', bare_git._project.name, ' '.join(saved)) |
759 | def _GetPreciousObjectsState(project: Project, opt): | ||
760 | """Get the preciousObjects state for the project. | ||
761 | |||
762 | Args: | ||
763 | project (Project): the project to examine, and possibly correct. | ||
764 | opt (optparse.Values): options given to sync. | ||
765 | |||
766 | Returns: | ||
767 | Expected state of extensions.preciousObjects: | ||
768 | False: Should be disabled. (not present) | ||
769 | True: Should be enabled. | ||
770 | """ | ||
771 | if project.use_git_worktrees: | ||
772 | return False | ||
773 | projects = project.manifest.GetProjectsWithName(project.name, | ||
774 | all_manifests=True) | ||
775 | if len(projects) == 1: | ||
776 | return False | ||
777 | relpath = project.RelPath(local=opt.this_manifest_only) | ||
778 | if len(projects) > 1: | ||
779 | # Objects are potentially shared with another project. | ||
780 | # See the logic in Project.Sync_NetworkHalf regarding UseAlternates. | ||
781 | # - When False, shared projects share (via symlink) | ||
782 | # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects | ||
783 | # directory. All objects are precious, since there is no project with a | ||
784 | # complete set of refs. | ||
785 | # - When True, shared projects share (via info/alternates) | ||
786 | # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store, | ||
787 | # which is written only on the first clone of the project, and is not | ||
788 | # written subsequently. (When Sync_NetworkHalf sees that it exists, it | ||
789 | # makes sure that the alternates file points there, and uses a | ||
790 | # project-local .git/objects directory for all syncs going forward. | ||
791 | # We do not support switching between the options. The environment | ||
792 | # variable is present for testing and migration only. | ||
793 | return not project.UseAlternates | ||
794 | print(f'\r{relpath}: project not found in manifest.', file=sys.stderr) | ||
795 | return False | ||
796 | |||
797 | def _RepairPreciousObjectsState(self, project: Project, opt): | ||
798 | """Correct the preciousObjects state for the project. | ||
799 | |||
800 | Args: | ||
801 | project (Project): the project to examine, and possibly correct. | ||
802 | opt (optparse.Values): options given to sync. | ||
803 | """ | ||
804 | expected = self._GetPreciousObjectsState(project, opt) | ||
805 | actual = project.config.GetBoolean('extensions.preciousObjects') or False | ||
806 | relpath = project.RelPath(local = opt.this_manifest_only) | ||
807 | |||
808 | if (expected != actual and | ||
809 | not project.config.GetBoolean('repo.preservePreciousObjects')): | ||
810 | # If this is unexpected, log it and repair. | ||
811 | Trace(f'{relpath} expected preciousObjects={expected}, got {actual}') | ||
812 | if expected: | ||
813 | if not opt.quiet: | ||
814 | print('\r%s: Shared project %s found, disabling pruning.' % | ||
815 | (relpath, project.name)) | ||
816 | if git_require((2, 7, 0)): | ||
817 | project.EnableRepositoryExtension('preciousObjects') | ||
818 | else: | ||
819 | # This isn't perfect, but it's the best we can do with old git. | ||
820 | print('\r%s: WARNING: shared projects are unreliable when using ' | ||
821 | 'old versions of git; please upgrade to git-2.7.0+.' | ||
822 | % (relpath,), | ||
823 | file=sys.stderr) | ||
824 | project.config.SetString('gc.pruneExpire', 'never') | ||
825 | else: | ||
826 | if not opt.quiet: | ||
827 | print(f'\r{relpath}: not shared, disabling pruning.') | ||
828 | project.config.SetString('extensions.preciousObjects', None) | ||
829 | project.config.SetString('gc.pruneExpire', None) | ||
830 | 760 | ||
831 | def _GCProjects(self, projects, opt, err_event): | 761 | def _GCProjects(self, projects, opt, err_event): |
832 | pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet) | 762 | pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet) |
@@ -834,8 +764,27 @@ later is required to fix a server side protocol bug. | |||
834 | 764 | ||
835 | tidy_dirs = {} | 765 | tidy_dirs = {} |
836 | for project in projects: | 766 | for project in projects: |
837 | self._RepairPreciousObjectsState(project, opt) | 767 | # Make sure pruning never kicks in with shared projects that do not use |
838 | 768 | # alternates to avoid corruption. | |
769 | if (not project.use_git_worktrees and | ||
770 | len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1): | ||
771 | if project.UseAlternates: | ||
772 | # Undo logic set by previous versions of repo. | ||
773 | project.config.SetString('extensions.preciousObjects', None) | ||
774 | project.config.SetString('gc.pruneExpire', None) | ||
775 | else: | ||
776 | if not opt.quiet: | ||
777 | print('\r%s: Shared project %s found, disabling pruning.' % | ||
778 | (project.relpath, project.name)) | ||
779 | if git_require((2, 7, 0)): | ||
780 | project.EnableRepositoryExtension('preciousObjects') | ||
781 | else: | ||
782 | # This isn't perfect, but it's the best we can do with old git. | ||
783 | print('\r%s: WARNING: shared projects are unreliable when using old ' | ||
784 | 'versions of git; please upgrade to git-2.7.0+.' | ||
785 | % (project.relpath,), | ||
786 | file=sys.stderr) | ||
787 | project.config.SetString('gc.pruneExpire', 'never') | ||
839 | project.config.SetString('gc.autoDetach', 'false') | 788 | project.config.SetString('gc.autoDetach', 'false') |
840 | # Only call git gc once per objdir, but call pack-refs for the remainder. | 789 | # Only call git gc once per objdir, but call pack-refs for the remainder. |
841 | if project.objdir not in tidy_dirs: | 790 | if project.objdir not in tidy_dirs: |