14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
17
__all__ = ['show_bzrdir_info']
21
from io import StringIO
19
from cStringIO import StringIO
26
branch as _mod_branch,
29
26
hooks as _mod_hooks,
36
from .errors import (NoWorkingTree, NotBranchError,
30
from bzrlib.errors import (NoWorkingTree, NotBranchError,
37
31
NoRepositoryPresent, NotLocalUrl)
38
from .missing import find_unmerged
41
def plural(n, base=u'', pl=None):
32
from bzrlib.missing import find_unmerged
35
def plural(n, base='', pl=None):
44
38
elif pl is not None:
50
44
class LocationList(object):
61
55
path = urlutils.local_path_from_url(url)
62
except urlutils.InvalidURL:
56
except errors.InvalidURL:
63
57
self.locs.append((label, url))
65
59
self.add_path(label, path)
82
76
return [" %*s: %s\n" % (max_len, l, u) for l, u in self.locs ]
85
def gather_location_info(repository=None, branch=None, working=None,
79
def gather_location_info(repository, branch=None, working=None):
81
repository_path = repository.user_url
88
82
if branch is not None:
89
83
branch_path = branch.user_url
90
84
master_path = branch.get_bound_location()
116
105
locs['branch root'] = branch_path
118
107
working_path = None
119
if repository is not None and repository.is_shared():
108
if repository.is_shared():
120
109
# lightweight checkout of branch in shared repository
121
110
if branch_path is not None:
122
111
locs['repository branch'] = branch_path
123
112
elif branch_path is not None:
125
114
locs['branch root'] = branch_path
126
elif repository is not None:
127
locs['repository'] = repository.user_url
128
elif control is not None:
129
locs['control directory'] = control.user_url
115
if master_path != branch_path:
116
locs['bound to branch'] = master_path
131
# Really, at least a control directory should be
132
# passed in for this method to be useful.
134
if master_path != branch_path:
135
locs['bound to branch'] = master_path
136
if repository is not None and repository.is_shared():
118
locs['repository'] = repository_path
119
if repository.is_shared():
137
120
# lightweight checkout of branch in shared repository
138
locs['shared repository'] = repository.user_url
139
order = ['control directory', 'light checkout root',
140
'repository checkout root', 'checkout root',
141
'checkout of branch', 'shared repository',
121
locs['shared repository'] = repository_path
122
order = ['light checkout root', 'repository checkout root',
123
'checkout root', 'checkout of branch', 'shared repository',
142
124
'repository', 'repository branch', 'branch root',
143
125
'bound to branch']
144
126
return [(n, locs[n]) for n in order if n in locs]
161
143
locs.add_url('submit branch', branch.get_submit_branch())
163
145
locs.add_url('stacked on', branch.get_stacked_on_url())
164
except (_mod_branch.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
146
except (errors.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
165
147
errors.NotStacked):
176
158
outfile.writelines(locs.get_lines())
179
def _show_control_dir_info(control, outfile):
180
"""Show control dir information."""
181
if control._format.colocated_branches:
183
outfile.write('Control directory:\n')
184
outfile.write(' %d branches\n' % len(control.list_branches()))
187
161
def _show_format_info(control=None, repository=None, branch=None,
188
162
working=None, outfile=None):
189
163
"""Show known formats for control, working, branch and repository."""
203
177
repository._format.get_format_description())
206
def _show_locking_info(repository=None, branch=None, working=None,
180
def _show_locking_info(repository, branch=None, working=None, outfile=None):
208
181
"""Show locking status of working, branch and repository."""
209
if (repository and repository.get_physical_lock_status() or
182
if (repository.get_physical_lock_status() or
210
183
(branch and branch.get_physical_lock_status()) or
211
184
(working and working.get_physical_lock_status())):
212
185
outfile.write('\n')
248
221
"""Show missing revisions in working tree."""
249
222
branch = working.branch
250
223
basis = working.basis_tree()
252
branch_revno, branch_last_revision = branch.last_revision_info()
253
except errors.UnsupportedOperation:
224
work_inv = working.inventory
225
branch_revno, branch_last_revision = branch.last_revision_info()
256
227
tree_last_id = working.get_parent_ids()[0]
257
228
except IndexError:
268
239
def _show_working_stats(working, outfile):
269
240
"""Show statistics about a working tree."""
270
241
basis = working.basis_tree()
242
work_inv = working.inventory
271
243
delta = working.changes_from(basis, want_unchanged=True)
273
245
outfile.write('\n')
288
260
outfile.write(' %8d ignored\n' % ignore_cnt)
291
for path, entry in working.iter_entries_by_dir():
292
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)):
294
267
outfile.write(' %8d versioned %s\n' % (dir_cnt,
295
268
plural(dir_cnt, 'subdirectory', 'subdirectories')))
298
271
def _show_branch_stats(branch, verbose, outfile):
299
272
"""Show statistics about a branch."""
301
revno, head = branch.last_revision_info()
302
except errors.UnsupportedOperation:
273
revno, head = branch.last_revision_info()
304
274
outfile.write('\n')
305
275
outfile.write('Branch history:\n')
306
276
outfile.write(' %8d revision%s\n' % (revno, plural(revno)))
339
309
f.write(' %8d KiB\n' % (stats['size']/1024))
340
310
for hook in hooks['repository']:
341
311
hook(repository, stats, f)
342
if f.getvalue() != b"":
312
if f.getvalue() != "":
343
313
outfile.write('\n')
344
314
outfile.write('Repository:\n')
345
315
outfile.write(f.getvalue())
348
def show_bzrdir_info(a_controldir, verbose=False, outfile=None):
349
"""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."""
350
320
if outfile is None:
351
321
outfile = sys.stdout
353
tree = a_controldir.open_workingtree(
323
tree = a_bzrdir.open_workingtree(
354
324
recommend_upgrade=False)
355
except (NoWorkingTree, NotLocalUrl, NotBranchError):
325
except (NoWorkingTree, NotLocalUrl):
358
branch = a_controldir.open_branch(name="")
328
branch = a_bzrdir.open_branch()
359
329
except NotBranchError:
362
repository = a_controldir.open_repository()
332
repository = a_bzrdir.open_repository()
363
333
except NoRepositoryPresent:
334
# Return silently; cmd_info already returned NotBranchError
335
# if no bzrdir could be opened.
367
338
lockable = repository
373
344
repository = branch.repository
376
if lockable is not None:
379
show_component_info(a_controldir, repository, branch, tree, verbose,
349
show_component_info(a_bzrdir, repository, branch, tree, verbose,
382
if lockable is not None:
386
355
def show_component_info(control, repository, branch=None, working=None,
393
362
if verbose is True:
395
layout = describe_layout(repository, branch, working, control)
364
layout = describe_layout(repository, branch, working)
396
365
format = describe_format(control, repository, branch, working)
397
366
outfile.write("%s (format: %s)\n" % (layout, format))
399
gather_location_info(control=control, repository=repository,
400
branch=branch, working=working),
367
_show_location_info(gather_location_info(repository, branch, working),
402
369
if branch is not None:
403
370
_show_related_info(branch, outfile)
406
373
_show_format_info(control, repository, branch, working, outfile)
407
374
_show_locking_info(repository, branch, working, outfile)
408
_show_control_dir_info(control, outfile)
409
375
if branch is not None:
410
376
_show_missing_revisions_branch(branch, outfile)
411
377
if working is not None:
416
382
if branch is not None:
417
383
show_committers = verbose >= 2
418
384
stats = _show_branch_stats(branch, show_committers, outfile)
419
elif repository is not None:
420
386
stats = repository.gather_stats()
421
if branch is None and working is None and repository is not None:
387
if branch is None and working is None:
422
388
_show_repository_info(repository, outfile)
423
if repository is not None:
424
_show_repository_stats(repository, stats, outfile)
427
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):
428
393
"""Convert a control directory layout into a user-understandable term
430
395
Common outputs include "Standalone tree", "Repository branch" and
431
396
"Checkout". Uncommon outputs include "Unshared repository with trees"
432
397
and "Empty control directory"
434
if branch is None and control is not None:
436
branch_reference = control.get_branch_reference()
437
except NotBranchError:
440
if branch_reference is not None:
441
return "Dangling branch reference"
442
399
if repository is None:
443
400
return 'Empty control directory'
444
401
if branch is None and tree is None:
446
403
phrase = 'Shared repository'
448
405
phrase = 'Unshared repository'
450
406
if repository.make_working_trees():
451
extra.append('trees')
452
if len(control.get_branches()) > 0:
453
extra.append('colocated branches')
455
phrase += ' with ' + " and ".join(extra)
407
phrase += ' with trees'
458
410
if repository.is_shared():
466
418
if branch is None and tree is not None:
467
419
phrase = "branchless tree"
469
if (tree is not None and tree.controldir.control_url !=
470
branch.controldir.control_url):
421
if (tree is not None and tree.user_url !=
471
423
independence = ''
472
424
phrase = "Lightweight checkout"
473
425
elif branch.get_bound_location() is not None:
495
447
branch.user_url != tree.user_url):
497
449
repository = None
498
non_aliases = set(controldir.format_registry.keys())
499
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())
500
452
for key in non_aliases:
501
format = controldir.format_registry.make_controldir(key)
453
format = bzrdir.format_registry.make_bzrdir(key)
502
454
if isinstance(format, bzrdir.BzrDirMetaFormat1):
503
455
if (tree and format.workingtree_format !=
517
469
candidates.sort()
518
470
new_candidates = [c for c in candidates if not
519
controldir.format_registry.get_info(c).hidden]
471
bzrdir.format_registry.get_info(c).hidden]
520
472
if len(new_candidates) > 0:
521
473
# If there are any non-hidden formats that match, only return those to
522
474
# avoid listing hidden formats except when only a hidden format will
529
481
"""Hooks for the info command."""
531
483
def __init__(self):
532
super(InfoHooks, self).__init__("breezy.info", "hooks")
533
self.add_hook('repository',
484
super(InfoHooks, self).__init__()
485
self.create_hook(_mod_hooks.HookPoint('repository',
534
486
"Invoked when displaying the statistics for a repository. "
535
487
"repository is called with a statistics dictionary as returned "
536
"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),
539
492
hooks = InfoHooks()