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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
__all__ = ['show_bzrdir_info']
19
from cStringIO import StringIO
23
22
from bzrlib import (
30
28
from bzrlib.errors import (NoWorkingTree, NotBranchError,
31
29
NoRepositoryPresent, NotLocalUrl)
32
30
from bzrlib.missing import find_unmerged
31
from bzrlib.symbol_versioning import (deprecated_function,
32
zero_eight, zero_eighteen)
35
35
def plural(n, base='', pl=None):
44
class LocationList(object):
46
def __init__(self, base_path):
48
self.base_path = base_path
50
def add_url(self, label, url):
51
"""Add a URL to the list, converting it to a path if possible"""
55
path = urlutils.local_path_from_url(url)
56
except errors.InvalidURL:
57
self.locs.append((label, url))
59
self.add_path(label, path)
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)
61
72
def add_path(self, label, path):
62
"""Add a path, converting it to a relative path if possible"""
64
path = osutils.relpath(self.base_path, path)
65
except errors.PathNotChild:
71
path = path.rstrip('/')
72
self.locs.append((label, path))
73
self.urls.append((label, path))
75
max_len = max(len(l) for l, u in self.locs)
76
return [" %*s: %s\n" % (max_len, l, u) for l, u in self.locs ]
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
81
def gather_location_info(repository, branch=None, working=None):
81
repository_path = repository.user_url
83
repository_path = repository.bzrdir.root_transport.base
82
84
if branch is not None:
83
branch_path = branch.user_url
85
branch_path = branch.bzrdir.root_transport.base
84
86
master_path = branch.get_bound_location()
85
87
if master_path is None:
86
88
master_path = branch_path
126
130
return [(n, locs[n]) for n in order if n in locs]
129
def _show_location_info(locs, outfile):
133
def _show_location_info(locs):
130
134
"""Show known locations for working, branch and repository."""
131
outfile.write('Location:\n')
132
path_list = LocationList(osutils.getcwd())
136
path_list = _UrlList()
133
137
for name, loc in locs:
134
138
path_list.add_url(name, loc)
135
outfile.writelines(path_list.get_lines())
138
def _gather_related_branches(branch):
139
locs = LocationList(osutils.getcwd())
140
locs.add_url('public branch', branch.get_public_branch())
141
locs.add_url('push branch', branch.get_push_location())
142
locs.add_url('parent branch', branch.get_parent())
143
locs.add_url('submit branch', branch.get_submit_branch())
145
locs.add_url('stacked on', branch.get_stacked_on_url())
146
except (errors.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
152
def _show_related_info(branch, outfile):
139
path_list.print_lines()
142
def _show_related_info(branch):
153
143
"""Show parent and push location of branch."""
154
locs = _gather_related_branches(branch)
155
if len(locs.locs) > 0:
157
outfile.write('Related branches:\n')
158
outfile.writelines(locs.get_lines())
161
def _show_format_info(control=None, repository=None, branch=None,
162
working=None, outfile=None):
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):
163
157
"""Show known formats for control, working, branch and repository."""
165
outfile.write('Format:\n')
167
outfile.write(' control: %s\n' %
168
control._format.get_format_description())
161
print ' control: %s' % control._format.get_format_description()
170
outfile.write(' working tree: %s\n' %
171
working._format.get_format_description())
163
print ' working tree: %s' % working._format.get_format_description()
173
outfile.write(' branch: %s\n' %
174
branch._format.get_format_description())
165
print ' branch: %s' % branch._format.get_format_description()
176
outfile.write(' repository: %s\n' %
177
repository._format.get_format_description())
180
def _show_locking_info(repository, branch=None, working=None, outfile=None):
167
print ' repository: %s' % repository._format.get_format_description()
170
def _show_locking_info(repository, branch=None, working=None):
181
171
"""Show locking status of working, branch and repository."""
182
172
if (repository.get_physical_lock_status() or
183
173
(branch and branch.get_physical_lock_status()) or
184
174
(working and working.get_physical_lock_status())):
186
outfile.write('Lock status:\n')
188
178
if working.get_physical_lock_status():
189
179
status = 'locked'
191
181
status = 'unlocked'
192
outfile.write(' working tree: %s\n' % status)
182
print ' working tree: %s' % status
194
184
if branch.get_physical_lock_status():
195
185
status = 'locked'
197
187
status = 'unlocked'
198
outfile.write(' branch: %s\n' % status)
188
print ' branch: %s' % status
200
190
if repository.get_physical_lock_status():
201
191
status = 'locked'
203
193
status = 'unlocked'
204
outfile.write(' repository: %s\n' % status)
207
def _show_missing_revisions_branch(branch, outfile):
194
print ' repository: %s' % status
197
def _show_missing_revisions_branch(branch):
208
198
"""Show missing master revisions in branch."""
209
199
# Try with inaccessible branch ?
210
200
master = branch.get_master_branch()
212
202
local_extra, remote_extra = find_unmerged(branch, master)
215
outfile.write(('Branch is out of date: missing %d '
216
'revision%s.\n') % (len(remote_extra),
217
plural(len(remote_extra))))
220
def _show_missing_revisions_working(working, outfile):
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):
221
210
"""Show missing revisions in working tree."""
222
211
branch = working.branch
223
212
basis = working.basis_tree()
231
220
if branch_revno and tree_last_id != branch_last_revision:
232
221
tree_last_revno = branch.revision_id_to_revno(tree_last_id)
233
222
missing_count = branch_revno - tree_last_revno
235
outfile.write(('Working tree is out of date: missing %d '
236
'revision%s.\n') % (missing_count, plural(missing_count)))
239
def _show_working_stats(working, outfile):
224
print 'Working tree is out of date: missing %d revision%s.' % (
225
missing_count, plural(missing_count))
228
def _show_working_stats(working):
240
229
"""Show statistics about a working tree."""
241
230
basis = working.basis_tree()
242
231
work_inv = working.inventory
243
232
delta = working.changes_from(basis, want_unchanged=True)
246
outfile.write('In the working tree:\n')
247
outfile.write(' %8s unchanged\n' % len(delta.unchanged))
248
outfile.write(' %8d modified\n' % len(delta.modified))
249
outfile.write(' %8d added\n' % len(delta.added))
250
outfile.write(' %8d removed\n' % len(delta.removed))
251
outfile.write(' %8d renamed\n' % len(delta.renamed))
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)
253
242
ignore_cnt = unknown_cnt = 0
254
243
for path in working.extras():
259
outfile.write(' %8d unknown\n' % unknown_cnt)
260
outfile.write(' %8d ignored\n' % ignore_cnt)
248
print ' %8d unknown' % unknown_cnt
249
print ' %8d ignored' % ignore_cnt
263
252
for file_id in work_inv:
264
if (work_inv.get_file_kind(file_id) == 'directory' and
253
if (work_inv.get_file_kind(file_id) == 'directory' and
265
254
not work_inv.is_root(file_id)):
267
outfile.write(' %8d versioned %s\n' % (dir_cnt,
268
plural(dir_cnt, 'subdirectory', 'subdirectories')))
271
def _show_branch_stats(branch, verbose, outfile):
256
print ' %8d versioned %s' \
258
plural(dir_cnt, 'subdirectory', 'subdirectories'))
261
def _show_branch_stats(branch, verbose):
272
262
"""Show statistics about a branch."""
273
263
revno, head = branch.last_revision_info()
275
outfile.write('Branch history:\n')
276
outfile.write(' %8d revision%s\n' % (revno, plural(revno)))
265
print 'Branch history:'
266
print ' %8d revision%s' % (revno, plural(revno))
277
267
stats = branch.repository.gather_stats(head, committers=verbose)
279
269
committers = stats['committers']
280
outfile.write(' %8d committer%s\n' % (committers,
270
print ' %8d committer%s' % (committers, plural(committers))
283
272
timestamp, timezone = stats['firstrev']
284
273
age = int((time.time() - timestamp) / 3600 / 24)
285
outfile.write(' %8d day%s old\n' % (age, plural(age)))
286
outfile.write(' first revision: %s\n' %
287
osutils.format_date(timestamp, timezone))
274
print ' %8d day%s old' % (age, plural(age))
275
print ' first revision: %s' % osutils.format_date(timestamp,
288
277
timestamp, timezone = stats['latestrev']
289
outfile.write(' latest revision: %s\n' %
290
osutils.format_date(timestamp, timezone))
278
print ' latest revision: %s' % osutils.format_date(timestamp,
294
def _show_repository_info(repository, outfile):
283
def _show_repository_info(repository):
295
284
"""Show settings of a repository."""
296
285
if repository.make_working_trees():
298
outfile.write('Create working tree for new branches inside '
302
def _show_repository_stats(repository, stats, outfile):
287
print 'Create working tree for new branches inside the repository.'
290
def _show_repository_stats(stats):
303
291
"""Show statistics about a repository."""
292
if 'revisions' in stats or 'size' in stats:
305
295
if 'revisions' in stats:
306
296
revisions = stats['revisions']
307
f.write(' %8d revision%s\n' % (revisions, plural(revisions)))
297
print ' %8d revision%s' % (revisions, plural(revisions))
308
298
if 'size' in stats:
309
f.write(' %8d KiB\n' % (stats['size']/1024))
310
for hook in hooks['repository']:
311
hook(repository, stats, f)
312
if f.getvalue() != "":
314
outfile.write('Repository:\n')
315
outfile.write(f.getvalue())
318
def show_bzrdir_info(a_bzrdir, verbose=False, outfile=None):
299
print ' %8d KiB' % (stats['size']/1024)
301
def show_bzrdir_info(a_bzrdir, verbose=False):
319
302
"""Output to stdout the 'info' for a_bzrdir."""
323
304
tree = a_bzrdir.open_workingtree(
324
305
recommend_upgrade=False)
347
328
lockable.lock_read()
349
show_component_info(a_bzrdir, repository, branch, tree, verbose,
330
show_component_info(a_bzrdir, repository, branch, tree, verbose)
352
332
lockable.unlock()
355
335
def show_component_info(control, repository, branch=None, working=None,
356
verbose=1, outfile=None):
357
337
"""Write info about all bzrdir components to stdout"""
360
338
if verbose is False:
362
340
if verbose is True:
364
342
layout = describe_layout(repository, branch, working)
365
343
format = describe_format(control, repository, branch, working)
366
outfile.write("%s (format: %s)\n" % (layout, format))
367
_show_location_info(gather_location_info(repository, branch, working),
369
if branch is not None:
370
_show_related_info(branch, outfile)
344
print "%s (format: %s)" % (layout, format)
345
_show_location_info(gather_location_info(repository, branch, working))
373
_show_format_info(control, repository, branch, working, outfile)
374
_show_locking_info(repository, branch, working, outfile)
375
if branch is not None:
376
_show_missing_revisions_branch(branch, outfile)
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)
377
354
if working is not None:
378
_show_missing_revisions_working(working, outfile)
379
_show_working_stats(working, outfile)
355
_show_missing_revisions_working(working)
356
_show_working_stats(working)
380
357
elif branch is not None:
381
_show_missing_revisions_branch(branch, outfile)
358
_show_missing_revisions_branch(branch)
382
359
if branch is not None:
383
show_committers = verbose >= 2
384
stats = _show_branch_stats(branch, show_committers, outfile)
360
stats = _show_branch_stats(branch, verbose==2)
386
362
stats = repository.gather_stats()
387
363
if branch is None and working is None:
388
_show_repository_info(repository, outfile)
389
_show_repository_stats(repository, stats, outfile)
364
_show_repository_info(repository)
365
_show_repository_stats(stats)
392
368
def describe_layout(repository=None, branch=None, tree=None):
466
441
candidates.append(key)
467
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
470
447
new_candidates = [c for c in candidates if not
471
448
bzrdir.format_registry.get_info(c).hidden]
472
449
if len(new_candidates) > 0:
473
# If there are any non-hidden formats that match, only return those to
474
# avoid listing hidden formats except when only a hidden format will
476
450
candidates = new_candidates
477
451
return ' or '.join(candidates)
480
class InfoHooks(_mod_hooks.Hooks):
481
"""Hooks for the info command."""
484
super(InfoHooks, self).__init__()
485
self.create_hook(_mod_hooks.HookPoint('repository',
486
"Invoked when displaying the statistics for a repository. "
487
"repository is called with a statistics dictionary as returned "
488
"by the repository and a file-like object to write to.", (1, 15),
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)