Commits

Lukasz Balcerzak  committed be56af1

Replaced EmptyChangeset class with Repository.EMPTY_CHANGESET enum - refers #96

  • Participants
  • Parent commits 4d86cd8

Comments (0)

Files changed (5)

File vcs/backends/base.py

     """
     scm = None
     DEFAULT_BRANCH_NAME = None
+    EMPTY_CHANGESET = '0' * 40
 
     def __init__(self, repo_path, create=False, **kwargs):
         """
         """
         raise NotImplementedError
 
+    def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False,
+            context=3):
+        """
+        Returns (git like) *diff*, as plain text. Shows changes introduced by
+        ``rev2`` since ``rev1``.
+
+        :param rev1: Entry point from which diff is shown. Can be
+          ``self.EMPTY_CHANGESET`` - in this case, patch showing all
+          the changes since empty state of the repository until ``rev2``
+        :param rev2: Until which revision changes should be shown.
+        :param ignore_whitespace: If set to ``True``, would not show whitespace
+          changes. Defaults to ``False``.
+        :param context: How many lines before/after changed lines should be
+          shown. Defaults to ``3``.
+        """
+        raise NotImplementedError
+
     # ========== #
     # COMMIT API #
     # ========== #
 
     **Attributes**
 
-        ``is_empty_changeset``
-            indicates that this is **NOT** an empty changeset. It should be
-            ``False`` for instance of BaseChangeset subclasses. Also see
-            :class:`vcs.backends.base.EmptyRepositoryError`.
-
         ``repository``
             repository object within which changeset exists
 
             otherwise; trying to access this attribute while there is no
             changesets would raise ``EmptyRepositoryError``
     """
-
-    is_empty_changeset = property(lambda self: False)
-
     def __str__(self):
         return '<%s at %s:%s>' % (self.__class__.__name__, self.revision,
             self.short_id)
         return data
 
 
-class EmptyChangeset(object):
-    """
-    Simple class that could be used as *placeholder* for empty changeset. It
-    does **NOT** implement :class:`BaseChangeset` *API*. ``is_empty_changeset``
-    is the only attribute that is set on this class, and is ``True``.
-    """
-    is_empty_changeset = True
-
-
 class BaseWorkdir(object):
     """
     Working directory representation of single repository.

File vcs/backends/git/repository.py

 from string import Template
 from subprocess import Popen, PIPE
 from vcs.backends.base import BaseRepository
-from vcs.backends.base import EmptyChangeset
 from vcs.exceptions import BranchDoesNotExistError
 from vcs.exceptions import ChangesetDoesNotExistError
 from vcs.exceptions import EmptyRepositoryError
         Returns ``GitChangeset`` object representing commit from git repository
         at the given revision or head (most recent commit) if None given.
         """
-        if (revision is EmptyChangeset or inspect.isclass(revision)
-            and issubclass(revision, EmptyChangeset)):
-            return EmptyChangeset()
         if isinstance(revision, GitChangeset):
             return revision
         revision = self._get_revision(revision)
 
     def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False,
             context=3):
-        cs1 = self.get_changeset(rev1)
-        cs2 = self.get_changeset(rev2)
+        """
+        Returns (git like) *diff*, as plain text. Shows changes introduced by
+        ``rev2`` since ``rev1``.
 
+        :param rev1: Entry point from which diff is shown. Can be
+          ``self.EMPTY_CHANGESET`` - in this case, patch showing all
+          the changes since empty state of the repository until ``rev2``
+        :param rev2: Until which revision changes should be shown.
+        :param ignore_whitespace: If set to ``True``, would not show whitespace
+          changes. Defaults to ``False``.
+        :param context: How many lines before/after changed lines should be
+          shown. Defaults to ``3``.
+        """
         flags = ['-U%s' % context]
         if ignore_whitespace:
             flags.append('-w')
 
-        if cs1.is_empty_changeset:
-            cmd = ' '.join(['show'] + flags + [cs2.raw_id])
+        if rev1 == self.EMPTY_CHANGESET:
+            rev2 = self.get_changeset(rev2).raw_id
+            cmd = ' '.join(['show'] + flags + [rev2])
         else:
-            cmd = ' '.join(['diff'] + flags + [cs1.raw_id, cs2.raw_id])
+            rev1 = self.get_changeset(rev1).raw_id
+            rev2 = self.get_changeset(rev2).raw_id
+            cmd = ' '.join(['diff'] + flags + [rev1, rev2])
 
         if path:
             cmd += ' -- "%s"' % path
         stdout, stderr = self.run_git_command(cmd)
         # If we used 'show' command, strip first few lines (until actual diff
         # starts)
-        if cs1.is_empty_changeset:
+        if rev1 == self.EMPTY_CHANGESET:
             lines = stdout.splitlines()
             x = 0
             for line in lines:

File vcs/backends/hg.py

 
         return map(lambda x: hex(x[7]), self._repo.changelog.index)[:-1]
 
-    def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False,
+    def get_diff(self, rev1, rev2, path='', ignore_whitespace=False,
                   context=3):
+        """
+        Returns (git like) *diff*, as plain text. Shows changes introduced by
+        ``rev2`` since ``rev1``.
+
+        :param rev1: Entry point from which diff is shown. Can be
+          ``self.EMPTY_CHANGESET`` - in this case, patch showing all
+          the changes since empty state of the repository until ``rev2``
+        :param rev2: Until which revision changes should be shown.
+        :param ignore_whitespace: If set to ``True``, would not show whitespace
+          changes. Defaults to ``False``.
+        :param context: How many lines before/after changed lines should be
+          shown. Defaults to ``3``.
+        """
+        # Check if given revisions are present at repository (may raise
+        # ChangesetDoesNotExistError)
+        if rev1 != self.EMPTY_CHANGESET:
+            self.get_changeset(rev1)
+        self.get_changeset(rev2)
+
         file_filter = match(self.path, '', [path])
-        return patch.diff(self._repo, rev1, rev2, match=file_filter,
+        return ''.join(patch.diff(self._repo, rev1, rev2, match=file_filter,
                           opts=diffopts(git=True,
                                         ignorews=ignore_whitespace,
-                                        context=context))
+                                        context=context)))
 
     def _check_url(self, url):
         """

File vcs/tests/test_git.py

 
     def test_get_diff_runs_git_command_with_str_hashes(self):
         self.repo.run_git_command = mock.Mock(return_value=['', ''])
-        self.repo.get_diff('0' * 40, 1)
-        self.repo.run_git_command.assert_called_once_with('diff -U%s %s %s' %
-            (3, self.repo._get_revision(0), self.repo._get_revision(1)))
+        self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
+        self.repo.run_git_command.assert_called_once_with('show -U%s %s' %
+            (3, self.repo._get_revision(1)))
 
     def test_get_diff_runs_git_command_with_path_if_its_given(self):
         self.repo.run_git_command = mock.Mock(return_value=['', ''])

File vcs/tests/test_repository.py

 from base import BackendTestMixin
 from conf import SCM_TESTS
 from conf import TEST_USER_CONFIG_FILE
-from vcs.backends.base import EmptyChangeset
 from vcs.nodes import FileNode
 from vcs.utils.compat import unittest
+from vcs.exceptions import ChangesetDoesNotExistError
 
 
 class RepositoryBaseTest(BackendTestMixin):
         ]
         return commits
 
+    def test_raise_for_wrong(self):
+        with self.assertRaises(ChangesetDoesNotExistError):
+            self.repo.get_diff('a' * 40, 'b' * 40)
+
+class GitRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
+    backend_alias = 'git'
+
     def test_initial_commit_diff(self):
         initial_rev = self.repo.revisions[0]
-        self.assertEqual(self.repo.get_diff(EmptyChangeset, initial_rev), '''diff --git a/foobar b/foobar
+        self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
 new file mode 100644
 index 0000000..f6ea049
 --- /dev/null
 ''')
 
 
+class HgRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
+    backend_alias = 'hg'
+
+    def test_initial_commit_diff(self):
+        initial_rev = self.repo.revisions[0]
+        self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
+new file mode 100755
+--- /dev/null
++++ b/foobar
+@@ -0,0 +1,1 @@
++foobar
+\ No newline at end of file
+diff --git a/foobar2 b/foobar2
+new file mode 100755
+--- /dev/null
++++ b/foobar2
+@@ -0,0 +1,1 @@
++foobar2
+\ No newline at end of file
+''')
+
+    def test_second_changeset_diff(self):
+        revs = self.repo.revisions
+        self.assertEqual(self.repo.get_diff(revs[0], revs[1]), '''diff --git a/foobar b/foobar
+--- a/foobar
++++ b/foobar
+@@ -1,1 +1,1 @@
+-foobar
+\ No newline at end of file
++FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+new file mode 100755
+--- /dev/null
++++ b/foobar3
+@@ -0,0 +1,1 @@
++foobar3
+\ No newline at end of file
+''')
+
+    def test_third_changeset_diff(self):
+        revs = self.repo.revisions
+        self.assertEqual(self.repo.get_diff(revs[1], revs[2]), '''diff --git a/foobar b/foobar
+deleted file mode 100755
+--- a/foobar
++++ /dev/null
+@@ -1,1 +0,0 @@
+-FOOBAR
+\ No newline at end of file
+diff --git a/foobar3 b/foobar3
+--- a/foobar3
++++ b/foobar3
+@@ -1,1 +1,3 @@
+-foobar3
+\ No newline at end of file
++FOOBAR
++FOOBAR
++FOOBAR
+''')
+
+
 # For each backend create test case class
 for alias in SCM_TESTS:
     attrs = {
     bases = (RepositoryBaseTest, unittest.TestCase)
     globals()[cls_name] = type(cls_name, bases, attrs)
 
-for alias in SCM_TESTS:
-    attrs = {
-        'backend_alias': alias,
-    }
-    cls_name = alias.capitalize() + RepositoryGetDiffTest.__name__
-    bases = (RepositoryGetDiffTest, unittest.TestCase)
-    globals()[cls_name] = type(cls_name, bases, attrs)
-
-
 if __name__ == '__main__':
     unittest.main()