~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/info.py

  • Committer: Robert Collins
  • Date: 2009-07-07 04:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4524.
  • Revision ID: robertc@robertcollins.net-20090707043213-4hjjhgr40iq7gk2d
More informative assertions in xml serialisation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
 
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
__all__ = ['show_bzrdir_info']
18
18
 
 
19
from cStringIO import StringIO
19
20
import os
20
21
import time
21
22
import sys
24
25
    bzrdir,
25
26
    diff,
26
27
    errors,
 
28
    hooks as _mod_hooks,
27
29
    osutils,
28
30
    urlutils,
29
31
    )
30
32
from bzrlib.errors import (NoWorkingTree, NotBranchError,
31
33
                           NoRepositoryPresent, NotLocalUrl)
32
34
from bzrlib.missing import find_unmerged
33
 
from bzrlib.symbol_versioning import (deprecated_function,
34
 
        zero_eight, zero_eighteen)
35
35
 
36
36
 
37
37
def plural(n, base='', pl=None):
128
128
    return [(n, locs[n]) for n in order if n in locs]
129
129
 
130
130
 
131
 
def _show_location_info(locs):
 
131
def _show_location_info(locs, outfile):
132
132
    """Show known locations for working, branch and repository."""
133
 
    print 'Location:'
134
 
    path_list = LocationList(os.getcwd())
 
133
    outfile.write('Location:\n')
 
134
    path_list = LocationList(osutils.getcwd())
135
135
    for name, loc in locs:
136
136
        path_list.add_url(name, loc)
137
 
    sys.stdout.writelines(path_list.get_lines())
 
137
    outfile.writelines(path_list.get_lines())
 
138
 
138
139
 
139
140
def _gather_related_branches(branch):
140
 
    locs = LocationList(os.getcwd())
 
141
    locs = LocationList(osutils.getcwd())
141
142
    locs.add_url('public branch', branch.get_public_branch())
142
143
    locs.add_url('push branch', branch.get_push_location())
143
144
    locs.add_url('parent branch', branch.get_parent())
144
145
    locs.add_url('submit branch', branch.get_submit_branch())
 
146
    try:
 
147
        locs.add_url('stacked on', branch.get_stacked_on_url())
 
148
    except (errors.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
 
149
        errors.NotStacked):
 
150
        pass
145
151
    return locs
146
152
 
 
153
 
147
154
def _show_related_info(branch, outfile):
148
155
    """Show parent and push location of branch."""
149
156
    locs = _gather_related_branches(branch)
150
157
    if len(locs.locs) > 0:
151
 
        print >> outfile
152
 
        print >> outfile, 'Related branches:'
 
158
        outfile.write('\n')
 
159
        outfile.write('Related branches:\n')
153
160
        outfile.writelines(locs.get_lines())
154
161
 
155
162
 
156
 
def _show_format_info(control=None, repository=None, branch=None, working=None):
 
163
def _show_format_info(control=None, repository=None, branch=None,
 
164
                      working=None, outfile=None):
157
165
    """Show known formats for control, working, branch and repository."""
158
 
    print
159
 
    print 'Format:'
 
166
    outfile.write('\n')
 
167
    outfile.write('Format:\n')
160
168
    if control:
161
 
        print '       control: %s' % control._format.get_format_description()
 
169
        outfile.write('       control: %s\n' %
 
170
            control._format.get_format_description())
162
171
    if working:
163
 
        print '  working tree: %s' % working._format.get_format_description()
 
172
        outfile.write('  working tree: %s\n' %
 
173
            working._format.get_format_description())
164
174
    if branch:
165
 
        print '        branch: %s' % branch._format.get_format_description()
 
175
        outfile.write('        branch: %s\n' %
 
176
            branch._format.get_format_description())
166
177
    if repository:
167
 
        print '    repository: %s' % repository._format.get_format_description()
168
 
 
169
 
 
170
 
def _show_locking_info(repository, branch=None, working=None):
 
178
        outfile.write('    repository: %s\n' %
 
179
            repository._format.get_format_description())
 
180
 
 
181
 
 
182
def _show_locking_info(repository, branch=None, working=None, outfile=None):
171
183
    """Show locking status of working, branch and repository."""
172
184
    if (repository.get_physical_lock_status() or
173
185
        (branch and branch.get_physical_lock_status()) or
174
186
        (working and working.get_physical_lock_status())):
175
 
        print
176
 
        print 'Lock status:'
 
187
        outfile.write('\n')
 
188
        outfile.write('Lock status:\n')
177
189
        if working:
178
190
            if working.get_physical_lock_status():
179
191
                status = 'locked'
180
192
            else:
181
193
                status = 'unlocked'
182
 
            print '  working tree: %s' % status
 
194
            outfile.write('  working tree: %s\n' % status)
183
195
        if branch:
184
196
            if branch.get_physical_lock_status():
185
197
                status = 'locked'
186
198
            else:
187
199
                status = 'unlocked'
188
 
            print '        branch: %s' % status
 
200
            outfile.write('        branch: %s\n' % status)
189
201
        if repository:
190
202
            if repository.get_physical_lock_status():
191
203
                status = 'locked'
192
204
            else:
193
205
                status = 'unlocked'
194
 
            print '    repository: %s' % status
195
 
 
196
 
 
197
 
def _show_missing_revisions_branch(branch):
 
206
            outfile.write('    repository: %s\n' % status)
 
207
 
 
208
 
 
209
def _show_missing_revisions_branch(branch, outfile):
198
210
    """Show missing master revisions in branch."""
199
211
    # Try with inaccessible branch ?
200
212
    master = branch.get_master_branch()
201
213
    if master:
202
214
        local_extra, remote_extra = find_unmerged(branch, master)
203
215
        if remote_extra:
204
 
            print
205
 
            print 'Branch is out of date: missing %d revision%s.' % (
206
 
                len(remote_extra), plural(len(remote_extra)))
207
 
 
208
 
 
209
 
def _show_missing_revisions_working(working):
 
216
            outfile.write('\n')
 
217
            outfile.write(('Branch is out of date: missing %d '
 
218
                'revision%s.\n') % (len(remote_extra),
 
219
                plural(len(remote_extra))))
 
220
 
 
221
 
 
222
def _show_missing_revisions_working(working, outfile):
210
223
    """Show missing revisions in working tree."""
211
224
    branch = working.branch
212
225
    basis = working.basis_tree()
220
233
    if branch_revno and tree_last_id != branch_last_revision:
221
234
        tree_last_revno = branch.revision_id_to_revno(tree_last_id)
222
235
        missing_count = branch_revno - tree_last_revno
223
 
        print
224
 
        print 'Working tree is out of date: missing %d revision%s.' % (
225
 
            missing_count, plural(missing_count))
226
 
 
227
 
 
228
 
def _show_working_stats(working):
 
236
        outfile.write('\n')
 
237
        outfile.write(('Working tree is out of date: missing %d '
 
238
            'revision%s.\n') % (missing_count, plural(missing_count)))
 
239
 
 
240
 
 
241
def _show_working_stats(working, outfile):
229
242
    """Show statistics about a working tree."""
230
243
    basis = working.basis_tree()
231
244
    work_inv = working.inventory
232
245
    delta = working.changes_from(basis, want_unchanged=True)
233
246
 
234
 
    print
235
 
    print 'In the working tree:'
236
 
    print '  %8s unchanged' % len(delta.unchanged)
237
 
    print '  %8d modified' % len(delta.modified)
238
 
    print '  %8d added' % len(delta.added)
239
 
    print '  %8d removed' % len(delta.removed)
240
 
    print '  %8d renamed' % len(delta.renamed)
 
247
    outfile.write('\n')
 
248
    outfile.write('In the working tree:\n')
 
249
    outfile.write('  %8s unchanged\n' % len(delta.unchanged))
 
250
    outfile.write('  %8d modified\n' % len(delta.modified))
 
251
    outfile.write('  %8d added\n' % len(delta.added))
 
252
    outfile.write('  %8d removed\n' % len(delta.removed))
 
253
    outfile.write('  %8d renamed\n' % len(delta.renamed))
241
254
 
242
255
    ignore_cnt = unknown_cnt = 0
243
256
    for path in working.extras():
245
258
            ignore_cnt += 1
246
259
        else:
247
260
            unknown_cnt += 1
248
 
    print '  %8d unknown' % unknown_cnt
249
 
    print '  %8d ignored' % ignore_cnt
 
261
    outfile.write('  %8d unknown\n' % unknown_cnt)
 
262
    outfile.write('  %8d ignored\n' % ignore_cnt)
250
263
 
251
264
    dir_cnt = 0
252
265
    for file_id in work_inv:
253
 
        if (work_inv.get_file_kind(file_id) == 'directory' and 
 
266
        if (work_inv.get_file_kind(file_id) == 'directory' and
254
267
            not work_inv.is_root(file_id)):
255
268
            dir_cnt += 1
256
 
    print '  %8d versioned %s' \
257
 
          % (dir_cnt,
258
 
             plural(dir_cnt, 'subdirectory', 'subdirectories'))
259
 
 
260
 
 
261
 
def _show_branch_stats(branch, verbose):
 
269
    outfile.write('  %8d versioned %s\n' % (dir_cnt,
 
270
        plural(dir_cnt, 'subdirectory', 'subdirectories')))
 
271
 
 
272
 
 
273
def _show_branch_stats(branch, verbose, outfile):
262
274
    """Show statistics about a branch."""
263
275
    revno, head = branch.last_revision_info()
264
 
    print
265
 
    print 'Branch history:'
266
 
    print '  %8d revision%s' % (revno, plural(revno))
 
276
    outfile.write('\n')
 
277
    outfile.write('Branch history:\n')
 
278
    outfile.write('  %8d revision%s\n' % (revno, plural(revno)))
267
279
    stats = branch.repository.gather_stats(head, committers=verbose)
268
280
    if verbose:
269
281
        committers = stats['committers']
270
 
        print '  %8d committer%s' % (committers, plural(committers))
 
282
        outfile.write('  %8d committer%s\n' % (committers,
 
283
            plural(committers)))
271
284
    if revno:
272
285
        timestamp, timezone = stats['firstrev']
273
286
        age = int((time.time() - timestamp) / 3600 / 24)
274
 
        print '  %8d day%s old' % (age, plural(age))
275
 
        print '   first revision: %s' % osutils.format_date(timestamp,
276
 
            timezone)
 
287
        outfile.write('  %8d day%s old\n' % (age, plural(age)))
 
288
        outfile.write('   first revision: %s\n' %
 
289
            osutils.format_date(timestamp, timezone))
277
290
        timestamp, timezone = stats['latestrev']
278
 
        print '  latest revision: %s' % osutils.format_date(timestamp,
279
 
            timezone)
 
291
        outfile.write('  latest revision: %s\n' %
 
292
            osutils.format_date(timestamp, timezone))
280
293
    return stats
281
294
 
282
295
 
283
 
def _show_repository_info(repository):
 
296
def _show_repository_info(repository, outfile):
284
297
    """Show settings of a repository."""
285
298
    if repository.make_working_trees():
286
 
        print
287
 
        print 'Create working tree for new branches inside the repository.'
288
 
 
289
 
 
290
 
def _show_repository_stats(stats):
 
299
        outfile.write('\n')
 
300
        outfile.write('Create working tree for new branches inside '
 
301
            'the repository.\n')
 
302
 
 
303
 
 
304
def _show_repository_stats(repository, stats, outfile):
291
305
    """Show statistics about a repository."""
292
 
    if 'revisions' in stats or 'size' in stats:
293
 
        print
294
 
        print 'Repository:'
 
306
    f = StringIO()
295
307
    if 'revisions' in stats:
296
308
        revisions = stats['revisions']
297
 
        print '  %8d revision%s' % (revisions, plural(revisions))
 
309
        f.write('  %8d revision%s\n' % (revisions, plural(revisions)))
298
310
    if 'size' in stats:
299
 
        print '  %8d KiB' % (stats['size']/1024)
300
 
 
301
 
def show_bzrdir_info(a_bzrdir, verbose=False):
 
311
        f.write('  %8d KiB\n' % (stats['size']/1024))
 
312
    for hook in hooks['repository']:
 
313
        hook(repository, stats, f)
 
314
    if f.getvalue() != "":
 
315
        outfile.write('\n')
 
316
        outfile.write('Repository:\n')
 
317
        outfile.write(f.getvalue())
 
318
 
 
319
 
 
320
def show_bzrdir_info(a_bzrdir, verbose=False, outfile=None):
302
321
    """Output to stdout the 'info' for a_bzrdir."""
 
322
    if outfile is None:
 
323
        outfile = sys.stdout
303
324
    try:
304
325
        tree = a_bzrdir.open_workingtree(
305
326
            recommend_upgrade=False)
327
348
 
328
349
    lockable.lock_read()
329
350
    try:
330
 
        show_component_info(a_bzrdir, repository, branch, tree, verbose)
 
351
        show_component_info(a_bzrdir, repository, branch, tree, verbose,
 
352
                            outfile)
331
353
    finally:
332
354
        lockable.unlock()
333
355
 
334
356
 
335
357
def show_component_info(control, repository, branch=None, working=None,
336
 
    verbose=1):
 
358
    verbose=1, outfile=None):
337
359
    """Write info about all bzrdir components to stdout"""
 
360
    if outfile is None:
 
361
        outfile = sys.stdout
338
362
    if verbose is False:
339
363
        verbose = 1
340
364
    if verbose is True:
341
365
        verbose = 2
342
366
    layout = describe_layout(repository, branch, working)
343
367
    format = describe_format(control, repository, branch, working)
344
 
    print "%s (format: %s)" % (layout, format)
345
 
    _show_location_info(gather_location_info(repository, branch, working))
 
368
    outfile.write("%s (format: %s)\n" % (layout, format))
 
369
    _show_location_info(gather_location_info(repository, branch, working),
 
370
                        outfile)
346
371
    if branch is not None:
347
 
        _show_related_info(branch, sys.stdout)
 
372
        _show_related_info(branch, outfile)
348
373
    if verbose == 0:
349
374
        return
350
 
    _show_format_info(control, repository, branch, working)
351
 
    _show_locking_info(repository, branch, working)
 
375
    _show_format_info(control, repository, branch, working, outfile)
 
376
    _show_locking_info(repository, branch, working, outfile)
352
377
    if branch is not None:
353
 
        _show_missing_revisions_branch(branch)
 
378
        _show_missing_revisions_branch(branch, outfile)
354
379
    if working is not None:
355
 
        _show_missing_revisions_working(working)
356
 
        _show_working_stats(working)
 
380
        _show_missing_revisions_working(working, outfile)
 
381
        _show_working_stats(working, outfile)
357
382
    elif branch is not None:
358
 
        _show_missing_revisions_branch(branch)
 
383
        _show_missing_revisions_branch(branch, outfile)
359
384
    if branch is not None:
360
 
        stats = _show_branch_stats(branch, verbose==2)
 
385
        show_committers = verbose >= 2
 
386
        stats = _show_branch_stats(branch, show_committers, outfile)
361
387
    else:
362
388
        stats = repository.gather_stats()
363
389
    if branch is None and working is None:
364
 
        _show_repository_info(repository)
365
 
    _show_repository_stats(stats)
 
390
        _show_repository_info(repository, outfile)
 
391
    _show_repository_stats(repository, stats, outfile)
366
392
 
367
393
 
368
394
def describe_layout(repository=None, branch=None, tree=None):
424
450
        tree.bzrdir.root_transport.base):
425
451
        branch = None
426
452
        repository = None
427
 
    for key in bzrdir.format_registry.keys():
 
453
    non_aliases = set(bzrdir.format_registry.keys())
 
454
    non_aliases.difference_update(bzrdir.format_registry.aliases())
 
455
    for key in non_aliases:
428
456
        format = bzrdir.format_registry.make_bzrdir(key)
429
457
        if isinstance(format, bzrdir.BzrDirMetaFormat1):
430
458
            if (tree and format.workingtree_format !=
441
469
        candidates.append(key)
442
470
    if len(candidates) == 0:
443
471
        return 'unnamed'
444
 
    new_candidates = [c for c in candidates if c != 'default']
445
 
    if len(new_candidates) > 0:
446
 
        candidates = new_candidates
 
472
    candidates.sort()
447
473
    new_candidates = [c for c in candidates if not
448
474
        bzrdir.format_registry.get_info(c).hidden]
449
475
    if len(new_candidates) > 0:
 
476
        # If there are any non-hidden formats that match, only return those to
 
477
        # avoid listing hidden formats except when only a hidden format will
 
478
        # do.
450
479
        candidates = new_candidates
451
480
    return ' or '.join(candidates)
452
481
 
453
 
@deprecated_function(zero_eight)
454
 
def show_info(b):
455
 
    """Please see show_bzrdir_info."""
456
 
    return show_bzrdir_info(b.bzrdir)
457
 
 
458
 
 
459
 
@deprecated_function(zero_eighteen)
460
 
def show_tree_info(working, verbose):
461
 
    """Output to stdout the 'info' for working."""
462
 
    branch = working.branch
463
 
    repository = branch.repository
464
 
    control = working.bzrdir
465
 
    show_component_info(control, repository, branch, working, verbose)
466
 
 
467
 
 
468
 
@deprecated_function(zero_eighteen)
469
 
def show_branch_info(branch, verbose):
470
 
    """Output to stdout the 'info' for branch."""
471
 
    repository = branch.repository
472
 
    control = branch.bzrdir
473
 
    show_component_info(control, repository, branch, verbose=verbose)
474
 
 
475
 
 
476
 
@deprecated_function(zero_eighteen)
477
 
def show_repository_info(repository, verbose):
478
 
    """Output to stdout the 'info' for repository."""
479
 
    control = repository.bzrdir
480
 
    show_component_info(control, repository, verbose=verbose)
 
482
 
 
483
class InfoHooks(_mod_hooks.Hooks):
 
484
    """Hooks for the info command."""
 
485
 
 
486
    def __init__(self):
 
487
        self.create_hook(_mod_hooks.HookPoint('repository',
 
488
            "Invoked when displaying the statistics for a repository. "
 
489
            "repository is called with a statistics dictionary as returned "
 
490
            "by the repository and a file-like object to write to.", (1, 15), 
 
491
            None))
 
492
 
 
493
 
 
494
hooks = InfoHooks()