~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):
218 by Aaron Bentley
Show working tree changes in annotate output
28
    max_revno = branch.revno()
29
    later_revision = max_revno
30
    q = range(max_revno)
31
    q.append(max_revno)
85 by Aaron Bentley
Added annotate plugin
32
    q.reverse()
218 by Aaron Bentley
Show working tree changes in annotate output
33
    next_tree = branch.working_tree()
34
    later_text_sha1 = next_tree.get_file_sha1(file_id)
85 by Aaron Bentley
Added annotate plugin
35
    i = 0
36
    for revno in q:
37
        i += 1
158 by Aaron Bentley
Updated to match API changes
38
        cur_tree = branch.revision_tree(branch.get_rev_id(revno))
85 by Aaron Bentley
Added annotate plugin
39
        if file_id not in cur_tree.inventory:
217 by Aaron Bentley
Used text+sha1 instead of text_id in annotate
40
            text_sha1 = None
85 by Aaron Bentley
Added annotate plugin
41
        else:
218 by Aaron Bentley
Show working tree changes in annotate output
42
            text_sha1 = cur_tree.get_file_sha1(file_id)
217 by Aaron Bentley
Used text+sha1 instead of text_id in annotate
43
        if text_sha1 != later_text_sha1:
218 by Aaron Bentley
Show working tree changes in annotate output
44
            patch = get_patch(branch, cur_tree, next_tree, file_id)
45
            next_tree = cur_tree
46
            if revno == max_revno:
47
                display_id = "Tree"
48
            else:
49
                display_id = str(revno+1)
50
            yield display_id, patch.iter_inserted(), patch
85 by Aaron Bentley
Added annotate plugin
51
            later_revision = revno
217 by Aaron Bentley
Used text+sha1 instead of text_id in annotate
52
            later_text_sha1 = text_sha1
85 by Aaron Bentley
Added annotate plugin
53
        yield progress.Progress("revisions", i)
54
218 by Aaron Bentley
Show working tree changes in annotate output
55
def get_patch(branch, old_tree, new_tree, file_id):
85 by Aaron Bentley
Added annotate plugin
56
    if file_id in old_tree.inventory:
57
        old_file = old_tree.get_file(file_id).readlines()
58
    else:
59
        old_file = []
60
    ud = difflib.unified_diff(old_file, new_tree.get_file(file_id).readlines())
61
    return patches.parse_patch(ud)
62
63
class cmd_annotate(Command):
64
    """Show which revision added each line in a file"""
65
66
    takes_args = ['filename']
67
    def run(self, filename):
68
        if not os.path.exists(filename):
69
            raise BzrCommandError("The file %s does not exist." % filename)
321.1.2 by Aaron Bentley
Applied Robert's random fixes as non-merges
70
        branch,relpath = (Branch.open_containing(filename))
71
        file_id = branch.working_tree().path2id(relpath)
85 by Aaron Bentley
Added annotate plugin
72
        if file_id is None:
73
            raise BzrCommandError("The file %s is not versioned." % filename)
74
        lines = branch.basis_tree().get_file(file_id)
75
        total = branch.revno()
76
        anno_d_iter = iter_anno_data(branch, file_id)
77
        progress_bar = progress.ProgressBar()
78
        try:
91 by Aaron Bentley
Pulled all annotation code into a separate files
79
            for result in iter_annotate_file(lines, anno_d_iter):
85 by Aaron Bentley
Added annotate plugin
80
                if isinstance(result, progress.Progress):
81
                    result.total = total
102 by Aaron Bentley
Got baz2bzr/annotate working now that ProgressBar is a function
82
                    show_progress(progress_bar, result)
85 by Aaron Bentley
Added annotate plugin
83
                else:
84
                    anno_lines = result
85
        finally:
90 by Aaron Bentley
Adapted bzrlib's progress bar
86
            progress_bar.clear()
85 by Aaron Bentley
Added annotate plugin
87
        for line in anno_lines:
88
            sys.stdout.write("%4s:%s" % (str(line.log), line.text))
89
91 by Aaron Bentley
Pulled all annotation code into a separate files
90
91
class AnnotateLine:
92
    """A line associated with the log that produced it"""
93
    def __init__(self, text, log=None):
94
        self.text = text
95
        self.log = log
96
97
class CantGetRevisionData(Exception):
98
    def __init__(self, revision):
99
        Exception.__init__(self, "Can't get data for revision %s" % revision)
100
        
101
def annotate_file2(file_lines, anno_iter):
102
    for result in iter_annotate_file(file_lines, anno_iter):
103
        pass
104
    return result
105
106
        
107
def iter_annotate_file(file_lines, anno_iter):
108
    lines = [AnnotateLine(f) for f in file_lines]
109
    patches = []
110
    try:
111
        for result in anno_iter:
112
            if isinstance(result, progress.Progress):
113
                yield result
114
                continue
115
            log, iter_inserted, patch = result
116
            for (num, line) in iter_inserted:
117
                old_num = num
118
119
                for cur_patch in patches:
120
                    num = cur_patch.pos_in_mod(num)
121
                    if num == None: 
122
                        break
123
124
                if num >= len(lines):
125
                    continue
126
                if num is not None and lines[num].log is None:
127
                    lines[num].log = log
128
            patches=[patch]+patches
129
    except CantGetRevisionData:
130
        pass
131
    yield lines