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