Rev 2580: (Kent Gibson) Fix bug #4663: bzr log -r to support selecting merge revisions. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Wed Jul 4 05:10:14 BST 2007
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 2580
revision-id: pqm at pqm.ubuntu.com-20070704041011-o8aw2m812hzhz8yr
parent: pqm at pqm.ubuntu.com-20070704024013-cwx0ld720nvdfb8q
parent: andrew.bennetts at canonical.com-20070704031237-faouhtp7ckc4m69t
committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2007-07-04 05:10:11 +0100
message:
(Kent Gibson) Fix bug #4663: bzr log -r to support selecting merge revisions.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/log.py log.py-20050505065812-c40ce11702fe5fb1
bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
bzrlib/tests/test_log.py testlog.py-20050728115707-1a514809d7d49309
------------------------------------------------------------
revno: 2578.2.1
merged: andrew.bennetts at canonical.com-20070704031237-faouhtp7ckc4m69t
parent: pqm at pqm.ubuntu.com-20070703184919-6iz3vo2rx42smf25
parent: warthog618 at gmail.com-20070504104926-dmz6x2gw7ydb8i7p
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: kent-bug-4663
timestamp: Wed 2007-07-04 13:12:37 +1000
message:
Merge Kent Gibson's fix for bug #4663, resolving conflicts.
------------------------------------------------------------
revno: 2466.8.1.3.1
merged: warthog618 at gmail.com-20070504104926-dmz6x2gw7ydb8i7p
parent: warthog618 at gmail.com-20070429034630-p3xbitz2xyl6pbie
committer: Kent Gibson <warthog618 at gmail.com>
branch nick: log_range
timestamp: Fri 2007-05-04 18:49:26 +0800
message:
Fix ``bzr log -r`` to support selecting merge revisions.
=== modified file 'NEWS'
--- a/NEWS 2007-07-03 18:00:34 +0000
+++ b/NEWS 2007-07-04 03:12:37 +0000
@@ -20,6 +20,10 @@
via a proxy to enable SSL tunneling.
(Vincent Ladeuil, #120678)
+ * Fix ``bzr log -r`` to support selecting merge revisions, both
+ individually and as part of revision ranges.
+ (Kent Gibson, #4663)
+
IMPROVEMENTS:
* The --lsprof-file option now dumps a text rendering of the profiling
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2007-07-04 01:53:26 +0000
+++ b/bzrlib/builtins.py 2007-07-04 04:10:11 +0000
@@ -1640,7 +1640,7 @@
rev1 = None
rev2 = None
elif len(revision) == 1:
- rev1 = rev2 = revision[0].in_history(b).revno
+ rev1 = rev2 = revision[0].in_history(b)
elif len(revision) == 2:
if revision[1].get_branch() != revision[0].get_branch():
# b is taken from revision[0].get_branch(), and
@@ -1649,27 +1649,12 @@
raise errors.BzrCommandError(
"Log doesn't accept two revisions in different"
" branches.")
- if revision[0].spec is None:
- # missing begin-range means first revision
- rev1 = 1
- else:
- rev1 = revision[0].in_history(b).revno
-
- if revision[1].spec is None:
- # missing end-range means last known revision
- rev2 = b.revno()
- else:
- rev2 = revision[1].in_history(b).revno
+ rev1 = revision[0].in_history(b)
+ rev2 = revision[1].in_history(b)
else:
raise errors.BzrCommandError(
'bzr log --revision takes one or two values.')
- # By this point, the revision numbers are converted to the +ve
- # form if they were supplied in the -ve form, so we can do
- # this comparison in relative safety
- if rev1 > rev2:
- (rev2, rev1) = (rev1, rev2)
-
if log_format is None:
log_format = log.log_formatter_registry.get_default(b)
=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py 2007-07-02 15:01:18 +0000
+++ b/bzrlib/log.py 2007-07-04 03:12:37 +0000
@@ -59,6 +59,9 @@
symbol_versioning,
)
import bzrlib.errors as errors
+from bzrlib.revisionspec import(
+ RevisionInfo
+ )
from bzrlib.symbol_versioning import (
deprecated_method,
zero_eleven,
@@ -115,7 +118,6 @@
revno += 1
-
def _enumerate_history(branch):
rh = []
revno = 1
@@ -202,32 +204,14 @@
else:
searchRE = None
- which_revs = _enumerate_history(branch)
-
- if start_revision is None:
- start_revision = 1
- else:
- branch.check_real_revno(start_revision)
-
- if end_revision is None:
- end_revision = len(which_revs)
- else:
- branch.check_real_revno(end_revision)
-
- # list indexes are 0-based; revisions are 1-based
- cut_revs = which_revs[(start_revision-1):(end_revision)]
- if not cut_revs:
+ mainline_revs, rev_nos, start_rev_id, end_rev_id = \
+ _get_mainline_revs(branch, start_revision, end_revision)
+ if not mainline_revs:
return
- # convert the revision history to a dictionary:
- rev_nos = dict((k, v) for v, k in cut_revs)
-
- # override the mainline to look like the revision history.
- mainline_revs = [revision_id for index, revision_id in cut_revs]
- if cut_revs[0][0] == 1:
- mainline_revs.insert(0, None)
- else:
- mainline_revs.insert(0, which_revs[start_revision-2][1])
+ if direction == 'reverse':
+ start_rev_id, end_rev_id = end_rev_id, start_rev_id
+
legacy_lf = getattr(lf, 'log_revision', None) is None
if legacy_lf:
# pre-0.17 formatters use show for mainline revisions.
@@ -250,13 +234,14 @@
False)
view_revs_iter = get_view_revisions(mainline_revs, rev_nos, branch,
direction, include_merges=generate_merge_revisions)
+ view_revisions = _filter_revision_range(list(view_revs_iter),
+ start_rev_id,
+ end_rev_id)
if specific_fileid:
- view_revisions = _get_revisions_touching_file_id(branch,
+ view_revisions = _filter_revisions_touching_file_id(branch,
specific_fileid,
mainline_revs,
- view_revs_iter)
- else:
- view_revisions = list(view_revs_iter)
+ view_revisions)
rev_tag_dict = {}
generate_tags = getattr(lf, 'supports_tags', False)
@@ -318,10 +303,125 @@
break
-def _get_revisions_touching_file_id(branch, file_id, mainline_revisions,
- view_revs_iter):
+def _get_mainline_revs(branch, start_revision, end_revision):
+ """Get the mainline revisions from the branch.
+
+ Generates the list of mainline revisions for the branch.
+
+ :param branch: The branch containing the revisions.
+
+ :param start_revision: The first revision to be logged.
+ For backwards compatibility this may be a mainline integer revno,
+ but for merge revision support a RevisionInfo is expected.
+
+ :param end_revision: The last revision to be logged.
+ For backwards compatibility this may be a mainline integer revno,
+ but for merge revision support a RevisionInfo is expected.
+
+ :return: A (mainline_revs, rev_nos, start_rev_id, end_rev_id) tuple.
+ """
+ which_revs = _enumerate_history(branch)
+ if not which_revs:
+ return None, None, None, None
+
+ # For mainline generation, map start_revision and end_revision to
+ # mainline revnos. If the revision is not on the mainline choose the
+ # appropriate extreme of the mainline instead - the extra will be
+ # filtered later.
+ # Also map the revisions to rev_ids, to be used in the later filtering
+ # stage.
+ start_rev_id = None
+ if start_revision is None:
+ start_revno = 1
+ else:
+ if isinstance(start_revision,RevisionInfo):
+ start_rev_id = start_revision.rev_id
+ start_revno = start_revision.revno or 1
+ else:
+ branch.check_real_revno(start_revision)
+ start_revno = start_revision
+
+ end_rev_id = None
+ if end_revision is None:
+ end_revno = len(which_revs)
+ else:
+ if isinstance(end_revision,RevisionInfo):
+ end_rev_id = end_revision.rev_id
+ end_revno = end_revision.revno or len(which_revs)
+ else:
+ branch.check_real_revno(end_revision)
+ end_revno = end_revision
+
+ if start_revno > end_revno:
+ from bzrlib.errors import BzrCommandError
+ raise BzrCommandError("Start revision must be older than "
+ "the end revision.")
+
+ # list indexes are 0-based; revisions are 1-based
+ cut_revs = which_revs[(start_revno-1):(end_revno)]
+ if not cut_revs:
+ return None, None, None, None
+
+ # convert the revision history to a dictionary:
+ rev_nos = dict((k, v) for v, k in cut_revs)
+
+ # override the mainline to look like the revision history.
+ mainline_revs = [revision_id for index, revision_id in cut_revs]
+ if cut_revs[0][0] == 1:
+ mainline_revs.insert(0, None)
+ else:
+ mainline_revs.insert(0, which_revs[start_revno-2][1])
+ return mainline_revs, rev_nos, start_rev_id, end_rev_id
+
+
+def _filter_revision_range(view_revisions, start_rev_id, end_rev_id):
+ """Filter view_revisions based on revision ranges.
+
+ :param view_revisions: A list of (revision_id, dotted_revno, merge_depth)
+ tuples to be filtered.
+
+ :param start_rev_id: If not NONE specifies the first revision to be logged.
+ If NONE then all revisions up to the end_rev_id are logged.
+
+ :param end_rev_id: If not NONE specifies the last revision to be logged.
+ If NONE then all revisions up to the end of the log are logged.
+
+ :return: The filtered view_revisions.
+ """
+ if start_rev_id or end_rev_id:
+ revision_ids = [r for r, n, d in view_revisions]
+ if start_rev_id:
+ start_index = revision_ids.index(start_rev_id)
+ else:
+ start_index = 0
+ if start_rev_id == end_rev_id:
+ end_index = start_index
+ else:
+ if end_rev_id:
+ end_index = revision_ids.index(end_rev_id)
+ else:
+ end_index = len(view_revisions) - 1
+ # To include the revisions merged into the last revision,
+ # extend end_rev_id down to, but not including, the next rev
+ # with the same or lesser merge_depth
+ end_merge_depth = view_revisions[end_index][2]
+ try:
+ for index in xrange(end_index+1, len(view_revisions)+1):
+ if view_revisions[index][2] <= end_merge_depth:
+ end_index = index - 1
+ break
+ except IndexError:
+ # if the search falls off the end then log to the end as well
+ end_index = len(view_revisions) - 1
+ view_revisions = view_revisions[start_index:end_index+1]
+ return view_revisions
+
+
+def _filter_revisions_touching_file_id(branch, file_id, mainline_revisions,
+ view_revs_iter):
"""Return the list of revision ids which touch a given file id.
+ The function filters view_revisions and returns a subset.
This includes the revisions which directly change the file id,
and the revisions which merge these changes. So if the
revision graph is::
=== modified file 'bzrlib/tests/blackbox/test_log.py'
--- a/bzrlib/tests/blackbox/test_log.py 2007-07-02 15:01:18 +0000
+++ b/bzrlib/tests/blackbox/test_log.py 2007-07-04 03:12:37 +0000
@@ -93,6 +93,12 @@
log = self.run_bzr("log -r 1..3")[0]
self.assertEqualDiff(self.full_log, log)
+ def test_log_reversed_revspecs(self):
+ self._prepare()
+ self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
+ 'the end revision.\n',),
+ 'log', '-r3..1')
+
def test_log_revno_n_path(self):
os.mkdir('branch1')
os.chdir('branch1')
@@ -148,6 +154,8 @@
self.run_bzr('commit -m merge_branch_1')
log = self.run_bzr("log -r-1")[0]
self.assertContainsRe(log, r' tags: tag1')
+ log = self.run_bzr("log -r3.1.1")[0]
+ self.assertContainsRe(log, r' tags: tag1')
def test_log_limit(self):
self._prepare()
@@ -156,7 +164,157 @@
self.assertTrue('revno: 2\n' in log)
self.assertTrue('revno: 3\n' in log)
-
+class TestLogMerges(ExternalBase):
+
+ def _prepare(self):
+ self.build_tree(['parent/'])
+ self.run_bzr('init', 'parent')
+ self.run_bzr('commit', '-m', 'first post', '--unchanged', 'parent')
+ self.run_bzr('branch', 'parent', 'child')
+ self.run_bzr('commit', '-m', 'branch 1', '--unchanged', 'child')
+ self.run_bzr('branch', 'child', 'smallerchild')
+ self.run_bzr('commit', '-m', 'branch 2', '--unchanged', 'smallerchild')
+ os.chdir('child')
+ self.run_bzr('merge', '../smallerchild')
+ self.run_bzr('commit', '-m', 'merge branch 2')
+ os.chdir('../parent')
+ self.run_bzr('merge', '../child')
+ self.run_bzr('commit', '-m', 'merge branch 1')
+
+ def test_merges_are_indented_by_level(self):
+ self._prepare()
+ out,err = self.run_bzr('log')
+ # the log will look something like:
+# self.assertEqual("""\
+#------------------------------------------------------------
+#revno: 2
+#committer: Robert Collins <foo at example.com>
+#branch nick: parent
+#timestamp: Tue 2006-03-28 22:31:40 +1100
+#message:
+# merge branch 1
+# ------------------------------------------------------------
+# revno: 1.1.2
+# committer: Robert Collins <foo at example.com>
+# branch nick: child
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# merge branch 2
+# ------------------------------------------------------------
+# revno: 1.1.1.1
+# committer: Robert Collins <foo at example.com>
+# branch nick: smallerchild
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# branch 2
+# ------------------------------------------------------------
+# revno: 1.1.1
+# committer: Robert Collins <foo at example.com>
+# branch nick: child
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# branch 1
+#------------------------------------------------------------
+#revno: 1
+#committer: Robert Collins <foo at example.com>
+#branch nick: parent
+#timestamp: Tue 2006-03-28 22:31:39 +1100
+#message:
+# first post
+#""", out)
+ # but we dont have a nice pattern matcher hooked up yet, so:
+ # we check for the indenting of the commit message and the
+ # revision numbers
+ self.assertTrue('revno: 2' in out)
+ self.assertTrue(' merge branch 1' in out)
+ self.assertTrue(' revno: 1.1.2' in out)
+ self.assertTrue(' merge branch 2' in out)
+ self.assertTrue(' revno: 1.1.1.1' in out)
+ self.assertTrue(' branch 2' in out)
+ self.assertTrue(' revno: 1.1.1' in out)
+ self.assertTrue(' branch 1' in out)
+ self.assertTrue('revno: 1\n' in out)
+ self.assertTrue(' first post' in out)
+ self.assertEqual('', err)
+
+ def test_merges_single_merge_rev(self):
+ self._prepare()
+ out,err = self.run_bzr('log', '-r1.1.2')
+ # the log will look something like:
+# self.assertEqual("""\
+# ------------------------------------------------------------
+# revno: 1.1.2
+# committer: Robert Collins <foo at example.com>
+# branch nick: child
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# merge branch 2
+# ------------------------------------------------------------
+# revno: 1.1.1.1
+# committer: Robert Collins <foo at example.com>
+# branch nick: smallerchild
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# branch 2
+#""", out)
+ # but we dont have a nice pattern matcher hooked up yet, so:
+ # we check for the indenting of the commit message and the
+ # revision numbers
+ self.assertTrue('revno: 2' not in out)
+ self.assertTrue(' merge branch 1' not in out)
+ self.assertTrue(' revno: 1.1.2' in out)
+ self.assertTrue(' merge branch 2' in out)
+ self.assertTrue(' revno: 1.1.1.1' in out)
+ self.assertTrue(' branch 2' in out)
+ self.assertTrue(' revno: 1.1.1\n' not in out)
+ self.assertTrue(' branch 1' not in out)
+ self.assertTrue('revno: 1\n' not in out)
+ self.assertTrue(' first post' not in out)
+ self.assertEqual('', err)
+
+ def test_merges_partial_range(self):
+ self._prepare()
+ out,err = self.run_bzr('log', '-r1.1.1..1.1.2')
+ # the log will look something like:
+# self.assertEqual("""\
+# ------------------------------------------------------------
+# revno: 1.1.2
+# committer: Robert Collins <foo at example.com>
+# branch nick: child
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# merge branch 2
+# ------------------------------------------------------------
+# revno: 1.1.1.1
+# committer: Robert Collins <foo at example.com>
+# branch nick: smallerchild
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# branch 2
+# ------------------------------------------------------------
+# revno: 1.1.1
+# committer: Robert Collins <foo at example.com>
+# branch nick: child
+# timestamp: Tue 2006-03-28 22:31:40 +1100
+# message:
+# branch 1
+#""", out)
+ # but we dont have a nice pattern matcher hooked up yet, so:
+ # we check for the indenting of the commit message and the
+ # revision numbers
+ self.assertTrue('revno: 2' not in out)
+ self.assertTrue(' merge branch 1' not in out)
+ self.assertTrue(' revno: 1.1.2' in out)
+ self.assertTrue(' merge branch 2' in out)
+ self.assertTrue(' revno: 1.1.1.1' in out)
+ self.assertTrue(' branch 2' in out)
+ self.assertTrue(' revno: 1.1.1' in out)
+ self.assertTrue(' branch 1' in out)
+ self.assertTrue('revno: 1\n' not in out)
+ self.assertTrue(' first post' not in out)
+ self.assertEqual('', err)
+
+
class TestLogEncodings(TestCaseInTempDir):
_mu = u'\xb5'
@@ -285,7 +443,7 @@
os.chdir('parent')
self.run_bzr('merge', '../child')
self.run_bzr('commit', '-m', 'merge child branch')
-
+
log = self.run_bzr('log', 'file1')[0]
self.assertContainsRe(log, 'revno: 1\n')
self.assertNotContainsRe(log, 'revno: 2\n')
@@ -304,3 +462,27 @@
self.assertContainsRe(log, 'revno: 3\n')
self.assertNotContainsRe(log, 'revno: 3.1.1\n')
self.assertNotContainsRe(log, 'revno: 4\n')
+ log = self.run_bzr('log', '-r3.1.1', 'file2')[0]
+ self.assertNotContainsRe(log, 'revno: 1\n')
+ self.assertNotContainsRe(log, 'revno: 2\n')
+ self.assertNotContainsRe(log, 'revno: 3\n')
+ self.assertContainsRe(log, 'revno: 3.1.1\n')
+ self.assertNotContainsRe(log, 'revno: 4\n')
+ log = self.run_bzr('log', '-r4', 'file2')[0]
+ self.assertNotContainsRe(log, 'revno: 1\n')
+ self.assertNotContainsRe(log, 'revno: 2\n')
+ self.assertNotContainsRe(log, 'revno: 3\n')
+ self.assertContainsRe(log, 'revno: 3.1.1\n')
+ self.assertContainsRe(log, 'revno: 4\n')
+ log = self.run_bzr('log', '-r3..', 'file2')[0]
+ self.assertNotContainsRe(log, 'revno: 1\n')
+ self.assertNotContainsRe(log, 'revno: 2\n')
+ self.assertNotContainsRe(log, 'revno: 3\n')
+ self.assertContainsRe(log, 'revno: 3.1.1\n')
+ self.assertContainsRe(log, 'revno: 4\n')
+ log = self.run_bzr('log', '-r..3', 'file2')[0]
+ self.assertNotContainsRe(log, 'revno: 1\n')
+ self.assertContainsRe(log, 'revno: 2\n')
+ self.assertNotContainsRe(log, 'revno: 3\n')
+ self.assertNotContainsRe(log, 'revno: 3.1.1\n')
+ self.assertNotContainsRe(log, 'revno: 4\n')
=== modified file 'bzrlib/tests/test_log.py'
--- a/bzrlib/tests/test_log.py 2007-07-02 15:01:18 +0000
+++ b/bzrlib/tests/test_log.py 2007-07-04 03:12:37 +0000
@@ -710,9 +710,9 @@
self.assertEqual([], delta.removed)
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
- """Make sure _get_revisions_touching_file_id returns the right values.
+ """Make sure _filter_revisions_touching_file_id returns the right values.
- Get the return value from _get_revisions_touching_file_id and make
+ Get the return value from _filter_revisions_touching_file_id and make
sure they are correct.
"""
# The api for _get_revisions_touching_file_id is a little crazy,
@@ -722,9 +722,11 @@
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
'reverse', True)
- actual_revs = log._get_revisions_touching_file_id(tree.branch, file_id,
- mainline,
- view_revs_iter)
+ actual_revs = log._filter_revisions_touching_file_id(
+ tree.branch,
+ file_id,
+ mainline,
+ list(view_revs_iter))
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
def test_file_id_f1(self):
More information about the bazaar-commits
mailing list