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
20
22
delta as _mod_delta,
24
27
revision as _mod_revision,
26
import bzrlib.errors as errors
27
from bzrlib.trace import mutter, warning
29
from . import errors as errors
30
from .trace import mutter, warning
31
from .workingtree import ShelvingUnsupported
29
34
# TODO: when showing single-line logs, truncate to the width of the terminal
30
35
# if known, but only if really going to the terminal (not into a file)
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):
38
def report_changes(to_file, old, new, specific_files,
39
show_short_reporter, show_long_callback,
40
short=False, want_unchanged=False,
41
want_unversioned=False, show_ids=False, classify=True):
37
42
"""Display summary of changes.
39
This compares two trees with regards to a list of files, and delegates
44
This compares two trees with regards to a list of files, and delegates
40
45
the display to underlying elements.
42
47
For short output, it creates an iterator on all changes, and lets a given
59
64
:param show_ids: If set, includes each file's id.
60
65
:param want_unversioned: If False, only shows versioned files.
66
:param classify: Add special symbols to indicate file kind.
64
70
changes = new.iter_changes(old, want_unchanged, specific_files,
65
require_versioned=False, want_unversioned=want_unversioned)
71
require_versioned=False, want_unversioned=want_unversioned)
66
72
_mod_delta.report_changes(changes, show_short_reporter)
69
74
delta = new.changes_from(old, want_unchanged=want_unchanged,
70
specific_files=specific_files,
71
want_unversioned=want_unversioned)
75
specific_files=specific_files,
76
want_unversioned=want_unversioned)
72
77
# filter out unknown files. We may want a tree method for
74
79
delta.unversioned = [unversioned for unversioned in
75
delta.unversioned if not new.is_ignored(unversioned[0])]
76
show_long_callback(to_file, delta,
80
delta.unversioned if not new.is_ignored(unversioned[0])]
81
show_long_callback(to_file, delta,
78
show_unchanged=want_unchanged)
81
def show_tree_status(wt, show_unchanged=None,
83
show_unchanged=want_unchanged,
87
def show_tree_status(wt,
82
88
specific_files=None,
116
121
:param verbose: If True, show all merged revisions, not just
118
123
:param versioned: If True, only shows versioned files.
119
:param show_long_callback: A callback: message = show_long_callback(to_file, delta,
124
:param classify: Add special symbols to indicate file kind.
125
:param show_long_callback: A callback: message = show_long_callback(to_file, delta,
120
126
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)
126
128
if to_file is None:
127
129
to_file = sys.stdout
131
132
new_is_working_tree = True
132
133
if revision is None:
133
134
if wt.last_revision() != wt.branch.last_revision():
134
warning("working tree is out of date, run 'bzr update'")
135
warning("working tree is out of date, run 'brz update'")
136
137
old = new.basis_tree()
137
138
elif len(revision) > 0:
139
140
old = revision[0].as_tree(wt.branch)
140
except errors.NoSuchRevision, e:
141
except errors.NoSuchRevision as e:
141
142
raise errors.BzrCommandError(str(e))
142
143
if (len(revision) > 1) and (revision[1].spec is not None):
144
145
new = revision[1].as_tree(wt.branch)
145
146
new_is_working_tree = False
146
except errors.NoSuchRevision, e:
147
except errors.NoSuchRevision as e:
147
148
raise errors.BzrCommandError(str(e))
151
with old.lock_read(), new.lock_read():
152
for hook in hooks['pre_status']:
153
hook(StatusHookParams(old, new, to_file, versioned,
154
show_ids, short, verbose, specific_files=specific_files))
153
156
specific_files, nonexistents \
154
157
= _filter_nonexistent(specific_files, old, new)
155
158
want_unversioned = not versioned
157
160
# Reporter used for short outputs
158
161
reporter = _mod_delta._ChangeReporter(output_file=to_file,
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)
162
unversioned_filter=new.is_ignored, classify=classify)
163
report_changes(to_file, old, new, specific_files,
164
reporter, show_long_callback,
165
short=short, want_unversioned=want_unversioned,
166
show_ids=show_ids, classify=classify)
165
168
# show the ignored files among specific files (i.e. show the files
166
# identified from input that we choose to ignore).
169
# identified from input that we choose to ignore).
167
170
if specific_files is not None:
168
171
# Ignored files is sorted because specific_files is already sorted
169
172
ignored_files = [specific for specific in
170
specific_files if new.is_ignored(specific)]
173
specific_files if new.is_ignored(specific)]
171
174
if len(ignored_files) > 0 and not short:
172
175
to_file.write("ignored:\n")
301
303
merge_extra.discard(_mod_revision.NULL_REVISION)
303
305
# Get a handle to all of the revisions we will need
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
306
revisions = dict(branch.repository.iter_revisions(merge_extra))
318
308
# Display the revisions brought in by this merge.
319
309
rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
320
branch.repository.get_parent_map(merge_extra))
310
branch.repository.get_parent_map(merge_extra))
321
311
# Skip the first node
322
num, first, depth, eom = rev_id_iterator.next()
312
num, first, depth, eom = next(rev_id_iterator)
323
313
if first != merge:
324
314
raise AssertionError('Somehow we misunderstood how'
325
' iter_topo_order works %s != %s' % (first, merge))
315
' iter_topo_order works %s != %s' % (first, merge))
326
316
for num, sub_merge, depth, eom in rev_id_iterator:
327
317
rev = revisions[sub_merge]
329
to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
319
to_file.write(sub_prefix + '(ghost) ' +
320
sub_merge.decode('utf-8') + '\n')
331
322
show_log_message(revisions[sub_merge], sub_prefix)
350
341
s = old_tree.filter_unversioned_files(orig_paths)
351
342
s = new_tree.filter_unversioned_files(s)
352
343
nonexistent = [path for path in s if not new_tree.has_filename(path)]
353
remaining = [path for path in orig_paths if not path in nonexistent]
344
remaining = [path for path in orig_paths if path not in nonexistent]
354
345
# Sorting the 'remaining' list doesn't have much effect in
355
346
# practice, since the various status output sections will sort
356
347
# their groups individually. But for consistency of this
357
348
# function's API, it's better to sort both than just 'nonexistent'.
358
349
return sorted(remaining), sorted(nonexistent)
352
class StatusHooks(_mod_hooks.Hooks):
353
"""A dictionary mapping hook name to a list of callables for status hooks.
355
e.g. ['post_status'] Is the list of items to be called when the
356
status command has finished printing the status.
360
"""Create the default hooks.
362
These are all empty initially, because by default nothing should get
365
_mod_hooks.Hooks.__init__(self, "breezy.status", "hooks")
368
"Called with argument StatusHookParams after Bazaar has "
369
"displayed the status. StatusHookParams has the attributes "
370
"(old_tree, new_tree, to_file, versioned, show_ids, short, "
371
"verbose). The last four arguments correspond to the command "
372
"line options specified by the user for the status command. "
373
"to_file is the output stream for writing.",
377
"Called with argument StatusHookParams before Bazaar "
378
"displays the status. StatusHookParams has the attributes "
379
"(old_tree, new_tree, to_file, versioned, show_ids, short, "
380
"verbose). The last four arguments correspond to the command "
381
"line options specified by the user for the status command. "
382
"to_file is the output stream for writing.",
386
class StatusHookParams(object):
387
"""Object holding parameters passed to post_status hooks.
389
:ivar old_tree: Start tree (basis tree) for comparison.
390
:ivar new_tree: Working tree.
391
:ivar to_file: If set, write to this file.
392
:ivar versioned: Show only versioned files.
393
:ivar show_ids: Show internal object ids.
394
:ivar short: Use short status indicators.
395
:ivar verbose: Verbose flag.
398
def __init__(self, old_tree, new_tree, to_file, versioned, show_ids,
399
short, verbose, specific_files=None):
400
"""Create a group of post_status hook parameters.
402
:param old_tree: Start tree (basis tree) for comparison.
403
:param new_tree: Working tree.
404
:param to_file: If set, write to this file.
405
:param versioned: Show only versioned files.
406
:param show_ids: Show internal object ids.
407
:param short: Use short status indicators.
408
:param verbose: Verbose flag.
409
:param specific_files: If set, a list of filenames whose status should be
410
shown. It is an error to give a filename that is not in the
411
working tree, or in the working inventory or in the basis inventory.
413
self.old_tree = old_tree
414
self.new_tree = new_tree
415
self.to_file = to_file
416
self.versioned = versioned
417
self.show_ids = show_ids
419
self.verbose = verbose
420
self.specific_files = specific_files
422
def __eq__(self, other):
423
return self.__dict__ == other.__dict__
426
return "<%s(%s, %s, %s, %s, %s, %s, %s, %s)>" % (
427
self.__class__.__name__, self.old_tree, self.new_tree,
428
self.to_file, self.versioned, self.show_ids, self.short,
429
self.verbose, self.specific_files)
432
def _show_shelve_summary(params):
433
"""post_status hook to display a summary of shelves.
435
:param params: StatusHookParams.
437
# Don't show shelves if status of specific files is being shown, only if
438
# no file arguments have been passed
439
if params.specific_files:
441
get_shelf_manager = getattr(params.new_tree, 'get_shelf_manager', None)
442
if get_shelf_manager is None:
445
manager = get_shelf_manager()
446
except ShelvingUnsupported:
447
mutter('shelving not supported by tree, not displaying shelves.')
449
shelves = manager.active_shelves()
451
singular = '%d shelf exists. '
452
plural = '%d shelves exist. '
453
if len(shelves) == 1:
457
params.to_file.write(fmt % len(shelves))
458
params.to_file.write('See "brz shelve --list" for details.\n')
461
hooks = StatusHooks()
464
hooks.install_named_hook('post_status', _show_shelve_summary,