~abentley/bzrtools/bzrtools.dev

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Copyright (C) 2004 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

import sys
import os

#ensure that the parent directory is in the path (for epydoc)
sys.path=[os.path.realpath(os.path.dirname(__file__)+"/..")]+sys.path

import commands
import cmdutil
import arch
import pylon
import time
import terminal

__docformat__ = "restructuredtext"
__doc__ = "command template"

class Annotate(commands.BaseCommand):
    """Print an annotated version of a file"""
    def __init__(self):
        self.description = self.__doc__

#   override get_completer if you want custom completion
#    def get_completer(self, arg, index):
#        return cmdutil.iter_dir_completions(arg)

    def do_command(self, cmdargs):
        parser=self.get_parser()
        (options, args) = parser.parse_args(cmdargs)

        if len(args) < 1 or len(args) > 2:
            raise pylon.errors.GetHelp
        file = args[0]
        tree = arch.tree_root(".")
        if len(args) == 2:
            revision = cmdutil.determine_revision_tree(tree, args[1])
        else:
            revision = pylon.tree_latest(tree)
        try:
            lines = pylon.annotate_file(tree, revision, file)
        except IOError, e:
            raise pylon.errors.CommandFailedWrapper(e)
        if options.deep:
            diffs = {}
            for line in lines:
                if line.log is None:
                    continue
                line.log = pylon.merge_blame(line.log, file, 
                                             pylon.tree_cwd(tree)+"/"+file, 
                                             line.text, diffs)
        if options.baz_style or options.patchlevels:
            pre_tag = True
            ancestors = list(pylon.iter_ancestry(tree))
            recent_ancestors = []
            name_map = {}
            
            name_map["UNKNOWN"] = str(ancestors[-1])
            if (options.patchlevels):
                anc_level = 0
                for revision in ancestors:
                    if anc_level == 0 and revision.version != tree.tree_version:
                        anc_level = 1
                    if anc_level > 0:
                        name_map[str(revision)] = "ances-%i" % anc_level
                        anc_level += 1
                    else:
                        name_map[str(revision)] = revision.patchlevel
            baz_display(lines, name_map)
        else:
            colorized_display(lines, options)
        return 0

    def get_parser(self):
        parser = cmdutil.CmdOptionParser("annotate FILE [REVISION]")
        parser.add_option("-s", "--summary", action="store_true", 
                          dest="summary", help="Show patchlog summary")
        parser.add_option("-D", "--date", action="store_true", 
                          dest="date", help="Show patchlog date")
        parser.add_option("-c", "--creator", action="store_true", 
                          dest="creator", help="Show the id that committed the"
                          " revision")
        parser.add_option("-b", "--baz", action="store_true", dest="baz_style", 
                          help="Show annotations in baz style")
        parser.add_option("-p", "--patchlevels", action="store_true", 
                          dest="patchlevels", 
                          help="Show patchlevels (and ances for ancestor"
                          " revisions)")
        parser.add_option("--notws", action="store_true", 
                          dest="notws", 
                          help="Show, but don't annotate whitespace lines")
        parser.add_option("", "--deep", action="store_true", 
                          dest="deep", 
                          help="Assign blame to merged revisions")
        return parser

    def help(self, parser=None):
        """
        Prints a help message.

        :param parser: If supplied, the parser to use for generating help.  If \
        not supplied, it is retrieved.
        :type parser: cmdutil.CmdOptionParser
        """
        if parser==None:
            parser=self.get_parser()
        parser.print_help()
        print """
Annotate will print up a version of a file, showing which revision added
each line.

Lines whose derivation cannot be determined as listed as UNASSIGNED.

The --deep option attempts to determine which merge inserted a line, but
because merges are inexact patching, it can be confused by lines which are
repeated more than once in a revision's diff (e.g. whitespace).
"""
def baz_display(lines, name_map={}):
    for line in lines:
        if line.log == None:
            revision_text = "UNKNOWN"
        else:
            revision_text = str(line.log.revision)
        try:
            while(True):
                revision_text = name_map[revision_text]
        except KeyError, e:
            pass
        print "%s: %s" % (revision_text, line.text.rstrip("\n"))


def colorized_display(lines, options):
    last = None
    for line in lines:
        if last is None or str(line.log) != str(last.log):
            if options.notws and line.text.strip() == "":
                continue
            if line.log == None:
                print terminal.colorstring("UNASSIGNED", 'blue')
            else:
                print terminal.colorstring(str(line.log.revision), 'blue')
                if options.creator:
                    print terminal.colorstring("    %s" % line.log.creator,
                                               'yellow')
                if options.date:
                    print terminal.colorstring("    %s" % \
                        time.strftime('%Y-%m-%d %H:%M:%S %Z',
                        line.log.date), 'yellow')
                if options.summary and line.log.summary != "":
                    print terminal.colorstring("    %s" % line.log.summary,
                                               'yellow')
            last=line
        print(line.text.rstrip("\n"))


#This function assigns the command class to a user command name
def add_command(commands):
    commands["annotate"] = Annotate



# arch-tag: c9833e6f-0ebe-4cb9-a799-d9ec2e50674e