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