~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/info.py

  • Committer: Robert Collins
  • Date: 2007-07-04 08:08:13 UTC
  • mfrom: (2572 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2587.
  • Revision ID: robertc@robertcollins.net-20070704080813-wzebx0r88fvwj5rq
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
 
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
6
6
# (at your option) any later version.
7
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
19
import time
20
20
 
21
21
 
22
 
import bzrlib.diff as diff
 
22
from bzrlib import (
 
23
    bzrdir,
 
24
    diff,
 
25
    osutils,
 
26
    urlutils,
 
27
    )
23
28
from bzrlib.errors import (NoWorkingTree, NotBranchError,
24
29
                           NoRepositoryPresent, NotLocalUrl)
25
30
from bzrlib.missing import find_unmerged
26
 
import bzrlib.osutils as osutils
27
 
from bzrlib.symbol_versioning import (deprecated_function, 
28
 
        zero_eight)
 
31
from bzrlib.symbol_versioning import (deprecated_function,
 
32
        zero_eight, zero_eighteen)
29
33
 
30
34
 
31
35
def plural(n, base='', pl=None):
37
41
        return 's'
38
42
 
39
43
 
40
 
def _repo_relpath(repo_path, path):
 
44
def _repo_rel_url(repo_url, inner_url):
41
45
    """Return path with common prefix of repository path removed.
42
46
 
43
47
    If path is not part of the repository, the original path is returned.
44
48
    If path is equal to the repository, the current directory marker '.' is
45
49
    returned.
 
50
    Otherwise, a relative path is returned, with trailing '/' stripped.
46
51
    """
47
 
    path = osutils.normalizepath(path)
48
 
    repo_path = osutils.normalizepath(repo_path)
49
 
    if path == repo_path:
 
52
    inner_url = urlutils.normalize_url(inner_url)
 
53
    repo_url = urlutils.normalize_url(repo_url)
 
54
    if inner_url == repo_url:
50
55
        return '.'
51
 
    if osutils.is_inside(repo_path, path):
52
 
        return osutils.relpath(repo_path, path)
53
 
    return path
54
 
 
55
 
 
56
 
def _show_location_info(repository, branch=None, working=None):
57
 
    """Show known locations for working, branch and repository."""
 
56
    result = urlutils.relative_url(repo_url, inner_url)
 
57
    if result != inner_url:
 
58
        result = result.rstrip('/')
 
59
    return result
 
60
 
 
61
class _UrlList(object):
 
62
 
 
63
    def __init__(self):
 
64
        self.urls = []
 
65
 
 
66
    def add_url(self, label, url):
 
67
        self.add_path(label, urlutils.unescape_for_display(url, 'ascii'))
 
68
 
 
69
    def add_url(self, label, url):
 
70
        self.add_path(label, url)
 
71
 
 
72
    def add_path(self, label, path):
 
73
        self.urls.append((label, path))
 
74
 
 
75
    def print_lines(self):
 
76
        max_len = max(len(l) for l, u in self.urls)
 
77
        for label, url in self.urls:
 
78
            print "  %*s: %s" % (max_len, label, url)
 
79
 
 
80
 
 
81
def gather_location_info(repository, branch=None, working=None):
 
82
    locs = {}
58
83
    repository_path = repository.bzrdir.root_transport.base
59
 
    print 'Location:'
60
 
    if working and branch:
 
84
    if branch is not None:
 
85
        branch_path = branch.bzrdir.root_transport.base
 
86
        master_path = branch.get_bound_location()
 
87
        if master_path is None:
 
88
            master_path = branch_path
 
89
    else:
 
90
        branch_path = None
 
91
        master_path = None
 
92
    if working:
61
93
        working_path = working.bzrdir.root_transport.base
62
 
        branch_path = branch.bzrdir.root_transport.base
63
94
        if working_path != branch_path:
64
 
            # lightweight checkout
65
 
            print ' light checkout root: %s' % working_path
 
95
            locs['light checkout root'] = working_path
 
96
        if master_path != branch_path:
66
97
            if repository.is_shared():
67
 
                # lightweight checkout of branch in shared repository
68
 
                print '   shared repository: %s' % repository_path
69
 
                print '   repository branch: %s' % (
70
 
                    _repo_relpath(repository_path, branch_path))
 
98
                locs['repository checkout root'] = branch_path
71
99
            else:
72
 
                # lightweight checkout of standalone branch
73
 
                print '  checkout of branch: %s' % branch_path
 
100
                locs['checkout root'] = branch_path
 
101
        if working_path != master_path:
 
102
            locs['checkout of branch'] = master_path
74
103
        elif repository.is_shared():
75
 
            # branch with tree inside shared repository
76
 
            print '    shared repository: %s' % repository_path
77
 
            print '  repository checkout: %s' % (
78
 
                _repo_relpath(repository_path, branch_path))
79
 
        elif branch.get_bound_location():
80
 
            # normal checkout
81
 
            print '       checkout root: %s' % working_path
82
 
            print '  checkout of branch: %s' % branch.get_bound_location()
83
 
        else:
 
104
            locs['repository branch'] = _repo_rel_url(repository_path,
 
105
                branch_path)
 
106
        elif branch_path is not None:
84
107
            # standalone
85
 
            print '  branch root: %s' % working_path
86
 
    elif branch:
87
 
        branch_path = branch.bzrdir.root_transport.base
 
108
            locs['branch root'] = branch_path
 
109
    else:
 
110
        working_path = None
88
111
        if repository.is_shared():
89
 
            # branch is part of shared repository
90
 
            print '  shared repository: %s' % repository_path
91
 
            print '  repository branch: %s' % (
92
 
                _repo_relpath(repository_path, branch_path))
 
112
            # lightweight checkout of branch in shared repository
 
113
            if branch_path is not None:
 
114
                locs['repository branch'] = _repo_rel_url(repository_path,
 
115
                                                          branch_path)
 
116
        elif branch_path is not None:
 
117
            # standalone
 
118
            locs['branch root'] = branch_path
 
119
            if master_path != branch_path:
 
120
                locs['bound to branch'] = master_path
93
121
        else:
94
 
            # standalone branch
95
 
            print '  branch root: %s' % branch_path
96
 
    else:
97
 
        # shared repository
98
 
        assert repository.is_shared()
99
 
        print '  shared repository: %s' % repository_path
 
122
            locs['repository'] = repository_path
 
123
    if repository.is_shared():
 
124
        # lightweight checkout of branch in shared repository
 
125
        locs['shared repository'] = repository_path
 
126
    order = ['light checkout root', 'repository checkout root',
 
127
             'checkout root', 'checkout of branch', 'shared repository',
 
128
             'repository', 'repository branch', 'branch root',
 
129
             'bound to branch']
 
130
    return [(n, locs[n]) for n in order if n in locs]
 
131
 
 
132
 
 
133
def _show_location_info(locs):
 
134
    """Show known locations for working, branch and repository."""
 
135
    print 'Location:'
 
136
    path_list = _UrlList()
 
137
    for name, loc in locs:
 
138
        path_list.add_url(name, loc)
 
139
    path_list.print_lines()
100
140
 
101
141
 
102
142
def _show_related_info(branch):
171
211
    branch = working.branch
172
212
    basis = working.basis_tree()
173
213
    work_inv = working.inventory
174
 
    delta = working.changes_from(basis, want_unchanged=True)
175
 
    history = branch.revision_history()
 
214
    branch_revno, branch_last_revision = branch.last_revision_info()
176
215
    try:
177
216
        tree_last_id = working.get_parent_ids()[0]
178
217
    except IndexError:
179
218
        tree_last_id = None
180
219
 
181
 
    if len(history) and tree_last_id != history[-1]:
 
220
    if branch_revno and tree_last_id != branch_last_revision:
182
221
        tree_last_revno = branch.revision_id_to_revno(tree_last_id)
183
 
        missing_count = len(history) - tree_last_revno
 
222
        missing_count = branch_revno - tree_last_revno
184
223
        print
185
224
        print 'Working tree is out of date: missing %d revision%s.' % (
186
225
            missing_count, plural(missing_count))
210
249
    print '  %8d ignored' % ignore_cnt
211
250
 
212
251
    dir_cnt = 0
213
 
    entries = work_inv.iter_entries()
214
 
    entries.next()
215
 
    dir_cnt = sum(1 for path, ie in entries if ie.kind == 'directory')
 
252
    for file_id in work_inv:
 
253
        if (work_inv.get_file_kind(file_id) == 'directory' and 
 
254
            not work_inv.is_root(file_id)):
 
255
            dir_cnt += 1
216
256
    print '  %8d versioned %s' \
217
257
          % (dir_cnt,
218
258
             plural(dir_cnt, 'subdirectory', 'subdirectories'))
220
260
 
221
261
def _show_branch_stats(branch, verbose):
222
262
    """Show statistics about a branch."""
223
 
    repository = branch.repository
224
 
    history = branch.revision_history()
225
 
 
 
263
    revno, head = branch.last_revision_info()
226
264
    print
227
265
    print 'Branch history:'
228
 
    revno = len(history)
229
266
    print '  %8d revision%s' % (revno, plural(revno))
 
267
    stats = branch.repository.gather_stats(head, committers=verbose)
230
268
    if verbose:
231
 
        committers = {}
232
 
        for rev in history:
233
 
            committers[repository.get_revision(rev).committer] = True
234
 
        print '  %8d committer%s' % (len(committers), plural(len(committers)))
235
 
    if revno > 0:
236
 
        firstrev = repository.get_revision(history[0])
237
 
        age = int((time.time() - firstrev.timestamp) / 3600 / 24)
 
269
        committers = stats['committers']
 
270
        print '  %8d committer%s' % (committers, plural(committers))
 
271
    if revno:
 
272
        timestamp, timezone = stats['firstrev']
 
273
        age = int((time.time() - timestamp) / 3600 / 24)
238
274
        print '  %8d day%s old' % (age, plural(age))
239
 
        print '   first revision: %s' % osutils.format_date(firstrev.timestamp,
240
 
                                                            firstrev.timezone)
241
 
 
242
 
        lastrev = repository.get_revision(history[-1])
243
 
        print '  latest revision: %s' % osutils.format_date(lastrev.timestamp,
244
 
                                                            lastrev.timezone)
245
 
 
246
 
#     print
247
 
#     print 'Text store:'
248
 
#     c, t = branch.text_store.total_size()
249
 
#     print '  %8d file texts' % c
250
 
#     print '  %8d KiB' % (t/1024)
251
 
 
252
 
#     print
253
 
#     print 'Inventory store:'
254
 
#     c, t = branch.inventory_store.total_size()
255
 
#     print '  %8d inventories' % c
256
 
#     print '  %8d KiB' % (t/1024)
 
275
        print '   first revision: %s' % osutils.format_date(timestamp,
 
276
            timezone)
 
277
        timestamp, timezone = stats['latestrev']
 
278
        print '  latest revision: %s' % osutils.format_date(timestamp,
 
279
            timezone)
 
280
    return stats
257
281
 
258
282
 
259
283
def _show_repository_info(repository):
263
287
        print 'Create working tree for new branches inside the repository.'
264
288
 
265
289
 
266
 
def _show_repository_stats(repository):
 
290
def _show_repository_stats(stats):
267
291
    """Show statistics about a repository."""
268
 
    if repository.bzrdir.root_transport.listable():
 
292
    if 'revisions' in stats or 'size' in stats:
269
293
        print
270
 
        print 'Revision store:'
271
 
        c, t = repository._revision_store.total_size(repository.get_transaction())
272
 
        print '  %8d revision%s' % (c, plural(c))
273
 
        print '  %8d KiB' % (t/1024)
274
 
 
 
294
        print 'Repository:'
 
295
    if 'revisions' in stats:
 
296
        revisions = stats['revisions']
 
297
        print '  %8d revision%s' % (revisions, plural(revisions))
 
298
    if 'size' in stats:
 
299
        print '  %8d KiB' % (stats['size']/1024)
 
300
 
 
301
def show_bzrdir_info(a_bzrdir, verbose=False):
 
302
    """Output to stdout the 'info' for a_bzrdir."""
 
303
    try:
 
304
        tree = a_bzrdir.open_workingtree(
 
305
            recommend_upgrade=False)
 
306
    except (NoWorkingTree, NotLocalUrl):
 
307
        tree = None
 
308
        try:
 
309
            branch = a_bzrdir.open_branch()
 
310
        except NotBranchError:
 
311
            branch = None
 
312
            try:
 
313
                repository = a_bzrdir.open_repository()
 
314
            except NoRepositoryPresent:
 
315
                # Return silently; cmd_info already returned NotBranchError
 
316
                # if no bzrdir could be opened.
 
317
                return
 
318
            else:
 
319
                lockable = repository
 
320
        else:
 
321
            repository = branch.repository
 
322
            lockable = branch
 
323
    else:
 
324
        branch = tree.branch
 
325
        repository = branch.repository
 
326
        lockable = tree
 
327
 
 
328
    lockable.lock_read()
 
329
    try:
 
330
        show_component_info(a_bzrdir, repository, branch, tree, verbose)
 
331
    finally:
 
332
        lockable.unlock()
 
333
 
 
334
 
 
335
def show_component_info(control, repository, branch=None, working=None,
 
336
    verbose=1):
 
337
    """Write info about all bzrdir components to stdout"""
 
338
    if verbose is False:
 
339
        verbose = 1
 
340
    if verbose is True:
 
341
        verbose = 2
 
342
    layout = describe_layout(repository, branch, working)
 
343
    format = describe_format(control, repository, branch, working)
 
344
    print "%s (format: %s)" % (layout, format)
 
345
    _show_location_info(gather_location_info(repository, branch, working))
 
346
    if verbose == 0:
 
347
        return
 
348
    if branch is not None:
 
349
        _show_related_info(branch)
 
350
    _show_format_info(control, repository, branch, working)
 
351
    _show_locking_info(repository, branch, working)
 
352
    if branch is not None:
 
353
        _show_missing_revisions_branch(branch)
 
354
    if working is not None:
 
355
        _show_missing_revisions_working(working)
 
356
        _show_working_stats(working)
 
357
    elif branch is not None:
 
358
        _show_missing_revisions_branch(branch)
 
359
    if branch is not None:
 
360
        stats = _show_branch_stats(branch, verbose==2)
 
361
    else:
 
362
        stats = repository.gather_stats()
 
363
    if branch is None and working is None:
 
364
        _show_repository_info(repository)
 
365
    _show_repository_stats(stats)
 
366
 
 
367
 
 
368
def describe_layout(repository=None, branch=None, tree=None):
 
369
    """Convert a control directory layout into a user-understandable term
 
370
 
 
371
    Common outputs include "Standalone tree", "Repository branch" and
 
372
    "Checkout".  Uncommon outputs include "Unshared repository with trees"
 
373
    and "Empty control directory"
 
374
    """
 
375
    if repository is None:
 
376
        return 'Empty control directory'
 
377
    if branch is None and tree is None:
 
378
        if repository.is_shared():
 
379
            phrase = 'Shared repository'
 
380
        else:
 
381
            phrase = 'Unshared repository'
 
382
        if repository.make_working_trees():
 
383
            phrase += ' with trees'
 
384
        return phrase
 
385
    else:
 
386
        if repository.is_shared():
 
387
            independence = "Repository "
 
388
        else:
 
389
            independence = "Standalone "
 
390
        if tree is not None:
 
391
            phrase = "tree"
 
392
        else:
 
393
            phrase = "branch"
 
394
        if branch is None and tree is not None:
 
395
            phrase = "branchless tree"
 
396
        else:
 
397
            if (tree is not None and tree.bzrdir.root_transport.base !=
 
398
                branch.bzrdir.root_transport.base):
 
399
                independence = ''
 
400
                phrase = "Lightweight checkout"
 
401
            elif branch.get_bound_location() is not None:
 
402
                if independence == 'Standalone ':
 
403
                    independence = ''
 
404
                if tree is None:
 
405
                    phrase = "Bound branch"
 
406
                else:
 
407
                    phrase = "Checkout"
 
408
        if independence != "":
 
409
            phrase = phrase.lower()
 
410
        return "%s%s" % (independence, phrase)
 
411
 
 
412
 
 
413
def describe_format(control, repository, branch, tree):
 
414
    """Determine the format of an existing control directory
 
415
 
 
416
    Several candidates may be found.  If so, the names are returned as a
 
417
    single string, separated by ' or '.
 
418
 
 
419
    If no matching candidate is found, "unnamed" is returned.
 
420
    """
 
421
    candidates  = []
 
422
    if (branch is not None and tree is not None and
 
423
        branch.bzrdir.root_transport.base !=
 
424
        tree.bzrdir.root_transport.base):
 
425
        branch = None
 
426
        repository = None
 
427
    for key in bzrdir.format_registry.keys():
 
428
        format = bzrdir.format_registry.make_bzrdir(key)
 
429
        if isinstance(format, bzrdir.BzrDirMetaFormat1):
 
430
            if (tree and format.workingtree_format !=
 
431
                tree._format):
 
432
                continue
 
433
            if (branch and format.get_branch_format() !=
 
434
                branch._format):
 
435
                continue
 
436
            if (repository and format.repository_format !=
 
437
                repository._format):
 
438
                continue
 
439
        if format.__class__ is not control._format.__class__:
 
440
            continue
 
441
        candidates.append(key)
 
442
    if len(candidates) == 0:
 
443
        return 'unnamed'
 
444
    new_candidates = [c for c in candidates if c != 'default']
 
445
    if len(new_candidates) > 0:
 
446
        candidates = new_candidates
 
447
    new_candidates = [c for c in candidates if not
 
448
        bzrdir.format_registry.get_info(c).hidden]
 
449
    if len(new_candidates) > 0:
 
450
        candidates = new_candidates
 
451
    return ' or '.join(candidates)
275
452
 
276
453
@deprecated_function(zero_eight)
277
454
def show_info(b):
279
456
    return show_bzrdir_info(b.bzrdir)
280
457
 
281
458
 
282
 
def show_bzrdir_info(a_bzrdir, verbose=False):
283
 
    """Output to stdout the 'info' for a_bzrdir."""
284
 
    try:
285
 
        working = a_bzrdir.open_workingtree()
286
 
        working.lock_read()
287
 
        try:
288
 
            show_tree_info(working, verbose)
289
 
        finally:
290
 
            working.unlock()
291
 
        return
292
 
    except (NoWorkingTree, NotLocalUrl):
293
 
        pass
294
 
 
295
 
    try:
296
 
        branch = a_bzrdir.open_branch()
297
 
        branch.lock_read()
298
 
        try:
299
 
            show_branch_info(branch, verbose)
300
 
        finally:
301
 
            branch.unlock()
302
 
        return
303
 
    except NotBranchError:
304
 
        pass
305
 
 
306
 
    try:
307
 
        repository = a_bzrdir.open_repository()
308
 
        repository.lock_read()
309
 
        try:
310
 
            show_repository_info(repository, verbose)
311
 
        finally:
312
 
            repository.unlock()
313
 
        return
314
 
    except NoRepositoryPresent:
315
 
        pass
316
 
 
317
 
    # Return silently, cmd_info already returned NotBranchError if no bzrdir
318
 
    # could be opened.
319
 
 
320
 
 
 
459
@deprecated_function(zero_eighteen)
321
460
def show_tree_info(working, verbose):
322
461
    """Output to stdout the 'info' for working."""
323
462
    branch = working.branch
324
463
    repository = branch.repository
325
464
    control = working.bzrdir
326
 
 
327
 
    _show_location_info(repository, branch, working)
328
 
    _show_related_info(branch)
329
 
    _show_format_info(control, repository, branch, working)
330
 
    _show_locking_info(repository, branch, working)
331
 
    _show_missing_revisions_branch(branch)
332
 
    _show_missing_revisions_working(working)
333
 
    _show_working_stats(working)
334
 
    _show_branch_stats(branch, verbose)
335
 
    _show_repository_stats(repository)
336
 
 
337
 
 
 
465
    show_component_info(control, repository, branch, working, verbose)
 
466
 
 
467
 
 
468
@deprecated_function(zero_eighteen)
338
469
def show_branch_info(branch, verbose):
339
470
    """Output to stdout the 'info' for branch."""
340
471
    repository = branch.repository
341
472
    control = branch.bzrdir
342
 
 
343
 
    _show_location_info(repository, branch)
344
 
    _show_related_info(branch)
345
 
    _show_format_info(control, repository, branch)
346
 
    _show_locking_info(repository, branch)
347
 
    _show_missing_revisions_branch(branch)
348
 
    _show_branch_stats(branch, verbose)
349
 
    _show_repository_stats(repository)
350
 
 
351
 
 
 
473
    show_component_info(control, repository, branch, verbose=verbose)
 
474
 
 
475
 
 
476
@deprecated_function(zero_eighteen)
352
477
def show_repository_info(repository, verbose):
353
478
    """Output to stdout the 'info' for repository."""
354
479
    control = repository.bzrdir
355
 
 
356
 
    _show_location_info(repository)
357
 
    _show_format_info(control, repository)
358
 
    _show_locking_info(repository)
359
 
    _show_repository_info(repository)
360
 
    _show_repository_stats(repository)
 
480
    show_component_info(control, repository, verbose=verbose)