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
19
17
__all__ = ['show_bzrdir_info']
24
import bzrlib.diff as diff
25
30
from bzrlib.errors import (NoWorkingTree, NotBranchError,
26
31
NoRepositoryPresent, NotLocalUrl)
27
32
from bzrlib.missing import find_unmerged
28
import bzrlib.osutils as osutils
29
from bzrlib.symbol_versioning import *
33
from bzrlib.symbol_versioning import (deprecated_function,
32
37
def plural(n, base='', pl=None):
41
def _repo_relpath(repo_path, path):
42
"""Return path with common prefix of repository path removed.
44
If path is not part of the repository, the original path is returned.
45
If path is equal to the repository, the current directory marker '.' is
48
path = osutils.normalizepath(path)
49
repo_path = osutils.normalizepath(repo_path)
52
if osutils.is_inside(repo_path, path):
53
return osutils.relpath(repo_path, path)
57
def _show_location_info(repository, branch=None, working=None):
58
"""Show known locations for working, branch and repository."""
46
class LocationList(object):
48
def __init__(self, base_path):
50
self.base_path = base_path
52
def add_url(self, label, url):
53
"""Add a URL to the list, converting it to a path if possible"""
57
path = urlutils.local_path_from_url(url)
58
except errors.InvalidURL:
59
self.locs.append((label, url))
61
self.add_path(label, path)
63
def add_path(self, label, path):
64
"""Add a path, converting it to a relative path if possible"""
66
path = osutils.relpath(self.base_path, path)
67
except errors.PathNotChild:
73
path = path.rstrip('/')
74
self.locs.append((label, path))
77
max_len = max(len(l) for l, u in self.locs)
78
return [" %*s: %s\n" % (max_len, l, u) for l, u in self.locs ]
81
def gather_location_info(repository, branch=None, working=None):
59
83
repository_path = repository.bzrdir.root_transport.base
61
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
62
93
working_path = working.bzrdir.root_transport.base
63
branch_path = branch.bzrdir.root_transport.base
64
94
if working_path != branch_path:
65
# lightweight checkout
66
print ' light checkout root: %s' % working_path
95
locs['light checkout root'] = working_path
96
if master_path != branch_path:
67
97
if repository.is_shared():
68
# lightweight checkout of branch in shared repository
69
print ' shared repository: %s' % repository_path
70
print ' repository branch: %s' % (
71
_repo_relpath(repository_path, branch_path))
98
locs['repository checkout root'] = branch_path
73
# lightweight checkout of standalone branch
74
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
75
103
elif repository.is_shared():
76
# branch with tree inside shared repository
77
print ' shared repository: %s' % repository_path
78
print ' repository checkout: %s' % (
79
_repo_relpath(repository_path, branch_path))
80
elif branch.get_bound_location():
82
print ' checkout root: %s' % working_path
83
print ' checkout of branch: %s' % branch.get_bound_location()
104
locs['repository branch'] = branch_path
105
elif branch_path is not None:
86
print ' branch root: %s' % working_path
88
branch_path = branch.bzrdir.root_transport.base
107
locs['branch root'] = branch_path
89
110
if repository.is_shared():
90
# branch is part of shared repository
91
print ' shared repository: %s' % repository_path
92
print ' repository branch: %s' % (
93
_repo_relpath(repository_path, branch_path))
111
# lightweight checkout of branch in shared repository
112
if branch_path is not None:
113
locs['repository branch'] = branch_path
114
elif branch_path is not None:
116
locs['branch root'] = branch_path
117
if master_path != branch_path:
118
locs['bound to branch'] = master_path
96
print ' branch root: %s' % branch_path
99
assert repository.is_shared()
100
print ' shared repository: %s' % repository_path
103
def _show_related_info(branch):
120
locs['repository'] = repository_path
121
if repository.is_shared():
122
# lightweight checkout of branch in shared repository
123
locs['shared repository'] = repository_path
124
order = ['light checkout root', 'repository checkout root',
125
'checkout root', 'checkout of branch', 'shared repository',
126
'repository', 'repository branch', 'branch root',
128
return [(n, locs[n]) for n in order if n in locs]
131
def _show_location_info(locs):
132
"""Show known locations for working, branch and repository."""
134
path_list = LocationList(os.getcwd())
135
for name, loc in locs:
136
path_list.add_url(name, loc)
137
sys.stdout.writelines(path_list.get_lines())
139
def _gather_related_branches(branch):
140
locs = LocationList(os.getcwd())
141
locs.add_url('public branch', branch.get_public_branch())
142
locs.add_url('push branch', branch.get_push_location())
143
locs.add_url('parent branch', branch.get_parent())
144
locs.add_url('submit branch', branch.get_submit_branch())
147
def _show_related_info(branch, outfile):
104
148
"""Show parent and push location of branch."""
105
if branch.get_parent() or branch.get_push_location():
107
print 'Related branches:'
108
if branch.get_parent():
109
if branch.get_push_location():
110
print ' parent branch: %s' % branch.get_parent()
112
print ' parent branch: %s' % branch.get_parent()
113
if branch.get_push_location():
114
print ' publish to branch: %s' % branch.get_push_location()
149
locs = _gather_related_branches(branch)
150
if len(locs.locs) > 0:
152
print >> outfile, 'Related branches:'
153
outfile.writelines(locs.get_lines())
117
156
def _show_format_info(control=None, repository=None, branch=None, working=None):
219
261
def _show_branch_stats(branch, verbose):
220
262
"""Show statistics about a branch."""
221
repository = branch.repository
222
history = branch.revision_history()
263
revno, head = branch.last_revision_info()
225
265
print 'Branch history:'
227
266
print ' %8d revision%s' % (revno, plural(revno))
267
stats = branch.repository.gather_stats(head, committers=verbose)
231
committers[repository.get_revision(rev).committer] = True
232
print ' %8d committer%s' % (len(committers), plural(len(committers)))
234
firstrev = repository.get_revision(history[0])
235
age = int((time.time() - firstrev.timestamp) / 3600 / 24)
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)
236
274
print ' %8d day%s old' % (age, plural(age))
237
print ' first revision: %s' % osutils.format_date(firstrev.timestamp,
240
lastrev = repository.get_revision(history[-1])
241
print ' latest revision: %s' % osutils.format_date(lastrev.timestamp,
245
# print 'Text store:'
246
# c, t = branch.text_store.total_size()
247
# print ' %8d file texts' % c
248
# print ' %8d KiB' % (t/1024)
251
# print 'Inventory store:'
252
# c, t = branch.inventory_store.total_size()
253
# print ' %8d inventories' % c
254
# print ' %8d KiB' % (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,
257
283
def _show_repository_info(repository):
261
287
print 'Create working tree for new branches inside the repository.'
264
def _show_repository_stats(repository):
290
def _show_repository_stats(stats):
265
291
"""Show statistics about a repository."""
266
if repository.bzrdir.root_transport.listable():
292
if 'revisions' in stats or 'size' in stats:
268
print 'Revision store:'
269
c, t = repository._revision_store.total_size(repository.get_transaction())
270
print ' %8d revision%s' % (c, plural(c))
271
print ' %8d KiB' % (t/1024)
274
@deprecated_function(zero_eight)
276
"""Please see show_bzrdir_info."""
277
return show_bzrdir_info(b.bzrdir)
295
if 'revisions' in stats:
296
revisions = stats['revisions']
297
print ' %8d revision%s' % (revisions, plural(revisions))
299
print ' %8d KiB' % (stats['size']/1024)
280
301
def show_bzrdir_info(a_bzrdir, verbose=False):
281
302
"""Output to stdout the 'info' for a_bzrdir."""
283
working = a_bzrdir.open_workingtree()
286
show_tree_info(working, verbose)
304
tree = a_bzrdir.open_workingtree(
305
recommend_upgrade=False)
290
306
except (NoWorkingTree, NotLocalUrl):
294
branch = a_bzrdir.open_branch()
297
show_branch_info(branch, verbose)
301
except NotBranchError:
305
repository = a_bzrdir.open_repository()
306
repository.lock_read()
308
show_repository_info(repository, verbose)
312
except NoRepositoryPresent:
315
# Return silently, cmd_info already returned NotBranchError if no bzrdir
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))
346
if branch is not None:
347
_show_related_info(branch, sys.stdout)
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)
454
@deprecated_function(zero_eighteen)
319
455
def show_tree_info(working, verbose):
320
456
"""Output to stdout the 'info' for working."""
321
457
branch = working.branch
322
458
repository = branch.repository
323
459
control = working.bzrdir
325
_show_location_info(repository, branch, working)
326
_show_related_info(branch)
327
_show_format_info(control, repository, branch, working)
328
_show_locking_info(repository, branch, working)
329
_show_missing_revisions_branch(branch)
330
_show_missing_revisions_working(working)
331
_show_working_stats(working)
332
_show_branch_stats(branch, verbose)
333
_show_repository_stats(repository)
460
show_component_info(control, repository, branch, working, verbose)
463
@deprecated_function(zero_eighteen)
336
464
def show_branch_info(branch, verbose):
337
465
"""Output to stdout the 'info' for branch."""
338
466
repository = branch.repository
339
467
control = branch.bzrdir
341
_show_location_info(repository, branch)
342
_show_related_info(branch)
343
_show_format_info(control, repository, branch)
344
_show_locking_info(repository, branch)
345
_show_missing_revisions_branch(branch)
346
_show_branch_stats(branch, verbose)
347
_show_repository_stats(repository)
468
show_component_info(control, repository, branch, verbose=verbose)
471
@deprecated_function(zero_eighteen)
350
472
def show_repository_info(repository, verbose):
351
473
"""Output to stdout the 'info' for repository."""
352
474
control = repository.bzrdir
354
_show_location_info(repository)
355
_show_format_info(control, repository)
356
_show_locking_info(repository)
357
_show_repository_info(repository)
358
_show_repository_stats(repository)
475
show_component_info(control, repository, verbose=verbose)