1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
# Copyright (C) 2004, 2005 Aaron Bentley
# <aaron.bentley@utoronto.ca>
#
# 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
from bzrlib.branch import Branch
from bzrlib.commands import Command
from bzrlib.errors import BzrCommandError
import os
import progress
from progress import show_progress
import patches
import difflib
import sys
def iter_anno_data(branch, file_id):
max_revno = branch.revno()
later_revision = max_revno
q = range(max_revno)
q.append(max_revno)
q.reverse()
next_tree = branch.working_tree()
later_text_sha1 = next_tree.get_file_sha1(file_id)
i = 0
for revno in q:
i += 1
cur_tree = branch.revision_tree(branch.get_rev_id(revno))
if file_id not in cur_tree.inventory:
text_sha1 = None
else:
text_sha1 = cur_tree.get_file_sha1(file_id)
if text_sha1 != later_text_sha1:
patch = get_patch(branch, cur_tree, next_tree, file_id)
next_tree = cur_tree
if revno == max_revno:
display_id = "Tree"
else:
display_id = str(revno+1)
yield display_id, patch.iter_inserted(), patch
later_revision = revno
later_text_sha1 = text_sha1
yield progress.Progress("revisions", i)
def get_patch(branch, old_tree, new_tree, file_id):
if file_id in old_tree.inventory:
old_file = old_tree.get_file(file_id).readlines()
else:
old_file = []
ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
return patches.parse_patch(ud)
class cmd_annotate(Command):
"""Show which revision added each line in a file"""
takes_args = ['filename']
def run(self, filename):
if not os.path.exists(filename):
raise BzrCommandError("The file %s does not exist." % filename)
branch,relpath = (Branch.open_containing(filename))
file_id = branch.working_tree().path2id(relpath)
if file_id is None:
raise BzrCommandError("The file %s is not versioned." % filename)
lines = branch.basis_tree().get_file(file_id)
total = branch.revno()
anno_d_iter = iter_anno_data(branch, file_id)
progress_bar = progress.ProgressBar()
try:
for result in iter_annotate_file(lines, anno_d_iter):
if isinstance(result, progress.Progress):
result.total = total
show_progress(progress_bar, result)
else:
anno_lines = result
finally:
progress_bar.clear()
for line in anno_lines:
sys.stdout.write("%4s:%s" % (str(line.log), line.text))
class AnnotateLine:
"""A line associated with the log that produced it"""
def __init__(self, text, log=None):
self.text = text
self.log = log
class CantGetRevisionData(Exception):
def __init__(self, revision):
Exception.__init__(self, "Can't get data for revision %s" % revision)
def annotate_file2(file_lines, anno_iter):
for result in iter_annotate_file(file_lines, anno_iter):
pass
return result
def iter_annotate_file(file_lines, anno_iter):
lines = [AnnotateLine(f) for f in file_lines]
patches = []
try:
for result in anno_iter:
if isinstance(result, progress.Progress):
yield result
continue
log, iter_inserted, patch = result
for (num, line) in iter_inserted:
old_num = num
for cur_patch in patches:
num = cur_patch.pos_in_mod(num)
if num == None:
break
if num >= len(lines):
continue
if num is not None and lines[num].log is None:
lines[num].log = log
patches=[patch]+patches
except CantGetRevisionData:
pass
yield lines
|