Rev 57: Fix more tests. in http://people.samba.org/bzr/jelmer/bzr-git/trunk

Jelmer Vernooij jelmer at samba.org
Sat Jul 26 17:27:37 BST 2008


At http://people.samba.org/bzr/jelmer/bzr-git/trunk

------------------------------------------------------------
revno: 57
revision-id: jelmer at samba.org-20080726002424-tvazkvugrgtgyonh
parent: jelmer at samba.org-20080725225643-o8adohxgoruo3goj
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: trunk
timestamp: Sat 2008-07-26 02:24:24 +0200
message:
  Fix more tests.
modified:
  git_branch.py                  git_branch.py-20071108230535-su8dxk529f4uk9fx-2
  git_dir.py                     git_dir.py-20071108234408-ygidvy5hviixghsd-1
  git_repository.py              git_repository.py-20071108234408-ygidvy5hviixghsd-2
  tests/test_git_repository.py   test_git_repository.-20071108234408-ygidvy5hviixghsd-5
=== modified file 'git_branch.py'
--- a/git_branch.py	2007-12-29 21:35:28 +0000
+++ b/git_branch.py	2008-07-26 00:24:24 +0000
@@ -48,15 +48,12 @@
 class GitBranch(branch.Branch):
     """An adapter to git repositories for bzr Branch objects."""
 
-    def __init__(self, gitdir, lockfiles):
+    def __init__(self, repository, head, base, lockfiles):
         super(GitBranch, self).__init__()
-        from bzrlib.plugins.git import git_repository
-        self.bzrdir = gitdir
         self.control_files = lockfiles
-        self.repository = git_repository.GitRepository(gitdir, lockfiles)
-        self.base = gitdir.root_transport.base
-        if '.git' not in gitdir.root_transport.list_dir('.'):
-            raise errors.NotBranchError(self.base)
+        self.repository = repository
+        self.head = head
+        self.base = base
         self._format = GitBranchFormat()
 
     def lock_write(self):
@@ -65,25 +62,19 @@
     @needs_read_lock
     def last_revision(self):
         # perhaps should escape this ?
-        head_git_id = self.repository._git.get_head()
-        if head_git_id is None:
+        if self.head is None:
             return revision.NULL_REVISION
-        return ids.convert_revision_id_git_to_bzr(head_git_id)
+        return ids.convert_revision_id_git_to_bzr(self.head.commit.id)
 
-    @needs_read_lock
-    def revision_history(self):
-        node = self.last_revision()
-        if node == revision.NULL_REVISION:
-            return []
-        ancestors = self.repository.get_revision_graph(node)
-        history = []
-        while node is not None:
-            history.append(node)
-            if len(ancestors[node]) > 0:
-                node = ancestors[node][0]
-            else:
-                node = None
-        return list(reversed(history))
+    def _gen_revision_history(self):
+        skip = 0
+        max_count = 1000
+        cms = []
+        ret = []
+        while cms != []:
+            cms = self.repository._git.commits(self.head.commit.id, max_count=1000, skip=skip)
+            ret += [ids.convert_revision_id_git_to_bzr(cm.id) for cm in cms]
+        return ret
 
     def get_config(self):
         return GitBranchConfig(self)

=== modified file 'git_dir.py'
--- a/git_dir.py	2007-12-30 06:58:28 +0000
+++ b/git_dir.py	2008-07-26 00:24:24 +0000
@@ -80,8 +80,14 @@
         return True
 
     def open_branch(self, ignored=None):
-        """'crate' a branch for this dir."""
-        return git_branch.GitBranch(self, self._lockfiles)
+        """'create' a branch for this dir."""
+        repo = self.open_repository()
+        if repo._git.heads == []:
+            head = None
+        else:
+            head = repo._git.heads[0]
+        return git_branch.GitBranch(repo, head, 
+                                    self.root_transport.base, self._lockfiles)
 
     def open_repository(self, shared=False):
         """'open' a repository for this dir."""

=== modified file 'git_repository.py'
--- a/git_repository.py	2008-07-25 22:56:43 +0000
+++ b/git_repository.py	2008-07-26 00:24:24 +0000
@@ -18,6 +18,7 @@
 
 import git
 import os
+import time
 
 import bzrlib
 from bzrlib import (
@@ -82,51 +83,12 @@
         """)
         self.cachedb.commit()
 
-    def _ancestor_revisions(self, revision_ids):
-        if revision_ids is not None:
-            git_revisions = [gitrevid_from_bzr(r) for r in revision_ids]
-        else:
-            git_revisions = None
-        for lines in self._git.ancestor_lines(git_revisions):
-            yield self._parse_rev(lines)
-        # print "fetched ancestors:", git_revisions
-
     def is_shared(self):
         return True
 
     def supports_rich_root(self):
         return False
 
-    def get_revision_graph(self, revision_id=None):
-        result = {}
-        if revision_id is not None:
-            param = [ids.convert_revision_id_bzr_to_git(revision_id)]
-        else:
-            param = None
-        git_graph = self._git.get_revision_graph(param)
-        # print "fetched revision graph:", param
-        for node, parents in git_graph.iteritems():
-            bzr_node = ids.convert_revision_id_git_to_bzr(node)
-            bzr_parents = [ids.convert_revision_id_git_to_bzr(n)
-                           for n in parents]
-            result[bzr_node] = bzr_parents
-        return result
-
-    def get_revision_graph_with_ghosts(self, revision_ids=None):
-        graph = deprecated_graph.Graph()
-        if revision_ids is not None:
-            revision_ids = [ids.convert_revision_id_bzr_to_git(r)
-                            for r in revision_ids]
-        git_graph = self._git.get_revision_graph(revision_ids)
-        # print "fetched revision graph (ghosts):", revision_ids
-        for node, parents in git_graph.iteritems():
-            bzr_node = ids.convert_revision_id_git_to_bzr(node)
-            bzr_parents = [ids.convert_revision_id_git_to_bzr(n)
-                           for n in parents]
-
-            graph.add_node(bzr_node, bzr_parents)
-        return graph
-
     def get_ancestry(self, revision_id):
         param = [ids.convert_revision_id_bzr_to_git(revision_id)]
         git_ancestry = self._git.get_ancestry(param)
@@ -138,14 +100,20 @@
     def get_signature_text(self, revision_id):
         raise errors.NoSuchRevision(self, revision_id)
 
+    def get_parent_map(self, revision_ids):
+        ret = {}
+        for revid in revision_ids:
+            commit = self._git.commit(ids.convert_revision_id_bzr_to_git(revid))
+            ret[revid] = tuple([ids.convert_revision_id_git_to_bzr(p.commit.id) for p in commit.parents])
+        return ret
 
     def get_revision(self, revision_id):
         if revision_id in self._revision_cache:
             return self._revision_cache[revision_id]
         git_commit_id = ids.convert_revision_id_bzr_to_git(revision_id)
-        raw = self._git.rev_list([git_commit_id], max_count=1, header=True)
+        commit = self._git.commit(git_commit_id)
         # print "fetched revision:", git_commit_id
-        revision = self._parse_rev(raw)
+        revision = self._parse_rev(commit)
         self._revision_cache[revision_id] = revision
         return revision
 
@@ -161,92 +129,33 @@
         return [self.get_revision(r) for r in revisions]
 
     @classmethod
-    def _parse_rev(klass, raw):
-        """Parse a single git revision.
-
-        * The first line is the git commit id.
-        * Following lines conform to the 'name value' structure, until the
-          first blank line.
-        * All lines after the first blank line and until the NULL line have 4
-          leading spaces and constitute the commit message.
-
-        :param raw: sequence of newline-terminated strings, its last item is a
-            single NULL character.
+    def _parse_rev(klass, commit):
+        """Convert a git commit to a bzr revision.
+
         :return: a `bzrlib.revision.Revision` object.
         """
-        parents = []
-        message_lines = []
-        in_message = False
-        committer_was_set = False
-        revision_id = ids.convert_revision_id_git_to_bzr(raw[0][:-1])
-        rev = revision.Revision(revision_id)
+        rev = revision.Revision(ids.convert_revision_id_git_to_bzr(commit.id))
+        rev.parent_ids = tuple([ids.convert_revision_id_git_to_bzr(p.id) for p in commit.parents])
         rev.inventory_sha1 = ""
-        assert raw[-1] == '\x00', (
-            "Last item of raw was not a single NULL character.")
-        for line in raw[1:-1]:
-            if in_message:
-                assert line[:4] == '    ', (
-                    "Unexpected line format in commit message: %r" % line)
-                message_lines.append(line[4:])
-                continue
-            if line == '\n':
-                in_message = True
-                continue
-            name, value = line[:-1].split(' ', 1)
-            if name == 'parent':
-                rev.parent_ids.append(
-                    ids.convert_revision_id_git_to_bzr(value))
-                continue
-            if name == 'author':
-                author, timestamp, timezone = value.rsplit(' ', 2)
-                rev.properties['author'] = author
-                rev.properties['git-author-timestamp'] = timestamp
-                rev.properties['git-author-timezone'] = timezone
-                if not committer_was_set:
-                    rev.committer = author
-                    rev.timestamp = float(timestamp)
-                    rev.timezone = klass._parse_tz(timezone)
-                continue
-            if name == 'committer':
-                committer_was_set = True
-                committer, timestamp, timezone = value.rsplit(' ', 2)
-                rev.committer = committer
-                rev.timestamp = float(timestamp)
-                rev.timezone = klass._parse_tz(timezone)
-                continue
-            if name == 'tree':
-                rev.properties['git-tree-id'] = value
-                continue
-
-        rev.message = ''.join(message_lines)
-
-        # XXX: That should not be needed, but current revision serializers do
-        # not know how how to handle text that is illegal in xml. Note: when
-        # this is fixed, we will need to rev up the revision namespace when
-        # removing the escaping code. -- David Allouche 2007-12-30
-        rev.message = escape_for_xml(rev.message)
-        rev.committer = escape_for_xml(rev.committer)
-        rev.properties['author'] = escape_for_xml(rev.properties['author'])
-
+        rev.message = commit.message
+        rev.committer = commit.committer
+        rev.properties['author'] = commit.author
+        rev.timestamp = time.mktime(commit.committed_date)
+        rev.timezone = 0
         return rev
 
-    @classmethod
-    def _parse_tz(klass, tz):
-        """Parse a timezone specification in the [+|-]HHMM format.
-
-        :return: the timezone offset in seconds.
-        """
-        assert len(tz) == 5
-        sign = {'+': +1, '-': -1}[tz[0]]
-        hours = int(tz[1:3])
-        minutes = int(tz[3:])
-        return sign * 60 * (60 * hours + minutes)
-
     def revision_trees(self, revids):
         for revid in revids:
             yield self.revision_tree(revid)
 
     def revision_tree(self, revision_id):
+        revision_id = revision.ensure_null(revision_id)
+
+        if revision_id == revision.NULL_REVISION:
+            inv = inventory.Inventory(root_id=None)
+            inv.revision_id = revision_id
+            return revisiontree.RevisionTree(self, inv, revision_id)
+
         return GitRevisionTree(self, revision_id)
 
     def _fetch_blob(self, git_id):
@@ -281,76 +190,8 @@
             return size, sha1
 
     def get_inventory(self, revision_id):
-        if revision_id is None:
-            revision_id = revision.NULL_REVISION
-        if revision_id == revision.NULL_REVISION:
-            return inventory.Inventory(
-                revision_id=revision_id, root_id=None)
-
-        # First pass at building the inventory. We need this one to get the
-        # git ids, so we do not have to cache the entire tree text. Ideally,
-        # this should be all we need to do.
-        git_commit = ids.convert_revision_id_bzr_to_git(revision_id)
-        git_inventory = self._git.get_inventory(git_commit)
-        # print "fetched inventory:", git_commit
-        inv = self._parse_inventory(revision_id, git_inventory)
-
-        # Second pass at building the inventory. There we retrieve additional
-        # data that bzrlib requires: text sizes, sha1s, symlink targets and
-        # revisions that introduced inventory entries
-        self._building_inventory = inv
-        self._building_inventory.git_file_data = {}
-        for file_id in sorted(inv.git_ids.iterkeys()):
-            git_id = inv.git_ids[file_id]
-            entry = inv[file_id]
-            self._set_entry_text_info(inv, entry, git_id)
-        for file_id in sorted(inv.git_ids.iterkeys()):
-            git_id = inv.git_ids[file_id]
-            entry = inv[file_id]
-            path = inv.id2path(file_id)
-            self._set_entry_revision(entry, revision_id, path, git_id)
-
-        # At this point the entry_revision table is fully populated for this
-        # revision. So record that we have inventory data for this revision.
-        self.cachedb.execute(
-            "insert or ignore into inventory (revid) values (?)",
-            (revision_id,))
-        self.cachedb.commit()
-        self._building_inventory = None
-        return inv
-
-    @classmethod
-    def _parse_inventory(klass, revid, git_inv):
-        # For now, git inventory do not have root ids. It is not clear that we
-        # can reliably support root ids. -- David Allouche 2007-12-28
-        inv = inventory.Inventory(revision_id=revid)
-        inv.git_ids = {}
-        for perms, git_kind, git_id, path in git_inv:
-            text_sha1 = None
-            executable = False
-            if git_kind == 'blob':
-                if perms[1] == '0':
-                    kind = 'file'
-                    executable = bool(int(perms[-3:], 8) & 0111)
-                elif perms[1] == '2':
-                    kind = 'symlink'
-                else:
-                    raise AssertionError(
-                        "Unknown blob kind, perms=%r." % (perms,))
-            elif git_kind == 'tree':
-                kind = 'directory'
-            else:
-                raise AssertionError(
-                    "Unknown git entry kind: %r" % (git_kind,))
-            # XXX: Maybe the file id should be prefixed by file kind, so when
-            # the kind of path changes, the id changes too.
-            # -- David Allouche 2007-12-28.
-            file_id = escape_file_id(path.encode('utf-8'))
-            entry = inv.add_path(path, kind, file_id=file_id)
-            entry.executable = executable
-            inv.git_ids[file_id] = git_id
-        inv.root.revision = revid
-        return inv
+        assert revision_id != None
+        return self.revision_tree(revision_id).inventory
 
     def _set_entry_text_info(self, inv, entry, git_id):
         if entry.kind == 'directory':
@@ -430,37 +271,12 @@
     return file_id.replace('_', '__').replace(' ', '_s')
 
 
-def escape_for_xml(message):
-    """Replace xml-incompatible control characters."""
-    # Copied from _escape_commit_message from bzr-svn.
-    # -- David Allouche 2007-12-29.
-    if message is None:
-        return None
-    import re
-    # FIXME: RBC 20060419 this should be done by the revision
-    # serialiser not by commit. Then we can also add an unescaper
-    # in the deserializer and start roundtripping revision messages
-    # precisely. See repository_implementations/test_repository.py
-    
-    # Python strings can include characters that can't be
-    # represented in well-formed XML; escape characters that
-    # aren't listed in the XML specification
-    # (http://www.w3.org/TR/REC-xml/#NT-Char).
-    message, _ = re.subn(
-        u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
-        lambda match: match.group(0).encode('unicode_escape'),
-        message)
-    return message
-
-
 class GitRevisionTree(revisiontree.RevisionTree):
 
     def __init__(self, repository, revision_id):
-        if revision_id is None:
-            revision_id = revision.NULL_REVISION
-        self._inventory = repository.get_inventory(revision_id)
         self._repository = repository
-        self._revision_id = revision_id
+        git_id = ids.convert_revision_id_bzr_to_git(revision_id)
+        self.tree = repository._git.commit(git_id).tree
 
     def get_file_lines(self, file_id):
         entry = self._inventory[file_id]

=== modified file 'tests/test_git_repository.py'
--- a/tests/test_git_repository.py	2008-07-25 22:56:43 +0000
+++ b/tests/test_git_repository.py	2008-07-26 00:24:24 +0000
@@ -49,43 +49,6 @@
         repo = repository.Repository.open('.')
         self.assertIsInstance(repo._git, git.repo.Repo)
 
-    def test_revision_graph(self):
-        tests.run_git('init')
-        builder = tests.GitBranchBuilder()
-        builder.set_file('a', 'text for a\n', False)
-        commit1_handle = builder.commit('Joe Foo <joe at foo.com>', u'message')
-        builder.set_file('a', 'new a\n', False)
-        commit2_handle = builder.commit('Joe Foo <joe at foo.com>', u'new a')
-        builder.set_file('b', 'text for b\n', False)
-        commit3_handle = builder.commit('Jerry Bar <jerry at foo.com>', u'b',
-                                        base=commit1_handle)
-        commit4_handle = builder.commit('Jerry Bar <jerry at foo.com>', u'merge',
-                                        base=commit3_handle,
-                                        merge=[commit2_handle],)
-
-        mapping = builder.finish()
-        commit1_id = mapping[commit1_handle]
-        commit2_id = mapping[commit2_handle]
-        commit3_id = mapping[commit3_handle]
-        commit4_id = mapping[commit4_handle]
-
-        revisions = tests.run_git('rev-list', '--topo-order',
-                                  commit4_id)
-        revisions = revisions.splitlines()
-        self.assertEqual([commit4_id, commit2_id, commit3_id, commit1_id],
-                         revisions)
-        bzr_revisions = [ids.convert_revision_id_git_to_bzr(r) for r in revisions]
-        graph = {bzr_revisions[0]:[bzr_revisions[2], bzr_revisions[1]],
-                 bzr_revisions[1]:[bzr_revisions[3]],
-                 bzr_revisions[2]:[bzr_revisions[3]],
-                 bzr_revisions[3]:[],
-                }
-
-        repo = repository.Repository.open('.')
-        self.assertEqual(graph, repo.get_revision_graph(bzr_revisions[0]))
-        self.assertEqual({bzr_revisions[3]:[]},
-                         repo.get_revision_graph(bzr_revisions[3]))
-
     def test_get_revision(self):
         # GitRepository.get_revision gives a Revision object.
 
@@ -146,34 +109,12 @@
             " sha1='67b75c3e49f31fcadddbf9df6a1d8be8c3e44290', len=12))")
 
 
-class MemoryGitRepository(git_repository.GitRepository):
-    """A git repository without real git data on disk."""
-
-    @classmethod
-    def _make_model(klass, transport):
-        return None
-
-
-class MemoryGitDir(git_dir.GitDir):
-    """A git tree with real data on disk."""
-
-    _gitrepository_class = MemoryGitRepository
-
-
-class MemoryGitBzrDirFormat(git_dir.GitBzrDirFormat):
-    """Format for a git tree without real data on disk."""
-
-    _gitdir_class = MemoryGitDir
-
-
 class TestGitRepository(tests.TestCaseWithTransport):
 
     def setUp(self):
         tests.TestCaseWithTransport.setUp(self)
-        self.transport = self.get_transport()
-        self.transport.mkdir('.git')
-        self.git_dir = MemoryGitBzrDirFormat().open(self.transport)
-        self.git_repo = self.git_dir.open_repository()
+        git.repo.Repo.create(self.test_dir)
+        self.git_repo = repository.Repository.open(self.test_dir)
 
     def test_supports_rich_root(self):
         # GitRepository.supports_rich_root is False, at least for now.
@@ -188,106 +129,13 @@
     def test_get_inventory_none(self):
         # GitRepository.get_inventory(None) returns the null inventory.
         repo = self.git_repo
-        inv = repo.get_inventory(None)
+        inv = repo.get_inventory(revision.NULL_REVISION)
         self.assertIsNullInventory(inv)
 
     def test_revision_tree_none(self):
         # GitRepository.revision_tree(None) returns the null tree.
         repo = self.git_repo
-        tree = repo.revision_tree(None)
+        tree = repo.revision_tree(revision.NULL_REVISION)
         self.assertEqual(tree.get_revision_id(), revision.NULL_REVISION)
         self.assertIsNullInventory(tree.inventory)
 
-
-class TestGitRepositoryParseRev(tests.TestCase):
-    """Unit tests for GitRepository._parse_rev."""
-
-    def test_base_commit(self):
-        # GitRepository._parse_rev works for a simple base commit.
-        rev = git_repository.GitRepository._parse_rev([
-            "873a8ae0d682b0e63e9795bc53056d32ed3de93f\n",
-            "tree aaff74984cccd156a469afa7d9ab10e4777beb24\n",
-            "author Jane Bar <jane at bar.com> 1198784533 +0200\n",
-            "committer Joe Foo <joe at foo.com> 1198784532 +0100\n",
-            "\n",
-            "    message\n",
-            "\x00"])
-        self.assertEqual(rev.revision_id,
-            'git-experimental-r:873a8ae0d682b0e63e9795bc53056d32ed3de93f')
-        self.assertEqual(rev.parent_ids, [])
-        self.assertEqual(rev.committer, 'Joe Foo <joe at foo.com>')
-        self.assertEqual(repr(rev.timestamp), '1198784532.0')
-        self.assertEqual(repr(rev.timezone), '3600')
-        self.assertEqual(rev.message, 'message\n')
-        self.assertEqual(
-            rev.properties,
-            {'git-tree-id': 'aaff74984cccd156a469afa7d9ab10e4777beb24',
-             'author': 'Jane Bar <jane at bar.com>',
-             'git-author-timestamp': '1198784533',
-             'git-author-timezone': '+0200'})
-
-    def test_merge_commit(self):
-        # Multi-parent commits (merges) are parsed correctly.
-        rev = git_repository.GitRepository._parse_rev([
-            "873a8ae0d682b0e63e9795bc53056d32ed3de93f\n",
-            "tree aaff74984cccd156a469afa7d9ab10e4777beb24\n",
-            "parent 263ed20f0d4898be994404ca418bafe8e89abb8a\n",
-            "parent 546563eb8f3e94a557f3bb779b6e5a2bd9658752\n",
-            "parent 3116d42db7b5c5e69e58f651721e179791479c23\n",
-            "author Jane Bar <jane at bar.com> 1198784533 +0200\n",
-            "committer Joe Foo <joe at foo.com> 1198784532 +0100\n",
-            "\n",
-            "    message\n",
-            "\x00"])
-        # Git records merges in the same way as bzr. The first parent is the
-        # commit base, the following parents are the ordered merged revisions.
-        self.assertEqual(
-            rev.parent_ids,
-            ['git-experimental-r:263ed20f0d4898be994404ca418bafe8e89abb8a',
-             'git-experimental-r:546563eb8f3e94a557f3bb779b6e5a2bd9658752',
-             'git-experimental-r:3116d42db7b5c5e69e58f651721e179791479c23'])
-
-    def test_redundant_spaces(self):
-        # Redundant spaces in author and committer are preserved.
-        rev = git_repository.GitRepository._parse_rev([
-            "873a8ae0d682b0e63e9795bc53056d32ed3de93f\n",
-            "tree aaff74984cccd156a469afa7d9ab10e4777beb24\n",
-            "author  Jane  Bar  <jane at bar.com>  1198784533 +0200\n",
-            "committer  Joe  Foo  <joe at foo.com>  1198784532 +0100\n",
-            "\n",
-            "    message\n",
-            "\x00"])
-        self.assertEqual(rev.committer, ' Joe  Foo  <joe at foo.com> ')
-        self.assertEqual(
-            rev.properties['author'], ' Jane  Bar  <jane at bar.com> ')
-
-    def test_no_committer(self):
-        # If committer is not set, then author is used.
-        #
-        # Folks in #git say that git fsck would likely accept commits that do
-        # not set committer, but that author is a mandatory value.
-        rev = git_repository.GitRepository._parse_rev([
-            "873a8ae0d682b0e63e9795bc53056d32ed3de93f\n",
-            "tree aaff74984cccd156a469afa7d9ab10e4777beb24\n",
-            "author Jane Bar <jane at bar.com> 1198784533 +0200\n",
-            "\n",
-            "    message\n",
-            "\x00"])
-        self.assertEqual(rev.committer, 'Jane Bar <jane at bar.com>')
-        self.assertEqual(repr(rev.timestamp), '1198784533.0')
-        self.assertEqual(repr(rev.timezone), '7200')
-        self.assertEqual(rev.properties['author'], 'Jane Bar <jane at bar.com>')
-        self.assertEqual(rev.properties['git-author-timestamp'], '1198784533')
-        self.assertEqual(rev.properties['git-author-timezone'], '+0200')
-
-    def test_parse_tz(self):
-        # Simple tests for the _parse_tz helper.
-        parse_tz = git_repository.GitRepository._parse_tz
-        self.assertEqual(repr(parse_tz('+0000')), '0')
-        self.assertEqual(repr(parse_tz('+0001')), '60')
-        self.assertEqual(repr(parse_tz('-0001')), '-60')
-        self.assertEqual(repr(parse_tz('+0100')), '3600')
-        self.assertEqual(repr(parse_tz('-0100')), '-3600')
-        self.assertEqual(repr(parse_tz('+9959')), '359940')
-        self.assertEqual(repr(parse_tz('-9959')), '-359940')
-




More information about the bazaar-commits mailing list