Rev 1: Put together a plugin to do the minimal work necessary in http://bzr.arbash-meinel.com/plugins/file_log

John Arbash Meinel john at arbash-meinel.com
Thu Sep 18 16:15:04 BST 2008


At http://bzr.arbash-meinel.com/plugins/file_log

------------------------------------------------------------
revno: 1
revision-id: john at arbash-meinel.com-20080918151504-959o091bpv2kxfgn
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: file_log
timestamp: Thu 2008-09-18 10:15:04 -0500
message:
  Put together a plugin to do the minimal work necessary
  to make 'bzr log file' work and be fast.
-------------- next part --------------
=== added file '__init__.py'
--- a/__init__.py	1970-01-01 00:00:00 +0000
+++ b/__init__.py	2008-09-18 15:15:04 +0000
@@ -0,0 +1,75 @@
+# Copyright (C) 2008 Canonical Development 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Display the log for a single file."""
+
+from bzrlib import (
+    commands,
+    errors,
+    )
+
+class cmd_file_log(commands.Command):
+    """Show the log of a single file.
+
+    This is not quite the same as "bzr log file", as it only shows revisions
+    which modified the file, and not revisions which then merged those changes.
+    It also does not yet support revision ranges, etc.
+    """
+
+    takes_args = ['filename']
+    takes_options = ['log-format']
+
+    def run(self, filename, log_format=None):
+        import file_log
+        tree, relpath = WorkingTree.open_containing(filename)
+        tree.lock_read()
+        try:
+            file_id = tree.path2id(relpath)
+            if file_id is None:
+                raise BzrCommandError('"%s" is not a versioned file'
+                                      % (filename,))
+
+            # XXX: There should be a more direct way of just grabbing the
+            #      last-modified revision from a tree
+            base_tree = tree.basis_tree()
+            base_tree.lock_read()
+            try:
+                if file_id not in base_tree.inventory:
+                    raise BzrCommandError('"%s" is a newly added file'
+                                          % (filename,))
+                revision_id = base_tree.inventory[file_id].revision
+            finally:
+                base_tree.unlock()
+
+            if log_format is None:
+                log_format = log.log_formatter_registry.get_default(tree.branch)
+            lf = log_format(show_ids=False, to_file=self.outf,
+                            show_timezone='original')
+            file_log.log_one_file(tree, file_id, revision_id, lf)
+        finally:
+            tree.unlock()
+
+commands.register_command(cmd_file_log)
+
+
+def load_tests(basic_tests, module, loader):
+    suite = loader.suiteClass()
+
+    test_files = [__name__ + '.' + x for x in [
+        '',
+    ]]
+    suite.addTests(loader.loadTestsFromModuleNames(test_files))
+    return suite

=== added file 'file_log.py'
--- a/file_log.py	1970-01-01 00:00:00 +0000
+++ b/file_log.py	2008-09-18 15:15:04 +0000
@@ -0,0 +1,62 @@
+# Copyright (C) 2008 Canonical Development 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Create a log for a single file."""
+
+from bzrlib import (
+    log,
+    errors,
+    )
+
+
+def log_one_file(tree, file_id, revision_id, log_formatter):
+    """Print out the revision graph for a single file.
+
+    :param tree: A WorkingTree with the given file_id present.
+        The tree should already be locked.
+    :param file_id: The file to log
+    :param revision_id: The tip revision to start from. Typically this is
+        tree.basis_tree().inventory[file_id].revision
+    :param log_formatter: An instance of LogFormatter, which we will use to
+        print out the revision information.
+    """
+    from bzrlib import graph
+
+    file_graph = graph.Graph(tree.branch.repository.texts)
+    tip_key = (file_id, revision_id)
+    # TODO: Topological sorting?
+    # file_ancestry = dict(file_graph.iter_ancestry([tip_key]))
+    # TODO: Remove nodes which are ghosts, and references to ghosts
+    # ghosts = set(key for key, parents in file_ancestry.iteritems()
+    #                   if parents is None)
+    # TODO: Batch up revision_ids to be logged to grab them multiples at a
+    #       time.
+
+    # This iterates the per-file graph, and collects the revisions which are
+    # not considered ghosts.
+    # TODO: Use the whole-tree graph to compute dotted revnos, etc.
+    revisions = [(key[1], None, 0)
+                 for key, parents in file_graph.iter_ancestry([tip_key])
+                  if parents is not None]
+    revision_iterator = log.make_log_rev_iterator(tree.branch, revisions,
+                                                  generate_delta=False,
+                                                  search=None)
+    rev_tag_dict = {}
+    for revs in revision_iterator:
+        for (rev_id, revno, merge_depth), rev, delta in revs:
+            lr = LogRevision(rev, revno, merge_depth, delta,
+                             rev_tag_dict.get(rev_id))
+            log_formatter.log_revision(lr)



More information about the bazaar-commits mailing list