19
from bzrlib.diff import _raise_if_nonexistent
25
revision as _mod_revision,
20
27
import bzrlib.errors as errors
21
from bzrlib.log import line_log
22
28
from bzrlib.osutils import is_inside_any
23
29
from bzrlib.symbol_versioning import (deprecated_function,
26
from bzrlib.trace import warning
31
from bzrlib.trace import mutter, warning
28
33
# TODO: when showing single-line logs, truncate to the width of the terminal
29
34
# if known, but only if really going to the terminal (not into a file)
32
@deprecated_function(zero_eight)
33
def show_status(branch, show_unchanged=None,
39
"""Display summary of changes.
41
Please use show_tree_status instead.
43
By default this compares the working tree to a previous revision.
44
If the revision argument is given, summarizes changes between the
45
working tree and another, or between two revisions.
47
The result is written out as Unicode and to_file should be able
51
If set, includes unchanged files.
54
If set, only show the status of files in this list.
57
If set, includes each file's id.
60
If set, write to this file (default stdout.)
63
If set, write pending merges.
66
If None the compare latest revision with working tree
67
If one revision show compared it with working tree.
68
If two revisions show status between first and second.
70
show_tree_status(branch.bzrdir.open_workingtree(), show_unchanged,
71
specific_files, show_ids, to_file, show_pending, revision)
74
37
def show_tree_status(wt, show_unchanged=None,
75
38
specific_files=None,
81
45
"""Display summary of changes.
83
47
By default this compares the working tree to a previous revision.
121
86
old = new.basis_tree()
122
87
elif len(revision) > 0:
124
rev_id = revision[0].in_history(wt.branch).rev_id
125
old = wt.branch.repository.revision_tree(rev_id)
89
old = revision[0].as_tree(wt.branch)
126
90
except errors.NoSuchRevision, e:
127
91
raise errors.BzrCommandError(str(e))
128
92
if (len(revision) > 1) and (revision[1].spec is not None):
130
rev_id = revision[1].in_history(wt.branch).rev_id
131
new = wt.branch.repository.revision_tree(rev_id)
94
new = revision[1].as_tree(wt.branch)
132
95
new_is_working_tree = False
133
96
except errors.NoSuchRevision, e:
134
97
raise errors.BzrCommandError(str(e))
137
_raise_if_nonexistent(specific_files, old, new)
138
delta = new.changes_from(old, want_unchanged=show_unchanged,
139
specific_files=specific_files)
142
show_unchanged=show_unchanged,
144
short_status_letter = '?'
146
short_status_letter = ''
147
list_paths('unknown', new.unknowns(), specific_files, to_file,
149
conflict_title = False
150
# show the new conflicts only for now. XXX: get them from the delta.
151
for conflict in new.conflicts():
152
if not short and conflict_title is False:
153
print >> to_file, "conflicts:"
154
conflict_title = True
103
_raise_if_nonexistent(specific_files, old, new)
104
want_unversioned = not versioned
106
changes = new.iter_changes(old, show_unchanged, specific_files,
107
require_versioned=False, want_unversioned=want_unversioned)
108
reporter = _mod_delta._ChangeReporter(output_file=to_file,
109
unversioned_filter=new.is_ignored)
110
_mod_delta.report_changes(changes, reporter)
159
print >> to_file, "%s %s" % (prefix, conflict)
160
if new_is_working_tree and show_pending:
161
show_pending_merges(new, to_file, short)
112
delta = new.changes_from(old, want_unchanged=show_unchanged,
113
specific_files=specific_files,
114
want_unversioned=want_unversioned)
115
# filter out unknown files. We may want a tree method for
117
delta.unversioned = [unversioned for unversioned in
118
delta.unversioned if not new.is_ignored(unversioned[0])]
121
show_unchanged=show_unchanged,
123
# show the new conflicts only for now. XXX: get them from the
125
conflicts = new.conflicts()
126
if specific_files is not None:
127
conflicts = conflicts.select_conflicts(new, specific_files,
128
ignore_misses=True, recurse=True)[1]
129
if len(conflicts) > 0 and not short:
130
to_file.write("conflicts:\n")
131
for conflict in conflicts:
136
to_file.write("%s %s\n" % (prefix, conflict))
137
if (new_is_working_tree and show_pending):
138
show_pending_merges(new, to_file, short)
146
def _get_sorted_revisions(tip_revision, revision_ids, parent_map):
147
"""Get an iterator which will return the revisions in merge sorted order.
149
This will build up a list of all nodes, such that only nodes in the list
150
are referenced. It then uses MergeSorter to return them in 'merge-sorted'
153
:param revision_ids: A set of revision_ids
154
:param parent_map: The parent information for each node. Revisions which
155
are considered ghosts should not be present in the map.
156
:return: iterator from MergeSorter.iter_topo_order()
158
# MergeSorter requires that all nodes be present in the graph, so get rid
159
# of any references pointing outside of this graph.
161
for revision_id in revision_ids:
162
if revision_id not in parent_map: # ghost
163
parent_graph[revision_id] = []
165
# Only include parents which are in this sub-graph
166
parent_graph[revision_id] = [p for p in parent_map[revision_id]
167
if p in revision_ids]
168
sorter = tsort.MergeSorter(parent_graph, tip_revision)
169
return sorter.iter_topo_order()
165
172
def show_pending_merges(new, to_file, short=False):
166
173
"""Write out a display of pending merges in a working tree."""
167
174
parents = new.get_parent_ids()
168
175
if len(parents) < 2:
178
# we need one extra space for terminals that wrap on last char
179
term_width = osutils.terminal_width() - 1
170
187
pending = parents[1:]
171
188
branch = new.branch
172
189
last_revision = parents[0]
174
print >>to_file, 'pending merges:'
175
if last_revision is not None:
177
ignore = set(branch.repository.get_ancestry(last_revision))
178
except errors.NoSuchRevision:
179
# the last revision is a ghost : assume everything is new
181
ignore = set([None, last_revision])
184
# TODO: this could be improved using merge_sorted - we'd get the same
185
# output rather than one level of indent.
191
to_file.write('pending merges:\n')
192
graph = branch.repository.get_graph()
193
other_revisions = [last_revision]
194
log_formatter = log.LineLogFormatter(to_file)
186
195
for merge in pending:
189
from bzrlib.osutils import terminal_width
190
width = terminal_width()
191
m_revision = branch.repository.get_revision(merge)
196
print >> to_file, prefix, line_log(m_revision, width - 4)
197
inner_merges = branch.repository.get_ancestry(merge)
198
assert inner_merges[0] is None
200
inner_merges.reverse()
201
for mmerge in inner_merges:
204
mm_revision = branch.repository.get_revision(mmerge)
209
print >> to_file, prefix, line_log(mm_revision, width - 5)
197
rev = branch.repository.get_revisions([merge])[0]
211
198
except errors.NoSuchRevision:
216
print >> to_file, prefix, merge
218
def list_paths(header, paths, specific_files, to_file, short_status_letter=''):
221
if specific_files and not is_inside_any(specific_files, path):
199
# If we are missing a revision, just print out the revision id
200
to_file.write(first_prefix + '(ghost) ' + merge + '\n')
201
other_revisions.append(merge)
223
if not short_status_letter and not done_header:
224
print >>to_file, '%s:' % header
226
print >>to_file, '%s %s' % (short_status_letter, path)
204
# Log the merge, as it gets a slightly different formatting
205
log_message = log_formatter.log_string(None, rev,
206
term_width - len(first_prefix))
207
to_file.write(first_prefix + log_message + '\n')
208
# Find all of the revisions in the merge source, which are not in the
209
# last committed revision.
210
merge_extra = graph.find_unique_ancestors(merge, other_revisions)
211
other_revisions.append(merge)
212
merge_extra.discard(_mod_revision.NULL_REVISION)
214
# Get a handle to all of the revisions we will need
216
revisions = dict((rev.revision_id, rev) for rev in
217
branch.repository.get_revisions(merge_extra))
218
except errors.NoSuchRevision:
219
# One of the sub nodes is a ghost, check each one
221
for revision_id in merge_extra:
223
rev = branch.repository.get_revisions([revision_id])[0]
224
except errors.NoSuchRevision:
225
revisions[revision_id] = None
227
revisions[revision_id] = rev
229
# Display the revisions brought in by this merge.
230
rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
231
branch.repository.get_parent_map(merge_extra))
232
# Skip the first node
233
num, first, depth, eom = rev_id_iterator.next()
235
raise AssertionError('Somehow we misunderstood how'
236
' iter_topo_order works %s != %s' % (first, merge))
237
for num, sub_merge, depth, eom in rev_id_iterator:
238
rev = revisions[sub_merge]
240
to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
242
log_message = log_formatter.log_string(None,
243
revisions[sub_merge],
244
term_width - len(sub_prefix))
245
to_file.write(sub_prefix + log_message + '\n')
248
def _raise_if_nonexistent(paths, old_tree, new_tree):
249
"""Complain if paths are not in either inventory or tree.
251
It's OK with the files exist in either tree's inventory, or
252
if they exist in the tree but are not versioned.
254
This can be used by operations such as bzr status that can accept
255
unknown or ignored files.
257
mutter("check paths: %r", paths)
260
s = old_tree.filter_unversioned_files(paths)
261
s = new_tree.filter_unversioned_files(s)
262
s = [path for path in s if not new_tree.has_filename(path)]
264
raise errors.PathsDoNotExist(sorted(s))