~abentley/bzrtools/bzrtools.dev

89 by Aaron Bentley
Added copyright/GPL notices
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
115 by aaron.bentley at utoronto
Import fixes from magnus@therning.org
17
from bzrlib.branch import Branch
85 by Aaron Bentley
Added annotate plugin
18
from bzrlib.commands import Command
98 by Aaron Bentley
Fixed missing import
19
from bzrlib.errors import BzrCommandError
85 by Aaron Bentley
Added annotate plugin
20
import os
21
import progress
102 by Aaron Bentley
Got baz2bzr/annotate working now that ProgressBar is a function
22
from progress import show_progress
85 by Aaron Bentley
Added annotate plugin
23
import patches
24
import difflib
25
import sys
26
27
def iter_anno_data(branch, file_id):
28
    later_revision = branch.revno()
29
    q = range(branch.revno())
30
    q.reverse()
31
    later_text_id = branch.basis_tree().inventory[file_id].text_id
32
    i = 0
33
    for revno in q:
34
        i += 1
35
        cur_tree = branch.revision_tree(branch.lookup_revision(revno))
36
        if file_id not in cur_tree.inventory:
37
            text_id = None
38
        else:
39
            text_id = cur_tree.inventory[file_id].text_id
40
        if text_id != later_text_id:
41
            patch = get_patch(branch, revno, later_revision, file_id)
42
            yield revno, patch.iter_inserted(), patch
43
            later_revision = revno
44
            later_text_id = text_id
45
        yield progress.Progress("revisions", i)
46
47
def get_patch(branch, old_revno, new_revno, file_id):
48
    old_tree = branch.revision_tree(branch.lookup_revision(old_revno))
49
    new_tree = branch.revision_tree(branch.lookup_revision(new_revno))
50
    if file_id in old_tree.inventory:
51
        old_file = old_tree.get_file(file_id).readlines()
52
    else:
53
        old_file = []
54
    ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
55
    return patches.parse_patch(ud)
56
57
class cmd_annotate(Command):
58
    """Show which revision added each line in a file"""
59
60
    takes_args = ['filename']
61
    def run(self, filename):
62
        if not os.path.exists(filename):
63
            raise BzrCommandError("The file %s does not exist." % filename)
64
        branch = (Branch(filename))
99 by Aaron Bentley
Convert paths to branch-relative in annotate
65
        file_id = branch.working_tree().path2id(branch.relpath(filename))
85 by Aaron Bentley
Added annotate plugin
66
        if file_id is None:
67
            raise BzrCommandError("The file %s is not versioned." % filename)
68
        lines = branch.basis_tree().get_file(file_id)
69
        total = branch.revno()
70
        anno_d_iter = iter_anno_data(branch, file_id)
71
        progress_bar = progress.ProgressBar()
72
        try:
91 by Aaron Bentley
Pulled all annotation code into a separate files
73
            for result in iter_annotate_file(lines, anno_d_iter):
85 by Aaron Bentley
Added annotate plugin
74
                if isinstance(result, progress.Progress):
75
                    result.total = total
102 by Aaron Bentley
Got baz2bzr/annotate working now that ProgressBar is a function
76
                    show_progress(progress_bar, result)
85 by Aaron Bentley
Added annotate plugin
77
                else:
78
                    anno_lines = result
79
        finally:
90 by Aaron Bentley
Adapted bzrlib's progress bar
80
            progress_bar.clear()
85 by Aaron Bentley
Added annotate plugin
81
        for line in anno_lines:
82
            sys.stdout.write("%4s:%s" % (str(line.log), line.text))
83
91 by Aaron Bentley
Pulled all annotation code into a separate files
84
85
class AnnotateLine:
86
    """A line associated with the log that produced it"""
87
    def __init__(self, text, log=None):
88
        self.text = text
89
        self.log = log
90
91
class CantGetRevisionData(Exception):
92
    def __init__(self, revision):
93
        Exception.__init__(self, "Can't get data for revision %s" % revision)
94
        
95
def annotate_file2(file_lines, anno_iter):
96
    for result in iter_annotate_file(file_lines, anno_iter):
97
        pass
98
    return result
99
100
        
101
def iter_annotate_file(file_lines, anno_iter):
102
    lines = [AnnotateLine(f) for f in file_lines]
103
    patches = []
104
    try:
105
        for result in anno_iter:
106
            if isinstance(result, progress.Progress):
107
                yield result
108
                continue
109
            log, iter_inserted, patch = result
110
            for (num, line) in iter_inserted:
111
                old_num = num
112
113
                for cur_patch in patches:
114
                    num = cur_patch.pos_in_mod(num)
115
                    if num == None: 
116
                        break
117
118
                if num >= len(lines):
119
                    continue
120
                if num is not None and lines[num].log is None:
121
                    lines[num].log = log
122
            patches=[patch]+patches
123
    except CantGetRevisionData:
124
        pass
125
    yield lines