Rev 3394: (jam) 'bzr check' and 'bzr reconcile' will detect and repair in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Apr 30 19:22:40 BST 2008


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3394
revision-id:pqm at pqm.ubuntu.com-20080430182230-iz5tit0t2ut5clww
parent: pqm at pqm.ubuntu.com-20080430161812-ns7wh4cab7ovfnsy
parent: john at arbash-meinel.com-20080430150249-viu4vgzglhfu7k7y
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2008-04-30 19:22:30 +0100
message:
  (jam) 'bzr check' and 'bzr reconcile' will detect and repair
  	inconsistencies in branch revision-history, (bug #177855)
added:
  bzrlib/tests/branch_implementations/test_check.py test_check.py-20080429151303-1sbfclxhddpz0tnj-1
  bzrlib/tests/branch_implementations/test_reconcile.py test_reconcile.py-20080429161555-qlmccuyeyt6pvho7-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/reconcile.py            reweave_inventory.py-20051108164726-1e5e0934febac06e
  bzrlib/tests/blackbox/test_reconcile.py test_fix.py-20060223013051-9a188e15a5ee9451
  bzrlib/tests/branch_implementations/__init__.py __init__.py-20060123013057-b12a52c3f361daf4
  bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
  bzrlib/tests/test_reconcile.py test_reconcile.py-20060225054842-50aa618584a86f26
    ------------------------------------------------------------
    revno: 3389.2.8
    revision-id:john at arbash-meinel.com-20080430150249-viu4vgzglhfu7k7y
    parent: john at arbash-meinel.com-20080430133215-t1x69j3vg01yu3i0
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Wed 2008-04-30 10:02:49 -0500
    message:
      Update some tests to pass with the branch checks.
    modified:
      bzrlib/tests/blackbox/test_reconcile.py test_fix.py-20060223013051-9a188e15a5ee9451
      bzrlib/tests/test_reconcile.py test_reconcile.py-20060225054842-50aa618584a86f26
    ------------------------------------------------------------
    revno: 3389.2.7
    revision-id:john at arbash-meinel.com-20080430133215-t1x69j3vg01yu3i0
    parent: john at arbash-meinel.com-20080429170124-13qtjqvwsd7mfldn
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Wed 2008-04-30 08:32:15 -0500
    message:
      Review comments from Ian
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/reconcile.py            reweave_inventory.py-20051108164726-1e5e0934febac06e
    ------------------------------------------------------------
    revno: 3389.2.6
    revision-id:john at arbash-meinel.com-20080429170124-13qtjqvwsd7mfldn
    parent: john at arbash-meinel.com-20080429164314-n3q4obl92y7suwah
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 12:01:24 -0500
    message:
      minor fix to test file comment
    modified:
      bzrlib/tests/branch_implementations/test_check.py test_check.py-20080429151303-1sbfclxhddpz0tnj-1
    ------------------------------------------------------------
    revno: 3389.2.5
    revision-id:john at arbash-meinel.com-20080429164314-n3q4obl92y7suwah
    parent: john at arbash-meinel.com-20080429163438-scr993idmrty20bv
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 11:43:14 -0500
    message:
      Fix the line endings on test_check and test_reconcile
    modified:
      bzrlib/tests/branch_implementations/test_check.py test_check.py-20080429151303-1sbfclxhddpz0tnj-1
      bzrlib/tests/branch_implementations/test_reconcile.py test_reconcile.py-20080429161555-qlmccuyeyt6pvho7-1
    ------------------------------------------------------------
    revno: 3389.2.4
    revision-id:john at arbash-meinel.com-20080429163438-scr993idmrty20bv
    parent: john at arbash-meinel.com-20080429163158-hui1lprno5hd18pv
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 11:34:38 -0500
    message:
      NEWS about fixing bug #177855
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3389.2.3
    revision-id:john at arbash-meinel.com-20080429163158-hui1lprno5hd18pv
    parent: john at arbash-meinel.com-20080429155330-4mb0y4xc8gb62tan
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 11:31:58 -0500
    message:
      Add Branch.reconcile() functionality.
    added:
      bzrlib/tests/branch_implementations/test_reconcile.py test_reconcile.py-20080429161555-qlmccuyeyt6pvho7-1
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/reconcile.py            reweave_inventory.py-20051108164726-1e5e0934febac06e
      bzrlib/tests/branch_implementations/__init__.py __init__.py-20060123013057-b12a52c3f361daf4
      bzrlib/tests/test_reconcile.py test_reconcile.py-20060225054842-50aa618584a86f26
    ------------------------------------------------------------
    revno: 3389.2.2
    revision-id:john at arbash-meinel.com-20080429155330-4mb0y4xc8gb62tan
    parent: john at arbash-meinel.com-20080429154714-iqrqt3dgg412ertk
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 10:53:30 -0500
    message:
      assert the right text is given in the error
    modified:
      bzrlib/tests/branch_implementations/test_check.py test_check.py-20080429151303-1sbfclxhddpz0tnj-1
    ------------------------------------------------------------
    revno: 3389.2.1
    revision-id:john at arbash-meinel.com-20080429154714-iqrqt3dgg412ertk
    parent: pqm at pqm.ubuntu.com-20080429014232-4b86ax5pwynnf11i
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: reconcile_rev_history_177855
    timestamp: Tue 2008-04-29 10:47:14 -0500
    message:
      Add code to 'bzr check' to detect when the mainline history is inconsistent.
    added:
      bzrlib/tests/branch_implementations/test_check.py test_check.py-20080429151303-1sbfclxhddpz0tnj-1
    modified:
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/tests/branch_implementations/__init__.py __init__.py-20060123013057-b12a52c3f361daf4
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
=== added file 'bzrlib/tests/branch_implementations/test_check.py'
--- a/bzrlib/tests/branch_implementations/test_check.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/branch_implementations/test_check.py	2008-04-29 17:01:24 +0000
@@ -0,0 +1,65 @@
+# Copyright (C) 2008 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Tests for branch implementations - test check() functionality"""
+
+from bzrlib import errors
+from bzrlib.tests.branch_implementations import TestCaseWithBranch
+
+
+class TestBranchCheck(TestCaseWithBranch):
+
+    def test_check_detects_invalid_revhistory(self):
+        # Different formats have different ways of handling invalid revision
+        # histories, so the setup portion is customized
+        tree = self.make_branch_and_tree('test')
+        r1 = tree.commit('one')
+        r2 = tree.commit('two')
+        r3 = tree.commit('three')
+        r4 = tree.commit('four')
+        # create an alternate branch
+        tree.set_parent_ids([r1])
+        tree.branch.set_last_revision_info(1, r1)
+        r2b = tree.commit('two-b')
+
+        # now go back and merge the commit
+        tree.set_parent_ids([r4, r2b])
+        tree.branch.set_last_revision_info(4, r4)
+
+        r5 = tree.commit('five')
+        # Now, try to set an invalid history
+        try:
+            tree.branch.set_revision_history([r1, r2b, r5])
+        except errors.NotLefthandHistory:
+            # Branch5 allows set_revision_history to be wrong
+            # Branch6 raises NotLefthandHistory, but we can force bogus stuff
+            # with set_last_revision_info
+            tree.branch.set_last_revision_info(3, r5)
+
+        e = self.assertRaises(errors.BzrCheckError,
+                              tree.branch.check)
+        self.assertEqual('Internal check failed:'
+                         ' revno does not match len(mainline) 3 != 5', str(e))
+
+    def test_check_branch_report_results(self):
+        """Checking a branch produces results which can be printed"""
+        branch = self.make_branch('.')
+        result = branch.check()
+        # reports results through logging
+        result.report_results(verbose=True)
+        result.report_results(verbose=False)
+
+

=== added file 'bzrlib/tests/branch_implementations/test_reconcile.py'
--- a/bzrlib/tests/branch_implementations/test_reconcile.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/branch_implementations/test_reconcile.py	2008-04-29 16:43:14 +0000
@@ -0,0 +1,67 @@
+# Copyright (C) 2008 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Tests for branch implementations - test reconcile() functionality"""
+
+from bzrlib import errors, reconcile
+from bzrlib.tests.branch_implementations import TestCaseWithBranch
+
+
+class TestBranchReconcile(TestCaseWithBranch):
+
+    def test_reconcile_fixes_invalid_revhistory(self):
+        # Different formats have different ways of handling invalid revision
+        # histories, so the setup portion is customized
+        tree = self.make_branch_and_tree('test')
+        r1 = tree.commit('one')
+        r2 = tree.commit('two')
+        r3 = tree.commit('three')
+        r4 = tree.commit('four')
+        # create an alternate branch
+        tree.set_parent_ids([r1])
+        tree.branch.set_last_revision_info(1, r1)
+        r2b = tree.commit('two-b')
+
+        # now go back and merge the commit
+        tree.set_parent_ids([r4, r2b])
+        tree.branch.set_last_revision_info(4, r4)
+
+        r5 = tree.commit('five')
+        # Now, try to set an invalid history
+        try:
+            tree.branch.set_revision_history([r1, r2b, r5])
+        except errors.NotLefthandHistory:
+            # Branch5 allows set_revision_history to be wrong
+            # Branch6 raises NotLefthandHistory, but we can force bogus stuff
+            # with set_last_revision_info
+            tree.branch.set_last_revision_info(3, r5)
+
+        self.assertEqual((3, r5), tree.branch.last_revision_info())
+        reconciler = tree.branch.reconcile()
+        self.assertEqual((5, r5), tree.branch.last_revision_info())
+        self.assertIs(True, reconciler.fixed_history)
+
+    def test_reconcile_returns_reconciler(self):
+        a_branch = self.make_branch('a_branch')
+        result = a_branch.reconcile()
+        self.assertIsInstance(result, reconcile.BranchReconciler)
+        # No history to fix
+        self.assertIs(False, result.fixed_history)
+
+    def test_reconcile_supports_thorough(self):
+        a_branch = self.make_branch('a_branch')
+        a_branch.reconcile(thorough=False)
+        a_branch.reconcile(thorough=True)

=== modified file 'NEWS'
--- a/NEWS	2008-04-30 16:18:12 +0000
+++ b/NEWS	2008-04-30 18:22:30 +0000
@@ -32,6 +32,10 @@
     * Avoid muttering every time a child update does not cause a progress bar
       update. (John Arbash Meinel, #213771)
 
+    * ``Branch.reconcile()`` is now implemented. This allows ``bzr reconcile``
+      to fix when a Branch has a non-canonical mainline history. ``bzr check``
+      also detects this condition. (John Arbash Meinel, #177855)
+
     * ``bzr commit`` now works with Microsoft's FTP service.
       (Andreas Deininger)
 

=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2008-04-11 04:51:44 +0000
+++ b/bzrlib/branch.py	2008-04-30 13:32:15 +0000
@@ -704,7 +704,16 @@
         :return: A BranchCheckResult.
         """
         mainline_parent_id = None
-        for revision_id in self.revision_history():
+        last_revno, last_revision_id = self.last_revision_info()
+        real_rev_history = list(self.repository.iter_reverse_revision_history(
+                                last_revision_id))
+        real_rev_history.reverse()
+        if len(real_rev_history) != last_revno:
+            raise errors.BzrCheckError('revno does not match len(mainline)'
+                ' %s != %s' % (last_revno, len(real_rev_history)))
+        # TODO: We should probably also check that real_rev_history actually
+        #       matches self.revision_history()
+        for revision_id in real_rev_history:
             try:
                 revision = self.repository.get_revision(revision_id)
             except errors.NoSuchRevision, e:
@@ -783,6 +792,14 @@
             basis_tree.unlock()
         return tree
 
+    @needs_write_lock
+    def reconcile(self, thorough=True):
+        """Make sure the data stored in this branch is consistent."""
+        from bzrlib.reconcile import BranchReconciler
+        reconciler = BranchReconciler(self, thorough=thorough)
+        reconciler.reconcile()
+        return reconciler
+
     def reference_parent(self, file_id, path):
         """Return the parent branch for a tree-reference file_id
         :param file_id: The file_id of the tree reference

=== modified file 'bzrlib/reconcile.py'
--- a/bzrlib/reconcile.py	2008-03-26 21:42:35 +0000
+++ b/bzrlib/reconcile.py	2008-04-30 13:32:15 +0000
@@ -67,6 +67,9 @@
                               ancestry was being reported incorrectly.
         garbage_inventories: The number of inventory objects without revisions
                              that were garbage collected.
+        fixed_branch_history: None if there was no branch, False if the branch
+                              history was correct, True if the branch history
+                              needed to be re-normalized.
         """
         self.pb = ui.ui_factory.nested_progress_bar()
         try:
@@ -76,6 +79,22 @@
 
     def _reconcile(self):
         """Helper function for performing reconciliation."""
+        self._reconcile_branch()
+        self._reconcile_repository()
+
+    def _reconcile_branch(self):
+        try:
+            self.branch = self.bzrdir.open_branch()
+        except errors.NotBranchError:
+            # Nothing to check here
+            self.fixed_branch_history = None
+            return
+        self.pb.note('Reconciling branch %s',
+                     self.branch.base)
+        branch_reconciler = self.branch.reconcile(thorough=True)
+        self.fixed_branch_history = branch_reconciler.fixed_history
+
+    def _reconcile_repository(self):
         self.repo = self.bzrdir.find_repository()
         self.pb.note('Reconciling repository %s',
                      self.repo.bzrdir.root_transport.base)
@@ -92,6 +111,49 @@
             self.pb.note('Reconciliation complete.')
 
 
+class BranchReconciler(object):
+    """Reconciler that works on a branch."""
+
+    def __init__(self, a_branch, thorough=False):
+        self.fixed_history = None
+        self.thorough = thorough
+        self.branch = a_branch
+
+    def reconcile(self):
+        self.branch.lock_write()
+        try:
+            self.pb = ui.ui_factory.nested_progress_bar()
+            try:
+                self._reconcile_steps()
+            finally:
+                self.pb.finished()
+        finally:
+            self.branch.unlock()
+
+    def _reconcile_steps(self):
+        self._reconcile_revision_history()
+
+    def _reconcile_revision_history(self):
+        repo = self.branch.repository
+        last_revno, last_revision_id = self.branch.last_revision_info()
+        real_history = list(repo.iter_reverse_revision_history(
+                                last_revision_id))
+        real_history.reverse()
+        if last_revno != len(real_history):
+            self.fixed_history = True
+            # Technically for Branch5 formats, it is more efficient to use
+            # set_revision_history, as this will regenerate it again.
+            # Not really worth a whole BranchReconciler class just for this,
+            # though.
+            self.pb.note('Fixing last revision info %s => %s',
+                         last_revno, len(real_history))
+            self.branch.set_last_revision_info(len(real_history),
+                                               last_revision_id)
+        else:
+            self.fixed_history = False
+            self.pb.note('revision_history ok.')
+
+
 class RepoReconciler(object):
     """Reconciler that reconciles a repository.
 

=== modified file 'bzrlib/tests/blackbox/test_reconcile.py'
--- a/bzrlib/tests/blackbox/test_reconcile.py	2007-11-22 02:09:49 +0000
+++ b/bzrlib/tests/blackbox/test_reconcile.py	2008-04-30 15:02:49 +0000
@@ -46,10 +46,13 @@
             does_backup_text = "Inventory ok.\n"
         else:
             does_backup_text = ""
-        self.assertEqualDiff(out, "Reconciling repository %s\n"
+        self.assertEqualDiff(out, "Reconciling branch %s\n"
+                                  "revision_history ok.\n"
+                                  "Reconciling repository %s\n"
                                   "%s"
                                   "Reconciliation complete.\n" %
-                                  (t.bzrdir.root_transport.base,
+                                  (t.branch.base,
+                                   t.bzrdir.root_transport.base,
                                    does_backup_text))
         self.assertEqualDiff(err, "")
 
@@ -71,9 +74,13 @@
                 "Inventory regenerated.\n")
         else:
             does_backup_text = ""
-        expected = ("Reconciling repository %s\n"
+        expected = ("Reconciling branch %s\n"
+                    "revision_history ok.\n"
+                    "Reconciling repository %s\n"
                     "%s"
                     "Reconciliation complete.\n" %
-                    (t.bzrdir.root_transport.base, does_backup_text))
+                    (t.branch.base,
+                     t.bzrdir.root_transport.base,
+                     does_backup_text))
         self.assertEqualDiff(expected, out)
         self.assertEqualDiff(err, "")

=== modified file 'bzrlib/tests/branch_implementations/__init__.py'
--- a/bzrlib/tests/branch_implementations/__init__.py	2007-06-28 05:19:04 +0000
+++ b/bzrlib/tests/branch_implementations/__init__.py	2008-04-29 16:31:58 +0000
@@ -141,6 +141,7 @@
         'bzrlib.tests.branch_implementations.test_bound_sftp',
         'bzrlib.tests.branch_implementations.test_branch',
         'bzrlib.tests.branch_implementations.test_break_lock',
+        'bzrlib.tests.branch_implementations.test_check',
         'bzrlib.tests.branch_implementations.test_create_checkout',
         'bzrlib.tests.branch_implementations.test_commit',
         'bzrlib.tests.branch_implementations.test_get_revision_id_to_revno_map',
@@ -152,6 +153,7 @@
         'bzrlib.tests.branch_implementations.test_permissions',
         'bzrlib.tests.branch_implementations.test_pull',
         'bzrlib.tests.branch_implementations.test_push',
+        'bzrlib.tests.branch_implementations.test_reconcile',
         'bzrlib.tests.branch_implementations.test_revision_history',
         'bzrlib.tests.branch_implementations.test_revision_id_to_revno',
         'bzrlib.tests.branch_implementations.test_sprout',

=== modified file 'bzrlib/tests/branch_implementations/test_branch.py'
--- a/bzrlib/tests/branch_implementations/test_branch.py	2008-03-28 04:58:53 +0000
+++ b/bzrlib/tests/branch_implementations/test_branch.py	2008-04-29 15:47:14 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -373,14 +373,6 @@
         text = tree.branch._format.get_format_description()
         self.failUnless(len(text))
 
-    def test_check_branch_report_results(self):
-        """Checking a branch produces results which can be printed"""
-        branch = self.make_branch('.')
-        result = branch.check()
-        # reports results through logging
-        result.report_results(verbose=True)
-        result.report_results(verbose=False)
-
     def test_get_commit_builder(self):
         branch = self.make_branch(".")
         branch.lock_write()

=== modified file 'bzrlib/tests/test_reconcile.py'
--- a/bzrlib/tests/test_reconcile.py	2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/test_reconcile.py	2008-04-30 15:02:49 +0000
@@ -17,8 +17,7 @@
 """Tests for reconiliation behaviour that is repository independent."""
 
 
-import bzrlib
-import bzrlib.errors as errors
+from bzrlib import bzrdir, errors, tests
 from bzrlib.reconcile import reconcile, Reconciler
 from bzrlib.revision import Revision
 from bzrlib.tests.repository_implementations.test_repository import TestCaseWithRepository
@@ -30,10 +29,10 @@
 
     def test_reweave_empty(self):
         # we want a repo capable format
-        parent = bzrlib.bzrdir.BzrDirMetaFormat1().initialize('.')
+        parent = bzrdir.BzrDirMetaFormat1().initialize('.')
         parent.create_repository(shared=True)
         parent.root_transport.mkdir('child')
-        child = bzrlib.bzrdir.BzrDirMetaFormat1().initialize('child')
+        child = bzrdir.BzrDirMetaFormat1().initialize('child')
         self.assertRaises(errors.NoRepositoryPresent, child.open_repository)
         reconciler = Reconciler(child)
         reconciler.reconcile()
@@ -44,3 +43,27 @@
         self.assertEqual(0, reconciler.inconsistent_parents)
         # and no garbage inventories
         self.assertEqual(0, reconciler.garbage_inventories)
+
+
+class TestReconciler(tests.TestCaseWithTransport):
+    
+    def test_reconciler_with_no_branch(self):
+        repo = self.make_repository('repo')
+        reconciler = Reconciler(repo.bzrdir)
+        reconciler.reconcile()
+        # no inconsistent parents should have been found
+        # but the values should have been set.
+        self.assertEqual(0, reconciler.inconsistent_parents)
+        # and no garbage inventories
+        self.assertEqual(0, reconciler.garbage_inventories)
+        self.assertIs(None, reconciler.fixed_branch_history)
+
+    def test_reconciler_finds_branch(self):
+        a_branch = self.make_branch('a_branch')
+        reconciler = Reconciler(a_branch.bzrdir)
+        reconciler.reconcile()
+
+        # It should have checked the repository, and the branch
+        self.assertEqual(0, reconciler.inconsistent_parents)
+        self.assertEqual(0, reconciler.garbage_inventories)
+        self.assertIs(False, reconciler.fixed_branch_history)




More information about the bazaar-commits mailing list