diff --git a/pyvcs/backends/bzr.py b/pyvcs/backends/bzr.py index 0d89cde..21896a8 100644 --- a/pyvcs/backends/bzr.py +++ b/pyvcs/backends/bzr.py @@ -26,12 +26,16 @@ def __init__(self, *args, **kwargs): def _rev_to_commit(self, rev): # TODO: this doesn't yet handle the case of multiple parent revisions current = self._branch.repository.revision_tree(rev.revision_id) - prev = self._branch.repository.revision_tree(rev.parent_ids[0]) + if len(rev.parent_ids): + prev = self._branch.repository.revision_tree(rev.parent_ids[0]) + else: + prev = self._branch.repository.revision_tree('null:') delta = current.changes_from(prev) files = [f[0] for f in delta.added + delta.removed + delta.renamed + delta.kind_changed + delta.modified] diff_file = StringIO.StringIO() + diff_tree = diff.DiffTree(prev, current, diff_file) self._branch.lock_read() @@ -85,9 +89,7 @@ def get_recent_commits(self, since=None): def list_directory(self, path, revision=None): path = path.rstrip(os.path.sep) - tree = self._get_tree(revision) - dir_iter = tree.walkdirs(path) try: entries = dir_iter.next() diff --git a/pyvcs/backends/git.py b/pyvcs/backends/git.py index 329b33f..00f84ef 100644 --- a/pyvcs/backends/git.py +++ b/pyvcs/backends/git.py @@ -4,6 +4,7 @@ from dulwich.repo import Repo from dulwich import objects +from dulwich.errors import NotCommitError from pyvcs.commit import Commit from pyvcs.exceptions import CommitDoesNotExist, FileDoesNotExist, FolderDoesNotExist @@ -63,7 +64,7 @@ def __init__(self, *args, **kwargs): def _get_commit(self, commit_id): try: - return self._repo.commit(commit_id) + return self._repo[commit_id] except Exception, e: raise CommitDoesNotExist("%s is not a commit" % commit_id) @@ -71,33 +72,40 @@ def _get_obj(self, sha): return self._repo.get_object(sha) def _diff_files(self, commit_id1, commit_id2): + if commit_id1 == 'NULL': + commit_id1 = None + if commit_id2 == 'NULL': + commit_id2 = None + tree1 = self._get_obj(self._get_obj(commit_id1).tree) if commit_id1 else None + tree2 = self._get_obj(self._get_obj(commit_id2).tree) if commit_id2 else None return sorted(get_differing_files( self._repo, - self._get_obj(self._get_obj(commit_id1).tree), - self._get_obj(self._get_obj(commit_id2).tree), + tree1, + tree2, )) def get_commit_by_id(self, commit_id): commit = self._get_commit(commit_id) - files = self._diff_files(commit.id, commit.parents[0]) + parent = commit.parents[0] if len(commit.parents) else 'NULL' + files = self._diff_files(commit.id, parent) return Commit(commit.id, commit.committer, datetime.fromtimestamp(commit.commit_time), commit.message, files, - lambda: generate_unified_diff(self, files, commit.parents[0], commit.id)) + lambda: generate_unified_diff(self, files, parent, commit.id)) def get_recent_commits(self, since=None): if since is None: - since = datetime.fromtimestamp(self._repo.commit(self._repo.head()).commit_time) - timedelta(days=5) + #since = datetime.fromtimestamp(self._repo.commit(self._repo.head()).commit_time) - timedelta(days=5) + since = datetime.fromtimestamp(self._repo[self._repo.head()].commit_time) - timedelta(days=5) pending_commits = self._repo.get_refs().values()#[self._repo.head()] history = {} while pending_commits: head = pending_commits.pop(0) try: - commit = self._get_obj(head) + commit = self._repo[head] except KeyError: raise CommitDoesNotExist - if not hasattr(commit, 'commit_time'): - continue - if commit.id in history or datetime.fromtimestamp(commit.commit_time) <= since: + if not isinstance(commit, objects.Commit) or commit.id in history or\ + datetime.fromtimestamp(commit.commit_time) <= since: continue history[commit.id] = commit pending_commits.extend(commit.parents) @@ -106,16 +114,21 @@ def get_recent_commits(self, since=None): return sorted(commits, key=attrgetter('time'), reverse=True) def list_directory(self, path, revision=None): - commit = self._get_commit(revision or self._repo.head()) - tree = self._repo.tree(commit.tree) + if revision is None: + commit = self._get_commit(self._repo.head()) + elif revision is 'NULL': + return ([],[]) + else: + commit = self._get_commit(revision) + tree = self._repo[commit.tree] path = filter(bool, path.split(os.path.sep)) while path: part = path.pop(0) found = False - for mode, name, hexsha in self._repo.tree(tree.id).entries(): + for mode, name, hexsha in self._repo[tree.id].entries(): if part == name: found = True - tree = self._repo.tree(hexsha) + tree = self._repo[hexsha] break if not found: raise FolderDoesNotExist @@ -128,17 +141,22 @@ def list_directory(self, path, revision=None): return files, folders def file_contents(self, path, revision=None): - commit = self._get_commit(revision or self._repo.head()) - tree = self._repo.tree(commit.tree) + if revision is None: + commit = self._get_commit(self._repo.head()) + elif revision is 'NULL': + return '' + else: + commit = self._get_commit(revision) + tree = self._repo[commit.tree] path = path.split(os.path.sep) path, filename = path[:-1], path[-1] while path: part = path.pop(0) - for mode, name, hexsha in self._repo.tree(tree.id).entries(): + for mode, name, hexsha in self._repo[tree.id].entries(): if part == name: - tree = self._repo.tree(hexsha) + tree = self._repo[hexsha] break for mode, name, hexsha in tree.entries(): if name == filename: - return self._repo.get_object(hexsha).as_pretty_string() + return self._repo[hexsha].as_pretty_string() raise FileDoesNotExist diff --git a/tests/setup_git_test.sh b/tests/setup_git_test.sh new file mode 100755 index 0000000..ea28a35 --- /dev/null +++ b/tests/setup_git_test.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +#NOTE: this script depends on git being installed and having access to the /tmp directory... + +echo Setting up the directory for the git repository +mkdir /tmp/pyvcs-test +mkdir /tmp/pyvcs-test/git-test + +cd /tmp/pyvcs-test/git-test +git init +echo this is a test README file for a mock project > README +echo print 'hello, world!' > hello_world.py +git add README +git add hello_world.py +git commit -m "initial add of files to repo" +echo this is a new line added to the README >> README +git commit -a -m "slight change to the README" + diff --git a/tests/simple_git_tests.py b/tests/simple_git_tests.py new file mode 100755 index 0000000..71a0ef5 --- /dev/null +++ b/tests/simple_git_tests.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +from datetime import datetime +import unittest +import os +import subprocess + +from pyvcs.backends import get_backend +from pyvcs.exceptions import FileDoesNotExist, FolderDoesNotExist, CommitDoesNotExist + +class GitSimpleTest(unittest.TestCase): + + def setUp(self): + git = get_backend('git') + ret = subprocess.call('./setup_git_test.sh') + self.repo = git.Repository('/tmp/pyvcs-test/git-test/') + + def tearDown(self): + ret = subprocess.call('./teardown_git_test.sh') + + def test_recent_commits(self): + recent_commits = self.repo.get_recent_commits() + self.assertEqual(len(recent_commits),2) + + def test_commits(self): + recent_commits = self.repo.get_recent_commits() + commit = self.repo.get_commit_by_id(recent_commits[1].commit_id) + self.assert_(commit.message.startswith('initial add of files')) + self.assertEqual(commit.time.date(), datetime.today().date()) + self.assertEqual(commit.files, ['README', 'hello_world.py']) + self.assert_('this is a test README file for a mock project' in commit.diff) + self.assertRaises(CommitDoesNotExist,self.repo.get_commit_by_id,'crap') + + def test_list_directory(self): + files, folders = self.repo.list_directory('') + self.assertEqual(files, ['README', 'hello_world.py']) + self.assertEqual(folders, []) + self.assertRaises(FolderDoesNotExist, self.repo.list_directory, 'tests/awesometests/') + + def test_file_contents(self): + contents = self.repo.file_contents('hello_world.py') + self.assertEqual(contents,'print hello, world!\n') + self.assertRaises(FileDoesNotExist, self.repo.file_contents, 'waybettertest.py') + + def test_diffs(self): + recent_commits = self.repo.get_recent_commits() + self.assertEqual(self.repo._diff_files(recent_commits[0].commit_id,recent_commits[1].commit_id),['README']) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/teardown_git_test.sh b/tests/teardown_git_test.sh new file mode 100755 index 0000000..af7b970 --- /dev/null +++ b/tests/teardown_git_test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +rm -r -f /tmp/pyvcs-test