/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/status.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import sys
20
18
 
21
 
from . import (
 
19
from bzrlib import (
22
20
    delta as _mod_delta,
23
 
    hooks as _mod_hooks,
24
21
    log,
25
22
    osutils,
26
23
    tsort,
27
24
    revision as _mod_revision,
28
25
    )
29
 
from . import errors as errors
30
 
from .sixish import text_type
31
 
from .trace import mutter, warning
32
 
from .workingtree import ShelvingUnsupported
33
 
 
 
26
import bzrlib.errors as errors
 
27
from bzrlib.trace import mutter, warning
34
28
 
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)
37
31
 
38
32
 
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.
44
38
 
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.
47
41
 
48
42
    For short output, it creates an iterator on all changes, and lets a given
64
58
        files.
65
59
    :param show_ids: If set, includes each file's id.
66
60
    :param want_unversioned: If False, only shows versioned files.
67
 
    :param classify: Add special symbols to indicate file kind.
68
61
    """
69
62
 
70
63
    if short:
71
64
        changes = new.iter_changes(old, want_unchanged, specific_files,
72
65
            require_versioned=False, want_unversioned=want_unversioned)
73
66
        _mod_delta.report_changes(changes, show_short_reporter)
 
67
        
74
68
    else:
75
69
        delta = new.changes_from(old, want_unchanged=want_unchanged,
76
70
                              specific_files=specific_files,
79
73
        # this
80
74
        delta.unversioned = [unversioned for unversioned in
81
75
            delta.unversioned if not new.is_ignored(unversioned[0])]
82
 
        show_long_callback(to_file, delta,
 
76
        show_long_callback(to_file, delta, 
83
77
                           show_ids=show_ids,
84
 
                           show_unchanged=want_unchanged,
85
 
                           classify=classify)
86
 
 
87
 
 
88
 
def show_tree_status(wt,
 
78
                           show_unchanged=want_unchanged)
 
79
 
 
80
 
 
81
def show_tree_status(wt, show_unchanged=None,
89
82
                     specific_files=None,
90
83
                     show_ids=False,
91
84
                     to_file=None,
94
87
                     short=False,
95
88
                     verbose=False,
96
89
                     versioned=False,
97
 
                     classify=True,
98
90
                     show_long_callback=_mod_delta.report_delta):
99
91
    """Display summary of changes.
100
92
 
108
100
    If showing the status of a working tree, extra information is included
109
101
    about unknown files, conflicts, and pending merges.
110
102
 
 
103
    :param show_unchanged: Deprecated parameter. If set, includes unchanged
 
104
        files.
111
105
    :param specific_files: If set, a list of filenames whose status should be
112
106
        shown.  It is an error to give a filename that is not in the working
113
107
        tree, or in the working inventory or in the basis inventory.
122
116
    :param verbose: If True, show all merged revisions, not just
123
117
        the merge tips
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
128
121
    """
 
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)
 
125
 
129
126
    if to_file is None:
130
127
        to_file = sys.stdout
131
128
 
132
 
    with wt.lock_read():
 
129
    wt.lock_read()
 
130
    try:
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'")
137
135
            new = wt
138
136
            old = new.basis_tree()
139
137
        elif len(revision) > 0:
140
138
            try:
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):
145
143
                try:
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))
150
148
            else:
151
149
                new = wt
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))
156
 
 
 
150
        old.lock_read()
 
151
        new.lock_read()
 
152
        try:
157
153
            specific_files, nonexistents \
158
154
                = _filter_nonexistent(specific_files, old, new)
159
155
            want_unversioned = not versioned
160
156
 
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)
168
164
 
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
193
189
                    prefix = 'C  '
194
190
                else:
195
191
                    prefix = ' '
196
 
                to_file.write("%s %s\n" % (prefix, conflict.describe()))
 
192
                to_file.write("%s %s\n" % (prefix, conflict))
197
193
            # Show files that were requested but don't exist (and are
198
194
            # not versioned).  We don't involve delta in this; these
199
195
            # paths are really the province of just the status
214
210
                show_pending_merges(new, to_file, short, verbose=verbose)
215
211
            if nonexistents:
216
212
                raise errors.PathsDoNotExist(nonexistents)
217
 
            for hook in hooks['post_status']:
218
 
                hook(StatusHookParams(old, new, to_file, versioned,
219
 
                    show_ids, short, verbose, specific_files=specific_files))
 
213
        finally:
 
214
            old.unlock()
 
215
            new.unlock()
 
216
    finally:
 
217
        wt.unlock()
220
218
 
221
219
 
222
220
def _get_sorted_revisions(tip_revision, revision_ids, parent_map):
284
282
    log_formatter = log.LineLogFormatter(to_file)
285
283
    for merge in pending:
286
284
        try:
287
 
            rev = branch.repository.get_revision(merge)
 
285
            rev = branch.repository.get_revisions([merge])[0]
288
286
        except errors.NoSuchRevision:
289
287
            # If we are missing a revision, just print out the revision id
290
 
            to_file.write(first_prefix + '(ghost) ' + merge.decode('utf-8') + '\n')
 
288
            to_file.write(first_prefix + '(ghost) ' + merge + '\n')
291
289
            other_revisions.append(merge)
292
290
            continue
293
291
 
303
301
        merge_extra.discard(_mod_revision.NULL_REVISION)
304
302
 
305
303
        # Get a handle to all of the revisions we will need
306
 
        revisions = dict(branch.repository.iter_revisions(merge_extra))
 
304
        try:
 
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
 
309
            revisions = {}
 
310
            for revision_id in merge_extra:
 
311
                try:
 
312
                    rev = branch.repository.get_revisions([revision_id])[0]
 
313
                except errors.NoSuchRevision:
 
314
                    revisions[revision_id] = None
 
315
                else:
 
316
                    revisions[revision_id] = rev
307
317
 
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]
318
328
            if rev is None:
319
 
                to_file.write(sub_prefix + '(ghost) ' + sub_merge.decode('utf-8') + '\n')
 
329
                to_file.write(sub_prefix + '(ghost) ' + sub_merge + '\n')
320
330
                continue
321
331
            show_log_message(revisions[sub_merge], sub_prefix)
322
332
 
331
341
 
332
342
    If either of the two lists is empty, return it as an empty list.
333
343
 
334
 
    This can be used by operations such as brz status that can accept
 
344
    This can be used by operations such as bzr status that can accept
335
345
    unknown or ignored files.
336
346
    """
337
347
    mutter("check paths: %r", orig_paths)
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)
349
 
 
350
 
 
351
 
class StatusHooks(_mod_hooks.Hooks):
352
 
    """A dictionary mapping hook name to a list of callables for status hooks.
353
 
 
354
 
    e.g. ['post_status'] Is the list of items to be called when the
355
 
    status command has finished printing the status.
356
 
    """
357
 
 
358
 
    def __init__(self):
359
 
        """Create the default hooks.
360
 
 
361
 
        These are all empty initially, because by default nothing should get
362
 
        notified.
363
 
        """
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.",
372
 
            (2, 3))
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.",
380
 
            (2, 3))
381
 
 
382
 
 
383
 
class StatusHookParams(object):
384
 
    """Object holding parameters passed to post_status hooks.
385
 
 
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.
393
 
    """
394
 
 
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.
398
 
 
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.
409
 
        """
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
415
 
        self.short = short
416
 
        self.verbose = verbose
417
 
        self.specific_files = specific_files
418
 
 
419
 
    def __eq__(self, other):
420
 
        return self.__dict__ == other.__dict__
421
 
 
422
 
    def __repr__(self):
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)
426
 
 
427
 
 
428
 
def _show_shelve_summary(params):
429
 
    """post_status hook to display a summary of shelves.
430
 
 
431
 
    :param params: StatusHookParams.
432
 
    """
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:
436
 
        return
437
 
    get_shelf_manager = getattr(params.new_tree, 'get_shelf_manager', None)
438
 
    if get_shelf_manager is None:
439
 
        return
440
 
    try:
441
 
        manager = get_shelf_manager()
442
 
    except ShelvingUnsupported:
443
 
        mutter('shelving not supported by tree, not displaying shelves.')
444
 
    else:
445
 
        shelves = manager.active_shelves()
446
 
        if shelves:
447
 
            singular = '%d shelf exists. '
448
 
            plural = '%d shelves exist. '
449
 
            if len(shelves) == 1:
450
 
                fmt = singular
451
 
            else:
452
 
                fmt = plural
453
 
            params.to_file.write(fmt % len(shelves))
454
 
            params.to_file.write('See "brz shelve --list" for details.\n')
455
 
 
456
 
 
457
 
hooks = StatusHooks()
458
 
 
459
 
 
460
 
hooks.install_named_hook('post_status', _show_shelve_summary,
461
 
    'brz status')
462