Rev 3472: (jam) Fix bug #235407, if someone merges the same revision twice, in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Jun 4 23:21:56 BST 2008


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

------------------------------------------------------------
revno: 3472
revision-id:pqm at pqm.ubuntu.com-20080604222149-sq8txbpiit3ckogx
parent: pqm at pqm.ubuntu.com-20080604192001-6k9g6cztr5beg1k5
parent: john at arbash-meinel.com-20080604215505-ko3ifumiiyn2vi6k
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2008-06-04 23:21:49 +0100
message:
  (jam) Fix bug #235407, if someone merges the same revision twice,
  	don't record the second one.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/blackbox/test_uncommit.py test_uncommit.py-20051027212835-84944b63adae51be
  bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
  bzrlib/tests/workingtree_implementations/test_parents.py test_set_parents.py-20060807231740-yicmnlci1mj8smu1-1
  bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3462.1.7
    revision-id:john at arbash-meinel.com-20080604215505-ko3ifumiiyn2vi6k
    parent: john at arbash-meinel.com-20080604175630-tngg9jlwn6jzebgv
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Wed 2008-06-04 16:55:05 -0500
    message:
      fix a test that assumed WT4.set_parent_trees() wouldn't filter the list.
    modified:
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
    ------------------------------------------------------------
    revno: 3462.1.6
    revision-id:john at arbash-meinel.com-20080604175630-tngg9jlwn6jzebgv
    parent: john at arbash-meinel.com-20080604175452-l0iwbtdlkdrbpnjc
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Wed 2008-06-04 12:56:30 -0500
    message:
      Update news for IN DEVELOPMENT
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3462.1.5
    revision-id:john at arbash-meinel.com-20080604175452-l0iwbtdlkdrbpnjc
    parent: john at arbash-meinel.com-20080604175258-ez2d1z1vpnucpjwy
    parent: pqm at pqm.ubuntu.com-20080604174215-d0m8mjs939ek9ed7
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Wed 2008-06-04 12:54:52 -0500
    message:
      merge bzr.dev
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/__init__.py             __init__.py-20050309040759-33e65acf91bbcd5d
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/lockdir.py              lockdir.py-20060220222025-98258adf27fbdda3
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/revisionspec.py         revisionspec.py-20050907152633-17567659fd5c0ddb
      bzrlib/smart/client.py         client.py-20061116014825-2k6ada6xgulslami-1
      bzrlib/smart/message.py        message.py-20080222013625-ncqmh3nrxjkxab87-1
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
      bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
      bzrlib/tests/test_lockdir.py   test_lockdir.py-20060220222025-33d4221569a3d600
      bzrlib/tests/test_revisionspec.py testrevisionnamespaces.py-20050711050225-8b4af89e6b1efe84
      bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
      bzrlib/tests/test_switch.py    test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3462.1.4
    revision-id:john at arbash-meinel.com-20080604175258-ez2d1z1vpnucpjwy
    parent: john at arbash-meinel.com-20080603175114-orr0xe2xg41dus7n
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Wed 2008-06-04 12:52:58 -0500
    message:
      fix up the uncommit tests now that set_parent_ids is filtering out ancestors.
    modified:
      bzrlib/tests/blackbox/test_uncommit.py test_uncommit.py-20051027212835-84944b63adae51be
    ------------------------------------------------------------
    revno: 3462.1.3
    revision-id:john at arbash-meinel.com-20080603175114-orr0xe2xg41dus7n
    parent: john at arbash-meinel.com-20080602160909-u5q4mzn2ou6kz2r7
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Tue 2008-06-03 12:51:14 -0500
    message:
      Remove the workaround in status, assuming that set_parent_(trees/ids) will do it for us.
    modified:
      bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
    ------------------------------------------------------------
    revno: 3462.1.2
    revision-id:john at arbash-meinel.com-20080602160909-u5q4mzn2ou6kz2r7
    parent: john at arbash-meinel.com-20080530222904-sn1c8r4ge8olh4r5
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Mon 2008-06-02 11:09:09 -0500
    message:
      Change WT.set_parent_(ids/trees) to filter out ancestors.
      
      This makes it impossible to trigger bug #235407, since we can't have repeated
      ancestors given.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/tests/test_status.py    test_status.py-20060516190614-fbf6432e4a6e8aa5
      bzrlib/tests/workingtree_implementations/test_parents.py test_set_parents.py-20060807231740-yicmnlci1mj8smu1-1
      bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3462.1.1
    revision-id:john at arbash-meinel.com-20080530222904-sn1c8r4ge8olh4r5
    parent: pqm at pqm.ubuntu.com-20080530080302-j1jh2bwxmpd0jn2q
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: status_double_pending_235407
    timestamp: Fri 2008-05-30 17:29:04 -0500
    message:
      Fix bug #235407, when the same revision is merged twice
      bzr status should at least show the top-level revision again,
      even though that merge introduced no new revisions.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
      bzrlib/tests/test_status.py    test_status.py-20060516190614-fbf6432e4a6e8aa5
=== modified file 'NEWS'
--- a/NEWS	2008-06-04 07:29:35 +0000
+++ b/NEWS	2008-06-04 17:56:30 +0000
@@ -5,6 +5,23 @@
 .. contents::
 
 
+IN DEVELOPMENT
+--------------
+
+  BUGFIXES:
+
+    * ``bzr status`` was breaking if you merged the same revision twice.
+      (John Arbash Meinel, #235407)
+
+  API CHANGES:
+
+    * ``WorkingTree.set_parent_(ids/trees)`` will now filter out revisions
+      which are in the ancestry of other revisions. So if you merge the same
+      tree twice, or merge an ancestor of an existing merge, it will only
+      record the newest. (If you merge a descendent, it will replace its
+      ancestor). (John Arbash Meinel, #235407)
+
+
 bzr 1.6beta1 2008-06-02
 -----------------------
 
@@ -69,6 +86,9 @@
     * ``bzr merge --lca`` should handle when two revisions have no common
       ancestor other than NULL_REVISION. (John Arbash Meinel, #235715)
 
+    * ``bzr status`` was breaking if you merged the same revision twice.
+      (John Arbash Meinel, #235407)
+
     * ``bzr push`` with both ``--overwrite`` and ``-r NNN`` options no longer
       fails.  (Andrew Bennetts, #234229)
       
@@ -194,6 +214,12 @@
       insert_record_stream are meant to efficiently replace this method.
       (Robert Collins)
 
+    * ``WorkingTree.set_parent_(ids/trees)`` will now filter out revisions
+      which are in the ancestry of other revisions. So if you merge the same
+      tree twice, or merge an ancestor of an existing merge, it will only
+      record the newest. (If you merge a descendent, it will replace its
+      ancestor). (John Arbash Meinel, #235407)
+
     * ``WorkingTreeFormat2.stub_initialize_remote`` is now private.
       (Martin Pool) 
 

=== modified file 'bzrlib/tests/blackbox/test_uncommit.py'
--- a/bzrlib/tests/blackbox/test_uncommit.py	2008-03-16 10:44:11 +0000
+++ b/bzrlib/tests/blackbox/test_uncommit.py	2008-06-04 17:52:58 +0000
@@ -173,47 +173,49 @@
         wt = self.create_simple_tree()
 
         tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
-
         tree2.commit('unchanged', rev_id='b3')
 
+        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
+        tree3.commit('unchanged', rev_id='c3')
+
         wt.merge_from_branch(tree2.branch)
         wt.commit('merge b3', rev_id='a3')
 
-        tree2.commit('unchanged', rev_id='b4')
-
-        wt.merge_from_branch(tree2.branch)
-        wt.commit('merge b4', rev_id='a4')
+        wt.merge_from_branch(tree3.branch)
+        wt.commit('merge c3', rev_id='a4')
 
         self.assertEqual(['a4'], wt.get_parent_ids())
 
         os.chdir('tree')
         out, err = self.run_bzr('uncommit --force -r 2')
 
-        self.assertEqual(['a2', 'b3', 'b4'], wt.get_parent_ids())
+        self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
 
     def test_uncommit_merge_plus_pending(self):
         wt = self.create_simple_tree()
 
         tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
-
         tree2.commit('unchanged', rev_id='b3')
+        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
+        tree3.commit('unchanged', rev_id='c3')
+
         wt.branch.fetch(tree2.branch)
         wt.set_pending_merges(['b3'])
         wt.commit('merge b3', rev_id='a3')
 
-        tree2.commit('unchanged', rev_id='b4')
-        wt.branch.fetch(tree2.branch)
-        wt.set_pending_merges(['b4'])
-
-        self.assertEqual(['a3', 'b4'], wt.get_parent_ids())
+
+        wt.merge_from_branch(tree3.branch)
+
+        self.assertEqual(['a3', 'c3'], wt.get_parent_ids())
 
         os.chdir('tree')
         out, err = self.run_bzr('uncommit --force -r 2')
 
-        self.assertEqual(['a2', 'b3', 'b4'], wt.get_parent_ids())
+        self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
 
     def test_uncommit_octopus_merge(self):
         # Check that uncommit keeps the pending merges in the same order
+        # though it will also filter out ones in the ancestry
         wt = self.create_simple_tree()
 
         tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
@@ -238,7 +240,7 @@
         os.chdir('tree')
         out, err = self.run_bzr('uncommit --force -r 2')
 
-        self.assertEqual(['a2', 'b3', 'c3', 'c4', 'b4'], wt.get_parent_ids())
+        self.assertEqual(['a2', 'c4', 'b4'], wt.get_parent_ids())
 
     def test_uncommit_nonascii(self):
         tree = self.make_branch_and_tree('tree')

=== modified file 'bzrlib/tests/test_workingtree_4.py'
--- a/bzrlib/tests/test_workingtree_4.py	2008-03-07 14:15:10 +0000
+++ b/bzrlib/tests/test_workingtree_4.py	2008-06-04 21:55:05 +0000
@@ -115,17 +115,19 @@
         rev2 = subtree2.commit('commit in subdir2')
 
         subtree.flush()
-        subtree.merge_from_branch(subtree2.branch)
-        rev3 = subtree.commit('merge from subdir2')
+        subtree3 = subtree.bzrdir.sprout('subdir3').open_workingtree()
+        rev3 = subtree3.commit('merge from subdir2')
 
         repo = tree.branch.repository
-        repo.fetch(subtree.branch.repository, rev3)
+        repo.fetch(subtree.branch.repository, rev1)
+        repo.fetch(subtree2.branch.repository, rev2)
+        repo.fetch(subtree3.branch.repository, rev3)
         # will also pull the others...
 
         # create repository based revision trees
-        rev1_revtree = subtree.branch.repository.revision_tree(rev1)
-        rev2_revtree = subtree2.branch.repository.revision_tree(rev2)
-        rev3_revtree = subtree.branch.repository.revision_tree(rev3)
+        rev1_revtree = repo.revision_tree(rev1)
+        rev2_revtree = repo.revision_tree(rev2)
+        rev3_revtree = repo.revision_tree(rev3)
         # tree doesn't contain a text merge yet but we'll just
         # set the parents as if a merge had taken place. 
         # this should cause the tree data to be folded into the 

=== modified file 'bzrlib/tests/workingtree_implementations/test_parents.py'
--- a/bzrlib/tests/workingtree_implementations/test_parents.py	2008-05-02 07:31:24 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_parents.py	2008-06-02 16:09:09 +0000
@@ -160,6 +160,73 @@
         self.assertConsistentParents(
             [first_revision, second_revision, third_revision], t)
 
+    def test_set_duplicate_parent_ids(self):
+        t = self.make_branch_and_tree('.')
+        rev1 = t.commit('first post')
+        uncommit(t.branch, tree=t)
+        rev2 = t.commit('second post')
+        uncommit(t.branch, tree=t)
+        rev3 = t.commit('third post')
+        uncommit(t.branch, tree=t)
+        t.set_parent_ids([rev1, rev2, rev2, rev3])
+        # We strip the duplicate, but preserve the ordering
+        self.assertConsistentParents([rev1, rev2, rev3], t)
+
+    def test_set_duplicate_parent_trees(self):
+        t = self.make_branch_and_tree('.')
+        rev1 = t.commit('first post')
+        uncommit(t.branch, tree=t)
+        rev2 = t.commit('second post')
+        uncommit(t.branch, tree=t)
+        rev3 = t.commit('third post')
+        uncommit(t.branch, tree=t)
+        rev_tree1 = t.branch.repository.revision_tree(rev1)
+        rev_tree2 = t.branch.repository.revision_tree(rev2)
+        rev_tree3 = t.branch.repository.revision_tree(rev3)
+        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
+                            (rev2, rev_tree2), (rev3, rev_tree3)])
+        # We strip the duplicate, but preserve the ordering
+        self.assertConsistentParents([rev1, rev2, rev3], t)
+
+    def test_set_parent_ids_in_ancestry(self):
+        t = self.make_branch_and_tree('.')
+        rev1 = t.commit('first post')
+        rev2 = t.commit('second post')
+        rev3 = t.commit('third post')
+        # Reset the tree, back to rev1
+        t.set_parent_ids([rev1])
+        t.branch.set_last_revision_info(1, rev1)
+        self.assertConsistentParents([rev1], t)
+        t.set_parent_ids([rev1, rev2, rev3])
+        # rev2 is in the ancestry of rev3, so it will be filtered out
+        self.assertConsistentParents([rev1, rev3], t)
+        # Order should be preserved, and the first revision should always be
+        # kept
+        t.set_parent_ids([rev2, rev3, rev1])
+        self.assertConsistentParents([rev2, rev3], t)
+
+    def test_set_parent_trees_in_ancestry(self):
+        t = self.make_branch_and_tree('.')
+        rev1 = t.commit('first post')
+        rev2 = t.commit('second post')
+        rev3 = t.commit('third post')
+        # Reset the tree, back to rev1
+        t.set_parent_ids([rev1])
+        t.branch.set_last_revision_info(1, rev1)
+        self.assertConsistentParents([rev1], t)
+        rev_tree1 = t.branch.repository.revision_tree(rev1)
+        rev_tree2 = t.branch.repository.revision_tree(rev2)
+        rev_tree3 = t.branch.repository.revision_tree(rev3)
+        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
+                            (rev3, rev_tree3)])
+        # rev2 is in the ancestry of rev3, so it will be filtered out
+        self.assertConsistentParents([rev1, rev3], t)
+        # Order should be preserved, and the first revision should always be
+        # kept
+        t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
+                            (rev3, rev_tree3)])
+        self.assertConsistentParents([rev2, rev3], t)
+
 
 class TestAddParent(TestParents):
 

=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py	2008-05-22 05:48:22 +0000
+++ b/bzrlib/workingtree.py	2008-06-02 16:09:09 +0000
@@ -724,6 +724,25 @@
         self._transport.put_bytes('pending-merges', '\n'.join(merges),
             mode=self._control_files._file_mode)
 
+    def _filter_parent_ids_by_ancestry(self, revision_ids):
+        """Check that all merged revisions are proper 'heads'.
+
+        This will always return the first revision_id, and any merged revisions
+        which are 
+        """
+        if len(revision_ids) == 0:
+            return revision_ids
+        graph = self.branch.repository.get_graph()
+        heads = graph.heads(revision_ids)
+        new_revision_ids = revision_ids[:1]
+        for revision_id in revision_ids[1:]:
+            if revision_id in heads and revision_id not in new_revision_ids:
+                new_revision_ids.append(revision_id)
+        if new_revision_ids != revision_ids:
+            trace.mutter('requested to set revision_ids = %s,'
+                         ' but filtered to %s', revision_ids, new_revision_ids)
+        return new_revision_ids
+
     @needs_tree_write_lock
     def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
         """Set the parent ids to revision_ids.
@@ -742,6 +761,8 @@
         for revision_id in revision_ids:
             _mod_revision.check_not_reserved_id(revision_id)
 
+        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
+
         if len(revision_ids) > 0:
             self.set_last_revision(revision_ids[0])
         else:
@@ -759,6 +780,8 @@
         self._check_parents_for_ghosts(parent_ids,
             allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
+        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
+
         if len(parent_ids) == 0:
             leftmost_parent_id = _mod_revision.NULL_REVISION
             leftmost_parent_tree = None

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2008-06-04 07:29:35 +0000
+++ b/bzrlib/workingtree_4.py	2008-06-04 17:54:52 +0000
@@ -1085,9 +1085,21 @@
                 raise errors.GhostRevisionUnusableHere(parents_list[0][0])
         real_trees = []
         ghosts = []
+
+        parent_ids = [rev_id for rev_id, tree in parents_list]
+        graph = self.branch.repository.get_graph()
+        heads = graph.heads(parent_ids)
+        accepted_revisions = set()
+
         # convert absent trees to the null tree, which we convert back to
         # missing on access.
         for rev_id, tree in parents_list:
+            if len(accepted_revisions) > 0:
+                # we always accept the first tree
+                if rev_id in accepted_revisions or rev_id not in heads:
+                    # We have already included either this tree, or its
+                    # descendent, so we skip it.
+                    continue
             _mod_revision.check_not_reserved_id(rev_id)
             if tree is not None:
                 real_trees.append((rev_id, tree))
@@ -1095,6 +1107,7 @@
                 real_trees.append((rev_id,
                     self.branch.repository.revision_tree(None)))
                 ghosts.append(rev_id)
+            accepted_revisions.add(rev_id)
         dirstate.set_parent_trees(real_trees, ghosts=ghosts)
         self._make_dirty(reset_inventory=False)
 




More information about the bazaar-commits mailing list