Skip to content

Commit

Permalink
Merge tag 'v2.20' into dev/fufu/2.19
Browse files Browse the repository at this point in the history
repo v2.20
  • Loading branch information
Sebastien Fusilier committed Jan 26, 2023
2 parents dbb3d18 + 98bb765 commit d156970
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 63 deletions.
97 changes: 39 additions & 58 deletions repo/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,7 @@ def __init__(self,

class Project(object):
# These objects can be shared between several working trees.
shareable_files = ['description', 'info']
shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
# These objects can only be used by a single working tree.
working_tree_files = ['config', 'packed-refs', 'shallow']
working_tree_dirs = ['logs', 'refs']
shareable_dirs = ['hooks', 'objects', 'rr-cache']

def __init__(self,
manifest,
Expand Down Expand Up @@ -2486,10 +2482,9 @@ def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
os.makedirs(self.gitdir)

if init_obj_dir or init_git_dir:
self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
copy_all=True)
self._ReferenceGitDir(self.objdir, self.gitdir, copy_all=True)
try:
self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
self._CheckDirReference(self.objdir, self.gitdir)
except GitError as e:
if force_sync:
print("Retrying clone after deleting %s" %
Expand Down Expand Up @@ -2556,6 +2551,11 @@ def _InitHooks(self, quiet=False):
hooks = platform_utils.realpath(os.path.join(self.objdir, 'hooks'))
if not os.path.exists(hooks):
os.makedirs(hooks)

# Delete sample hooks. They're noise.
for hook in glob.glob(os.path.join(hooks, '*.sample')):
platform_utils.remove(hook, missing_ok=True)

for stock_hook in _ProjectHooks():
name = os.path.basename(stock_hook)

Expand Down Expand Up @@ -2653,40 +2653,16 @@ def _InitAnyMRef(self, ref, active_git, detach=False):
else:
active_git.symbolic_ref('-m', msg, ref, dst)

def _CheckDirReference(self, srcdir, destdir, share_refs):
def _CheckDirReference(self, srcdir, destdir):
# Git worktrees don't use symlinks to share at all.
if self.use_git_worktrees:
return

symlink_files = self.shareable_files[:]
symlink_dirs = self.shareable_dirs[:]
if share_refs:
symlink_files += self.working_tree_files
symlink_dirs += self.working_tree_dirs
to_symlink = symlink_files + symlink_dirs
for name in set(to_symlink):
for name in self.shareable_dirs:
# Try to self-heal a bit in simple cases.
dst_path = os.path.join(destdir, name)
src_path = os.path.join(srcdir, name)

if name in self.working_tree_dirs:
# If the dir is missing under .repo/projects/, create it.
if not os.path.exists(src_path):
os.makedirs(src_path)

elif name in self.working_tree_files:
# If it's a file under the checkout .git/ and the .repo/projects/ has
# nothing, move the file under the .repo/projects/ tree.
if not os.path.exists(src_path) and os.path.isfile(dst_path):
platform_utils.rename(dst_path, src_path)

# If the path exists under the .repo/projects/ and there's no symlink
# under the checkout .git/, recreate the symlink.
if name in self.working_tree_dirs or name in self.working_tree_files:
if os.path.exists(src_path) and not os.path.exists(dst_path):
platform_utils.symlink(
os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)

dst = platform_utils.realpath(dst_path)
if os.path.lexists(dst):
src = platform_utils.realpath(src_path)
Expand All @@ -2699,23 +2675,17 @@ def _CheckDirReference(self, srcdir, destdir, share_refs):
' use `repo sync --force-sync {0}` to '
'proceed.'.format(self.relpath))

def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
def _ReferenceGitDir(self, gitdir, dotgit, copy_all):
"""Update |dotgit| to reference |gitdir|, using symlinks where possible.
Args:
gitdir: The bare git repository. Must already be initialized.
dotgit: The repository you would like to initialize.
share_refs: If true, |dotgit| will store its refs under |gitdir|.
Only one work tree can store refs under a given |gitdir|.
copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
This saves you the effort of initializing |dotgit| yourself.
"""
symlink_files = self.shareable_files[:]
symlink_dirs = self.shareable_dirs[:]
if share_refs:
symlink_files += self.working_tree_files
symlink_dirs += self.working_tree_dirs
to_symlink = symlink_files + symlink_dirs
to_symlink = symlink_dirs

to_copy = []
if copy_all:
Expand Down Expand Up @@ -2743,11 +2713,6 @@ def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
elif os.path.isfile(src):
shutil.copy(src, dst)

# If the source file doesn't exist, ensure the destination
# file doesn't either.
if name in symlink_files and not os.path.lexists(src):
platform_utils.remove(dst, missing_ok=True)

except OSError as e:
if e.errno == errno.EPERM:
raise DownloadError(self._get_symlink_error_message())
Expand Down Expand Up @@ -2851,24 +2816,40 @@ def _MigrateOldWorkTreeGitDir(cls, dotgit):
}
# Paths that we know will be in both, but are safe to clobber in .repo/projects/.
SAFE_TO_CLOBBER = {
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD',
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'gitk.cache', 'index', 'ORIG_HEAD',
}

# Now walk the paths and sync the .git/ to .repo/projects/.
# First see if we'd succeed before starting the migration.
unknown_paths = []
for name in platform_utils.listdir(dotgit):
# Ignore all temporary/backup names. These are common with vim & emacs.
if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
continue

dotgit_path = os.path.join(dotgit, name)
if name in KNOWN_LINKS:
if platform_utils.islink(dotgit_path):
platform_utils.remove(dotgit_path)
else:
raise GitError(f'{dotgit_path}: should be a symlink')
if not platform_utils.islink(dotgit_path):
unknown_paths.append(f'{dotgit_path}: should be a symlink')
else:
gitdir_path = os.path.join(gitdir, name)
if name in SAFE_TO_CLOBBER or not os.path.exists(gitdir_path):
platform_utils.remove(gitdir_path, missing_ok=True)
platform_utils.rename(dotgit_path, gitdir_path)
else:
raise GitError(f'{dotgit_path}: unknown file; please file a bug')
if name not in SAFE_TO_CLOBBER and os.path.exists(gitdir_path):
unknown_paths.append(f'{dotgit_path}: unknown file; please file a bug')
if unknown_paths:
raise GitError('Aborting migration: ' + '\n'.join(unknown_paths))

# Now walk the paths and sync the .git/ to .repo/projects/.
for name in platform_utils.listdir(dotgit):
dotgit_path = os.path.join(dotgit, name)

# Ignore all temporary/backup names. These are common with vim & emacs.
if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
platform_utils.remove(dotgit_path)
elif name in KNOWN_LINKS:
platform_utils.remove(dotgit_path)
else:
gitdir_path = os.path.join(gitdir, name)
platform_utils.remove(gitdir_path, missing_ok=True)
platform_utils.rename(dotgit_path, gitdir_path)

# Now that the dir should be empty, clear it out, and symlink it over.
platform_utils.rmdir(dotgit)
Expand Down
5 changes: 3 additions & 2 deletions repo/subcmds/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -988,10 +988,11 @@ def Execute(self, opt, args):

load_local_manifests = not self.manifest.HasLocalManifests
use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
if self.manifest.IsMirror or self.manifest.IsArchive:
if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
# Don't use superproject, because we have no working tree.
use_superproject = False
print('Defaulting to no-use-superproject because there is no working tree.')
if opt.use_superproject is not None:
print('Defaulting to no-use-superproject because there is no working tree.')
superproject_logging_data = {
'superproject': use_superproject,
'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

setup(
name="gitrepo",
version="2.19",
version="2.20",
packages=find_packages(),

package_data={
Expand Down
28 changes: 26 additions & 2 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ class MigrateWorkTreeTests(unittest.TestCase):
}
_FILES = {
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD',
'unknown-file-should-be-migrated',
}
_CLEAN_FILES = {
'a-vim-temp-file~', '#an-emacs-temp-file#',
}

@classmethod
Expand All @@ -365,10 +369,9 @@ def _simple_layout(cls):
dotgit.mkdir(parents=True)
for name in cls._SYMLINKS:
(dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}')
for name in cls._FILES:
for name in cls._FILES | cls._CLEAN_FILES:
(dotgit / name).write_text(name)

# subprocess.run(['tree', '-a', str(dotgit)])
yield tempdir

def test_standard(self):
Expand All @@ -385,3 +388,24 @@ def test_standard(self):
gitdir = tempdir / '.repo/projects/src/test.git'
for name in self._FILES:
self.assertEqual(name, (gitdir / name).read_text())
# Make sure files were removed.
for name in self._CLEAN_FILES:
self.assertFalse((gitdir / name).exists())

def test_unknown(self):
"""A checkout with unknown files should abort."""
with self._simple_layout() as tempdir:
dotgit = tempdir / 'src/test/.git'
(tempdir / '.repo/projects/src/test.git/random-file').write_text('one')
(dotgit / 'random-file').write_text('two')
with self.assertRaises(error.GitError):
project.Project._MigrateOldWorkTreeGitDir(str(dotgit))

# Make sure no content was actually changed.
self.assertTrue(dotgit.is_dir())
for name in self._FILES:
self.assertTrue((dotgit / name).is_file())
for name in self._CLEAN_FILES:
self.assertTrue((dotgit / name).is_file())
for name in self._SYMLINKS:
self.assertTrue((dotgit / name).is_symlink())

0 comments on commit d156970

Please sign in to comment.