Rev 3604: Implement lookups into the current working tree for bzr annotate, fixing bug 3439. in http://people.ubuntu.com/~robertc/baz2.0/3429
Robert Collins
robertc at robertcollins.net
Tue Aug 5 06:41:16 BST 2008
At http://people.ubuntu.com/~robertc/baz2.0/3429
------------------------------------------------------------
revno: 3604
revision-id: robertc at robertcollins.net-20080805054110-9prc026f7yq5iom8
parent: pqm at pqm.ubuntu.com-20080805011407-wmq7130znc0e6c4x
committer: Robert Collins <robertc at robertcollins.net>
branch nick: 3429
timestamp: Tue 2008-08-05 15:41:10 +1000
message:
Implement lookups into the current working tree for bzr annotate, fixing bug 3439.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/annotate.py annotate.py-20050922133147-7c60541d2614f022
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/blackbox/test_annotate.py testannotate.py-20051013044000-457f44801bfa9d39
=== modified file 'NEWS'
--- a/NEWS 2008-08-04 22:41:18 +0000
+++ b/NEWS 2008-08-05 05:41:10 +0000
@@ -20,6 +20,11 @@
IMPROVEMENTS:
+ * ``bzr annotate`` will now include uncommitted changes from the local
+ working tree by default. Such uncommitted changes are given the
+ revision number they would get if a commit was done, followed with a
+ ? to indicate that its not actually known. (Robert Collins, #3439)
+
* ``bzr check`` can now be told which elements at a location it should
check. (Daniel Watkins)
=== modified file 'bzrlib/annotate.py'
--- a/bzrlib/annotate.py 2008-06-18 07:56:09 +0000
+++ b/bzrlib/annotate.py 2008-08-05 05:41:10 +0000
@@ -35,6 +35,7 @@
tsort,
)
from bzrlib.config import extract_email_address
+from bzrlib.revision import CURRENT_REVISION, Revision
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
@@ -57,21 +58,58 @@
to_file = sys.stdout
# Handle the show_ids case
- last_rev_id = None
- if show_ids:
- annotations = _annotations(branch.repository, file_id, rev_id)
- max_origin_len = max(len(origin) for origin, text in annotations)
- for origin, text in annotations:
- if full or last_rev_id != origin:
- this = origin
- else:
- this = ''
- to_file.write('%*s | %s' % (max_origin_len, this, text))
- last_rev_id = origin
- return
-
- # Calculate the lengths of the various columns
- annotation = list(_annotate_file(branch, rev_id, file_id))
+ annotations = _annotations(branch.repository, file_id, rev_id)
+ if show_ids:
+ return _show_id_annotations(annotations, to_file, full)
+
+ # Calculate the lengths of the various columns
+ annotation = list(_expand_annotations(annotations, branch))
+ _print_annotations(annotation, verbose, to_file, full)
+
+
+def annotate_file_tree(tree, file_id, to_file, verbose=False, full=False,
+ show_ids=False):
+ """Annotate file_id in a tree.
+
+ The tree should already be read_locked() when annotate_file_tree is called.
+
+ :param tree: The tree to look for revision numbers and history from.
+ :param file_id: The file_id to annotate.
+ :param to_file: The file to output the annotation to.
+ :param verbose: Show all details rather than truncating to ensure
+ reasonable text width.
+ :param full: XXXX Not sure what this does.
+ :param show_ids: Show revision ids in the annotation output.
+ """
+ rev_id = tree.last_revision()
+ branch = tree.branch
+
+ # Handle the show_ids case
+ annotations = list(tree.annotate_iter(file_id))
+ if show_ids:
+ return _show_id_annotations(annotations, to_file, full)
+
+ # Calculate the lengths of the various columns
+ current_rev = Revision(CURRENT_REVISION)
+ current_rev.parent_ids = tree.get_parent_ids()
+ current_rev.committer = tree.branch.get_config().username()
+ current_rev.message = "?"
+ current_rev.timestamp = round(time.time(), 3)
+ current_rev.timezone = osutils.local_time_offset()
+ # Should get pending tags/fixes etc - pending commit attributes.
+ annotation = list(_expand_annotations(annotations, tree.branch,
+ current_rev))
+ _print_annotations(annotation, verbose, to_file, full)
+
+
+def _print_annotations(annotation, verbose, to_file, full):
+ """Print annotations to to_file.
+
+ :param to_file: The file to output the annotation to.
+ :param verbose: Show all details rather than truncating to ensure
+ reasonable text width.
+ :param full: XXXX Not sure what this does.
+ """
if len(annotation) == 0:
max_origin_len = max_revno_len = max_revid_len = 0
else:
@@ -110,6 +148,19 @@
prevanno = anno
+def _show_id_annotations(annotations, to_file, full):
+ last_rev_id = None
+ max_origin_len = max(len(origin) for origin, text in annotations)
+ for origin, text in annotations:
+ if full or last_rev_id != origin:
+ this = origin
+ else:
+ this = ''
+ to_file.write('%*s | %s' % (max_origin_len, this, text))
+ last_rev_id = origin
+ return
+
+
def _annotations(repo, file_id, rev_id):
"""Return the list of (origin_revision_id, line_text) for a revision of a file in a repository."""
annotations = repo.texts.annotate((file_id, rev_id))
@@ -117,20 +168,29 @@
return [(key[-1], line) for (key, line) in annotations]
-def _annotate_file(branch, rev_id, file_id):
- """Yield the origins for each line of a file.
-
- This includes detailed information, such as the author name, and
- date string for the commit, rather than just the revision id.
+def _expand_annotations(annotations, branch, current_rev=None):
+ """Expand a a files annotations into command line UI ready tuples.
+
+ Each tuple includes detailed information, such as the author name, and date
+ string for the commit, rather than just the revision id.
+
+ :param annotations: The annotations to expand.
+ :param revision_id_to_revno: A map from id to revision numbers.
+ :param branch: A locked branch to query for revision details.
"""
+ repository = branch.repository
revision_id_to_revno = branch.get_revision_id_to_revno_map()
- annotations = _annotations(branch.repository, file_id, rev_id)
last_origin = None
revision_ids = set(o for o, t in annotations)
+ revisions = {}
+ if CURRENT_REVISION in revision_ids:
+ revision_id_to_revno[CURRENT_REVISION] = (
+ "%d?" % (branch.revno() + 1),)
+ revisions[CURRENT_REVISION] = current_rev
revision_ids = [o for o in revision_ids if
- branch.repository.has_revision(o)]
- revisions = dict((r.revision_id, r) for r in
- branch.repository.get_revisions(revision_ids))
+ repository.has_revision(o)]
+ revisions.update((r.revision_id, r) for r in
+ repository.get_revisions(revision_ids))
for origin, text in annotations:
text = text.rstrip('\r\n')
if origin == last_origin:
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2008-08-04 07:29:51 +0000
+++ b/bzrlib/builtins.py 2008-08-05 05:41:10 +0000
@@ -3498,7 +3498,7 @@
@display_command
def run(self, filename, all=False, long=False, revision=None,
show_ids=False):
- from bzrlib.annotate import annotate_file
+ from bzrlib.annotate import annotate_file, annotate_file_tree
wt, branch, relpath = \
bzrdir.BzrDir.open_containing_tree_or_branch(filename)
if wt is not None:
@@ -3520,8 +3520,14 @@
if file_id is None:
raise errors.NotVersionedError(filename)
file_version = tree.inventory[file_id].revision
- annotate_file(branch, file_version, file_id, long, all, self.outf,
- show_ids=show_ids)
+ if wt is not None and revision is None:
+ # If there is a tree and we're not annotating historical
+ # versions, annotate the working tree's content.
+ annotate_file_tree(wt, file_id, self.outf, long, all,
+ show_ids=show_ids)
+ else:
+ annotate_file(branch, file_version, file_id, long, all, self.outf,
+ show_ids=show_ids)
finally:
if wt is not None:
wt.unlock()
=== modified file 'bzrlib/tests/blackbox/test_annotate.py'
--- a/bzrlib/tests/blackbox/test_annotate.py 2007-12-29 18:55:20 +0000
+++ b/bzrlib/tests/blackbox/test_annotate.py 2008-08-05 05:41:10 +0000
@@ -147,6 +147,37 @@
' exactly 1 argument\n',
err)
+
+class TestSimpleAnnotate(TestCaseWithTransport):
+ """Annotate tests with no complex setup."""
+
+ def _setup_edited_file(self):
+ """Create a tree with a locally edited file."""
+ tree = self.make_branch_and_tree('.')
+ self.build_tree_contents([('file', 'foo\ngam\n')])
+ tree.add('file')
+ tree.commit('add file', committer="test at host", rev_id="1")
+ self.build_tree_contents([('file', 'foo\nbar\ngam\n')])
+ tree.branch.get_config().set_user_option('email', 'current at host2')
+
+ def test_annotate_edited_file(self):
+ tree = self._setup_edited_file()
+ out, err = self.run_bzr('annotate file')
+ self.assertEqual(
+ '1 test at ho | foo\n'
+ '2? current | bar\n'
+ '1 test at ho | gam\n',
+ out)
+
+ def test_annotate_edited_file_show_ids(self):
+ tree = self._setup_edited_file()
+ out, err = self.run_bzr('annotate file --show-ids')
+ self.assertEqual(
+ ' 1 | foo\n'
+ 'current: | bar\n'
+ ' 1 | gam\n',
+ out)
+
def test_annotate_empty_file(self):
tree = self.make_branch_and_tree('tree')
self.build_tree_contents([('tree/empty', '')])
More information about the bazaar-commits
mailing list