~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/status.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-05-05 23:15:58 UTC
  • mfrom: (3377.3.45 submitted)
  • Revision ID: pqm@pqm.ubuntu.com-20080505231558-7w3zaehbvtcjk7jv
(jam) Make Graph.find_differences() correct,
        and create a Graph.find_unique_ancestors function.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from bzrlib import (
20
20
    delta as _mod_delta,
 
21
    log,
 
22
    osutils,
21
23
    tree,
 
24
    tsort,
 
25
    revision as _mod_revision,
22
26
    )
23
27
from bzrlib.diff import _raise_if_nonexistent
24
28
import bzrlib.errors as errors
25
 
from bzrlib.log import line_log
26
29
from bzrlib.osutils import is_inside_any
27
30
from bzrlib.symbol_versioning import (deprecated_function,
28
31
        )
143
146
    finally:
144
147
        wt.unlock()
145
148
 
 
149
 
 
150
def _get_sorted_revisions(tip_revision, revision_ids, parent_map):
 
151
    """Get an iterator which will return the revisions in merge sorted order.
 
152
 
 
153
    This will build up a list of all nodes, such that only nodes in the list
 
154
    are referenced. It then uses MergeSorter to return them in 'merge-sorted'
 
155
    order.
 
156
 
 
157
    :param revision_ids: A set of revision_ids
 
158
    :param parent_map: The parent information for each node. Revisions which
 
159
        are considered ghosts should not be present in the map.
 
160
    :return: iterator from MergeSorter.iter_topo_order()
 
161
    """
 
162
    # MergeSorter requires that all nodes be present in the graph, so get rid
 
163
    # of any references pointing outside of this graph.
 
164
    parent_graph = {}
 
165
    for revision_id in revision_ids:
 
166
        if revision_id not in parent_map: # ghost
 
167
            parent_graph[revision_id] = []
 
168
        else:
 
169
            # Only include parents which are in this sub-graph
 
170
            parent_graph[revision_id] = [p for p in parent_map[revision_id]
 
171
                                            if p in revision_ids]
 
172
    sorter = tsort.MergeSorter(parent_graph, tip_revision)
 
173
    return sorter.iter_topo_order()
 
174
 
 
175
 
146
176
def show_pending_merges(new, to_file, short=False):
147
177
    """Write out a display of pending merges in a working tree."""
148
178
    parents = new.get_parent_ids()
149
179
    if len(parents) < 2:
150
180
        return
 
181
 
 
182
    # we need one extra space for terminals that wrap on last char
 
183
    term_width = osutils.terminal_width() - 1
 
184
    if short:
 
185
        first_prefix = 'P   '
 
186
        sub_prefix = 'P.   '
 
187
    else:
 
188
        first_prefix = '  '
 
189
        sub_prefix = '    '
 
190
 
151
191
    pending = parents[1:]
152
192
    branch = new.branch
153
193
    last_revision = parents[0]
154
194
    if not short:
155
195
        to_file.write('pending merges:\n')
156
 
    if last_revision is not None:
157
 
        try:
158
 
            ignore = set(branch.repository.get_ancestry(last_revision,
159
 
                                                        topo_sorted=False))
160
 
        except errors.NoSuchRevision:
161
 
            # the last revision is a ghost : assume everything is new 
162
 
            # except for it
163
 
            ignore = set([None, last_revision])
164
 
    else:
165
 
        ignore = set([None])
166
 
    # TODO: this could be improved using merge_sorted - we'd get the same 
167
 
    # output rather than one level of indent.
 
196
    graph = branch.repository.get_graph()
 
197
    other_revisions = [last_revision]
 
198
    log_formatter = log.LineLogFormatter(to_file)
168
199
    for merge in pending:
169
 
        ignore.add(merge)
170
 
        try:
171
 
            from bzrlib.osutils import terminal_width
172
 
            width = terminal_width() - 1    # we need one extra space to avoid
173
 
                                            # extra blank lines
174
 
            m_revision = branch.repository.get_revision(merge)
175
 
            if short:
176
 
                prefix = 'P   '
177
 
            else:
178
 
                prefix = '  '
179
 
            to_file.write(prefix)
180
 
            to_file.write(line_log(m_revision, width - len(prefix)))
181
 
            to_file.write('\n')
182
 
            inner_merges = branch.repository.get_ancestry(merge)
183
 
            assert inner_merges[0] is None
184
 
            inner_merges.pop(0)
185
 
            inner_merges.reverse()
186
 
            for mmerge in inner_merges:
187
 
                if mmerge in ignore:
188
 
                    continue
189
 
                mm_revision = branch.repository.get_revision(mmerge)
190
 
                if short:
191
 
                    prefix = 'P.   '
 
200
        try:
 
201
            rev = branch.repository.get_revisions([merge])[0]
 
202
        except errors.NoSuchRevision:
 
203
            # If we are missing a revision, just print out the revision id
 
204
            to_file.write(first_prefix + '(ghost) ' + merge + '\n')
 
205
            other_revisions.append(merge)
 
206
            continue
 
207
 
 
208
        # Log the merge, as it gets a slightly different formatting
 
209
        log_message = log_formatter.log_string(None, rev,
 
210
                        term_width - len(first_prefix))
 
211
        to_file.write(first_prefix + log_message + '\n')
 
212
        # Find all of the revisions in the merge source, which are not in the
 
213
        # last committed revision.
 
214
        merge_extra = graph.find_unique_ancestors(merge, other_revisions)
 
215
        other_revisions.append(merge)
 
216
        merge_extra.discard(_mod_revision.NULL_REVISION)
 
217
 
 
218
        # Get a handle to all of the revisions we will need
 
219
        try:
 
220
            revisions = dict((rev.revision_id, rev) for rev in
 
221
                             branch.repository.get_revisions(merge_extra))
 
222
        except errors.NoSuchRevision:
 
223
            # One of the sub nodes is a ghost, check each one
 
224
            revisions = {}
 
225
            for revision_id in merge_extra:
 
226
                try:
 
227
                    rev = branch.repository.get_revisions([revision_id])[0]
 
228
                except errors.NoSuchRevision:
 
229
                    revisions[revision_id] = None
192
230
                else:
193
 
                    prefix = '    '
194
 
                to_file.write(prefix)
195
 
                to_file.write(line_log(mm_revision, width - len(prefix)))
196
 
                to_file.write('\n')
197
 
                ignore.add(mmerge)
198
 
        except errors.NoSuchRevision:
199
 
            if short:
200
 
                prefix = 'P  '
201
 
            else:
202
 
                prefix = ' '
203
 
            to_file.write(prefix + ' ' + merge)
204
 
            to_file.write('\n')
 
231
                    revisions[revision_id] = rev
 
232
 
 
233
        # Display the revisions brought in by this merge.
 
234
        rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
 
235
                            branch.repository.get_parent_map(merge_extra))
 
236
        # Skip the first node
 
237
        num, first, depth, eom = rev_id_iterator.next()
 
238
        if first != merge:
 
239
            raise AssertionError('Somehow we misunderstood how'
 
240
                ' iter_topo_order works %s != %s' % (first, merge))
 
241
        for num, sub_merge, depth, eom in rev_id_iterator:
 
242
            rev = revisions[sub_merge]
 
243
            if rev is None:
 
244
                to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
 
245
                continue
 
246
            log_message = log_formatter.log_string(None,
 
247
                            revisions[sub_merge],
 
248
                            term_width - len(sub_prefix))
 
249
            to_file.write(sub_prefix + log_message + '\n')