summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py109
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
60import platform_utils 60import platform_utils
61from project import SyncBuffer 61from project import SyncBuffer
62from progress import Progress 62from progress import Progress
63from repo_trace import Trace 63from repo_trace import IsTrace, Trace
64import ssh 64import ssh
65from wrapper import Wrapper 65from wrapper import Wrapper
66from manifest_xml import GitcManifest 66from 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: