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()
213
work_inv = working.inventory
224
214
branch_revno, branch_last_revision = branch.last_revision_info()
226
216
tree_last_id = working.get_parent_ids()[0]
230
220
if branch_revno and tree_last_id != branch_last_revision:
231
221
tree_last_revno = branch.revision_id_to_revno(tree_last_id)
232
222
missing_count = branch_revno - tree_last_revno
234
outfile.write(('Working tree is out of date: missing %d '
235
'revision%s.\n') % (missing_count, plural(missing_count)))
238
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):
239
229
"""Show statistics about a working tree."""
240
230
basis = working.basis_tree()
231
work_inv = working.inventory
241
232
delta = working.changes_from(basis, want_unchanged=True)
244
outfile.write('In the working tree:\n')
245
outfile.write(' %8s unchanged\n' % len(delta.unchanged))
246
outfile.write(' %8d modified\n' % len(delta.modified))
247
outfile.write(' %8d added\n' % len(delta.added))
248
outfile.write(' %8d removed\n' % len(delta.removed))
249
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)
251
242
ignore_cnt = unknown_cnt = 0
252
243
for path in working.extras():
257
outfile.write(' %8d unknown\n' % unknown_cnt)
258
outfile.write(' %8d ignored\n' % ignore_cnt)
248
print ' %8d unknown' % unknown_cnt
249
print ' %8d ignored' % ignore_cnt
261
root_id = working.get_root_id()
262
for path, entry in working.iter_entries_by_dir():
263
if entry.kind == 'directory' and entry.file_id != root_id:
252
for file_id in work_inv:
253
if (work_inv.get_file_kind(file_id) == 'directory' and
254
not work_inv.is_root(file_id)):
265
outfile.write(' %8d versioned %s\n' % (dir_cnt,
266
plural(dir_cnt, 'subdirectory', 'subdirectories')))
269
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):
270
262
"""Show statistics about a branch."""
271
263
revno, head = branch.last_revision_info()
273
outfile.write('Branch history:\n')
274
outfile.write(' %8d revision%s\n' % (revno, plural(revno)))
265
print 'Branch history:'
266
print ' %8d revision%s' % (revno, plural(revno))
275
267
stats = branch.repository.gather_stats(head, committers=verbose)
277
269
committers = stats['committers']
278
outfile.write(' %8d committer%s\n' % (committers,
270
print ' %8d committer%s' % (committers, plural(committers))
281
272
timestamp, timezone = stats['firstrev']
282
273
age = int((time.time() - timestamp) / 3600 / 24)
283
outfile.write(' %8d day%s old\n' % (age, plural(age)))
284
outfile.write(' first revision: %s\n' %
285
osutils.format_date(timestamp, timezone))
274
print ' %8d day%s old' % (age, plural(age))
275
print ' first revision: %s' % osutils.format_date(timestamp,
286
277
timestamp, timezone = stats['latestrev']
287
outfile.write(' latest revision: %s\n' %
288
osutils.format_date(timestamp, timezone))
278
print ' latest revision: %s' % osutils.format_date(timestamp,
292
def _show_repository_info(repository, outfile):
283
def _show_repository_info(repository):
293
284
"""Show settings of a repository."""
294
285
if repository.make_working_trees():
296
outfile.write('Create working tree for new branches inside '
300
def _show_repository_stats(repository, stats, outfile):
287
print 'Create working tree for new branches inside the repository.'
290
def _show_repository_stats(stats):
301
291
"""Show statistics about a repository."""
292
if 'revisions' in stats or 'size' in stats:
303
295
if 'revisions' in stats:
304
296
revisions = stats['revisions']
305
f.write(' %8d revision%s\n' % (revisions, plural(revisions)))
297
print ' %8d revision%s' % (revisions, plural(revisions))
306
298
if 'size' in stats:
307
f.write(' %8d KiB\n' % (stats['size']/1024))
308
for hook in hooks['repository']:
309
hook(repository, stats, f)
310
if f.getvalue() != "":
312
outfile.write('Repository:\n')
313
outfile.write(f.getvalue())
316
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):
317
302
"""Output to stdout the 'info' for a_bzrdir."""
321
304
tree = a_bzrdir.open_workingtree(
322
305
recommend_upgrade=False)
345
328
lockable.lock_read()
347
show_component_info(a_bzrdir, repository, branch, tree, verbose,
330
show_component_info(a_bzrdir, repository, branch, tree, verbose)
350
332
lockable.unlock()
353
335
def show_component_info(control, repository, branch=None, working=None,
354
verbose=1, outfile=None):
355
337
"""Write info about all bzrdir components to stdout"""
358
338
if verbose is False:
360
340
if verbose is True:
362
342
layout = describe_layout(repository, branch, working)
363
343
format = describe_format(control, repository, branch, working)
364
outfile.write("%s (format: %s)\n" % (layout, format))
365
_show_location_info(gather_location_info(repository, branch, working),
367
if branch is not None:
368
_show_related_info(branch, outfile)
344
print "%s (format: %s)" % (layout, format)
345
_show_location_info(gather_location_info(repository, branch, working))
371
_show_format_info(control, repository, branch, working, outfile)
372
_show_locking_info(repository, branch, working, outfile)
373
if branch is not None:
374
_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)
375
354
if working is not None:
376
_show_missing_revisions_working(working, outfile)
377
_show_working_stats(working, outfile)
355
_show_missing_revisions_working(working)
356
_show_working_stats(working)
378
357
elif branch is not None:
379
_show_missing_revisions_branch(branch, outfile)
358
_show_missing_revisions_branch(branch)
380
359
if branch is not None:
381
show_committers = verbose >= 2
382
stats = _show_branch_stats(branch, show_committers, outfile)
360
stats = _show_branch_stats(branch, verbose==2)
384
362
stats = repository.gather_stats()
385
363
if branch is None and working is None:
386
_show_repository_info(repository, outfile)
387
_show_repository_stats(repository, stats, outfile)
364
_show_repository_info(repository)
365
_show_repository_stats(stats)
390
368
def describe_layout(repository=None, branch=None, tree=None):
464
441
candidates.append(key)
465
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
468
447
new_candidates = [c for c in candidates if not
469
448
bzrdir.format_registry.get_info(c).hidden]
470
449
if len(new_candidates) > 0:
471
# If there are any non-hidden formats that match, only return those to
472
# avoid listing hidden formats except when only a hidden format will
474
450
candidates = new_candidates
475
451
return ' or '.join(candidates)
478
class InfoHooks(_mod_hooks.Hooks):
479
"""Hooks for the info command."""
482
super(InfoHooks, self).__init__("bzrlib.info", "hooks")
483
self.add_hook('repository',
484
"Invoked when displaying the statistics for a repository. "
485
"repository is called with a statistics dictionary as returned "
486
"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)