~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Martin Pool
  • Date: 2005-07-05 13:02:38 UTC
  • Revision ID: mbp@sourcefrog.net-20050705130238-485f0343122f8ce6
doc

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
* with file-ids and revision-ids shown
30
30
 
31
 
Logs are actually written out through an abstract LogFormatter
32
 
interface, which allows for different preferred formats.  Plugins can
33
 
register formats too.
34
 
 
35
 
Logs can be produced in either forward (oldest->newest) or reverse
36
 
(newest->oldest) order.
37
 
 
38
 
Logs can be filtered to show only revisions matching a particular
39
 
search string, or within a particular range of revisions.  The range
40
 
can be given as date/times, which are reduced to revisions before
41
 
calling in here.
42
 
 
43
 
In verbose mode we show a summary of what changed in each particular
44
 
revision.  Note that this is the delta for changes in that revision
45
 
relative to its mainline parent, not the delta relative to the last
46
 
logged revision.  So for example if you ask for a verbose log of
47
 
changes touching hello.c you will get a list of those revisions also
48
 
listing other things that were changed in the same revision, but not
49
 
all the changes since the previous revision that touched hello.c.
 
31
* from last to first or (not anymore) from first to last;
 
32
  the default is "reversed" because it shows the likely most
 
33
  relevant and interesting information first
 
34
 
 
35
* (not yet) in XML format
50
36
"""
51
37
 
52
38
 
53
 
from bzrlib.tree import EmptyTree
54
 
from bzrlib.delta import compare_trees
55
 
from bzrlib.trace import mutter
56
 
 
 
39
from trace import mutter
57
40
 
58
41
def find_touching_revisions(branch, file_id):
59
42
    """Yield a description of revisions which affect the file_id.
100
83
 
101
84
 
102
85
 
103
 
def _enumerate_history(branch):
104
 
    rh = []
105
 
    revno = 1
106
 
    for rev_id in branch.revision_history():
107
 
        rh.append((revno, rev_id))
108
 
        revno += 1
109
 
    return rh
110
 
 
111
 
 
112
86
def show_log(branch,
113
87
             lf,
114
88
             specific_fileid=None,
115
89
             verbose=False,
116
90
             direction='reverse',
117
91
             start_revision=None,
118
 
             end_revision=None,
119
 
             search=None):
 
92
             end_revision=None):
120
93
    """Write out human-readable log of commits to this branch.
121
94
 
122
95
    lf
151
124
    if specific_fileid:
152
125
        mutter('get log for file_id %r' % specific_fileid)
153
126
 
154
 
    if search is not None:
155
 
        import re
156
 
        searchRE = re.compile(search, re.IGNORECASE)
157
 
    else:
158
 
        searchRE = None
159
 
 
160
 
    which_revs = _enumerate_history(branch)
161
 
    
162
 
    if start_revision is None:
163
 
        start_revision = 1
164
 
    else:
165
 
        branch.check_real_revno(start_revision)
166
 
    
167
 
    if end_revision is None:
168
 
        end_revision = len(which_revs)
169
 
    else:
170
 
        branch.check_real_revno(end_revision)
171
 
 
172
 
    # list indexes are 0-based; revisions are 1-based
173
 
    cut_revs = which_revs[(start_revision-1):(end_revision)]
174
 
 
175
 
    if direction == 'reverse':
176
 
        cut_revs.reverse()
177
 
    elif direction == 'forward':
178
 
        pass
179
 
    else:
180
 
        raise ValueError('invalid direction %r' % direction)
181
 
 
182
 
    for revno, rev_id in cut_revs:
183
 
        if verbose or specific_fileid:
184
 
            delta = branch.get_revision_delta(revno)
185
 
            
 
127
    which_revs = branch.enum_history(direction)
 
128
    which_revs = [x for x in which_revs if (
 
129
            (start_revision is None or x[0] >= start_revision)
 
130
            and (end_revision is None or x[0] <= end_revision))]
 
131
 
 
132
    if not (verbose or specific_fileid):
 
133
        # no need to know what changed between revisions
 
134
        with_deltas = deltas_for_log_dummy(branch, which_revs)
 
135
    elif direction == 'reverse':
 
136
        with_deltas = deltas_for_log_reverse(branch, which_revs)
 
137
    else:        
 
138
        with_deltas = deltas_for_log_forward(branch, which_revs)
 
139
 
 
140
    for revno, rev, delta in with_deltas:
186
141
        if specific_fileid:
187
142
            if not delta.touches_file_id(specific_fileid):
188
143
                continue
191
146
            # although we calculated it, throw it away without display
192
147
            delta = None
193
148
 
194
 
        rev = branch.get_revision(rev_id)
195
 
 
196
 
        if searchRE:
197
 
            if not searchRE.search(rev.message):
198
 
                continue
199
 
 
200
149
        lf.show(revno, rev, delta)
201
150
 
202
151
 
203
152
 
204
153
def deltas_for_log_dummy(branch, which_revs):
205
 
    """Return all the revisions without intermediate deltas.
206
 
 
207
 
    Useful for log commands that won't need the delta information.
208
 
    """
209
 
    
210
154
    for revno, revision_id in which_revs:
211
155
        yield revno, branch.get_revision(revision_id), None
212
156
 
213
157
 
214
158
def deltas_for_log_reverse(branch, which_revs):
215
 
    """Compute deltas for display in latest-to-earliest order.
216
 
 
217
 
    branch
218
 
        Branch to traverse
219
 
 
220
 
    which_revs
221
 
        Sequence of (revno, revision_id) for the subset of history to examine
222
 
 
223
 
    returns 
224
 
        Sequence of (revno, rev, delta)
 
159
    """Compute deltas for display in reverse log.
 
160
 
 
161
    Given a sequence of (revno, revision_id) pairs, return
 
162
    (revno, rev, delta).
225
163
 
226
164
    The delta is from the given revision to the next one in the
227
165
    sequence, which makes sense if the log is being displayed from
228
166
    newest to oldest.
229
167
    """
 
168
    from tree import EmptyTree
 
169
    from diff import compare_trees
 
170
    
230
171
    last_revno = last_revision_id = last_tree = None
231
172
    for revno, revision_id in which_revs:
232
173
        this_tree = branch.revision_tree(revision_id)
235
176
        if last_revno:
236
177
            yield last_revno, last_revision, compare_trees(this_tree, last_tree, False)
237
178
 
238
 
        this_tree = EmptyTree(branch.get_root_id())
239
 
 
240
179
        last_revno = revno
241
180
        last_revision = this_revision
242
181
        last_tree = this_tree
243
182
 
244
183
    if last_revno:
245
184
        if last_revno == 1:
246
 
            this_tree = EmptyTree(branch.get_root_id())
 
185
            this_tree = EmptyTree()
247
186
        else:
248
187
            this_revno = last_revno - 1
249
188
            this_revision_id = branch.revision_history()[this_revno]
261
200
    sequence, which makes sense if the log is being displayed from
262
201
    newest to oldest.
263
202
    """
 
203
    from tree import EmptyTree
 
204
    from diff import compare_trees
 
205
 
264
206
    last_revno = last_revision_id = last_tree = None
265
 
    prev_tree = EmptyTree(branch.get_root_id())
266
 
 
267
207
    for revno, revision_id in which_revs:
268
208
        this_tree = branch.revision_tree(revision_id)
269
209
        this_revision = branch.get_revision(revision_id)
270
210
 
271
211
        if not last_revno:
272
212
            if revno == 1:
273
 
                last_tree = EmptyTree(branch.get_root_id())
 
213
                last_tree = EmptyTree()
274
214
            else:
275
215
                last_revno = revno - 1
276
216
                last_revision_id = branch.revision_history()[last_revno]
285
225
 
286
226
class LogFormatter(object):
287
227
    """Abstract class to display log messages."""
288
 
    def __init__(self, to_file, show_ids=False, show_timezone='original'):
 
228
    def __init__(self, to_file, show_ids=False, show_timezone=False):
289
229
        self.to_file = to_file
290
230
        self.show_ids = show_ids
291
231
        self.show_timezone = show_timezone
292
 
 
293
 
 
294
 
    def show(self, revno, rev, delta):
295
 
        raise NotImplementedError('not implemented in abstract base')
296
232
        
297
233
 
298
234
 
309
245
        print >>to_file,  'revno:', revno
310
246
        if self.show_ids:
311
247
            print >>to_file,  'revision-id:', rev.revision_id
312
 
 
313
 
            for parent in rev.parents:
314
 
                print >>to_file, 'parent:', parent.revision_id
315
 
            
316
248
        print >>to_file,  'committer:', rev.committer
317
 
 
318
 
        date_str = format_date(rev.timestamp,
319
 
                               rev.timezone or 0,
320
 
                               self.show_timezone)
321
 
        print >>to_file,  'timestamp: %s' % date_str
 
249
        print >>to_file,  'timestamp: %s' % (format_date(rev.timestamp, rev.timezone or 0,
 
250
                                             self.show_timezone))
322
251
 
323
252
        print >>to_file,  'message:'
324
253
        if not rev.message:
349
278
            for l in rev.message.split('\n'):
350
279
                print >>to_file,  '      ' + l
351
280
 
352
 
        # TODO: Why not show the modified files in a shorter form as
353
 
        # well? rewrap them single lines of appropriate length
354
281
        if delta != None:
355
282
            delta.show(to_file, self.show_ids)
356
283
        print
369
296
        return FORMATTERS[name](*args, **kwargs)
370
297
    except IndexError:
371
298
        raise BzrCommandError("unknown log formatter: %r" % name)
372
 
 
373
 
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
374
 
    # deprecated; for compatability
375
 
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
376
 
    lf.show(revno, rev, delta)