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,
37
NoRepositoryPresent, NotLocalUrl)
38
from .missing import find_unmerged
41
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):
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)
80
74
def get_lines(self):
81
75
max_len = max(len(l) for l, u in self.locs)
82
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,
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
88
82
if branch is not None:
89
83
branch_path = branch.user_url
90
84
master_path = branch.get_bound_location()
109
98
locs['checkout root'] = branch_path
110
99
if working_path != master_path:
111
(master_path_base, params) = urlutils.split_segment_parameters(
113
if working_path == master_path_base:
114
locs['checkout of co-located branch'] = params['branch']
115
elif 'branch' in params:
116
locs['checkout of branch'] = "%s, branch %s" % (
117
master_path_base, params['branch'])
119
locs['checkout of branch'] = master_path
100
locs['checkout of branch'] = master_path
120
101
elif repository.is_shared():
121
102
locs['repository branch'] = branch_path
122
103
elif branch_path is not None:
124
105
locs['branch root'] = branch_path
126
107
working_path = None
127
if repository is not None and repository.is_shared():
108
if repository.is_shared():
128
109
# lightweight checkout of branch in shared repository
129
110
if branch_path is not None:
130
111
locs['repository branch'] = branch_path
131
112
elif branch_path is not None:
133
114
locs['branch root'] = branch_path
134
elif repository is not None:
135
locs['repository'] = repository.user_url
136
elif control is not None:
137
locs['control directory'] = control.user_url
115
if master_path != branch_path:
116
locs['bound to branch'] = master_path
139
# Really, at least a control directory should be
140
# passed in for this method to be useful.
142
if master_path != branch_path:
143
locs['bound to branch'] = master_path
144
if repository is not None and repository.is_shared():
118
locs['repository'] = repository_path
119
if repository.is_shared():
145
120
# lightweight checkout of branch in shared repository
146
locs['shared repository'] = repository.user_url
147
order = ['control directory', 'light checkout root',
148
'repository checkout root', 'checkout root',
149
'checkout of branch', 'checkout of co-located branch',
150
'shared repository', 'repository', 'repository branch',
151
'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',
152
126
return [(n, locs[n]) for n in order if n in locs]
169
143
locs.add_url('submit branch', branch.get_submit_branch())
171
145
locs.add_url('stacked on', branch.get_stacked_on_url())
172
except (_mod_branch.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
146
except (errors.UnstackableBranchFormat, errors.UnstackableRepositoryFormat,
184
158
outfile.writelines(locs.get_lines())
187
def _show_control_dir_info(control, outfile):
188
"""Show control dir information."""
189
if control._format.colocated_branches:
191
outfile.write('Control directory:\n')
192
outfile.write(' %d branches\n' % len(control.list_branches()))
195
161
def _show_format_info(control=None, repository=None, branch=None,
196
162
working=None, outfile=None):
197
163
"""Show known formats for control, working, branch and repository."""
199
165
outfile.write('Format:\n')
201
167
outfile.write(' control: %s\n' %
202
control._format.get_format_description())
168
control._format.get_format_description())
204
170
outfile.write(' working tree: %s\n' %
205
working._format.get_format_description())
171
working._format.get_format_description())
207
173
outfile.write(' branch: %s\n' %
208
branch._format.get_format_description())
174
branch._format.get_format_description())
210
176
outfile.write(' repository: %s\n' %
211
repository._format.get_format_description())
214
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):
216
181
"""Show locking status of working, branch and repository."""
217
if (repository and repository.get_physical_lock_status() or
182
if (repository.get_physical_lock_status() or
218
183
(branch and branch.get_physical_lock_status()) or
219
(working and working.get_physical_lock_status())):
184
(working and working.get_physical_lock_status())):
220
185
outfile.write('\n')
221
186
outfile.write('Lock status:\n')
249
214
outfile.write('\n')
250
215
outfile.write(('Branch is out of date: missing %d '
251
'revision%s.\n') % (len(remote_extra),
252
plural(len(remote_extra))))
216
'revision%s.\n') % (len(remote_extra),
217
plural(len(remote_extra))))
255
220
def _show_missing_revisions_working(working, outfile):
256
221
"""Show missing revisions in working tree."""
257
222
branch = working.branch
259
branch_revno, branch_last_revision = branch.last_revision_info()
260
except errors.UnsupportedOperation:
223
basis = working.basis_tree()
224
work_inv = working.inventory
225
branch_revno, branch_last_revision = branch.last_revision_info()
263
227
tree_last_id = working.get_parent_ids()[0]
264
228
except IndexError:
269
233
missing_count = branch_revno - tree_last_revno
270
234
outfile.write('\n')
271
235
outfile.write(('Working tree is out of date: missing %d '
272
'revision%s.\n') % (missing_count, plural(missing_count)))
236
'revision%s.\n') % (missing_count, plural(missing_count)))
275
239
def _show_working_stats(working, outfile):
276
240
"""Show statistics about a working tree."""
277
241
basis = working.basis_tree()
242
work_inv = working.inventory
278
243
delta = working.changes_from(basis, want_unchanged=True)
280
245
outfile.write('\n')
284
249
outfile.write(' %8d added\n' % len(delta.added))
285
250
outfile.write(' %8d removed\n' % len(delta.removed))
286
251
outfile.write(' %8d renamed\n' % len(delta.renamed))
287
outfile.write(' %8d copied\n' % len(delta.copied))
289
253
ignore_cnt = unknown_cnt = 0
290
254
for path in working.extras():
296
260
outfile.write(' %8d ignored\n' % ignore_cnt)
299
for path, entry in working.iter_entries_by_dir():
300
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)):
302
267
outfile.write(' %8d versioned %s\n' % (dir_cnt,
303
plural(dir_cnt, 'subdirectory', 'subdirectories')))
268
plural(dir_cnt, 'subdirectory', 'subdirectories')))
306
271
def _show_branch_stats(branch, verbose, outfile):
307
272
"""Show statistics about a branch."""
309
revno, head = branch.last_revision_info()
310
except errors.UnsupportedOperation:
273
revno, head = branch.last_revision_info()
312
274
outfile.write('\n')
313
275
outfile.write('Branch history:\n')
314
276
outfile.write(' %8d revision%s\n' % (revno, plural(revno)))
317
279
committers = stats['committers']
318
280
outfile.write(' %8d committer%s\n' % (committers,
321
283
timestamp, timezone = stats['firstrev']
322
284
age = int((time.time() - timestamp) / 3600 / 24)
323
285
outfile.write(' %8d day%s old\n' % (age, plural(age)))
324
286
outfile.write(' first revision: %s\n' %
325
osutils.format_date(timestamp, timezone))
287
osutils.format_date(timestamp, timezone))
326
288
timestamp, timezone = stats['latestrev']
327
289
outfile.write(' latest revision: %s\n' %
328
osutils.format_date(timestamp, timezone))
290
osutils.format_date(timestamp, timezone))
344
306
revisions = stats['revisions']
345
307
f.write(' %8d revision%s\n' % (revisions, plural(revisions)))
346
308
if 'size' in stats:
347
f.write(' %8d KiB\n' % (stats['size'] / 1024))
309
f.write(' %8d KiB\n' % (stats['size']/1024))
348
310
for hook in hooks['repository']:
349
311
hook(repository, stats, f)
350
312
if f.getvalue() != "":
353
315
outfile.write(f.getvalue())
356
def show_bzrdir_info(a_controldir, verbose=False, outfile=None):
357
"""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."""
358
320
if outfile is None:
359
321
outfile = sys.stdout
361
tree = a_controldir.open_workingtree(
323
tree = a_bzrdir.open_workingtree(
362
324
recommend_upgrade=False)
363
except (NoWorkingTree, NotLocalUrl, NotBranchError):
325
except (NoWorkingTree, NotLocalUrl):
366
branch = a_controldir.open_branch(name="")
328
branch = a_bzrdir.open_branch()
367
329
except NotBranchError:
370
repository = a_controldir.open_repository()
332
repository = a_bzrdir.open_repository()
371
333
except NoRepositoryPresent:
334
# Return silently; cmd_info already returned NotBranchError
335
# if no bzrdir could be opened.
375
338
lockable = repository
381
344
repository = branch.repository
384
if lockable is not None:
387
show_component_info(a_controldir, repository, branch, tree, verbose,
349
show_component_info(a_bzrdir, repository, branch, tree, verbose,
390
if lockable is not None:
394
355
def show_component_info(control, repository, branch=None, working=None,
395
verbose=1, outfile=None):
356
verbose=1, outfile=None):
396
357
"""Write info about all bzrdir components to stdout"""
397
358
if outfile is None:
398
359
outfile = sys.stdout
401
362
if verbose is True:
403
layout = describe_layout(repository, branch, working, control)
364
layout = describe_layout(repository, branch, working)
404
365
format = describe_format(control, repository, branch, working)
405
366
outfile.write("%s (format: %s)\n" % (layout, format))
407
gather_location_info(control=control, repository=repository,
408
branch=branch, working=working),
367
_show_location_info(gather_location_info(repository, branch, working),
410
369
if branch is not None:
411
370
_show_related_info(branch, outfile)
414
373
_show_format_info(control, repository, branch, working, outfile)
415
374
_show_locking_info(repository, branch, working, outfile)
416
_show_control_dir_info(control, outfile)
417
375
if branch is not None:
418
376
_show_missing_revisions_branch(branch, outfile)
419
377
if working is not None:
424
382
if branch is not None:
425
383
show_committers = verbose >= 2
426
384
stats = _show_branch_stats(branch, show_committers, outfile)
427
elif repository is not None:
428
386
stats = repository.gather_stats()
429
if branch is None and working is None and repository is not None:
387
if branch is None and working is None:
430
388
_show_repository_info(repository, outfile)
431
if repository is not None:
432
_show_repository_stats(repository, stats, outfile)
435
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):
436
393
"""Convert a control directory layout into a user-understandable term
438
395
Common outputs include "Standalone tree", "Repository branch" and
439
396
"Checkout". Uncommon outputs include "Unshared repository with trees"
440
397
and "Empty control directory"
442
if branch is None and control is not None:
444
branch_reference = control.get_branch_reference()
445
except NotBranchError:
448
if branch_reference is not None:
449
return "Dangling branch reference"
450
399
if repository is None:
451
400
return 'Empty control directory'
452
401
if branch is None and tree is None:
454
403
phrase = 'Shared repository'
456
405
phrase = 'Unshared repository'
458
406
if repository.make_working_trees():
459
extra.append('trees')
460
if len(control.branch_names()) > 0:
461
extra.append('colocated branches')
463
phrase += ' with ' + " and ".join(extra)
407
phrase += ' with trees'
466
410
if repository.is_shared():
474
418
if branch is None and tree is not None:
475
419
phrase = "branchless tree"
477
if (tree is not None and tree.controldir.control_url !=
478
branch.controldir.control_url):
421
if (tree is not None and tree.user_url !=
479
423
independence = ''
480
424
phrase = "Lightweight checkout"
481
425
elif branch.get_bound_location() is not None:
499
443
If no matching candidate is found, "unnamed" is returned.
502
446
if (branch is not None and tree is not None and
503
branch.user_url != tree.user_url):
447
branch.user_url != tree.user_url):
505
449
repository = None
506
non_aliases = set(controldir.format_registry.keys())
507
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())
508
452
for key in non_aliases:
509
format = controldir.format_registry.make_controldir(key)
453
format = bzrdir.format_registry.make_bzrdir(key)
510
454
if isinstance(format, bzrdir.BzrDirMetaFormat1):
511
455
if (tree and format.workingtree_format !=
514
458
if (branch and format.get_branch_format() !=
517
461
if (repository and format.repository_format !=
520
464
if format.__class__ is not control._format.__class__:
525
469
candidates.sort()
526
470
new_candidates = [c for c in candidates if not
527
controldir.format_registry.get_info(c).hidden]
471
bzrdir.format_registry.get_info(c).hidden]
528
472
if len(new_candidates) > 0:
529
473
# If there are any non-hidden formats that match, only return those to
530
474
# avoid listing hidden formats except when only a hidden format will
537
481
"""Hooks for the info command."""
539
483
def __init__(self):
540
super(InfoHooks, self).__init__("breezy.info", "hooks")
484
super(InfoHooks, self).__init__()
485
self.create_hook(_mod_hooks.HookPoint('repository',
543
486
"Invoked when displaying the statistics for a repository. "
544
487
"repository is called with a statistics dictionary as returned "
545
"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),
548
492
hooks = InfoHooks()