summaryrefslogtreecommitdiff
path: root/bzrlib/missing.py
diff options
context:
space:
mode:
Diffstat (limited to 'bzrlib/missing.py')
-rw-r--r--bzrlib/missing.py228
1 files changed, 228 insertions, 0 deletions
diff --git a/bzrlib/missing.py b/bzrlib/missing.py
new file mode 100644
index 0000000..7a6dd83
--- /dev/null
+++ b/bzrlib/missing.py
@@ -0,0 +1,228 @@
+# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Display what revisions are missing in 'other' from 'this' and vice versa."""
+
+from __future__ import absolute_import
+
+from bzrlib import (
+ log,
+ symbol_versioning,
+ )
+import bzrlib.revision as _mod_revision
+
+
+def iter_log_revisions(revisions, revision_source, verbose, rev_tag_dict=None):
+ last_tree = revision_source.revision_tree(_mod_revision.NULL_REVISION)
+ last_rev_id = None
+
+ if rev_tag_dict is None:
+ rev_tag_dict = {}
+ for rev in revisions:
+ # We need the following for backward compatibilty (hopefully
+ # this will be deprecated soon :-/) -- vila 080911
+ if len(rev) == 2:
+ revno, rev_id = rev
+ merge_depth = 0
+ else:
+ revno, rev_id, merge_depth = rev
+ rev = revision_source.get_revision(rev_id)
+ if verbose:
+ delta = revision_source.get_revision_delta(rev_id)
+ else:
+ delta = None
+ yield log.LogRevision(rev, revno, merge_depth, delta=delta,
+ tags=rev_tag_dict.get(rev_id))
+
+
+def find_unmerged(local_branch, remote_branch, restrict='all',
+ include_merged=None, backward=False,
+ local_revid_range=None, remote_revid_range=None,
+ include_merges=symbol_versioning.DEPRECATED_PARAMETER):
+ """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.
+ :param include_merged: Show mainline revisions only if False,
+ all revisions otherwise.
+ :param backward: Show oldest versions first when True, newest versions
+ first when False.
+ :param local_revid_range: Revision-id range for filtering local_branch
+ revisions (lower bound, upper bound)
+ :param remote_revid_range: Revision-id range for filtering remote_branch
+ revisions (lower bound, upper bound)
+ :param include_merges: Deprecated historical alias for include_merged
+
+ :return: A list of [(revno, revision_id)] for the mainline revisions on
+ each side.
+ """
+ if symbol_versioning.deprecated_passed(include_merges):
+ symbol_versioning.warn(
+ 'include_merges was deprecated in 2.5.'
+ ' Use include_merged instead.',
+ DeprecationWarning, stacklevel=2)
+ if include_merged is None:
+ include_merged = include_merges
+ if include_merged is None:
+ include_merged = False
+ local_branch.lock_read()
+ try:
+ remote_branch.lock_read()
+ try:
+ return _find_unmerged(
+ local_branch, remote_branch, restrict=restrict,
+ include_merged=include_merged, backward=backward,
+ local_revid_range=local_revid_range,
+ remote_revid_range=remote_revid_range)
+ finally:
+ remote_branch.unlock()
+ finally:
+ local_branch.unlock()
+
+
+def _enumerate_mainline(ancestry, graph, tip_revno, tip, backward=True):
+ """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
+ :param backward: Show oldest versions first when True, newest versions
+ first when False.
+ :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
+ 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((str(cur_revno), cur))
+ cur = parents[0]
+ cur_revno -= 1
+ if not backward:
+ mainline.reverse()
+ return mainline
+
+
+def _enumerate_with_merges(branch, ancestry, graph, tip_revno, tip,
+ backward=True):
+ """Enumerate the revisions for the ancestry.
+
+ :param branch: The branch we care about
+ :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 the ancsetry
+ :param backward: Show oldest versions first when True, newest versions
+ first when False.
+ :return: [(revno, revision_id)] for all revisions in ancestry that
+ are parents from tip, or None if ancestry is None.
+ """
+ if ancestry is None:
+ return None
+ if not ancestry: #Empty ancestry, no need to do any work
+ return []
+
+ merge_sorted_revisions = branch.iter_merge_sorted_revisions()
+ # Now that we got the correct revnos, keep only the relevant
+ # revisions.
+ merge_sorted_revisions = [
+ # log.reverse_by_depth expects seq_num to be present, but it is
+ # stripped by iter_merge_sorted_revisions()
+ (0, revid, n, d, e) for revid, n, d, e in merge_sorted_revisions
+ if revid in ancestry]
+ if not backward:
+ merge_sorted_revisions = log.reverse_by_depth(merge_sorted_revisions)
+ revline = []
+ for seq, rev_id, merge_depth, revno, end_of_merge in merge_sorted_revisions:
+ revline.append(('.'.join(map(str, revno)), rev_id, merge_depth))
+ return revline
+
+
+def _filter_revs(graph, revs, revid_range):
+ if revid_range is None or revs is None:
+ return revs
+ return [rev for rev in revs
+ if graph.is_between(rev[1], revid_range[0], revid_range[1])]
+
+
+def _find_unmerged(local_branch, remote_branch, restrict,
+ include_merged, backward,
+ local_revid_range=None, remote_revid_range=None):
+ """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:
+ 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)
+ if include_merged:
+ locals = _enumerate_with_merges(local_branch, local_extra,
+ graph, local_revno,
+ local_revision_id, backward)
+ remotes = _enumerate_with_merges(remote_branch, remote_extra,
+ graph, remote_revno,
+ remote_revision_id, backward)
+ else:
+ # Now that we have unique ancestors, compute just the mainline, and
+ # generate revnos for them.
+ locals = _enumerate_mainline(local_extra, graph, local_revno,
+ local_revision_id, backward)
+ remotes = _enumerate_mainline(remote_extra, graph, remote_revno,
+ remote_revision_id, backward)
+ return _filter_revs(graph, locals, local_revid_range), _filter_revs(graph,
+ remotes, remote_revid_range)
+
+
+def sorted_revisions(revisions, history_map):
+ revisions = [(history_map[r],r) for r in revisions]
+ revisions.sort()
+ return revisions