/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/info.py

  • Committer: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

Show diffs side-by-side

added added

removed removed

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