Skip to content

Commit

Permalink
Merge tag 'v2.19' into dev/fufu/2.19
Browse files Browse the repository at this point in the history
repo v2.19
  • Loading branch information
Sebastien Fusilier committed Jan 26, 2023
2 parents 8cc7303 + 2a089cf commit dbb3d18
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 37 deletions.
117 changes: 81 additions & 36 deletions repo/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2784,50 +2784,95 @@ def _InitGitWorktree(self):
self._InitMRef()

def _InitWorkTree(self, force_sync=False, submodules=False):
realdotgit = os.path.join(self.worktree, '.git')
tmpdotgit = realdotgit + '.tmp'
init_dotgit = not os.path.exists(realdotgit)
if init_dotgit:
if self.use_git_worktrees:
"""Setup the worktree .git path.
This is the user-visible path like src/foo/.git/.
With non-git-worktrees, this will be a symlink to the .repo/projects/ path.
With git-worktrees, this will be a .git file using "gitdir: ..." syntax.
Older checkouts had .git/ directories. If we see that, migrate it.
This also handles changes in the manifest. Maybe this project was backed
by "foo/bar" on the server, but now it's "new/foo/bar". We have to update
the path we point to under .repo/projects/ to match.
"""
dotgit = os.path.join(self.worktree, '.git')

# If using an old layout style (a directory), migrate it.
if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
self._MigrateOldWorkTreeGitDir(dotgit)

init_dotgit = not os.path.exists(dotgit)
if self.use_git_worktrees:
if init_dotgit:
self._InitGitWorktree()
self._CopyAndLinkFiles()
return

dotgit = tmpdotgit
platform_utils.rmtree(tmpdotgit, ignore_errors=True)
os.makedirs(tmpdotgit)
self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
copy_all=False)
else:
dotgit = realdotgit
if not init_dotgit:
# See if the project has changed.
if platform_utils.realpath(self.gitdir) != platform_utils.realpath(dotgit):
platform_utils.remove(dotgit)

try:
self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
except GitError as e:
if force_sync and not init_dotgit:
try:
platform_utils.rmtree(dotgit)
return self._InitWorkTree(force_sync=False, submodules=submodules)
except Exception:
raise e
raise e
if init_dotgit or not os.path.exists(dotgit):
os.makedirs(self.worktree, exist_ok=True)
platform_utils.symlink(os.path.relpath(self.gitdir, self.worktree), dotgit)

if init_dotgit:
_lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
if init_dotgit:
_lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())

# Now that the .git dir is fully set up, move it to its final home.
platform_utils.rename(tmpdotgit, realdotgit)
# Finish checking out the worktree.
cmd = ['read-tree', '--reset', '-u', '-v', HEAD]
if GitCommand(self, cmd).Wait() != 0:
raise GitError('Cannot initialize work tree for ' + self.name)

# Finish checking out the worktree.
cmd = ['read-tree', '--reset', '-u']
cmd.append('-v')
cmd.append(HEAD)
if GitCommand(self, cmd).Wait() != 0:
raise GitError('Cannot initialize work tree for ' + self.name)
if submodules:
self._SyncSubmodules(quiet=True)
self._CopyAndLinkFiles()

if submodules:
self._SyncSubmodules(quiet=True)
self._CopyAndLinkFiles()
@classmethod
def _MigrateOldWorkTreeGitDir(cls, dotgit):
"""Migrate the old worktree .git/ dir style to a symlink.
This logic specifically only uses state from |dotgit| to figure out where to
move content and not |self|. This way if the backing project also changed
places, we only do the .git/ dir to .git symlink migration here. The path
updates will happen independently.
"""
# Figure out where in .repo/projects/ it's pointing to.
if not os.path.islink(os.path.join(dotgit, 'refs')):
raise GitError(f'{dotgit}: unsupported checkout state')
gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, 'refs')))

# Remove known symlink paths that exist in .repo/projects/.
KNOWN_LINKS = {
'config', 'description', 'hooks', 'info', 'logs', 'objects',
'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
}
# 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',
}

# 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)
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')
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')

# Now that the dir should be empty, clear it out, and symlink it over.
platform_utils.rmdir(dotgit)
platform_utils.symlink(os.path.relpath(gitdir, os.path.dirname(dotgit)), dotgit)

def _get_symlink_error_message(self):
if platform_utils.isWindows():
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.18",
version="2.19",
packages=find_packages(),

package_data={
Expand Down
50 changes: 50 additions & 0 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import contextlib
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
Expand Down Expand Up @@ -335,3 +336,52 @@ def test_update(self):
platform_utils.symlink(self.tempdir, dest)
lf._Link()
self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest))


class MigrateWorkTreeTests(unittest.TestCase):
"""Check _MigrateOldWorkTreeGitDir handling."""

_SYMLINKS = {
'config', 'description', 'hooks', 'info', 'logs', 'objects',
'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
}
_FILES = {
'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD',
}

@classmethod
@contextlib.contextmanager
def _simple_layout(cls):
"""Create a simple repo client checkout to test against."""
with tempfile.TemporaryDirectory() as tempdir:
tempdir = Path(tempdir)

gitdir = tempdir / '.repo/projects/src/test.git'
gitdir.mkdir(parents=True)
cmd = ['git', 'init', '--bare', str(gitdir)]
subprocess.check_call(cmd)

dotgit = tempdir / 'src/test/.git'
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:
(dotgit / name).write_text(name)

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

def test_standard(self):
"""Migrate a standard checkout that we expect."""
with self._simple_layout() as tempdir:
dotgit = tempdir / 'src/test/.git'
project.Project._MigrateOldWorkTreeGitDir(str(dotgit))

# Make sure the dir was transformed into a symlink.
self.assertTrue(dotgit.is_symlink())
self.assertEqual(os.readlink(str(dotgit)), '../../.repo/projects/src/test.git')

# Make sure files were moved over.
gitdir = tempdir / '.repo/projects/src/test.git'
for name in self._FILES:
self.assertEqual(name, (gitdir / name).read_text())

0 comments on commit dbb3d18

Please sign in to comment.