14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
22
20
delta as _mod_delta,
27
24
revision as _mod_revision,
29
from . import errors as errors
30
from .sixish import text_type
31
from .trace import mutter, warning
32
from .workingtree import ShelvingUnsupported
26
import bzrlib.errors as errors
27
from bzrlib.trace import mutter, warning
35
29
# TODO: when showing single-line logs, truncate to the width of the terminal
36
30
# if known, but only if really going to the terminal (not into a file)
39
def report_changes(to_file, old, new, specific_files,
40
show_short_reporter, show_long_callback,
41
short=False, want_unchanged=False,
42
want_unversioned=False, show_ids=False, classify=True):
33
def report_changes(to_file, old, new, specific_files,
34
show_short_reporter, show_long_callback,
35
short=False, want_unchanged=False,
36
want_unversioned=False, show_ids=False):
43
37
"""Display summary of changes.
45
This compares two trees with regards to a list of files, and delegates
39
This compares two trees with regards to a list of files, and delegates
46
40
the display to underlying elements.
48
42
For short output, it creates an iterator on all changes, and lets a given
122
116
:param verbose: If True, show all merged revisions, not just
124
118
:param versioned: If True, only shows versioned files.
125
:param classify: Add special symbols to indicate file kind.
126
:param show_long_callback: A callback: message = show_long_callback(to_file, delta,
119
:param show_long_callback: A callback: message = show_long_callback(to_file, delta,
127
120
show_ids, show_unchanged, indent, filter), only used with the long output
122
if show_unchanged is not None:
123
warn("show_tree_status with show_unchanged has been deprecated "
124
"since bzrlib 0.9", DeprecationWarning, stacklevel=2)
129
126
if to_file is None:
130
127
to_file = sys.stdout
133
131
new_is_working_tree = True
134
132
if revision is None:
135
133
if wt.last_revision() != wt.branch.last_revision():
136
warning("working tree is out of date, run 'brz update'")
134
warning("working tree is out of date, run 'bzr update'")
138
136
old = new.basis_tree()
139
137
elif len(revision) > 0:
141
139
old = revision[0].as_tree(wt.branch)
142
except errors.NoSuchRevision as e:
140
except errors.NoSuchRevision, e:
143
141
raise errors.BzrCommandError(str(e))
144
142
if (len(revision) > 1) and (revision[1].spec is not None):
146
144
new = revision[1].as_tree(wt.branch)
147
145
new_is_working_tree = False
148
except errors.NoSuchRevision as e:
146
except errors.NoSuchRevision, e:
149
147
raise errors.BzrCommandError(str(e))
152
with old.lock_read(), new.lock_read():
153
for hook in hooks['pre_status']:
154
hook(StatusHookParams(old, new, to_file, versioned,
155
show_ids, short, verbose, specific_files=specific_files))
157
153
specific_files, nonexistents \
158
154
= _filter_nonexistent(specific_files, old, new)
159
155
want_unversioned = not versioned
161
157
# Reporter used for short outputs
162
158
reporter = _mod_delta._ChangeReporter(output_file=to_file,
163
unversioned_filter=new.is_ignored, classify=classify)
164
report_changes(to_file, old, new, specific_files,
165
reporter, show_long_callback,
166
short=short, want_unversioned=want_unversioned,
167
show_ids=show_ids, classify=classify)
159
unversioned_filter=new.is_ignored)
160
report_changes(to_file, old, new, specific_files,
161
reporter, show_long_callback,
162
short=short, want_unchanged=show_unchanged,
163
want_unversioned=want_unversioned, show_ids=show_ids)
169
165
# show the ignored files among specific files (i.e. show the files
170
# identified from input that we choose to ignore).
166
# identified from input that we choose to ignore).
171
167
if specific_files is not None:
172
168
# Ignored files is sorted because specific_files is already sorted
173
169
ignored_files = [specific for specific in
303
301
merge_extra.discard(_mod_revision.NULL_REVISION)
305
303
# Get a handle to all of the revisions we will need
306
revisions = dict(branch.repository.iter_revisions(merge_extra))
305
revisions = dict((rev.revision_id, rev) for rev in
306
branch.repository.get_revisions(merge_extra))
307
except errors.NoSuchRevision:
308
# One of the sub nodes is a ghost, check each one
310
for revision_id in merge_extra:
312
rev = branch.repository.get_revisions([revision_id])[0]
313
except errors.NoSuchRevision:
314
revisions[revision_id] = None
316
revisions[revision_id] = rev
308
318
# Display the revisions brought in by this merge.
309
319
rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
310
320
branch.repository.get_parent_map(merge_extra))
311
321
# Skip the first node
312
num, first, depth, eom = next(rev_id_iterator)
322
num, first, depth, eom = rev_id_iterator.next()
313
323
if first != merge:
314
324
raise AssertionError('Somehow we misunderstood how'
315
325
' iter_topo_order works %s != %s' % (first, merge))
316
326
for num, sub_merge, depth, eom in rev_id_iterator:
317
327
rev = revisions[sub_merge]
319
to_file.write(sub_prefix + '(ghost) ' + sub_merge.decode('utf-8') + '\n')
329
to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
321
331
show_log_message(revisions[sub_merge], sub_prefix)
346
356
# their groups individually. But for consistency of this
347
357
# function's API, it's better to sort both than just 'nonexistent'.
348
358
return sorted(remaining), sorted(nonexistent)
351
class StatusHooks(_mod_hooks.Hooks):
352
"""A dictionary mapping hook name to a list of callables for status hooks.
354
e.g. ['post_status'] Is the list of items to be called when the
355
status command has finished printing the status.
359
"""Create the default hooks.
361
These are all empty initially, because by default nothing should get
364
_mod_hooks.Hooks.__init__(self, "breezy.status", "hooks")
365
self.add_hook('post_status',
366
"Called with argument StatusHookParams after Bazaar has "
367
"displayed the status. StatusHookParams has the attributes "
368
"(old_tree, new_tree, to_file, versioned, show_ids, short, "
369
"verbose). The last four arguments correspond to the command "
370
"line options specified by the user for the status command. "
371
"to_file is the output stream for writing.",
373
self.add_hook('pre_status',
374
"Called with argument StatusHookParams before Bazaar "
375
"displays the status. StatusHookParams has the attributes "
376
"(old_tree, new_tree, to_file, versioned, show_ids, short, "
377
"verbose). The last four arguments correspond to the command "
378
"line options specified by the user for the status command. "
379
"to_file is the output stream for writing.",
383
class StatusHookParams(object):
384
"""Object holding parameters passed to post_status hooks.
386
:ivar old_tree: Start tree (basis tree) for comparison.
387
:ivar new_tree: Working tree.
388
:ivar to_file: If set, write to this file.
389
:ivar versioned: Show only versioned files.
390
:ivar show_ids: Show internal object ids.
391
:ivar short: Use short status indicators.
392
:ivar verbose: Verbose flag.
395
def __init__(self, old_tree, new_tree, to_file, versioned, show_ids,
396
short, verbose, specific_files=None):
397
"""Create a group of post_status hook parameters.
399
:param old_tree: Start tree (basis tree) for comparison.
400
:param new_tree: Working tree.
401
:param to_file: If set, write to this file.
402
:param versioned: Show only versioned files.
403
:param show_ids: Show internal object ids.
404
:param short: Use short status indicators.
405
:param verbose: Verbose flag.
406
:param specific_files: If set, a list of filenames whose status should be
407
shown. It is an error to give a filename that is not in the working
408
tree, or in the working inventory or in the basis inventory.
410
self.old_tree = old_tree
411
self.new_tree = new_tree
412
self.to_file = to_file
413
self.versioned = versioned
414
self.show_ids = show_ids
416
self.verbose = verbose
417
self.specific_files = specific_files
419
def __eq__(self, other):
420
return self.__dict__ == other.__dict__
423
return "<%s(%s, %s, %s, %s, %s, %s, %s, %s)>" % (self.__class__.__name__,
424
self.old_tree, self.new_tree, self.to_file, self.versioned,
425
self.show_ids, self.short, self.verbose, self.specific_files)
428
def _show_shelve_summary(params):
429
"""post_status hook to display a summary of shelves.
431
:param params: StatusHookParams.
433
# Don't show shelves if status of specific files is being shown, only if
434
# no file arguments have been passed
435
if params.specific_files:
437
get_shelf_manager = getattr(params.new_tree, 'get_shelf_manager', None)
438
if get_shelf_manager is None:
441
manager = get_shelf_manager()
442
except ShelvingUnsupported:
443
mutter('shelving not supported by tree, not displaying shelves.')
445
shelves = manager.active_shelves()
447
singular = '%d shelf exists. '
448
plural = '%d shelves exist. '
449
if len(shelves) == 1:
453
params.to_file.write(fmt % len(shelves))
454
params.to_file.write('See "brz shelve --list" for details.\n')
457
hooks = StatusHooks()
460
hooks.install_named_hook('post_status', _show_shelve_summary,