diff --git a/dulwich/porcelain.py b/dulwich/porcelain.py index ade6e9d8f..b2123b388 100644 --- a/dulwich/porcelain.py +++ b/dulwich/porcelain.py @@ -174,16 +174,25 @@ def open_repo_closing(path_or_repo): def path_to_tree_path(repopath, path): - """Convert a path to a path usable in e.g. an index. + """Convert a path to a path usable in an index, e.g. bytes and relative to + the repository root. - :param repo: Repository - :param path: A path + :param repopath: Repository path, absolute or relative to the cwd + :param path: A path, absolute or relative to the cwd :return: A path formatted for use in e.g. an index """ - os.path.relpath(path, repopath) - if os.path.sep != '/': - path = path.replace(os.path.sep, '/') - return path.encode(sys.getfilesystemencoding()) + if not isinstance(path, bytes): + path = path.encode(sys.getfilesystemencoding()) + if not isinstance(repopath, bytes): + repopath = repopath.encode(sys.getfilesystemencoding()) + if os.path.isabs(path) and not os.path.isabs(repopath): + repopath = os.path.abspath(repopath) + elif os.path.isabs(repopath) and not os.path.isabs(path): + path = os.path.abspath(path) + treepath = os.path.relpath(path, repopath) + if treepath.startswith(b'..'): + raise ValueError('Path not in repo') + return treepath def archive(repo, committish=None, outstream=default_bytes_out_stream, @@ -1188,10 +1197,10 @@ def check_ignore(repo, paths, no_index=False): index = r.open_index() ignore_manager = IgnoreFilterManager.from_repo(r) for path in paths: - if os.path.isabs(path): - path = os.path.relpath(path, r.path) if not no_index and path_to_tree_path(r.path, path) in index: continue + if os.path.isabs(path): + path = os.path.relpath(path, r.path) if ignore_manager.is_ignored(path): yield path diff --git a/dulwich/tests/test_porcelain.py b/dulwich/tests/test_porcelain.py index 78548aa9c..49cf1d763 100644 --- a/dulwich/tests/test_porcelain.py +++ b/dulwich/tests/test_porcelain.py @@ -827,7 +827,7 @@ def test_empty(self): results.staged) self.assertEqual([], results.unstaged) - def test_status(self): + def test_status_base(self): """Integration test for `status` functionality.""" # Commit a dummy file then modify it @@ -859,6 +859,41 @@ def test_status(self): filename_add.encode('ascii')) self.assertEqual(results.unstaged, [b'foo']) + def test_status_all(self): + del_path = os.path.join(self.repo.path, 'foo') + mod_path = os.path.join(self.repo.path, 'bar') + add_path = os.path.join(self.repo.path, 'baz') + us_path = os.path.join(self.repo.path, 'blye') + ut_path = os.path.join(self.repo.path, 'blyat') + with open(del_path, 'w') as f: + f.write('origstuff') + with open(mod_path, 'w') as f: + f.write('origstuff') + with open(us_path, 'w') as f: + f.write('origstuff') + porcelain.add(repo=self.repo.path, paths=[del_path, mod_path, us_path]) + porcelain.commit(repo=self.repo.path, message=b'test status', + author=b'author ', + committer=b'committer ') + porcelain.remove(self.repo.path, [del_path]) + with open(add_path, 'w') as f: + f.write('origstuff') + with open(mod_path, 'w') as f: + f.write('more_origstuff') + with open(us_path, 'w') as f: + f.write('more_origstuff') + porcelain.add(repo=self.repo.path, paths=[add_path, mod_path]) + with open(us_path, 'w') as f: + f.write('\norigstuff') + with open(ut_path, 'w') as f: + f.write('origstuff') + results = porcelain.status(self.repo.path) + self.assertDictEqual( + {'add': [b'baz'], 'delete': [b'foo'], 'modify': [b'bar']}, + results.staged) + self.assertListEqual(results.unstaged, [b'blye']) + self.assertListEqual(results.untracked, ['blyat']) + def test_get_tree_changes_add(self): """Unit test for get_tree_changes add.""" @@ -1283,27 +1318,48 @@ class CheckIgnoreTests(PorcelainTestCase): def test_check_ignored(self): with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: - f.write("foo") - with open(os.path.join(self.repo.path, 'foo'), 'w') as f: - f.write("BAR") - with open(os.path.join(self.repo.path, 'bar'), 'w') as f: - f.write("BAR") + f.write('foo') + foo_path = os.path.join(self.repo.path, 'foo') + with open(foo_path, 'w') as f: + f.write('BAR') + bar_path = os.path.join(self.repo.path, 'bar') + with open(bar_path, 'w') as f: + f.write('BAR') self.assertEqual( ['foo'], - list(porcelain.check_ignore(self.repo, ['foo']))) - self.assertEqual([], list(porcelain.check_ignore(self.repo, ['bar']))) + list(porcelain.check_ignore(self.repo, [foo_path]))) + self.assertEqual( + [], list(porcelain.check_ignore(self.repo, [bar_path]))) - def test_check_added(self): - with open(os.path.join(self.repo.path, 'foo'), 'w') as f: - f.write("BAR") + def test_check_added_abs(self): + path = os.path.join(self.repo.path, 'foo') + with open(path, 'w') as f: + f.write('BAR') self.repo.stage(['foo']) with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: - f.write("foo\n") + f.write('foo\n') self.assertEqual( - [], list(porcelain.check_ignore(self.repo, ['foo']))) + [], list(porcelain.check_ignore(self.repo, [path]))) self.assertEqual( ['foo'], - list(porcelain.check_ignore(self.repo, ['foo'], no_index=True))) + list(porcelain.check_ignore(self.repo, [path], no_index=True))) + + def test_check_added_rel(self): + with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + f.write('BAR') + self.repo.stage(['foo']) + with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: + f.write('foo\n') + cwd = os.getcwd() + os.mkdir(os.path.join(self.repo.path, 'bar')) + os.chdir(os.path.join(self.repo.path, 'bar')) + try: + self.assertEqual( + list(porcelain.check_ignore(self.repo, ['../foo'])), []) + self.assertEqual(['../foo'], list( + porcelain.check_ignore(self.repo, ['../foo'], no_index=True))) + finally: + os.chdir(cwd) class UpdateHeadTests(PorcelainTestCase): @@ -1431,3 +1487,42 @@ def test_tag_and_commit(self): self.assertEqual( 'tryme-1-g{}'.format(sha[:7].decode('ascii')), porcelain.describe(self.repo.path)) + + +class HelperTests(PorcelainTestCase): + def test_path_to_tree_path_base(self): + self.assertEqual( + b'bar', porcelain.path_to_tree_path('/home/foo', '/home/foo/bar')) + self.assertEqual(b'bar', porcelain.path_to_tree_path('.', './bar')) + self.assertEqual(b'bar', porcelain.path_to_tree_path('.', 'bar')) + cwd = os.getcwd() + self.assertEqual( + b'bar', porcelain.path_to_tree_path('.', os.path.join(cwd, 'bar'))) + self.assertEqual(b'bar', porcelain.path_to_tree_path(cwd, 'bar')) + + def test_path_to_tree_path_syntax(self): + self.assertEqual(b'bar', porcelain.path_to_tree_path(b'.', './bar')) + self.assertEqual(b'bar', porcelain.path_to_tree_path('.', b'./bar')) + self.assertEqual(b'bar', porcelain.path_to_tree_path(b'.', b'./bar')) + + def test_path_to_tree_path_error(self): + with self.assertRaises(ValueError): + porcelain.path_to_tree_path('/home/foo/', '/home/bar/baz') + + def test_path_to_tree_path_rel(self): + cwd = os.getcwd() + os.mkdir(os.path.join(self.repo.path, 'foo')) + os.mkdir(os.path.join(self.repo.path, 'foo/bar')) + try: + os.chdir(os.path.join(self.repo.path, 'foo/bar')) + self.assertEqual(b'bar/baz', porcelain.path_to_tree_path( + '..', 'baz')) + self.assertEqual(b'bar/baz', porcelain.path_to_tree_path( + os.path.join(os.getcwd(), '..'), + os.path.join(os.getcwd(), 'baz'))) + self.assertEqual(b'bar/baz', porcelain.path_to_tree_path( + '..', os.path.join(os.getcwd(), 'baz'))) + self.assertEqual(b'bar/baz', porcelain.path_to_tree_path( + os.path.join(os.getcwd(), '..'), 'baz')) + finally: + os.chdir(cwd)