~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to annotate.py

  • Committer: aaron.bentley at utoronto
  • Date: 2005-07-03 18:59:58 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20050703185958-dc12104f57c0e61c
Added now-required imports

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005 Aaron Bentley
 
2
# <aaron.bentley@utoronto.ca>
 
3
#
 
4
#    This program is free software; you can redistribute it and/or modify
 
5
#    it under the terms of the GNU General Public License as published by
 
6
#    the Free Software Foundation; either version 2 of the License, or
 
7
#    (at your option) any later version.
 
8
#
 
9
#    This program is distributed in the hope that it will be useful,
 
10
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#    GNU General Public License for more details.
 
13
#
 
14
#    You should have received a copy of the GNU General Public License
 
15
#    along with this program; if not, write to the Free Software
 
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
from bzrlib import Branch
 
18
from bzrlib.commands import Command
 
19
import os
 
20
import progress
 
21
import patches
 
22
import difflib
 
23
import sys
 
24
 
 
25
def iter_anno_data(branch, file_id):
 
26
    later_revision = branch.revno()
 
27
    q = range(branch.revno())
 
28
    q.reverse()
 
29
    later_text_id = branch.basis_tree().inventory[file_id].text_id
 
30
    i = 0
 
31
    for revno in q:
 
32
        i += 1
 
33
        cur_tree = branch.revision_tree(branch.lookup_revision(revno))
 
34
        if file_id not in cur_tree.inventory:
 
35
            text_id = None
 
36
        else:
 
37
            text_id = cur_tree.inventory[file_id].text_id
 
38
        if text_id != later_text_id:
 
39
            patch = get_patch(branch, revno, later_revision, file_id)
 
40
            yield revno, patch.iter_inserted(), patch
 
41
            later_revision = revno
 
42
            later_text_id = text_id
 
43
        yield progress.Progress("revisions", i)
 
44
 
 
45
def get_patch(branch, old_revno, new_revno, file_id):
 
46
    old_tree = branch.revision_tree(branch.lookup_revision(old_revno))
 
47
    new_tree = branch.revision_tree(branch.lookup_revision(new_revno))
 
48
    if file_id in old_tree.inventory:
 
49
        old_file = old_tree.get_file(file_id).readlines()
 
50
    else:
 
51
        old_file = []
 
52
    ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
 
53
    return patches.parse_patch(ud)
 
54
 
 
55
class cmd_annotate(Command):
 
56
    """Show which revision added each line in a file"""
 
57
 
 
58
    takes_args = ['filename']
 
59
    def run(self, filename):
 
60
        if not os.path.exists(filename):
 
61
            raise BzrCommandError("The file %s does not exist." % filename)
 
62
        branch = (Branch(filename))
 
63
        file_id = branch.working_tree().path2id(filename)
 
64
        if file_id is None:
 
65
            raise BzrCommandError("The file %s is not versioned." % filename)
 
66
        lines = branch.basis_tree().get_file(file_id)
 
67
        total = branch.revno()
 
68
        anno_d_iter = iter_anno_data(branch, file_id)
 
69
        progress_bar = progress.ProgressBar()
 
70
        try:
 
71
            for result in iter_annotate_file(lines, anno_d_iter):
 
72
                if isinstance(result, progress.Progress):
 
73
                    result.total = total
 
74
                    progress_bar(result)
 
75
                else:
 
76
                    anno_lines = result
 
77
        finally:
 
78
            progress_bar.clear()
 
79
        for line in anno_lines:
 
80
            sys.stdout.write("%4s:%s" % (str(line.log), line.text))
 
81
 
 
82
 
 
83
class AnnotateLine:
 
84
    """A line associated with the log that produced it"""
 
85
    def __init__(self, text, log=None):
 
86
        self.text = text
 
87
        self.log = log
 
88
 
 
89
class CantGetRevisionData(Exception):
 
90
    def __init__(self, revision):
 
91
        Exception.__init__(self, "Can't get data for revision %s" % revision)
 
92
        
 
93
def annotate_file2(file_lines, anno_iter):
 
94
    for result in iter_annotate_file(file_lines, anno_iter):
 
95
        pass
 
96
    return result
 
97
 
 
98
        
 
99
def iter_annotate_file(file_lines, anno_iter):
 
100
    lines = [AnnotateLine(f) for f in file_lines]
 
101
    patches = []
 
102
    try:
 
103
        for result in anno_iter:
 
104
            if isinstance(result, progress.Progress):
 
105
                yield result
 
106
                continue
 
107
            log, iter_inserted, patch = result
 
108
            for (num, line) in iter_inserted:
 
109
                old_num = num
 
110
 
 
111
                for cur_patch in patches:
 
112
                    num = cur_patch.pos_in_mod(num)
 
113
                    if num == None: 
 
114
                        break
 
115
 
 
116
                if num >= len(lines):
 
117
                    continue
 
118
                if num is not None and lines[num].log is None:
 
119
                    lines[num].log = log
 
120
            patches=[patch]+patches
 
121
    except CantGetRevisionData:
 
122
        pass
 
123
    yield lines