Rev 3441: (jam) 'bzr missing' uses Graph.find* rather than using whole ancestry in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Tue May 20 22:00:41 BST 2008


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

------------------------------------------------------------
revno: 3441
revision-id:pqm at pqm.ubuntu.com-20080520210027-wetfxldz1ggc5u2a
parent: pqm at pqm.ubuntu.com-20080520094809-682f95w1gbrji5t0
parent: john at arbash-meinel.com-20080520182859-tvhy5hjx3jnlfzk6
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2008-05-20 22:00:27 +0100
message:
  (jam) 'bzr missing' uses Graph.find* rather than using whole ancestry
  	comparisons (bug #174625)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/missing.py              missing.py-20050812153334-097f7097e2a8bcd1
  bzrlib/tests/blackbox/test_missing.py test_missing.py-20051211212735-a2cf4c1840bb84c4
  bzrlib/tests/test_missing.py   test_missing.py-20051212000028-694fa4f658a81f48
    ------------------------------------------------------------
    revno: 3427.3.8
    revision-id:john at arbash-meinel.com-20080520182859-tvhy5hjx3jnlfzk6
    parent: john at arbash-meinel.com-20080520181835-kw4sd2fzblnw23mj
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Tue 2008-05-20 13:28:59 -0500
    message:
      Change the output to 'This branch' and 'Other branch', and document the text in NEWS
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/tests/blackbox/test_missing.py test_missing.py-20051211212735-a2cf4c1840bb84c4
    ------------------------------------------------------------
    revno: 3427.3.7
    revision-id:john at arbash-meinel.com-20080520181835-kw4sd2fzblnw23mj
    parent: john at arbash-meinel.com-20080520023401-42mkw5g7dhq9f5bh
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Tue 2008-05-20 13:18:35 -0500
    message:
      Update how 'bzr missing' works when given --mine-only or --theirs-only
      
      It used to always determine missing revisions on both sides, and then just suppress
      displaying them if the flag was given. It would also return status 1 if either side
      had extra revisions. However, if you are asking for --mine-only, it doesn't make sense
      to return status 1 when the local branch has nothing new.
      And the new code doesn't know whether there are new revisions on the other side anyway.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/tests/blackbox/test_missing.py test_missing.py-20051211212735-a2cf4c1840bb84c4
    ------------------------------------------------------------
    revno: 3427.3.6
    revision-id:john at arbash-meinel.com-20080520023401-42mkw5g7dhq9f5bh
    parent: john at arbash-meinel.com-20080519214725-z6g0mq5tsibsbrfj
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 21:34:01 -0500
    message:
      review feedback from Ian
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/missing.py              missing.py-20050812153334-097f7097e2a8bcd1
    ------------------------------------------------------------
    revno: 3427.3.5
    revision-id:john at arbash-meinel.com-20080519214725-z6g0mq5tsibsbrfj
    parent: john at arbash-meinel.com-20080519214254-aolweqts5hzrn879
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 16:47:25 -0500
    message:
      NEWS about fixing bug #174625
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3427.3.4
    revision-id:john at arbash-meinel.com-20080519214254-aolweqts5hzrn879
    parent: john at arbash-meinel.com-20080519213939-7d47qehv3kez1gfx
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 16:42:54 -0500
    message:
      Fix cmd_missing for --this or --other
    modified:
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
    ------------------------------------------------------------
    revno: 3427.3.3
    revision-id:john at arbash-meinel.com-20080519213939-7d47qehv3kez1gfx
    parent: john at arbash-meinel.com-20080519213552-8jc1yc4w8rqhgzri
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 16:39:39 -0500
    message:
      Revert cmd_missing to use the original function, only now supply restrict
    modified:
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
    ------------------------------------------------------------
    revno: 3427.3.2
    revision-id:john at arbash-meinel.com-20080519213552-8jc1yc4w8rqhgzri
    parent: john at arbash-meinel.com-20080519203537-6dqmxtn6fxlk1ter
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 16:35:52 -0500
    message:
      switch find_unmerged to use the new function
      
      The api is very compatible, so we don't really need to expose a new function.
      So make it hidden, and leave find_unmerged to take out the write lock and call it.
    modified:
      bzrlib/missing.py              missing.py-20050812153334-097f7097e2a8bcd1
      bzrlib/tests/test_missing.py   test_missing.py-20051212000028-694fa4f658a81f48
    ------------------------------------------------------------
    revno: 3427.3.1
    revision-id:john at arbash-meinel.com-20080519203537-6dqmxtn6fxlk1ter
    parent: pqm at pqm.ubuntu.com-20080512115743-6uz3lnmrrbhmoiqe
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: missing
    timestamp: Mon 2008-05-19 15:35:37 -0500
    message:
      Add bzrlib.missing.find_unmerged_mainline_revisions
      
      This is a helper function that is designed to give 'missing' what it wants
      without having to search all of ancestry.
    modified:
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/missing.py              missing.py-20050812153334-097f7097e2a8bcd1
      bzrlib/tests/test_missing.py   test_missing.py-20051212000028-694fa4f658a81f48
=== modified file 'NEWS'
--- a/NEWS	2008-05-20 07:16:50 +0000
+++ b/NEWS	2008-05-20 21:00:27 +0000
@@ -31,6 +31,10 @@
 
   IMPROVEMENTS:
 
+    * ``bzr missing`` uses the new ``Graph.find_unique_ancestors`` and
+      ``Graph.find_differences`` to determine missing revisions without having
+      to search the whole ancestry. (John Arbash Meinel, #174625)
+
   BUGFIXES:
 
     * Correctly track the base URL of a smart medium when using bzr+http://
@@ -68,6 +72,13 @@
 
   API BREAKS:
 
+    * ``bzr missing --mine-only`` will return status code 0 if you have no
+      new revisions, but the remote does. Similarly for ``--theirs-only``.
+      The new code only checks one side, so it doesn't know if the other
+      side has changes. This seems more accurate with the request anyway.
+      It also changes the output to print '[This|Other] branch is up to
+      date.' rather than displaying nothing.  (John Arbash Meinel)
+
     * Many methods on ``VersionedFile``, ``Repository`` and in
       ``bzrlib.revision``  deprecated before bzrlib 1.5 have been removed.
       (Robert Collins)

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2008-05-09 06:46:17 +0000
+++ b/bzrlib/builtins.py	2008-05-20 18:28:59 +0000
@@ -3308,9 +3308,17 @@
         from bzrlib.missing import find_unmerged, iter_log_revisions
 
         if this:
-          mine_only = this
+            mine_only = this
         if other:
-          theirs_only = other
+            theirs_only = other
+        # TODO: We should probably check that we don't have mine-only and
+        #       theirs-only set, but it gets complicated because we also have
+        #       this and other which could be used.
+        restrict = 'all'
+        if mine_only:
+            restrict = 'local'
+        elif theirs_only:
+            restrict = 'remote'
 
         local_branch = Branch.open_containing(u".")[0]
         parent = local_branch.get_parent()
@@ -3330,8 +3338,9 @@
         try:
             remote_branch.lock_read()
             try:
-                local_extra, remote_extra = find_unmerged(local_branch,
-                                                          remote_branch)
+                local_extra, remote_extra = find_unmerged(
+                    local_branch, remote_branch, restrict)
+
                 if log_format is None:
                     registry = log.log_formatter_registry
                     log_format = registry.get_default(local_branch)
@@ -3339,8 +3348,12 @@
                                 show_ids=show_ids,
                                 show_timezone='original')
                 if reverse is False:
-                    local_extra.reverse()
-                    remote_extra.reverse()
+                    if local_extra is not None:
+                        local_extra.reverse()
+                    if remote_extra is not None:
+                        remote_extra.reverse()
+
+                status_code = 0
                 if local_extra and not theirs_only:
                     self.outf.write("You have %d extra revision(s):\n" %
                                     len(local_extra))
@@ -3349,8 +3362,10 @@
                                         verbose):
                         lf.log_revision(revision)
                     printed_local = True
+                    status_code = 1
                 else:
                     printed_local = False
+
                 if remote_extra and not mine_only:
                     if printed_local is True:
                         self.outf.write("\n\n\n")
@@ -3360,11 +3375,19 @@
                                         remote_branch.repository,
                                         verbose):
                         lf.log_revision(revision)
-                if not remote_extra and not local_extra:
-                    status_code = 0
+                    status_code = 1
+
+                if mine_only and not local_extra:
+                    # We checked local, and found nothing extra
+                    self.outf.write('This branch is up to date.\n')
+                elif theirs_only and not remote_extra:
+                    # We checked remote, and found nothing extra
+                    self.outf.write('Other branch is up to date.\n')
+                elif not (mine_only or theirs_only or local_extra or
+                          remote_extra):
+                    # We checked both branches, and neither one had extra
+                    # revisions
                     self.outf.write("Branches are up to date.\n")
-                else:
-                    status_code = 1
             finally:
                 remote_branch.unlock()
         finally:

=== modified file 'bzrlib/missing.py'
--- a/bzrlib/missing.py	2008-05-02 07:31:24 +0000
+++ b/bzrlib/missing.py	2008-05-20 02:34:01 +0000
@@ -16,7 +16,6 @@
 
 """Display what revisions are missing in 'other' from 'this' and vice versa."""
 
-from bzrlib import ui
 from bzrlib.log import (
     LogRevision,
     )
@@ -43,82 +42,100 @@
         yield LogRevision(rev, revno, delta=delta)
 
 
-def find_unmerged(local_branch, remote_branch):
-    progress = ui.ui_factory.nested_progress_bar()
+def find_unmerged(local_branch, remote_branch, restrict='all'):
+    """Find revisions from each side that have not been merged.
+
+    :param local_branch: Compare the history of local_branch
+    :param remote_branch: versus the history of remote_branch, and determine
+        mainline revisions which have not been merged.
+    :param restrict: ('all', 'local', 'remote') If 'all', we will return the
+        unique revisions from both sides. If 'local', we will return None
+        for the remote revisions, similarly if 'remote' we will return None for
+        the local revisions.
+
+    :return: A list of [(revno, revision_id)] for the mainline revisions on
+        each side.
+    """
     local_branch.lock_read()
     try:
         remote_branch.lock_read()
         try:
-            # check for special case: both branches are equivalent
-            if (local_branch.last_revision_info() ==
-                remote_branch.last_revision_info()):
-                return [], []
-            local_rev_history, local_rev_history_map = \
-                _get_history(local_branch, progress, "local", 0)
-            remote_rev_history, remote_rev_history_map = \
-                _get_history(remote_branch, progress, "remote", 1)
-            result = _shortcut(local_rev_history, remote_rev_history)
-            if result is not None:
-                local_extra, remote_extra = result
-                local_extra = sorted_revisions(local_extra, 
-                                               local_rev_history_map)
-                remote_extra = sorted_revisions(remote_extra, 
-                                                remote_rev_history_map)
-                return local_extra, remote_extra
-
-            local_ancestry = _get_ancestry(local_branch.repository, progress, 
-                                           "local", 2, local_rev_history)
-            remote_ancestry = _get_ancestry(remote_branch.repository, progress,
-                                            "remote", 3, remote_rev_history)
-            progress.update('pondering', 4, 5)
-            extras = local_ancestry.symmetric_difference(remote_ancestry) 
-            local_extra = extras.intersection(set(local_rev_history))
-            remote_extra = extras.intersection(set(remote_rev_history))
-            local_extra = sorted_revisions(local_extra, local_rev_history_map)
-            remote_extra = sorted_revisions(remote_extra, 
-                                            remote_rev_history_map)
-                    
+            return _find_unmerged(local_branch,
+                remote_branch, restrict=restrict)
         finally:
             remote_branch.unlock()
     finally:
         local_branch.unlock()
-        progress.finished()
-    return (local_extra, remote_extra)
-
-def _shortcut(local_rev_history, remote_rev_history):
-    local_history = set(local_rev_history)
-    remote_history = set(remote_rev_history)
-    if len(local_rev_history) == 0:
-        return set(), remote_history
-    elif len(remote_rev_history) == 0:
-        return local_history, set()
-    elif local_rev_history[-1] in remote_history:
-        return set(), _after(remote_rev_history, local_rev_history)
-    elif remote_rev_history[-1] in local_history:
-        return _after(local_rev_history, remote_rev_history), set()
-    else:
+
+
+def _enumerate_mainline(ancestry, graph, tip_revno, tip):
+    """Enumerate the mainline revisions for these revisions.
+
+    :param ancestry: A set of revisions that we care about
+    :param graph: A Graph which lets us find the parents for a revision
+    :param tip_revno: The revision number for the tip revision
+    :param tip: The tip of mainline
+    :return: [(revno, revision_id)] for all revisions in ancestry that
+        are left-hand parents from tip, or None if ancestry is None.
+    """
+    if ancestry is None:
         return None
-
-def _after(larger_history, smaller_history):
-    return set(larger_history[larger_history.index(smaller_history[-1])+1:])
-
-def _get_history(branch, progress, label, step):
-    progress.update('%s history' % label, step, 5)
-    rev_history = branch.revision_history()
-    rev_history_map = dict(
-        [(rev, rev_history.index(rev) + 1)
-         for rev in rev_history])
-    return rev_history, rev_history_map
-
-def _get_ancestry(repository, progress, label, step, rev_history):
-    progress.update('%s ancestry' % label, step, 5)
-    if len(rev_history) > 0:
-        ancestry = set(repository.get_ancestry(rev_history[-1],
-                       topo_sorted=False))
+    if not ancestry: #Empty ancestry, no need to do any work
+        return []
+
+    # Optionally, we could make 1 call to graph.get_parent_map with all
+    # ancestors. However that will often check many more parents than we
+    # actually need, and the Graph is likely to already have the parents cached
+    # anyway.
+    mainline = []
+    cur = tip
+    cur_revno = tip_revno
+    while cur in ancestry:
+        parent_map = graph.get_parent_map([cur])
+        parents = parent_map.get(cur)
+        if not parents:
+            break # Ghost, we are done
+        mainline.append((cur_revno, cur))
+        cur = parents[0]
+        cur_revno -= 1
+    mainline.reverse()
+    return mainline
+
+
+def _find_unmerged(local_branch, remote_branch, restrict):
+    """See find_unmerged.
+
+    The branches should already be locked before entering.
+    """
+    local_revno, local_revision_id = local_branch.last_revision_info()
+    remote_revno, remote_revision_id = remote_branch.last_revision_info()
+    if local_revno == remote_revno and local_revision_id == remote_revision_id:
+        # A simple shortcut when the tips are at the same point
+        return [], []
+    graph = local_branch.repository.get_graph(
+                remote_branch.repository)
+    if restrict == 'remote':
+        local_extra = None
+        remote_extra = graph.find_unique_ancestors(
+            remote_revision_id, [local_revision_id])
+    elif restrict == 'local':
+        remote_extra = None
+        local_extra = graph.find_unique_ancestors(
+            local_revision_id, [remote_revision_id])
     else:
-        ancestry = set()
-    return ancestry
-    
+        if restrict != 'all':
+            raise ValueError('param restrict not one of "all", "local",'
+                             ' "remote": %r' % (restrict,))
+        local_extra, remote_extra = graph.find_difference(
+            local_revision_id, remote_revision_id)
+    # Now that we have unique ancestors, compute just the mainline, and
+    # generate revnos for them.
+    local_mainline = _enumerate_mainline(local_extra, graph, local_revno,
+                                         local_revision_id)
+    remote_mainline = _enumerate_mainline(remote_extra, graph, remote_revno,
+                                          remote_revision_id)
+    return local_mainline, remote_mainline
+
 
 def sorted_revisions(revisions, history_map):
     revisions = [(history_map[r],r) for r in revisions]

=== modified file 'bzrlib/tests/blackbox/test_missing.py'
--- a/bzrlib/tests/blackbox/test_missing.py	2007-08-08 12:48:16 +0000
+++ b/bzrlib/tests/blackbox/test_missing.py	2008-05-20 18:28:59 +0000
@@ -77,9 +77,8 @@
         lines2 = self.run_bzr('missing ../b --mine-only', retcode=1)[0]
         lines2 = lines2.splitlines()
         self.assertEqual(lines, lines2)
-        lines3 = self.run_bzr('missing ../b --theirs-only', retcode=1)[0]
-        lines3 = lines3.splitlines()
-        self.assertEqual(0, len(lines3))
+        lines3 = self.run_bzr('missing ../b --theirs-only', retcode=0)[0]
+        self.assertEqualDiff('Other branch is up to date.\n', lines3)
 
         # relative to a, missing the 'merge' commit 
         os.chdir('../b')
@@ -89,9 +88,8 @@
         lines2 = self.run_bzr('missing ../a --theirs-only', retcode=1)[0]
         lines2 = lines2.splitlines()
         self.assertEqual(lines, lines2)
-        lines3 = self.run_bzr('missing ../a --mine-only', retcode=1)[0]
-        lines3 = lines3.splitlines()
-        self.assertEqual(0, len(lines3))
+        lines3 = self.run_bzr('missing ../a --mine-only', retcode=0)[0]
+        self.assertEqualDiff('This branch is up to date.\n', lines3)
         lines4 = self.run_bzr('missing ../a --short', retcode=1)[0]
         lines4 = lines4.splitlines()
         self.assertEqual(4, len(lines4))
@@ -109,9 +107,22 @@
         self.assertEqual("modified:", lines8[-2])
         self.assertEqual("  a", lines8[-1])
 
+        os.chdir('../a')
+        self.assertEqualDiff('Other branch is up to date.\n',
+                             self.run_bzr('missing ../b --theirs-only')[0])
+
         # after a pull we're back on track
         b_tree.pull(a_branch)
-        self.assertEqual("Branches are up to date.\n", self.run_bzr('missing ../a')[0])
+        self.assertEqualDiff("Branches are up to date.\n",
+                             self.run_bzr('missing ../b')[0])
+        os.chdir('../b')
+        self.assertEqualDiff('Branches are up to date.\n',
+                             self.run_bzr('missing ../a')[0])
+        # If you supply mine or theirs you only know one side is up to date
+        self.assertEqualDiff('This branch is up to date.\n',
+                             self.run_bzr('missing ../a --mine-only')[0])
+        self.assertEqualDiff('Other branch is up to date.\n',
+                             self.run_bzr('missing ../a --theirs-only')[0])
 
     def test_missing_check_last_location(self):
         # check that last location shown as filepath not file URL

=== modified file 'bzrlib/tests/test_missing.py'
--- a/bzrlib/tests/test_missing.py	2008-05-02 07:31:24 +0000
+++ b/bzrlib/tests/test_missing.py	2008-05-19 21:35:52 +0000
@@ -17,8 +17,11 @@
 import os
 
 
+from bzrlib import (
+    missing,
+    tests,
+    )
 from bzrlib.missing import (
-    find_unmerged,
     iter_log_revisions,
     )
 from bzrlib.tests import TestCaseWithTransport
@@ -27,6 +30,10 @@
 
 class TestMissing(TestCaseWithTransport):
 
+    def assertUnmerged(self, expected, source, target, restrict='all'):
+        unmerged = missing.find_unmerged(source, target, restrict=restrict)
+        self.assertEqual(expected, unmerged)
+            
     def test_find_unmerged(self):
         original_tree = self.make_branch_and_tree('original')
         original = original_tree.branch
@@ -34,26 +41,26 @@
         puller = puller_tree.branch
         merger_tree = self.make_branch_and_tree('merger')
         merger = merger_tree.branch
-        self.assertEqual(find_unmerged(original, puller), ([], []))
+        self.assertUnmerged(([], []), original, puller)
         original_tree.commit('a', rev_id='a')
-        self.assertEqual(find_unmerged(original, puller), ([(1, u'a')], []))
+        self.assertUnmerged(([(1, 'a')], []), original, puller)
         puller_tree.pull(original)
-        self.assertEqual(find_unmerged(original, puller), ([], []))
+        self.assertUnmerged(([], []), original, puller)
         merger_tree.pull(original)
         original_tree.commit('b', rev_id='b')
         original_tree.commit('c', rev_id='c')
-        self.assertEqual(find_unmerged(original, puller), ([(2, u'b'), 
-                                                            (3, u'c')], []))
+        self.assertUnmerged(([(2, 'b'), (3, 'c')], []),
+                            original, puller)
 
         puller_tree.pull(original)
-        self.assertEqual(find_unmerged(original, puller), ([], []))
-        self.assertEqual(find_unmerged(original, merger), ([(2, u'b'), 
-                                                            (3, u'c')], []))
+        self.assertUnmerged(([], []), original, puller)
+        self.assertUnmerged(([(2, 'b'), (3, 'c')], []),
+                            original, merger)
         merger_tree.merge_from_branch(original)
-        self.assertEqual(find_unmerged(original, merger), ([(2, u'b'), 
-                                                            (3, u'c')], []))
+        self.assertUnmerged(([(2, 'b'), (3, 'c')], []),
+                            original, merger)
         merger_tree.commit('d', rev_id='d')
-        self.assertEqual(find_unmerged(original, merger), ([], [(2, 'd')]))
+        self.assertUnmerged(([], [(2, 'd')]), original, merger)
 
     def test_iter_log_revisions(self):
         base_tree = self.make_branch_and_tree('base')
@@ -76,8 +83,8 @@
         child_tree.rename_one('b', 'c')
         child_tree.commit('rename b=>c', rev_id='c-5')
 
-        base_extra, child_extra = find_unmerged(base_tree.branch,
-                                                child_tree.branch)
+        base_extra, child_extra = missing.find_unmerged(base_tree.branch,
+                                                        child_tree.branch)
         results = list(iter_log_revisions(base_extra, 
                             base_tree.branch.repository,
                             verbose=True))
@@ -123,3 +130,63 @@
         self.assertEqual([('b', 'c', 'b-id', 'file', False, False)],
                          delta3.renamed)
         self.assertEqual([], delta3.modified)
+
+
+class TestFindUnmerged(tests.TestCaseWithTransport):
+
+    def assertUnmerged(self, local, remote, local_branch, remote_branch,
+                       restrict):
+        """Check the output of find_unmerged_mainline_revisions"""
+        local_extra, remote_extra = missing.find_unmerged(
+                                        local_branch, remote_branch, restrict)
+        self.assertEqual(local, local_extra)
+        self.assertEqual(remote, remote_extra)
+
+    def test_same_branch(self):
+        tree = self.make_branch_and_tree('tree')
+        rev1 = tree.commit('one')
+        tree.lock_read()
+        self.addCleanup(tree.unlock)
+        self.assertUnmerged([], [], tree.branch, tree.branch, 'all')
+
+    def test_one_ahead(self):
+        tree = self.make_branch_and_tree('tree')
+        rev1 = tree.commit('one')
+        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
+        rev2 = tree2.commit('two')
+        tree.lock_read()
+        self.addCleanup(tree.unlock)
+        tree2.lock_read()
+        self.addCleanup(tree2.unlock)
+        self.assertUnmerged([], [(2, rev2)], tree.branch, tree2.branch, 'all')
+        self.assertUnmerged([(2, rev2)], [], tree2.branch, tree.branch, 'all')
+
+    def test_restrict(self):
+        tree = self.make_branch_and_tree('tree')
+        rev1 = tree.commit('one')
+        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
+        rev2 = tree2.commit('two')
+        tree.lock_read()
+        self.addCleanup(tree.unlock)
+        tree2.lock_read()
+        self.addCleanup(tree2.unlock)
+        self.assertUnmerged([], [(2, rev2)], tree.branch, tree2.branch, 'all')
+        self.assertUnmerged([], None, tree.branch, tree2.branch, 'local')
+        self.assertUnmerged(None, [(2, rev2)], tree.branch, tree2.branch,
+                                               'remote')
+
+    def test_merged(self):
+        tree = self.make_branch_and_tree('tree')
+        rev1 = tree.commit('one')
+        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
+        rev2 = tree2.commit('two')
+        rev3 = tree2.commit('three')
+        tree.merge_from_branch(tree2.branch)
+        rev4 = tree.commit('four')
+
+        tree.lock_read()
+        self.addCleanup(tree.unlock)
+        tree2.lock_read()
+        self.addCleanup(tree2.unlock)
+
+        self.assertUnmerged([(2, rev4)], [], tree.branch, tree2.branch, 'all')




More information about the bazaar-commits mailing list