17
17
__all__ = ['show_bzrdir_info']
19
from io import StringIO
19
from cStringIO import StringIO
24
branch as _mod_branch,
27
26
hooks as _mod_hooks,
34
from .errors import (NoWorkingTree, NotBranchError,
35
NoRepositoryPresent, NotLocalUrl)
36
from .missing import find_unmerged
39
def plural(n, base=u'', pl=None):
30
from bzrlib.errors import (NoWorkingTree, NotBranchError,
31
NoRepositoryPresent, NotLocalUrl)
32
from bzrlib.missing import find_unmerged
35
def plural(n, base='', pl=None):
42
38
elif pl is not None:
48
44
class LocationList(object):
59
55
path = urlutils.local_path_from_url(url)
60
except urlutils.InvalidURL:
56
except errors.InvalidURL:
61
57
self.locs.append((label, url))
63
59
self.add_path(label, path)
78
74
def get_lines(self):
79
75
max_len = max(len(l) for l, u in self.locs)
80
return [" %*s: %s\n" % (max_len, l, u) for l, u in self.locs]
83
def gather_location_info(repository=None, branch=None, working=None,
76
return [" %*s: %s\n" % (max_len, l, u) for l, u in self.locs ]
79
def gather_location_info(repository, branch=None, working=None):
81
repository_path = repository.user_url
86
82
if branch is not None:
87
83
branch_path = branch.user_url
88
84
master_path = branch.get_bound_location()
107
98
locs['checkout root'] = branch_path
108
99
if working_path != master_path:
109
(master_path_base, params) = urlutils.split_segment_parameters(
111
if working_path == master_path_base:
112
locs['checkout of co-located branch'] = params['branch']
113
elif 'branch' in params:
114
locs['checkout of branch'] = "%s, branch %s" % (
115
master_path_base, params['branch'])
117
locs['checkout of branch'] = master_path
100
locs['checkout of branch'] = master_path
118
101
elif repository.is_shared():
119
102
locs['repository branch'] = branch_path
120
103
elif branch_path is not None:
122
105
locs['branch root'] = branch_path
124
107
working_path = None
125
if repository is not None and repository.is_shared():
108
if repository.is_shared():
126
109
# lightweight checkout of branch in shared repository
127
110
if branch_path is not None:
128
111
locs['repository branch'] = branch_path
129
112
elif branch_path is not None:
131
114
locs['branch root'] = branch_path
132
elif repository is not None:
133
locs['repository'] = repository.user_url
134
elif control is not None:
135
locs['control directory'] = control.user_url
115
if master_path != branch_path:
116
locs['bound to branch'] = master_path
137
# Really, at least a control directory should be
138
# passed in for this method to be useful.
140
if master_path != branch_path:
141
locs['bound to branch'] = master_path
142
if repository is not None and repository.is_shared():
118
locs['repository'] = repository_path
119
if repository.is_shared():
143
120
# lightweight checkout of branch in shared repository
144
locs['shared repository'] = repository.user_url
145
order = ['control directory', 'light checkout root',
146
'repository checkout root', 'checkout root',
147
'checkout of branch', 'checkout of co-located branch',
148
'shared repository', 'repository', 'repository branch',
149
'branch root', 'bound to branch']
121
locs['shared repository'] = repository_path
122
order = ['light checkout root', 'repository checkout root',
123
'checkout root', 'checkout of branch', 'shared repository',
124
'repository', 'repository branch', 'branch root',
150
126
return [(n, locs[n]) for n in order if n in locs]
167
143
locs.add_url('submit branch', branch.get_submit_branch())
169
145
locs.add_url('stacked on', branch.get_stacked_on_url())
170
except (_mod_branch.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
146
except (errors.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
182
158
outfile.writelines(locs.get_lines())
185
def _show_control_dir_info(control, outfile):
186
"""Show control dir information."""
187
if control._format.colocated_branches:
189
outfile.write('Control directory:\n')
190
outfile.write(' %d branches\n' % len(control.list_branches()))
193
161
def _show_format_info(control=None, repository=None, branch=None,
194
162
working=None, outfile=None):
195
163
"""Show known formats for control, working, branch and repository."""
197
165
outfile.write('Format:\n')
199
167
outfile.write(' control: %s\n' %
200
control._format.get_format_description())
168
control._format.get_format_description())
202
170
outfile.write(' working tree: %s\n' %
203
working._format.get_format_description())
171
working._format.get_format_description())
205
173
outfile.write(' branch: %s\n' %
206
branch._format.get_format_description())
174
branch._format.get_format_description())
208
176
outfile.write(' repository: %s\n' %
209
repository._format.get_format_description())
212
def _show_locking_info(repository=None, branch=None, working=None,
177
repository._format.get_format_description())
180
def _show_locking_info(repository, branch=None, working=None, outfile=None):
214
181
"""Show locking status of working, branch and repository."""
215
if (repository and repository.get_physical_lock_status() or
182
if (repository.get_physical_lock_status() or
216
183
(branch and branch.get_physical_lock_status()) or
217
(working and working.get_physical_lock_status())):
184
(working and working.get_physical_lock_status())):
218
185
outfile.write('\n')
219
186
outfile.write('Lock status:\n')
247
214
outfile.write('\n')
248
215
outfile.write(('Branch is out of date: missing %d '
249
'revision%s.\n') % (len(remote_extra),
250
plural(len(remote_extra))))
216
'revision%s.\n') % (len(remote_extra),
217
plural(len(remote_extra))))
253
220
def _show_missing_revisions_working(working, outfile):
254
221
"""Show missing revisions in working tree."""
255
222
branch = working.branch
257
branch_revno, branch_last_revision = branch.last_revision_info()
258
except errors.UnsupportedOperation:
223
basis = working.basis_tree()
224
work_inv = working.inventory
225
branch_revno, branch_last_revision = branch.last_revision_info()
261
227
tree_last_id = working.get_parent_ids()[0]
262
228
except IndexError:
267
233
missing_count = branch_revno - tree_last_revno
268
234
outfile.write('\n')
269
235
outfile.write(('Working tree is out of date: missing %d '
270
'revision%s.\n') % (missing_count, plural(missing_count)))
236
'revision%s.\n') % (missing_count, plural(missing_count)))
273
239
def _show_working_stats(working, outfile):
274
240
"""Show statistics about a working tree."""
275
241
basis = working.basis_tree()
242
work_inv = working.inventory
276
243
delta = working.changes_from(basis, want_unchanged=True)
278
245
outfile.write('\n')
282
249
outfile.write(' %8d added\n' % len(delta.added))
283
250
outfile.write(' %8d removed\n' % len(delta.removed))
284
251
outfile.write(' %8d renamed\n' % len(delta.renamed))
285
outfile.write(' %8d copied\n' % len(delta.copied))
287
253
ignore_cnt = unknown_cnt = 0
288
254
for path in working.extras():
294
260
outfile.write(' %8d ignored\n' % ignore_cnt)
297
for path, entry in working.iter_entries_by_dir():
298
if entry.kind == 'directory' and path != '':
263
for file_id in work_inv:
264
if (work_inv.get_file_kind(file_id) == 'directory' and
265
not work_inv.is_root(file_id)):
300
267
outfile.write(' %8d versioned %s\n' % (dir_cnt,
301
plural(dir_cnt, 'subdirectory', 'subdirectories')))
268
plural(dir_cnt, 'subdirectory', 'subdirectories')))
304
271
def _show_branch_stats(branch, verbose, outfile):
305
272
"""Show statistics about a branch."""
307
revno, head = branch.last_revision_info()
308
except errors.UnsupportedOperation:
273
revno, head = branch.last_revision_info()
310
274
outfile.write('\n')
311
275
outfile.write('Branch history:\n')
312
276
outfile.write(' %8d revision%s\n' % (revno, plural(revno)))
315
279
committers = stats['committers']
316
280
outfile.write(' %8d committer%s\n' % (committers,
319
283
timestamp, timezone = stats['firstrev']
320
284
age = int((time.time() - timestamp) / 3600 / 24)
321
285
outfile.write(' %8d day%s old\n' % (age, plural(age)))
322
286
outfile.write(' first revision: %s\n' %
323
osutils.format_date(timestamp, timezone))
287
osutils.format_date(timestamp, timezone))
324
288
timestamp, timezone = stats['latestrev']
325
289
outfile.write(' latest revision: %s\n' %
326
osutils.format_date(timestamp, timezone))
290
osutils.format_date(timestamp, timezone))
342
306
revisions = stats['revisions']
343
307
f.write(' %8d revision%s\n' % (revisions, plural(revisions)))
344
308
if 'size' in stats:
345
f.write(' %8d KiB\n' % (stats['size'] / 1024))
309
f.write(' %8d KiB\n' % (stats['size']/1024))
346
310
for hook in hooks['repository']:
347
311
hook(repository, stats, f)
348
312
if f.getvalue() != "":
351
315
outfile.write(f.getvalue())
354
def show_bzrdir_info(a_controldir, verbose=False, outfile=None):
355
"""Output to stdout the 'info' for a_controldir."""
318
def show_bzrdir_info(a_bzrdir, verbose=False, outfile=None):
319
"""Output to stdout the 'info' for a_bzrdir."""
356
320
if outfile is None:
357
321
outfile = sys.stdout
359
tree = a_controldir.open_workingtree(
323
tree = a_bzrdir.open_workingtree(
360
324
recommend_upgrade=False)
361
except (NoWorkingTree, NotLocalUrl, NotBranchError):
325
except (NoWorkingTree, NotLocalUrl):
364
branch = a_controldir.open_branch(name="")
328
branch = a_bzrdir.open_branch()
365
329
except NotBranchError:
368
repository = a_controldir.open_repository()
332
repository = a_bzrdir.open_repository()
369
333
except NoRepositoryPresent:
334
# Return silently; cmd_info already returned NotBranchError
335
# if no bzrdir could be opened.
373
338
lockable = repository
379
344
repository = branch.repository
382
if lockable is not None:
385
show_component_info(a_controldir, repository, branch, tree, verbose,
349
show_component_info(a_bzrdir, repository, branch, tree, verbose,
388
if lockable is not None:
392
355
def show_component_info(control, repository, branch=None, working=None,
393
verbose=1, outfile=None):
356
verbose=1, outfile=None):
394
357
"""Write info about all bzrdir components to stdout"""
395
358
if outfile is None:
396
359
outfile = sys.stdout
399
362
if verbose is True:
401
layout = describe_layout(repository, branch, working, control)
364
layout = describe_layout(repository, branch, working)
402
365
format = describe_format(control, repository, branch, working)
403
366
outfile.write("%s (format: %s)\n" % (layout, format))
405
gather_location_info(control=control, repository=repository,
406
branch=branch, working=working),
367
_show_location_info(gather_location_info(repository, branch, working),
408
369
if branch is not None:
409
370
_show_related_info(branch, outfile)
412
373
_show_format_info(control, repository, branch, working, outfile)
413
374
_show_locking_info(repository, branch, working, outfile)
414
_show_control_dir_info(control, outfile)
415
375
if branch is not None:
416
376
_show_missing_revisions_branch(branch, outfile)
417
377
if working is not None:
422
382
if branch is not None:
423
383
show_committers = verbose >= 2
424
384
stats = _show_branch_stats(branch, show_committers, outfile)
425
elif repository is not None:
426
386
stats = repository.gather_stats()
427
if branch is None and working is None and repository is not None:
387
if branch is None and working is None:
428
388
_show_repository_info(repository, outfile)
429
if repository is not None:
430
_show_repository_stats(repository, stats, outfile)
433
def describe_layout(repository=None, branch=None, tree=None, control=None):
389
_show_repository_stats(repository, stats, outfile)
392
def describe_layout(repository=None, branch=None, tree=None):
434
393
"""Convert a control directory layout into a user-understandable term
436
395
Common outputs include "Standalone tree", "Repository branch" and
437
396
"Checkout". Uncommon outputs include "Unshared repository with trees"
438
397
and "Empty control directory"
440
if branch is None and control is not None:
442
branch_reference = control.get_branch_reference()
443
except NotBranchError:
446
if branch_reference is not None:
447
return "Dangling branch reference"
448
399
if repository is None:
449
400
return 'Empty control directory'
450
401
if branch is None and tree is None:
452
403
phrase = 'Shared repository'
454
405
phrase = 'Unshared repository'
456
406
if repository.make_working_trees():
457
extra.append('trees')
458
if len(control.branch_names()) > 0:
459
extra.append('colocated branches')
461
phrase += ' with ' + " and ".join(extra)
407
phrase += ' with trees'
464
410
if repository.is_shared():
472
418
if branch is None and tree is not None:
473
419
phrase = "branchless tree"
475
if (tree is not None and tree.controldir.control_url !=
476
branch.controldir.control_url):
421
if (tree is not None and tree.user_url !=
477
423
independence = ''
478
424
phrase = "Lightweight checkout"
479
425
elif branch.get_bound_location() is not None:
497
443
If no matching candidate is found, "unnamed" is returned.
500
446
if (branch is not None and tree is not None and
501
branch.user_url != tree.user_url):
447
branch.user_url != tree.user_url):
503
449
repository = None
504
non_aliases = set(controldir.format_registry.keys())
505
non_aliases.difference_update(controldir.format_registry.aliases())
450
non_aliases = set(bzrdir.format_registry.keys())
451
non_aliases.difference_update(bzrdir.format_registry.aliases())
506
452
for key in non_aliases:
507
format = controldir.format_registry.make_controldir(key)
453
format = bzrdir.format_registry.make_bzrdir(key)
508
454
if isinstance(format, bzrdir.BzrDirMetaFormat1):
509
455
if (tree and format.workingtree_format !=
512
458
if (branch and format.get_branch_format() !=
515
461
if (repository and format.repository_format !=
518
464
if format.__class__ is not control._format.__class__:
523
469
candidates.sort()
524
470
new_candidates = [c for c in candidates if not
525
controldir.format_registry.get_info(c).hidden]
471
bzrdir.format_registry.get_info(c).hidden]
526
472
if len(new_candidates) > 0:
527
473
# If there are any non-hidden formats that match, only return those to
528
474
# avoid listing hidden formats except when only a hidden format will
535
481
"""Hooks for the info command."""
537
483
def __init__(self):
538
super(InfoHooks, self).__init__("breezy.info", "hooks")
484
super(InfoHooks, self).__init__()
485
self.create_hook(_mod_hooks.HookPoint('repository',
541
486
"Invoked when displaying the statistics for a repository. "
542
487
"repository is called with a statistics dictionary as returned "
543
"by the repository and a file-like object to write to.", (1, 15))
488
"by the repository and a file-like object to write to.", (1, 15),
546
492
hooks = InfoHooks()