1
# Copyright (C) 2004, 2005 by Martin Pool
2
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
7
5
# the Free Software Foundation; either version 2 of the License, or
8
6
# (at your option) any later version.
10
8
# This program is distributed in the hope that it will be useful,
11
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
11
# GNU General Public License for more details.
15
13
# You should have received a copy of the GNU General Public License
16
14
# along with this program; if not, write to the Free Software
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
__all__ = ['show_bzrdir_info']
21
from osutils import format_date
25
# surely there's a builtin for this?
36
print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
38
def plural(n, base='', pl=None):
28
from bzrlib.errors import (NoWorkingTree, NotBranchError,
29
NoRepositoryPresent, NotLocalUrl)
30
from bzrlib.missing import find_unmerged
31
from bzrlib.symbol_versioning import (deprecated_function,
32
zero_eight, zero_eighteen)
35
def plural(n, base='', pl=None):
44
def _repo_rel_url(repo_url, inner_url):
45
"""Return path with common prefix of repository path removed.
47
If path is not part of the repository, the original path is returned.
48
If path is equal to the repository, the current directory marker '.' is
50
Otherwise, a relative path is returned, with trailing '/' stripped.
52
inner_url = urlutils.normalize_url(inner_url)
53
repo_url = urlutils.normalize_url(repo_url)
54
if inner_url == repo_url:
56
result = urlutils.relative_url(repo_url, inner_url)
57
if result != inner_url:
58
result = result.rstrip('/')
61
class _UrlList(object):
66
def add_url(self, label, url):
67
self.add_path(label, urlutils.unescape_for_display(url, 'ascii'))
69
def add_url(self, label, url):
70
self.add_path(label, url)
72
def add_path(self, label, path):
73
self.urls.append((label, path))
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)
81
def gather_location_info(repository, branch=None, working=None):
83
repository_path = repository.bzrdir.root_transport.base
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
93
working_path = working.bzrdir.root_transport.base
94
if working_path != branch_path:
95
locs['light checkout root'] = working_path
96
if master_path != branch_path:
97
if repository.is_shared():
98
locs['repository checkout root'] = branch_path
100
locs['checkout root'] = branch_path
101
if working_path != master_path:
102
locs['checkout of branch'] = master_path
103
elif repository.is_shared():
104
locs['repository branch'] = _repo_rel_url(repository_path,
106
elif branch_path is not None:
108
locs['branch root'] = branch_path
111
if repository.is_shared():
112
# lightweight checkout of branch in shared repository
113
if branch_path is not None:
114
locs['repository branch'] = _repo_rel_url(repository_path,
116
elif branch_path is not None:
118
locs['branch root'] = branch_path
119
if master_path != branch_path:
120
locs['bound to branch'] = master_path
46
count_version_dirs = 0
48
basis = b.basis_tree()
49
working = b.working_tree()
50
work_inv = working.inventory
51
delta = diff.compare_trees(basis, working, want_unchanged=True)
54
print 'in the working tree:'
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',
130
return [(n, locs[n]) for n in order if n in locs]
133
def _show_location_info(locs):
134
"""Show known locations for working, branch and repository."""
136
path_list = _UrlList()
137
for name, loc in locs:
138
path_list.add_url(name, loc)
139
path_list.print_lines()
142
def _show_related_info(branch):
143
"""Show parent and push location of branch."""
144
if branch.get_parent() or branch.get_push_location():
146
print 'Related branches:'
147
if branch.get_parent():
148
if branch.get_push_location():
149
print ' parent branch: %s' % branch.get_parent()
151
print ' parent branch: %s' % branch.get_parent()
152
if branch.get_push_location():
153
print ' publish to branch: %s' % branch.get_push_location()
156
def _show_format_info(control=None, repository=None, branch=None, working=None):
157
"""Show known formats for control, working, branch and repository."""
161
print ' control: %s' % control._format.get_format_description()
163
print ' working tree: %s' % working._format.get_format_description()
165
print ' branch: %s' % branch._format.get_format_description()
167
print ' repository: %s' % repository._format.get_format_description()
170
def _show_locking_info(repository, branch=None, working=None):
171
"""Show locking status of working, branch and repository."""
172
if (repository.get_physical_lock_status() or
173
(branch and branch.get_physical_lock_status()) or
174
(working and working.get_physical_lock_status())):
178
if working.get_physical_lock_status():
182
print ' working tree: %s' % status
184
if branch.get_physical_lock_status():
188
print ' branch: %s' % status
190
if repository.get_physical_lock_status():
194
print ' repository: %s' % status
197
def _show_missing_revisions_branch(branch):
198
"""Show missing master revisions in branch."""
199
# Try with inaccessible branch ?
200
master = branch.get_master_branch()
202
local_extra, remote_extra = find_unmerged(branch, master)
205
print 'Branch is out of date: missing %d revision%s.' % (
206
len(remote_extra), plural(len(remote_extra)))
209
def _show_missing_revisions_working(working):
210
"""Show missing revisions in working tree."""
211
branch = working.branch
212
basis = working.basis_tree()
213
work_inv = working.inventory
214
branch_revno, branch_last_revision = branch.last_revision_info()
216
tree_last_id = working.get_parent_ids()[0]
220
if branch_revno and tree_last_id != branch_last_revision:
221
tree_last_revno = branch.revision_id_to_revno(tree_last_id)
222
missing_count = branch_revno - tree_last_revno
224
print 'Working tree is out of date: missing %d revision%s.' % (
225
missing_count, plural(missing_count))
228
def _show_working_stats(working):
229
"""Show statistics about a working tree."""
230
basis = working.basis_tree()
231
work_inv = working.inventory
232
delta = working.changes_from(basis, want_unchanged=True)
235
print 'In the working tree:'
55
236
print ' %8s unchanged' % len(delta.unchanged)
56
237
print ' %8d modified' % len(delta.modified)
57
238
print ' %8d added' % len(delta.added)
68
248
print ' %8d unknown' % unknown_cnt
69
249
print ' %8d ignored' % ignore_cnt
72
252
for file_id in work_inv:
73
if work_inv.get_file_kind(file_id) == 'directory':
253
if (work_inv.get_file_kind(file_id) == 'directory' and
254
not work_inv.is_root(file_id)):
75
256
print ' %8d versioned %s' \
77
258
plural(dir_cnt, 'subdirectory', 'subdirectories'))
261
def _show_branch_stats(branch, verbose):
262
"""Show statistics about a branch."""
263
revno, head = branch.last_revision_info()
80
print 'branch history:'
81
history = b.revision_history()
265
print 'Branch history:'
83
266
print ' %8d revision%s' % (revno, plural(revno))
86
committers[b.get_revision(rev).committer] = True
87
print ' %8d committer%s' % (len(committers), plural(len(committers)))
89
firstrev = b.get_revision(history[0])
90
age = int((time.time() - firstrev.timestamp) / 3600 / 24)
267
stats = branch.repository.gather_stats(head, committers=verbose)
269
committers = stats['committers']
270
print ' %8d committer%s' % (committers, plural(committers))
272
timestamp, timezone = stats['firstrev']
273
age = int((time.time() - timestamp) / 3600 / 24)
91
274
print ' %8d day%s old' % (age, plural(age))
92
print ' first revision: %s' % format_date(firstrev.timestamp,
95
lastrev = b.get_revision(history[-1])
96
print ' latest revision: %s' % format_date(lastrev.timestamp,
101
c, t = b.text_store.total_size()
102
print ' %8d file texts' % c
103
print ' %8d kB' % (t/1024)
106
print 'revision store:'
107
c, t = b.revision_store.total_size()
108
print ' %8d revisions' % c
109
print ' %8d kB' % (t/1024)
113
print 'inventory store:'
114
c, t = b.inventory_store.total_size()
115
print ' %8d inventories' % c
116
print ' %8d kB' % (t/1024)
275
print ' first revision: %s' % osutils.format_date(timestamp,
277
timestamp, timezone = stats['latestrev']
278
print ' latest revision: %s' % osutils.format_date(timestamp,
283
def _show_repository_info(repository):
284
"""Show settings of a repository."""
285
if repository.make_working_trees():
287
print 'Create working tree for new branches inside the repository.'
290
def _show_repository_stats(stats):
291
"""Show statistics about a repository."""
292
if 'revisions' in stats or 'size' in stats:
295
if 'revisions' in stats:
296
revisions = stats['revisions']
297
print ' %8d revision%s' % (revisions, plural(revisions))
299
print ' %8d KiB' % (stats['size']/1024)
301
def show_bzrdir_info(a_bzrdir, verbose=False):
302
"""Output to stdout the 'info' for a_bzrdir."""
304
tree = a_bzrdir.open_workingtree(
305
recommend_upgrade=False)
306
except (NoWorkingTree, NotLocalUrl):
309
branch = a_bzrdir.open_branch()
310
except NotBranchError:
313
repository = a_bzrdir.open_repository()
314
except NoRepositoryPresent:
315
# Return silently; cmd_info already returned NotBranchError
316
# if no bzrdir could be opened.
319
lockable = repository
321
repository = branch.repository
325
repository = branch.repository
330
show_component_info(a_bzrdir, repository, branch, tree, verbose)
335
def show_component_info(control, repository, branch=None, working=None,
337
"""Write info about all bzrdir components to stdout"""
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))
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)
362
stats = repository.gather_stats()
363
if branch is None and working is None:
364
_show_repository_info(repository)
365
_show_repository_stats(stats)
368
def describe_layout(repository=None, branch=None, tree=None):
369
"""Convert a control directory layout into a user-understandable term
371
Common outputs include "Standalone tree", "Repository branch" and
372
"Checkout". Uncommon outputs include "Unshared repository with trees"
373
and "Empty control directory"
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'
381
phrase = 'Unshared repository'
382
if repository.make_working_trees():
383
phrase += ' with trees'
386
if repository.is_shared():
387
independence = "Repository "
389
independence = "Standalone "
394
if branch is None and tree is not None:
395
phrase = "branchless tree"
397
if (tree is not None and tree.bzrdir.root_transport.base !=
398
branch.bzrdir.root_transport.base):
400
phrase = "Lightweight checkout"
401
elif branch.get_bound_location() is not None:
402
if independence == 'Standalone ':
405
phrase = "Bound branch"
408
if independence != "":
409
phrase = phrase.lower()
410
return "%s%s" % (independence, phrase)
413
def describe_format(control, repository, branch, tree):
414
"""Determine the format of an existing control directory
416
Several candidates may be found. If so, the names are returned as a
417
single string, separated by ' or '.
419
If no matching candidate is found, "unnamed" is returned.
422
if (branch is not None and tree is not None and
423
branch.bzrdir.root_transport.base !=
424
tree.bzrdir.root_transport.base):
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 !=
433
if (branch and format.get_branch_format() !=
436
if (repository and format.repository_format !=
439
if format.__class__ is not control._format.__class__:
441
candidates.append(key)
442
if len(candidates) == 0:
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)
453
@deprecated_function(zero_eight)
455
"""Please see show_bzrdir_info."""
456
return show_bzrdir_info(b.bzrdir)
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)
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)
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)