diff options
| -rw-r--r-- | project.py | 38 | ||||
| -rw-r--r-- | tests/test_project.py | 28 |
2 files changed, 53 insertions, 13 deletions
| @@ -2818,24 +2818,40 @@ class Project(object): | |||
| 2818 | } | 2818 | } |
| 2819 | # Paths that we know will be in both, but are safe to clobber in .repo/projects/. | 2819 | # Paths that we know will be in both, but are safe to clobber in .repo/projects/. |
| 2820 | SAFE_TO_CLOBBER = { | 2820 | SAFE_TO_CLOBBER = { |
| 2821 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD', | 2821 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'gitk.cache', 'index', 'ORIG_HEAD', |
| 2822 | } | 2822 | } |
| 2823 | 2823 | ||
| 2824 | # Now walk the paths and sync the .git/ to .repo/projects/. | 2824 | # First see if we'd succeed before starting the migration. |
| 2825 | unknown_paths = [] | ||
| 2825 | for name in platform_utils.listdir(dotgit): | 2826 | for name in platform_utils.listdir(dotgit): |
| 2827 | # Ignore all temporary/backup names. These are common with vim & emacs. | ||
| 2828 | if name.endswith('~') or (name[0] == '#' and name[-1] == '#'): | ||
| 2829 | continue | ||
| 2830 | |||
| 2826 | dotgit_path = os.path.join(dotgit, name) | 2831 | dotgit_path = os.path.join(dotgit, name) |
| 2827 | if name in KNOWN_LINKS: | 2832 | if name in KNOWN_LINKS: |
| 2828 | if platform_utils.islink(dotgit_path): | 2833 | if not platform_utils.islink(dotgit_path): |
| 2829 | platform_utils.remove(dotgit_path) | 2834 | unknown_paths.append(f'{dotgit_path}: should be a symlink') |
| 2830 | else: | ||
| 2831 | raise GitError(f'{dotgit_path}: should be a symlink') | ||
| 2832 | else: | 2835 | else: |
| 2833 | gitdir_path = os.path.join(gitdir, name) | 2836 | gitdir_path = os.path.join(gitdir, name) |
| 2834 | if name in SAFE_TO_CLOBBER or not os.path.exists(gitdir_path): | 2837 | if name not in SAFE_TO_CLOBBER and os.path.exists(gitdir_path): |
| 2835 | platform_utils.remove(gitdir_path, missing_ok=True) | 2838 | unknown_paths.append(f'{dotgit_path}: unknown file; please file a bug') |
| 2836 | platform_utils.rename(dotgit_path, gitdir_path) | 2839 | if unknown_paths: |
| 2837 | else: | 2840 | raise GitError('Aborting migration: ' + '\n'.join(unknown_paths)) |
| 2838 | raise GitError(f'{dotgit_path}: unknown file; please file a bug') | 2841 | |
| 2842 | # Now walk the paths and sync the .git/ to .repo/projects/. | ||
| 2843 | for name in platform_utils.listdir(dotgit): | ||
| 2844 | dotgit_path = os.path.join(dotgit, name) | ||
| 2845 | |||
| 2846 | # Ignore all temporary/backup names. These are common with vim & emacs. | ||
| 2847 | if name.endswith('~') or (name[0] == '#' and name[-1] == '#'): | ||
| 2848 | platform_utils.remove(dotgit_path) | ||
| 2849 | elif name in KNOWN_LINKS: | ||
| 2850 | platform_utils.remove(dotgit_path) | ||
| 2851 | else: | ||
| 2852 | gitdir_path = os.path.join(gitdir, name) | ||
| 2853 | platform_utils.remove(gitdir_path, missing_ok=True) | ||
| 2854 | platform_utils.rename(dotgit_path, gitdir_path) | ||
| 2839 | 2855 | ||
| 2840 | # Now that the dir should be empty, clear it out, and symlink it over. | 2856 | # Now that the dir should be empty, clear it out, and symlink it over. |
| 2841 | platform_utils.rmdir(dotgit) | 2857 | platform_utils.rmdir(dotgit) |
diff --git a/tests/test_project.py b/tests/test_project.py index d578fe84..4f449227 100644 --- a/tests/test_project.py +++ b/tests/test_project.py | |||
| @@ -347,6 +347,10 @@ class MigrateWorkTreeTests(unittest.TestCase): | |||
| 347 | } | 347 | } |
| 348 | _FILES = { | 348 | _FILES = { |
| 349 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD', | 349 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD', |
| 350 | 'unknown-file-should-be-migrated', | ||
| 351 | } | ||
| 352 | _CLEAN_FILES = { | ||
| 353 | 'a-vim-temp-file~', '#an-emacs-temp-file#', | ||
| 350 | } | 354 | } |
| 351 | 355 | ||
| 352 | @classmethod | 356 | @classmethod |
| @@ -365,10 +369,9 @@ class MigrateWorkTreeTests(unittest.TestCase): | |||
| 365 | dotgit.mkdir(parents=True) | 369 | dotgit.mkdir(parents=True) |
| 366 | for name in cls._SYMLINKS: | 370 | for name in cls._SYMLINKS: |
| 367 | (dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}') | 371 | (dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}') |
| 368 | for name in cls._FILES: | 372 | for name in cls._FILES | cls._CLEAN_FILES: |
| 369 | (dotgit / name).write_text(name) | 373 | (dotgit / name).write_text(name) |
| 370 | 374 | ||
| 371 | subprocess.run(['tree', '-a', str(dotgit)]) | ||
| 372 | yield tempdir | 375 | yield tempdir |
| 373 | 376 | ||
| 374 | def test_standard(self): | 377 | def test_standard(self): |
| @@ -385,3 +388,24 @@ class MigrateWorkTreeTests(unittest.TestCase): | |||
| 385 | gitdir = tempdir / '.repo/projects/src/test.git' | 388 | gitdir = tempdir / '.repo/projects/src/test.git' |
| 386 | for name in self._FILES: | 389 | for name in self._FILES: |
| 387 | self.assertEqual(name, (gitdir / name).read_text()) | 390 | self.assertEqual(name, (gitdir / name).read_text()) |
| 391 | # Make sure files were removed. | ||
| 392 | for name in self._CLEAN_FILES: | ||
| 393 | self.assertFalse((gitdir / name).exists()) | ||
| 394 | |||
| 395 | def test_unknown(self): | ||
| 396 | """A checkout with unknown files should abort.""" | ||
| 397 | with self._simple_layout() as tempdir: | ||
| 398 | dotgit = tempdir / 'src/test/.git' | ||
| 399 | (tempdir / '.repo/projects/src/test.git/random-file').write_text('one') | ||
| 400 | (dotgit / 'random-file').write_text('two') | ||
| 401 | with self.assertRaises(error.GitError): | ||
| 402 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | ||
| 403 | |||
| 404 | # Make sure no content was actually changed. | ||
| 405 | self.assertTrue(dotgit.is_dir()) | ||
| 406 | for name in self._FILES: | ||
| 407 | self.assertTrue((dotgit / name).is_file()) | ||
| 408 | for name in self._CLEAN_FILES: | ||
| 409 | self.assertTrue((dotgit / name).is_file()) | ||
| 410 | for name in self._SYMLINKS: | ||
| 411 | self.assertTrue((dotgit / name).is_symlink()) | ||
