/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/builtins.py

  • Committer: Jelmer Vernooij
  • Date: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""builtin brz commands"""
18
18
 
 
19
from __future__ import absolute_import
 
20
 
19
21
import errno
20
22
import os
21
23
import sys
22
24
 
23
25
import breezy.bzr
24
 
import breezy.git
25
 
 
26
 
from . import (
27
 
    errors,
28
 
    )
29
26
 
30
27
from . import lazy_import
31
28
lazy_import.lazy_import(globals(), """
35
32
from breezy import (
36
33
    branch as _mod_branch,
37
34
    bugtracker,
 
35
    bundle,
38
36
    cache_utf8,
39
37
    controldir,
40
38
    directory_service,
41
39
    delta,
42
40
    config as _mod_config,
 
41
    errors,
43
42
    globbing,
44
 
    gpg,
45
43
    hooks,
46
44
    lazy_regex,
47
45
    log,
48
46
    merge as _mod_merge,
49
 
    mergeable as _mod_mergeable,
50
47
    merge_directive,
51
48
    osutils,
52
49
    reconfigure,
53
50
    rename_map,
54
51
    revision as _mod_revision,
 
52
    static_tuple,
55
53
    symbol_versioning,
56
54
    timestamp,
57
55
    transport,
58
 
    tree as _mod_tree,
59
56
    ui,
60
57
    urlutils,
61
58
    views,
 
59
    gpg,
 
60
    )
 
61
from breezy.bzr import (
 
62
    btree_index,
62
63
    )
63
64
from breezy.branch import Branch
64
65
from breezy.conflicts import ConflictList
84
85
    RevisionSpec,
85
86
    RevisionInfo,
86
87
    )
 
88
from .sixish import (
 
89
    BytesIO,
 
90
    text_type,
 
91
    viewitems,
 
92
    viewvalues,
 
93
)
87
94
from .trace import mutter, note, warning, is_quiet, get_verbosity_level
88
95
 
89
96
 
113
120
        and the full URL to the actual branch
114
121
    """
115
122
    # This path is meant to be relative to the existing branch
116
 
    this_url = _get_branch_location(
117
 
        control_dir, possible_transports=possible_transports)
 
123
    this_url = _get_branch_location(control_dir,
 
124
        possible_transports=possible_transports)
118
125
    # Perhaps the target control dir supports colocated branches?
119
126
    try:
120
 
        root = controldir.ControlDir.open(
121
 
            this_url, possible_transports=possible_transports)
 
127
        root = controldir.ControlDir.open(this_url,
 
128
            possible_transports=possible_transports)
122
129
    except errors.NotBranchError:
123
130
        return (False, this_url)
124
131
    else:
125
132
        try:
126
 
            control_dir.open_workingtree()
 
133
            wt = control_dir.open_workingtree()
127
134
        except (errors.NoWorkingTree, errors.NotLocalUrl):
128
135
            return (False, this_url)
129
136
        else:
145
152
        (colocated, this_url) = _is_colocated(control_dir, possible_transports)
146
153
 
147
154
        if colocated:
148
 
            return urlutils.join_segment_parameters(
149
 
                this_url, {"branch": urlutils.escape(location)})
 
155
            return urlutils.join_segment_parameters(this_url,
 
156
                {"branch": urlutils.escape(location)})
150
157
        else:
151
158
            return urlutils.join(this_url, '..', urlutils.escape(location))
152
159
    return location
162
169
    """
163
170
    try:
164
171
        # Perhaps it's a colocated branch?
165
 
        return control_dir.open_branch(
166
 
            location, possible_transports=possible_transports)
 
172
        return control_dir.open_branch(location, 
 
173
            possible_transports=possible_transports)
167
174
    except (errors.NotBranchError, errors.NoColocatedBranchSupport):
168
175
        this_url = _get_branch_location(control_dir)
169
176
        return Branch.open(
182
189
        if location is None:
183
190
            location = "."
184
191
        try:
185
 
            return Branch.open(
186
 
                location, possible_transports=possible_transports)
 
192
            return Branch.open(location,
 
193
                possible_transports=possible_transports)
187
194
        except errors.NotBranchError:
188
195
            near = "."
189
 
    cdir = controldir.ControlDir.open(
190
 
        near, possible_transports=possible_transports)
191
 
    return open_sibling_branch(
192
 
        cdir, location, possible_transports=possible_transports)
 
196
    cdir = controldir.ControlDir.open(near,
 
197
        possible_transports=possible_transports)
 
198
    return open_sibling_branch(cdir, location,
 
199
        possible_transports=possible_transports)
193
200
 
194
201
 
195
202
def iter_sibling_branches(control_dir, possible_transports=None):
198
205
    :param control_dir: Control directory for which to look up the siblings
199
206
    :return: Iterator over tuples with branch name and branch object
200
207
    """
 
208
    seen_urls = set()
201
209
    try:
202
210
        reference = control_dir.get_branch_reference()
203
211
    except errors.NotBranchError:
204
 
        reference = None
 
212
        # There is no active branch, just return the colocated branches.
 
213
        for name, branch in viewitems(control_dir.get_branches()):
 
214
            yield name, branch
 
215
        return
205
216
    if reference is not None:
206
 
        try:
207
 
            ref_branch = Branch.open(
208
 
                reference, possible_transports=possible_transports)
209
 
        except errors.NotBranchError:
210
 
            ref_branch = None
 
217
        ref_branch = Branch.open(reference,
 
218
            possible_transports=possible_transports)
211
219
    else:
212
220
        ref_branch = None
213
221
    if ref_branch is None or ref_branch.name:
214
222
        if ref_branch is not None:
215
223
            control_dir = ref_branch.controldir
216
 
        for name, branch in control_dir.get_branches().items():
 
224
        for name, branch in viewitems(control_dir.get_branches()):
217
225
            yield name, branch
218
226
    else:
219
227
        repo = ref_branch.controldir.find_repository()
220
228
        for branch in repo.find_branches(using=True):
221
 
            name = urlutils.relative_url(
222
 
                repo.user_url, branch.user_url).rstrip("/")
 
229
            name = urlutils.relative_url(repo.user_url,
 
230
                branch.user_url).rstrip("/")
223
231
            yield name, branch
224
232
 
225
233
 
251
259
            if view_files:
252
260
                file_list = view_files
253
261
                view_str = views.view_display_str(view_files)
254
 
                note(gettext("Ignoring files outside view. View is %s"),
255
 
                     view_str)
 
262
                note(gettext("Ignoring files outside view. View is %s") % view_str)
256
263
    return tree, file_list
257
264
 
258
265
 
260
267
    if revisions is None:
261
268
        return None
262
269
    if len(revisions) != 1:
263
 
        raise errors.CommandError(gettext(
 
270
        raise errors.BzrCommandError(gettext(
264
271
            'brz %s --revision takes exactly one revision identifier') % (
265
272
                command_name,))
266
273
    return revisions[0]
268
275
 
269
276
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
270
277
    """Get a revision tree. Not suitable for commands that change the tree.
271
 
 
 
278
    
272
279
    Specifically, the basis tree in dirstate trees is coupled to the dirstate
273
280
    and doing a commit/uncommit/pull will at best fail due to changing the
274
281
    basis revision data.
343
350
        Not versioned and not matching an ignore pattern.
344
351
 
345
352
    Additionally for directories, symlinks and files with a changed
346
 
    executable bit, Breezy indicates their type using a trailing
 
353
    executable bit, Bazaar indicates their type using a trailing
347
354
    character: '/', '@' or '*' respectively. These decorations can be
348
355
    disabled using the '--no-classify' option.
349
356
 
380
387
                            short_name='S'),
381
388
                     Option('versioned', help='Only show versioned files.',
382
389
                            short_name='V'),
383
 
                     Option('no-pending', help='Don\'t show pending merges.'),
 
390
                     Option('no-pending', help='Don\'t show pending merges.',
 
391
                           ),
384
392
                     Option('no-classify',
385
 
                            help='Do not mark object type using indicator.'),
 
393
                            help='Do not mark object type using indicator.',
 
394
                           ),
386
395
                     ]
387
396
    aliases = ['st', 'stat']
388
397
 
396
405
        from .status import show_tree_status
397
406
 
398
407
        if revision and len(revision) > 2:
399
 
            raise errors.CommandError(
400
 
                gettext('brz status --revision takes exactly'
401
 
                        ' one or two revision specifiers'))
 
408
            raise errors.BzrCommandError(gettext('brz status --revision takes exactly'
 
409
                                         ' one or two revision specifiers'))
402
410
 
403
411
        tree, relfile_list = WorkingTree.open_containing_paths(file_list)
404
412
        # Avoid asking for specific files when that is not needed.
441
449
    @display_command
442
450
    def run(self, revision_id=None, revision=None, directory=u'.'):
443
451
        if revision_id is not None and revision is not None:
444
 
            raise errors.CommandError(gettext('You can only supply one of'
445
 
                                                 ' revision_id or --revision'))
 
452
            raise errors.BzrCommandError(gettext('You can only supply one of'
 
453
                                         ' revision_id or --revision'))
446
454
        if revision_id is None and revision is None:
447
 
            raise errors.CommandError(
448
 
                gettext('You must supply either --revision or a revision_id'))
 
455
            raise errors.BzrCommandError(gettext('You must supply either'
 
456
                                         ' --revision or a revision_id'))
449
457
 
450
458
        b = controldir.ControlDir.open_containing_tree_or_branch(directory)[1]
451
459
 
452
 
        revisions = getattr(b.repository, "revisions", None)
 
460
        revisions = b.repository.revisions
453
461
        if revisions is None:
454
 
            raise errors.CommandError(
455
 
                gettext('Repository %r does not support '
456
 
                        'access to raw revision texts') % b.repository)
 
462
            raise errors.BzrCommandError(gettext('Repository %r does not support '
 
463
                'access to raw revision texts'))
457
464
 
458
465
        with b.repository.lock_read():
459
466
            # TODO: jam 20060112 should cat-revision always output utf-8?
462
469
                try:
463
470
                    self.print_revision(revisions, revision_id)
464
471
                except errors.NoSuchRevision:
465
 
                    msg = gettext(
466
 
                        "The repository {0} contains no revision {1}.").format(
467
 
                            b.repository.base, revision_id.decode('utf-8'))
468
 
                    raise errors.CommandError(msg)
 
472
                    msg = gettext("The repository {0} contains no revision {1}.").format(
 
473
                        b.repository.base, revision_id)
 
474
                    raise errors.BzrCommandError(msg)
469
475
            elif revision is not None:
470
476
                for rev in revision:
471
477
                    if rev is None:
472
 
                        raise errors.CommandError(
 
478
                        raise errors.BzrCommandError(
473
479
                            gettext('You cannot specify a NULL revision.'))
474
480
                    rev_id = rev.as_revision_id(b)
475
481
                    self.print_revision(revisions, rev_id)
476
482
 
477
483
 
 
484
class cmd_dump_btree(Command):
 
485
    __doc__ = """Dump the contents of a btree index file to stdout.
 
486
 
 
487
    PATH is a btree index file, it can be any URL. This includes things like
 
488
    .bzr/repository/pack-names, or .bzr/repository/indices/a34b3a...ca4a4.iix
 
489
 
 
490
    By default, the tuples stored in the index file will be displayed. With
 
491
    --raw, we will uncompress the pages, but otherwise display the raw bytes
 
492
    stored in the index.
 
493
    """
 
494
 
 
495
    # TODO: Do we want to dump the internal nodes as well?
 
496
    # TODO: It would be nice to be able to dump the un-parsed information,
 
497
    #       rather than only going through iter_all_entries. However, this is
 
498
    #       good enough for a start
 
499
    hidden = True
 
500
    encoding_type = 'exact'
 
501
    takes_args = ['path']
 
502
    takes_options = [Option('raw', help='Write the uncompressed bytes out,'
 
503
                                        ' rather than the parsed tuples.'),
 
504
                    ]
 
505
 
 
506
    def run(self, path, raw=False):
 
507
        dirname, basename = osutils.split(path)
 
508
        t = transport.get_transport(dirname)
 
509
        if raw:
 
510
            self._dump_raw_bytes(t, basename)
 
511
        else:
 
512
            self._dump_entries(t, basename)
 
513
 
 
514
    def _get_index_and_bytes(self, trans, basename):
 
515
        """Create a BTreeGraphIndex and raw bytes."""
 
516
        bt = btree_index.BTreeGraphIndex(trans, basename, None)
 
517
        bytes = trans.get_bytes(basename)
 
518
        bt._file = BytesIO(bytes)
 
519
        bt._size = len(bytes)
 
520
        return bt, bytes
 
521
 
 
522
    def _dump_raw_bytes(self, trans, basename):
 
523
        import zlib
 
524
 
 
525
        # We need to parse at least the root node.
 
526
        # This is because the first page of every row starts with an
 
527
        # uncompressed header.
 
528
        bt, bytes = self._get_index_and_bytes(trans, basename)
 
529
        for page_idx, page_start in enumerate(range(0, len(bytes),
 
530
                                                    btree_index._PAGE_SIZE)):
 
531
            page_end = min(page_start + btree_index._PAGE_SIZE, len(bytes))
 
532
            page_bytes = bytes[page_start:page_end]
 
533
            if page_idx == 0:
 
534
                self.outf.write('Root node:\n')
 
535
                header_end, data = bt._parse_header_from_bytes(page_bytes)
 
536
                self.outf.write(page_bytes[:header_end])
 
537
                page_bytes = data
 
538
            self.outf.write('\nPage %d\n' % (page_idx,))
 
539
            if len(page_bytes) == 0:
 
540
                self.outf.write('(empty)\n');
 
541
            else:
 
542
                decomp_bytes = zlib.decompress(page_bytes)
 
543
                self.outf.write(decomp_bytes)
 
544
                self.outf.write('\n')
 
545
 
 
546
    def _dump_entries(self, trans, basename):
 
547
        try:
 
548
            st = trans.stat(basename)
 
549
        except errors.TransportNotPossible:
 
550
            # We can't stat, so we'll fake it because we have to do the 'get()'
 
551
            # anyway.
 
552
            bt, _ = self._get_index_and_bytes(trans, basename)
 
553
        else:
 
554
            bt = btree_index.BTreeGraphIndex(trans, basename, st.st_size)
 
555
        for node in bt.iter_all_entries():
 
556
            # Node is made up of:
 
557
            # (index, key, value, [references])
 
558
            try:
 
559
                refs = node[3]
 
560
            except IndexError:
 
561
                refs_as_tuples = None
 
562
            else:
 
563
                refs_as_tuples = static_tuple.as_tuples(refs)
 
564
            as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
 
565
            self.outf.write('%s\n' % (as_tuple,))
 
566
 
 
567
 
478
568
class cmd_remove_tree(Command):
479
569
    __doc__ = """Remove the working tree from a given branch/checkout.
480
570
 
493
583
 
494
584
    def run(self, location_list, force=False):
495
585
        if not location_list:
496
 
            location_list = ['.']
 
586
            location_list=['.']
497
587
 
498
588
        for location in location_list:
499
589
            d = controldir.ControlDir.open(location)
501
591
            try:
502
592
                working = d.open_workingtree()
503
593
            except errors.NoWorkingTree:
504
 
                raise errors.CommandError(
505
 
                    gettext("No working tree to remove"))
 
594
                raise errors.BzrCommandError(gettext("No working tree to remove"))
506
595
            except errors.NotLocalUrl:
507
 
                raise errors.CommandError(
508
 
                    gettext("You cannot remove the working tree"
509
 
                            " of a remote path"))
 
596
                raise errors.BzrCommandError(gettext("You cannot remove the working tree"
 
597
                                             " of a remote path"))
510
598
            if not force:
511
599
                if (working.has_changes()):
512
600
                    raise errors.UncommittedChanges(working)
514
602
                    raise errors.ShelvedChanges(working)
515
603
 
516
604
            if working.user_url != working.branch.user_url:
517
 
                raise errors.CommandError(
518
 
                    gettext("You cannot remove the working tree"
519
 
                            " from a lightweight checkout"))
 
605
                raise errors.BzrCommandError(gettext("You cannot remove the working tree"
 
606
                                             " from a lightweight checkout"))
520
607
 
521
608
            d.destroy_workingtree()
522
609
 
537
624
    that, you can supply --revision to force the state of the tree.
538
625
    """
539
626
 
540
 
    takes_options = [
541
 
        'revision', 'directory',
 
627
    takes_options = ['revision', 'directory',
542
628
        Option('force',
543
629
               help='Reset the tree even if it doesn\'t appear to be'
544
630
                    ' corrupted.'),
547
633
 
548
634
    def run(self, revision=None, directory='.', force=False):
549
635
        tree, _ = WorkingTree.open_containing(directory)
550
 
        self.enter_context(tree.lock_tree_write())
 
636
        self.add_cleanup(tree.lock_tree_write().unlock)
551
637
        if not force:
552
638
            try:
553
639
                tree.check_state()
554
640
            except errors.BzrError:
555
 
                pass  # There seems to be a real error here, so we'll reset
 
641
                pass # There seems to be a real error here, so we'll reset
556
642
            else:
557
643
                # Refuse
558
 
                raise errors.CommandError(gettext(
 
644
                raise errors.BzrCommandError(gettext(
559
645
                    'The tree does not appear to be corrupt. You probably'
560
646
                    ' want "brz revert" instead. Use "--force" if you are'
561
647
                    ' sure you want to reset the working tree.'))
565
651
            revision_ids = [r.as_revision_id(tree.branch) for r in revision]
566
652
        try:
567
653
            tree.reset_state(revision_ids)
568
 
        except errors.BzrError:
 
654
        except errors.BzrError as e:
569
655
            if revision_ids is None:
570
 
                extra = gettext(', the header appears corrupt, try passing '
571
 
                                '-r -1 to set the state to the last commit')
 
656
                extra = (gettext(', the header appears corrupt, try passing -r -1'
 
657
                         ' to set the state to the last commit'))
572
658
            else:
573
659
                extra = ''
574
 
            raise errors.CommandError(
575
 
                gettext('failed to reset the tree state{0}').format(extra))
 
660
            raise errors.BzrCommandError(gettext('failed to reset the tree state{0}').format(extra))
576
661
 
577
662
 
578
663
class cmd_revno(Command):
591
676
    @display_command
592
677
    def run(self, tree=False, location=u'.', revision=None):
593
678
        if revision is not None and tree:
594
 
            raise errors.CommandError(
595
 
                gettext("--tree and --revision can not be used together"))
 
679
            raise errors.BzrCommandError(gettext("--tree and --revision can "
 
680
                "not be used together"))
596
681
 
597
682
        if tree:
598
683
            try:
599
684
                wt = WorkingTree.open_containing(location)[0]
600
 
                self.enter_context(wt.lock_read())
 
685
                self.add_cleanup(wt.lock_read().unlock)
601
686
            except (errors.NoWorkingTree, errors.NotLocalUrl):
602
687
                raise errors.NoWorkingTree(location)
603
688
            b = wt.branch
604
689
            revid = wt.last_revision()
605
690
        else:
606
691
            b = Branch.open_containing(location)[0]
607
 
            self.enter_context(b.lock_read())
 
692
            self.add_cleanup(b.lock_read().unlock)
608
693
            if revision:
609
694
                if len(revision) != 1:
610
 
                    raise errors.CommandError(gettext(
 
695
                    raise errors.BzrCommandError(gettext(
611
696
                        "Revision numbers only make sense for single "
612
697
                        "revisions, not ranges"))
613
698
                revid = revision[0].as_revision_id(b)
615
700
                revid = b.last_revision()
616
701
        try:
617
702
            revno_t = b.revision_id_to_dotted_revno(revid)
618
 
        except (errors.NoSuchRevision, errors.GhostRevisionsHaveNoRevno):
 
703
        except errors.NoSuchRevision:
619
704
            revno_t = ('???',)
620
705
        revno = ".".join(str(n) for n in revno_t)
621
706
        self.cleanup_now()
629
714
    takes_args = ['revision_info*']
630
715
    takes_options = [
631
716
        'revision',
632
 
        custom_help('directory', help='Branch to examine, '
633
 
                    'rather than the one containing the working directory.'),
 
717
        custom_help('directory',
 
718
            help='Branch to examine, '
 
719
                 'rather than the one containing the working directory.'),
634
720
        Option('tree', help='Show revno of working tree.'),
635
721
        ]
636
722
 
641
727
        try:
642
728
            wt = WorkingTree.open_containing(directory)[0]
643
729
            b = wt.branch
644
 
            self.enter_context(wt.lock_read())
 
730
            self.add_cleanup(wt.lock_read().unlock)
645
731
        except (errors.NoWorkingTree, errors.NotLocalUrl):
646
732
            wt = None
647
733
            b = Branch.open_containing(directory)[0]
648
 
            self.enter_context(b.lock_read())
 
734
            self.add_cleanup(b.lock_read().unlock)
649
735
        revision_ids = []
650
736
        if revision is not None:
651
737
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
671
757
            except errors.NoSuchRevision:
672
758
                revno = '???'
673
759
            maxlen = max(maxlen, len(revno))
674
 
            revinfos.append((revno, revision_id))
 
760
            revinfos.append([revno, revision_id])
675
761
 
676
762
        self.cleanup_now()
677
 
        for revno, revid in revinfos:
678
 
            self.outf.write(
679
 
                '%*s %s\n' % (maxlen, revno, revid.decode('utf-8')))
 
763
        for ri in revinfos:
 
764
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
680
765
 
681
766
 
682
767
class cmd_add(Command):
715
800
    branches that will be merged later (without showing the two different
716
801
    adds as a conflict). It is also useful when merging another project
717
802
    into a subdirectory of this one.
718
 
 
 
803
    
719
804
    Any files matching patterns in the ignore list will not be added
720
805
    unless they are explicitly mentioned.
721
 
 
722
 
    In recursive mode, files larger than the configuration option
 
806
    
 
807
    In recursive mode, files larger than the configuration option 
723
808
    add.maximum_file_size will be skipped. Named items are never skipped due
724
809
    to file size.
725
810
    """
729
814
               help="Don't recursively add the contents of directories.",
730
815
               short_name='N'),
731
816
        Option('dry-run',
732
 
               help="Show what would be done, but don't actually do "
733
 
                    "anything."),
 
817
               help="Show what would be done, but don't actually do anything."),
734
818
        'verbose',
735
819
        Option('file-ids-from',
736
 
               type=str,
 
820
               type=text_type,
737
821
               help='Lookup file ids from this tree.'),
738
822
        ]
739
823
    encoding_type = 'replace'
742
826
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
743
827
            file_ids_from=None):
744
828
        import breezy.add
745
 
        tree, file_list = tree_files_for_add(file_list)
746
 
 
747
 
        if file_ids_from is not None and not tree.supports_setting_file_ids():
748
 
            warning(
749
 
                gettext('Ignoring --file-ids-from, since the tree does not '
750
 
                        'support setting file ids.'))
751
 
            file_ids_from = None
752
829
 
753
830
        base_tree = None
754
831
        if file_ids_from is not None:
755
832
            try:
756
833
                base_tree, base_path = WorkingTree.open_containing(
757
 
                    file_ids_from)
 
834
                                            file_ids_from)
758
835
            except errors.NoWorkingTree:
759
836
                base_branch, base_path = Branch.open_containing(
760
 
                    file_ids_from)
 
837
                                            file_ids_from)
761
838
                base_tree = base_branch.basis_tree()
762
839
 
763
 
            action = breezy.add.AddFromBaseAction(
764
 
                base_tree, base_path, to_file=self.outf,
 
840
            action = breezy.add.AddFromBaseAction(base_tree, base_path,
 
841
                          to_file=self.outf, should_print=(not is_quiet()))
 
842
        else:
 
843
            action = breezy.add.AddWithSkipLargeAction(to_file=self.outf,
765
844
                should_print=(not is_quiet()))
766
 
        else:
767
 
            action = breezy.add.AddWithSkipLargeAction(
768
 
                to_file=self.outf, should_print=(not is_quiet()))
769
845
 
770
846
        if base_tree:
771
 
            self.enter_context(base_tree.lock_read())
772
 
        added, ignored = tree.smart_add(
773
 
            file_list, not no_recurse, action=action, save=not dry_run)
 
847
            self.add_cleanup(base_tree.lock_read().unlock)
 
848
        tree, file_list = tree_files_for_add(file_list)
 
849
        added, ignored = tree.smart_add(file_list, not
 
850
            no_recurse, action=action, save=not dry_run)
774
851
        self.cleanup_now()
775
852
        if len(ignored) > 0:
776
853
            if verbose:
777
854
                for glob in sorted(ignored):
778
855
                    for path in ignored[glob]:
779
856
                        self.outf.write(
780
 
                            gettext("ignored {0} matching \"{1}\"\n").format(
781
 
                                path, glob))
 
857
                         gettext("ignored {0} matching \"{1}\"\n").format(
 
858
                         path, glob))
782
859
 
783
860
 
784
861
class cmd_mkdir(Command):
858
935
    takes_options = [
859
936
        'revision',
860
937
        'show-ids',
861
 
        Option('include-root',
862
 
               help='Include the entry for the root of the tree, if any.'),
863
938
        Option('kind',
864
 
               help='List entries of a particular kind: file, directory, '
865
 
                    'symlink.',
866
 
               type=str),
 
939
               help='List entries of a particular kind: file, directory, symlink.',
 
940
               type=text_type),
867
941
        ]
868
942
    takes_args = ['file*']
869
943
 
870
944
    @display_command
871
 
    def run(self, revision=None, show_ids=False, kind=None, include_root=False,
872
 
            file_list=None):
 
945
    def run(self, revision=None, show_ids=False, kind=None, file_list=None):
873
946
        if kind and kind not in ['file', 'directory', 'symlink']:
874
 
            raise errors.CommandError(
875
 
                gettext('invalid kind %r specified') % (kind,))
 
947
            raise errors.BzrCommandError(gettext('invalid kind %r specified') % (kind,))
876
948
 
877
949
        revision = _get_one_revision('inventory', revision)
878
950
        work_tree, file_list = WorkingTree.open_containing_paths(file_list)
879
 
        self.enter_context(work_tree.lock_read())
 
951
        self.add_cleanup(work_tree.lock_read().unlock)
880
952
        if revision is not None:
881
953
            tree = revision.as_tree(work_tree.branch)
882
954
 
883
955
            extra_trees = [work_tree]
884
 
            self.enter_context(tree.lock_read())
 
956
            self.add_cleanup(tree.lock_read().unlock)
885
957
        else:
886
958
            tree = work_tree
887
959
            extra_trees = []
888
960
 
889
 
        self.enter_context(tree.lock_read())
 
961
        self.add_cleanup(tree.lock_read().unlock)
890
962
        if file_list is not None:
891
 
            paths = tree.find_related_paths_across_trees(
892
 
                file_list, extra_trees, require_versioned=True)
 
963
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
964
                                      require_versioned=True)
893
965
            # find_ids_across_trees may include some paths that don't
894
966
            # exist in 'tree'.
895
 
            entries = tree.iter_entries_by_dir(specific_files=paths)
 
967
            entries = tree.iter_entries_by_dir(specific_file_ids=file_ids)
896
968
        else:
897
969
            entries = tree.iter_entries_by_dir()
898
970
 
899
971
        for path, entry in sorted(entries):
900
972
            if kind and kind != entry.kind:
901
973
                continue
902
 
            if path == "" and not include_root:
 
974
            if path == "":
903
975
                continue
904
976
            if show_ids:
905
 
                self.outf.write('%-50s %s\n' % (
906
 
                    path, entry.file_id.decode('utf-8')))
 
977
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
907
978
            else:
908
979
                self.outf.write(path)
909
980
                self.outf.write('\n')
931
1002
    encoding_type = 'replace'
932
1003
 
933
1004
    def run(self, names_list):
 
1005
        import shutil
934
1006
        if names_list is None:
935
1007
            names_list = []
936
1008
        if len(names_list) < 2:
937
 
            raise errors.CommandError(gettext("missing file argument"))
938
 
        tree, rel_names = WorkingTree.open_containing_paths(
939
 
            names_list, canonicalize=False)
 
1009
            raise errors.BzrCommandError(gettext("missing file argument"))
 
1010
        tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
940
1011
        for file_name in rel_names[0:-1]:
941
1012
            if file_name == '':
942
 
                raise errors.CommandError(
943
 
                    gettext("can not copy root of branch"))
944
 
        self.enter_context(tree.lock_tree_write())
 
1013
                raise errors.BzrCommandError(gettext("can not copy root of branch"))
 
1014
        self.add_cleanup(tree.lock_tree_write().unlock)
945
1015
        into_existing = osutils.isdir(names_list[-1])
946
1016
        if not into_existing:
947
1017
            try:
948
1018
                (src, dst) = rel_names
949
1019
            except IndexError:
950
 
                raise errors.CommandError(
951
 
                    gettext('to copy multiple files the'
952
 
                            ' destination must be a versioned'
953
 
                            ' directory'))
 
1020
                raise errors.BzrCommandError(gettext('to copy multiple files the'
 
1021
                                                     ' destination must be a versioned'
 
1022
                                                     ' directory'))
954
1023
            pairs = [(src, dst)]
955
1024
        else:
956
 
            pairs = [
957
 
                (n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
958
 
                for n in rel_names[:-1]]
 
1025
            pairs = [(n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
 
1026
                     for n in rel_names[:-1]]
959
1027
 
960
1028
        for src, dst in pairs:
961
1029
            try:
962
1030
                src_kind = tree.stored_kind(src)
963
1031
            except errors.NoSuchFile:
964
 
                raise errors.CommandError(
965
 
                    gettext('Could not copy %s => %s: %s is not versioned.')
966
 
                    % (src, dst, src))
 
1032
                raise errors.BzrCommandError(
 
1033
                        gettext('Could not copy %s => %s: %s is not versioned.')
 
1034
                        % (src, dst, src))
967
1035
            if src_kind is None:
968
 
                raise errors.CommandError(
969
 
                    gettext('Could not copy %s => %s . %s is not versioned\\.'
970
 
                            % (src, dst, src)))
 
1036
                raise errors.BzrCommandError(
 
1037
                    gettext('Could not copy %s => %s . %s is not versioned\.'
 
1038
                        % (src, dst, src)))
971
1039
            if src_kind == 'directory':
972
 
                raise errors.CommandError(
 
1040
                raise errors.BzrCommandError(
973
1041
                    gettext('Could not copy %s => %s . %s is a directory.'
974
 
                            % (src, dst, src)))
 
1042
                        % (src, dst, src)))
975
1043
            dst_parent = osutils.split(dst)[0]
976
1044
            if dst_parent != '':
977
1045
                try:
978
1046
                    dst_parent_kind = tree.stored_kind(dst_parent)
979
1047
                except errors.NoSuchFile:
980
 
                    raise errors.CommandError(
981
 
                        gettext('Could not copy %s => %s: %s is not versioned.')
982
 
                        % (src, dst, dst_parent))
 
1048
                    raise errors.BzrCommandError(
 
1049
                            gettext('Could not copy %s => %s: %s is not versioned.')
 
1050
                            % (src, dst, dst_parent))
983
1051
                if dst_parent_kind != 'directory':
984
 
                    raise errors.CommandError(
985
 
                        gettext('Could not copy to %s: %s is not a directory.')
986
 
                        % (dst_parent, dst_parent))
 
1052
                    raise errors.BzrCommandError(
 
1053
                            gettext('Could not copy to %s: %s is not a directory.')
 
1054
                            % (dst_parent, dst_parent))
987
1055
 
988
1056
            tree.copy_one(src, dst)
989
1057
 
1011
1079
 
1012
1080
    takes_args = ['names*']
1013
1081
    takes_options = [Option("after", help="Move only the brz identifier"
1014
 
                            " of the file, because the file has already been moved."),
1015
 
                     Option('auto', help='Automatically guess renames.'),
1016
 
                     Option(
1017
 
                         'dry-run', help='Avoid making changes when guessing renames.'),
1018
 
                     ]
 
1082
        " of the file, because the file has already been moved."),
 
1083
        Option('auto', help='Automatically guess renames.'),
 
1084
        Option('dry-run', help='Avoid making changes when guessing renames.'),
 
1085
        ]
1019
1086
    aliases = ['move', 'rename']
1020
1087
    encoding_type = 'replace'
1021
1088
 
1023
1090
        if auto:
1024
1091
            return self.run_auto(names_list, after, dry_run)
1025
1092
        elif dry_run:
1026
 
            raise errors.CommandError(gettext('--dry-run requires --auto.'))
 
1093
            raise errors.BzrCommandError(gettext('--dry-run requires --auto.'))
1027
1094
        if names_list is None:
1028
1095
            names_list = []
1029
1096
        if len(names_list) < 2:
1030
 
            raise errors.CommandError(gettext("missing file argument"))
1031
 
        tree, rel_names = WorkingTree.open_containing_paths(
1032
 
            names_list, canonicalize=False)
 
1097
            raise errors.BzrCommandError(gettext("missing file argument"))
 
1098
        tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
1033
1099
        for file_name in rel_names[0:-1]:
1034
1100
            if file_name == '':
1035
 
                raise errors.CommandError(
1036
 
                    gettext("can not move root of branch"))
1037
 
        self.enter_context(tree.lock_tree_write())
 
1101
                raise errors.BzrCommandError(gettext("can not move root of branch"))
 
1102
        self.add_cleanup(tree.lock_tree_write().unlock)
1038
1103
        self._run(tree, names_list, rel_names, after)
1039
1104
 
1040
1105
    def run_auto(self, names_list, after, dry_run):
1041
1106
        if names_list is not None and len(names_list) > 1:
1042
 
            raise errors.CommandError(
1043
 
                gettext('Only one path may be specified to --auto.'))
 
1107
            raise errors.BzrCommandError(gettext('Only one path may be specified to'
 
1108
                                         ' --auto.'))
1044
1109
        if after:
1045
 
            raise errors.CommandError(
1046
 
                gettext('--after cannot be specified with --auto.'))
 
1110
            raise errors.BzrCommandError(gettext('--after cannot be specified with'
 
1111
                                         ' --auto.'))
1047
1112
        work_tree, file_list = WorkingTree.open_containing_paths(
1048
1113
            names_list, default_directory='.')
1049
 
        self.enter_context(work_tree.lock_tree_write())
1050
 
        rename_map.RenameMap.guess_renames(
1051
 
            work_tree.basis_tree(), work_tree, dry_run)
 
1114
        self.add_cleanup(work_tree.lock_tree_write().unlock)
 
1115
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
1052
1116
 
1053
1117
    def _run(self, tree, names_list, rel_names, after):
1054
1118
        into_existing = osutils.isdir(names_list[-1])
1059
1123
            #    a directory, but now doesn't exist in the working tree
1060
1124
            #    and the target is an existing directory, just rename it)
1061
1125
            if (not tree.case_sensitive
1062
 
                    and rel_names[0].lower() == rel_names[1].lower()):
 
1126
                and rel_names[0].lower() == rel_names[1].lower()):
1063
1127
                into_existing = False
1064
1128
            else:
1065
1129
                # 'fix' the case of a potential 'from'
1066
 
                from_path = tree.get_canonical_path(rel_names[0])
 
1130
                from_path = tree.get_canonical_inventory_path(rel_names[0])
 
1131
                from_id = tree.path2id(from_path)
1067
1132
                if (not osutils.lexists(names_list[0]) and
1068
 
                    tree.is_versioned(from_path) and
1069
 
                        tree.stored_kind(from_path) == "directory"):
 
1133
                    from_id and
 
1134
                    tree.stored_kind(from_path, from_id) == "directory"):
1070
1135
                    into_existing = False
1071
1136
        # move/rename
1072
1137
        if into_existing:
1073
1138
            # move into existing directory
1074
1139
            # All entries reference existing inventory items, so fix them up
1075
1140
            # for cicp file-systems.
1076
 
            rel_names = list(tree.get_canonical_paths(rel_names))
 
1141
            rel_names = tree.get_canonical_inventory_paths(rel_names)
1077
1142
            for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
1078
1143
                if not is_quiet():
1079
1144
                    self.outf.write("%s => %s\n" % (src, dest))
1080
1145
        else:
1081
1146
            if len(names_list) != 2:
1082
 
                raise errors.CommandError(gettext('to mv multiple files the'
1083
 
                                                     ' destination must be a versioned'
1084
 
                                                     ' directory'))
 
1147
                raise errors.BzrCommandError(gettext('to mv multiple files the'
 
1148
                                             ' destination must be a versioned'
 
1149
                                             ' directory'))
1085
1150
 
1086
1151
            # for cicp file-systems: the src references an existing inventory
1087
1152
            # item:
1088
 
            src = tree.get_canonical_path(rel_names[0])
 
1153
            src = tree.get_canonical_inventory_path(rel_names[0])
1089
1154
            # Find the canonical version of the destination:  In all cases, the
1090
1155
            # parent of the target must be in the inventory, so we fetch the
1091
1156
            # canonical version from there (we do not always *use* the
1092
1157
            # canonicalized tail portion - we may be attempting to rename the
1093
1158
            # case of the tail)
1094
 
            canon_dest = tree.get_canonical_path(rel_names[1])
 
1159
            canon_dest = tree.get_canonical_inventory_path(rel_names[1])
1095
1160
            dest_parent = osutils.dirname(canon_dest)
1096
1161
            spec_tail = osutils.basename(rel_names[1])
1097
1162
            # For a CICP file-system, we need to avoid creating 2 inventory
1108
1173
                if after:
1109
1174
                    # If 'after' is specified, the tail must refer to a file on disk.
1110
1175
                    if dest_parent:
1111
 
                        dest_parent_fq = osutils.pathjoin(
1112
 
                            tree.basedir, dest_parent)
 
1176
                        dest_parent_fq = osutils.pathjoin(tree.basedir, dest_parent)
1113
1177
                    else:
1114
1178
                        # pathjoin with an empty tail adds a slash, which breaks
1115
1179
                        # relpath :(
1116
1180
                        dest_parent_fq = tree.basedir
1117
1181
 
1118
1182
                    dest_tail = osutils.canonical_relpath(
1119
 
                        dest_parent_fq,
1120
 
                        osutils.pathjoin(dest_parent_fq, spec_tail))
 
1183
                                    dest_parent_fq,
 
1184
                                    osutils.pathjoin(dest_parent_fq, spec_tail))
1121
1185
                else:
1122
1186
                    # not 'after', so case as specified is used
1123
1187
                    dest_tail = spec_tail
1135
1199
    __doc__ = """Turn this branch into a mirror of another branch.
1136
1200
 
1137
1201
    By default, this command only works on branches that have not diverged.
1138
 
    Branches are considered diverged if the destination branch's most recent
1139
 
    commit is one that has not been merged (directly or indirectly) into the
 
1202
    Branches are considered diverged if the destination branch's most recent 
 
1203
    commit is one that has not been merged (directly or indirectly) into the 
1140
1204
    parent.
1141
1205
 
1142
1206
    If branches have diverged, you can use 'brz merge' to integrate the changes
1163
1227
 
1164
1228
    _see_also = ['push', 'update', 'status-flags', 'send']
1165
1229
    takes_options = ['remember', 'overwrite', 'revision',
1166
 
                     custom_help('verbose',
1167
 
                                 help='Show logs of pulled revisions.'),
1168
 
                     custom_help('directory',
1169
 
                                 help='Branch to pull into, '
1170
 
                                 'rather than the one containing the working directory.'),
1171
 
                     Option('local',
1172
 
                            help="Perform a local pull in a bound "
1173
 
                            "branch.  Local pulls are not applied to "
1174
 
                            "the master branch."
1175
 
                            ),
1176
 
                     Option('show-base',
1177
 
                            help="Show base revision text in conflicts."),
1178
 
                     Option('overwrite-tags',
1179
 
                            help="Overwrite tags only."),
1180
 
                     ]
 
1230
        custom_help('verbose',
 
1231
            help='Show logs of pulled revisions.'),
 
1232
        custom_help('directory',
 
1233
            help='Branch to pull into, '
 
1234
                 'rather than the one containing the working directory.'),
 
1235
        Option('local',
 
1236
            help="Perform a local pull in a bound "
 
1237
                 "branch.  Local pulls are not applied to "
 
1238
                 "the master branch."
 
1239
            ),
 
1240
        Option('show-base',
 
1241
            help="Show base revision text in conflicts."),
 
1242
        Option('overwrite-tags',
 
1243
            help="Overwrite tags only."),
 
1244
        ]
1181
1245
    takes_args = ['location?']
1182
1246
    encoding_type = 'replace'
1183
1247
 
1200
1264
        try:
1201
1265
            tree_to = WorkingTree.open_containing(directory)[0]
1202
1266
            branch_to = tree_to.branch
1203
 
            self.enter_context(tree_to.lock_write())
 
1267
            self.add_cleanup(tree_to.lock_write().unlock)
1204
1268
        except errors.NoWorkingTree:
1205
1269
            tree_to = None
1206
1270
            branch_to = Branch.open_containing(directory)[0]
1207
 
            self.enter_context(branch_to.lock_write())
 
1271
            self.add_cleanup(branch_to.lock_write().unlock)
1208
1272
            if show_base:
1209
1273
                warning(gettext("No working tree, ignoring --show-base"))
1210
1274
 
1214
1278
        possible_transports = []
1215
1279
        if location is not None:
1216
1280
            try:
1217
 
                mergeable = _mod_mergeable.read_mergeable_from_url(
1218
 
                    location, possible_transports=possible_transports)
 
1281
                mergeable = bundle.read_mergeable_from_url(location,
 
1282
                    possible_transports=possible_transports)
1219
1283
            except errors.NotABundle:
1220
1284
                mergeable = None
1221
1285
 
1222
1286
        stored_loc = branch_to.get_parent()
1223
1287
        if location is None:
1224
1288
            if stored_loc is None:
1225
 
                raise errors.CommandError(gettext("No pull location known or"
1226
 
                                                     " specified."))
 
1289
                raise errors.BzrCommandError(gettext("No pull location known or"
 
1290
                                             " specified."))
1227
1291
            else:
1228
1292
                display_url = urlutils.unescape_for_display(stored_loc,
1229
 
                                                            self.outf.encoding)
 
1293
                        self.outf.encoding)
1230
1294
                if not is_quiet():
1231
 
                    self.outf.write(
1232
 
                        gettext("Using saved parent location: %s\n") % display_url)
 
1295
                    self.outf.write(gettext("Using saved parent location: %s\n") % display_url)
1233
1296
                location = stored_loc
1234
1297
 
1235
1298
        revision = _get_one_revision('pull', revision)
1236
1299
        if mergeable is not None:
1237
1300
            if revision is not None:
1238
 
                raise errors.CommandError(gettext(
 
1301
                raise errors.BzrCommandError(gettext(
1239
1302
                    'Cannot use -r with merge directives or bundles'))
1240
1303
            mergeable.install_revisions(branch_to.repository)
1241
1304
            base_revision_id, revision_id, verified = \
1243
1306
            branch_from = branch_to
1244
1307
        else:
1245
1308
            branch_from = Branch.open(location,
1246
 
                                      possible_transports=possible_transports)
1247
 
            self.enter_context(branch_from.lock_read())
 
1309
                possible_transports=possible_transports)
 
1310
            self.add_cleanup(branch_from.lock_read().unlock)
1248
1311
            # Remembers if asked explicitly or no previous location is set
1249
1312
            if (remember
1250
 
                    or (remember is None and branch_to.get_parent() is None)):
 
1313
                or (remember is None and branch_to.get_parent() is None)):
1251
1314
                # FIXME: This shouldn't be done before the pull
1252
1315
                # succeeds... -- vila 2012-01-02
1253
1316
                branch_to.set_parent(branch_from.base)
1310
1373
 
1311
1374
    _see_also = ['pull', 'update', 'working-trees']
1312
1375
    takes_options = ['remember', 'overwrite', 'verbose', 'revision',
1313
 
                     Option('create-prefix',
1314
 
                            help='Create the path leading up to the branch '
1315
 
                            'if it does not already exist.'),
1316
 
                     custom_help('directory',
1317
 
                                 help='Branch to push from, '
1318
 
                                 'rather than the one containing the working directory.'),
1319
 
                     Option('use-existing-dir',
1320
 
                            help='By default push will fail if the target'
1321
 
                            ' directory exists, but does not already'
1322
 
                            ' have a control directory.  This flag will'
1323
 
                            ' allow push to proceed.'),
1324
 
                     Option('stacked',
1325
 
                            help='Create a stacked branch that references the public location '
1326
 
                            'of the parent branch.'),
1327
 
                     Option('stacked-on',
1328
 
                            help='Create a stacked branch that refers to another branch '
1329
 
                            'for the commit history. Only the work not present in the '
1330
 
                            'referenced branch is included in the branch created.',
1331
 
                            type=str),
1332
 
                     Option('strict',
1333
 
                            help='Refuse to push if there are uncommitted changes in'
1334
 
                            ' the working tree, --no-strict disables the check.'),
1335
 
                     Option('no-tree',
1336
 
                            help="Don't populate the working tree, even for protocols"
1337
 
                            " that support it."),
1338
 
                     Option('overwrite-tags',
1339
 
                            help="Overwrite tags only."),
1340
 
                     Option('lossy', help="Allow lossy push, i.e. dropping metadata "
1341
 
                            "that can't be represented in the target.")
1342
 
                     ]
 
1376
        Option('create-prefix',
 
1377
               help='Create the path leading up to the branch '
 
1378
                    'if it does not already exist.'),
 
1379
        custom_help('directory',
 
1380
            help='Branch to push from, '
 
1381
                 'rather than the one containing the working directory.'),
 
1382
        Option('use-existing-dir',
 
1383
               help='By default push will fail if the target'
 
1384
                    ' directory exists, but does not already'
 
1385
                    ' have a control directory.  This flag will'
 
1386
                    ' allow push to proceed.'),
 
1387
        Option('stacked',
 
1388
            help='Create a stacked branch that references the public location '
 
1389
                'of the parent branch.'),
 
1390
        Option('stacked-on',
 
1391
            help='Create a stacked branch that refers to another branch '
 
1392
                'for the commit history. Only the work not present in the '
 
1393
                'referenced branch is included in the branch created.',
 
1394
            type=text_type),
 
1395
        Option('strict',
 
1396
               help='Refuse to push if there are uncommitted changes in'
 
1397
               ' the working tree, --no-strict disables the check.'),
 
1398
        Option('no-tree',
 
1399
               help="Don't populate the working tree, even for protocols"
 
1400
               " that support it."),
 
1401
        Option('overwrite-tags',
 
1402
              help="Overwrite tags only."),
 
1403
        ]
1343
1404
    takes_args = ['location?']
1344
1405
    encoding_type = 'replace'
1345
1406
 
1346
1407
    def run(self, location=None, remember=None, overwrite=False,
1347
 
            create_prefix=False, verbose=False, revision=None,
1348
 
            use_existing_dir=False, directory=None, stacked_on=None,
1349
 
            stacked=False, strict=None, no_tree=False,
1350
 
            overwrite_tags=False, lossy=False):
1351
 
        from .location import location_to_url
 
1408
        create_prefix=False, verbose=False, revision=None,
 
1409
        use_existing_dir=False, directory=None, stacked_on=None,
 
1410
        stacked=False, strict=None, no_tree=False,
 
1411
        overwrite_tags=False):
1352
1412
        from .push import _show_push_branch
1353
1413
 
1354
1414
        if overwrite:
1376
1436
                more_warning='Uncommitted changes will not be pushed.')
1377
1437
        # Get the stacked_on branch, if any
1378
1438
        if stacked_on is not None:
1379
 
            stacked_on = location_to_url(stacked_on, 'read')
1380
1439
            stacked_on = urlutils.normalize_url(stacked_on)
1381
1440
        elif stacked:
1382
1441
            parent_url = br_from.get_parent()
1390
1449
                    # error by the feedback given to them. RBC 20080227.
1391
1450
                    stacked_on = parent_url
1392
1451
            if not stacked_on:
1393
 
                raise errors.CommandError(gettext(
 
1452
                raise errors.BzrCommandError(gettext(
1394
1453
                    "Could not determine branch to refer to."))
1395
1454
 
1396
1455
        # Get the destination location
1399
1458
            if stored_loc is None:
1400
1459
                parent_loc = br_from.get_parent()
1401
1460
                if parent_loc:
1402
 
                    raise errors.CommandError(gettext(
 
1461
                    raise errors.BzrCommandError(gettext(
1403
1462
                        "No push location known or specified. To push to the "
1404
1463
                        "parent branch (at %s), use 'brz push :parent'." %
1405
1464
                        urlutils.unescape_for_display(parent_loc,
1406
 
                                                      self.outf.encoding)))
 
1465
                            self.outf.encoding)))
1407
1466
                else:
1408
 
                    raise errors.CommandError(gettext(
 
1467
                    raise errors.BzrCommandError(gettext(
1409
1468
                        "No push location known or specified."))
1410
1469
            else:
1411
1470
                display_url = urlutils.unescape_for_display(stored_loc,
1412
 
                                                            self.outf.encoding)
 
1471
                        self.outf.encoding)
1413
1472
                note(gettext("Using saved push location: %s") % display_url)
1414
1473
                location = stored_loc
1415
1474
 
1416
1475
        _show_push_branch(br_from, revision_id, location, self.outf,
1417
 
                          verbose=verbose, overwrite=overwrite, remember=remember,
1418
 
                          stacked_on=stacked_on, create_prefix=create_prefix,
1419
 
                          use_existing_dir=use_existing_dir, no_tree=no_tree,
1420
 
                          lossy=lossy)
 
1476
            verbose=verbose, overwrite=overwrite, remember=remember,
 
1477
            stacked_on=stacked_on, create_prefix=create_prefix,
 
1478
            use_existing_dir=use_existing_dir, no_tree=no_tree)
1421
1479
 
1422
1480
 
1423
1481
class cmd_branch(Command):
1434
1492
    parameter, as in "branch foo/bar -r 5".
1435
1493
    """
1436
1494
 
1437
 
    aliase = ['sprout']
1438
1495
    _see_also = ['checkout']
1439
1496
    takes_args = ['from_location', 'to_location?']
1440
1497
    takes_options = ['revision',
1441
 
                     Option(
1442
 
                         'hardlink', help='Hard-link working tree files where possible.'),
1443
 
                     Option('files-from', type=str,
1444
 
                            help="Get file contents from this tree."),
1445
 
                     Option('no-tree',
1446
 
                            help="Create a branch without a working-tree."),
1447
 
                     Option('switch',
1448
 
                            help="Switch the checkout in the current directory "
1449
 
                            "to the new branch."),
1450
 
                     Option('stacked',
1451
 
                            help='Create a stacked branch referring to the source branch. '
1452
 
                            'The new branch will depend on the availability of the source '
1453
 
                            'branch for all operations.'),
1454
 
                     Option('standalone',
1455
 
                            help='Do not use a shared repository, even if available.'),
1456
 
                     Option('use-existing-dir',
1457
 
                            help='By default branch will fail if the target'
1458
 
                            ' directory exists, but does not already'
1459
 
                            ' have a control directory.  This flag will'
1460
 
                            ' allow branch to proceed.'),
1461
 
                     Option('bind',
1462
 
                            help="Bind new branch to from location."),
1463
 
                     Option('no-recurse-nested',
1464
 
                            help='Do not recursively check out nested trees.'),
1465
 
                     Option('colocated-branch', short_name='b',
1466
 
                            type=str, help='Name of colocated branch to sprout.'),
1467
 
                     ]
 
1498
        Option('hardlink', help='Hard-link working tree files where possible.'),
 
1499
        Option('files-from', type=text_type,
 
1500
               help="Get file contents from this tree."),
 
1501
        Option('no-tree',
 
1502
            help="Create a branch without a working-tree."),
 
1503
        Option('switch',
 
1504
            help="Switch the checkout in the current directory "
 
1505
                 "to the new branch."),
 
1506
        Option('stacked',
 
1507
            help='Create a stacked branch referring to the source branch. '
 
1508
                'The new branch will depend on the availability of the source '
 
1509
                'branch for all operations.'),
 
1510
        Option('standalone',
 
1511
               help='Do not use a shared repository, even if available.'),
 
1512
        Option('use-existing-dir',
 
1513
               help='By default branch will fail if the target'
 
1514
                    ' directory exists, but does not already'
 
1515
                    ' have a control directory.  This flag will'
 
1516
                    ' allow branch to proceed.'),
 
1517
        Option('bind',
 
1518
            help="Bind new branch to from location."),
 
1519
        ]
1468
1520
 
1469
1521
    def run(self, from_location, to_location=None, revision=None,
1470
1522
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1471
1523
            use_existing_dir=False, switch=False, bind=False,
1472
 
            files_from=None, no_recurse_nested=False, colocated_branch=None):
 
1524
            files_from=None):
1473
1525
        from breezy import switch as _mod_switch
1474
1526
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1475
 
            from_location, name=colocated_branch)
1476
 
        if no_recurse_nested:
1477
 
            recurse = 'none'
1478
 
        else:
1479
 
            recurse = 'down'
 
1527
            from_location)
1480
1528
        if not (hardlink or files_from):
1481
1529
            # accelerator_tree is usually slower because you have to read N
1482
1530
            # files (no readahead, lots of seeks, etc), but allow the user to
1485
1533
        if files_from is not None and files_from != from_location:
1486
1534
            accelerator_tree = WorkingTree.open(files_from)
1487
1535
        revision = _get_one_revision('branch', revision)
1488
 
        self.enter_context(br_from.lock_read())
 
1536
        self.add_cleanup(br_from.lock_read().unlock)
1489
1537
        if revision is not None:
1490
1538
            revision_id = revision.as_revision_id(br_from)
1491
1539
        else:
1494
1542
            # RBC 20060209
1495
1543
            revision_id = br_from.last_revision()
1496
1544
        if to_location is None:
1497
 
            to_location = urlutils.derive_to_location(from_location)
1498
 
        to_transport = transport.get_transport(to_location, purpose='write')
 
1545
            to_location = getattr(br_from, "name", None)
 
1546
            if not to_location:
 
1547
                to_location = urlutils.derive_to_location(from_location)
 
1548
        to_transport = transport.get_transport(to_location)
1499
1549
        try:
1500
1550
            to_transport.mkdir('.')
1501
1551
        except errors.FileExists:
1504
1554
                    to_transport)
1505
1555
            except errors.NotBranchError:
1506
1556
                if not use_existing_dir:
1507
 
                    raise errors.CommandError(gettext('Target directory "%s" '
1508
 
                                                         'already exists.') % to_location)
 
1557
                    raise errors.BzrCommandError(gettext('Target directory "%s" '
 
1558
                        'already exists.') % to_location)
1509
1559
                else:
1510
1560
                    to_dir = None
1511
1561
            else:
1516
1566
                else:
1517
1567
                    raise errors.AlreadyBranchError(to_location)
1518
1568
        except errors.NoSuchFile:
1519
 
            raise errors.CommandError(gettext('Parent of "%s" does not exist.')
 
1569
            raise errors.BzrCommandError(gettext('Parent of "%s" does not exist.')
1520
1570
                                         % to_location)
1521
1571
        else:
1522
1572
            to_dir = None
1528
1578
                    possible_transports=[to_transport],
1529
1579
                    accelerator_tree=accelerator_tree, hardlink=hardlink,
1530
1580
                    stacked=stacked, force_new_repo=standalone,
1531
 
                    create_tree_if_local=not no_tree, source_branch=br_from,
1532
 
                    recurse=recurse)
 
1581
                    create_tree_if_local=not no_tree, source_branch=br_from)
1533
1582
                branch = to_dir.open_branch(
1534
1583
                    possible_transports=[
1535
1584
                        br_from.controldir.root_transport, to_transport])
1537
1586
                to_transport.delete_tree('.')
1538
1587
                msg = gettext("The branch {0} has no revision {1}.").format(
1539
1588
                    from_location, revision)
1540
 
                raise errors.CommandError(msg)
 
1589
                raise errors.BzrCommandError(msg)
1541
1590
        else:
1542
1591
            try:
1543
1592
                to_repo = to_dir.open_repository()
1544
1593
            except errors.NoRepositoryPresent:
1545
1594
                to_repo = to_dir.create_repository()
1546
1595
            to_repo.fetch(br_from.repository, revision_id=revision_id)
1547
 
            branch = br_from.sprout(
1548
 
                to_dir, revision_id=revision_id)
 
1596
            branch = br_from.sprout(to_dir, revision_id=revision_id)
1549
1597
        br_from.tags.merge_to(branch.tags)
1550
1598
 
1551
1599
        # If the source branch is stacked, the new branch may
1553
1601
        # We therefore need a try/except here and not just 'if stacked:'
1554
1602
        try:
1555
1603
            note(gettext('Created new stacked branch referring to %s.') %
1556
 
                 branch.get_stacked_on_url())
 
1604
                branch.get_stacked_on_url())
1557
1605
        except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
1558
 
                errors.UnstackableRepositoryFormat) as e:
1559
 
            revno = branch.revno()
1560
 
            if revno is not None:
1561
 
                note(ngettext('Branched %d revision.',
1562
 
                              'Branched %d revisions.',
1563
 
                              branch.revno()) % revno)
1564
 
            else:
1565
 
                note(gettext('Created new branch.'))
 
1606
            errors.UnstackableRepositoryFormat) as e:
 
1607
            note(ngettext('Branched %d revision.', 'Branched %d revisions.', branch.revno()) % branch.revno())
1566
1608
        if bind:
1567
1609
            # Bind to the parent
1568
1610
            parent_branch = Branch.open(from_location)
1573
1615
            wt, _ = WorkingTree.open_containing('.')
1574
1616
            _mod_switch.switch(wt.controldir, branch)
1575
1617
            note(gettext('Switched to branch: %s'),
1576
 
                 urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1618
                urlutils.unescape_for_display(branch.base, 'utf-8'))
1577
1619
 
1578
1620
 
1579
1621
class cmd_branches(Command):
1585
1627
 
1586
1628
    takes_args = ['location?']
1587
1629
    takes_options = [
1588
 
        Option('recursive', short_name='R',
1589
 
               help='Recursively scan for branches rather than '
1590
 
               'just looking in the specified location.')]
 
1630
                  Option('recursive', short_name='R',
 
1631
                         help='Recursively scan for branches rather than '
 
1632
                              'just looking in the specified location.')]
1591
1633
 
1592
1634
    def run(self, location=".", recursive=False):
1593
1635
        if recursive:
1594
 
            t = transport.get_transport(location, purpose='read')
 
1636
            t = transport.get_transport(location)
1595
1637
            if not t.listable():
1596
 
                raise errors.CommandError(
 
1638
                raise errors.BzrCommandError(
1597
1639
                    "Can't scan this type of location.")
1598
1640
            for b in controldir.ControlDir.find_branches(t):
1599
1641
                self.outf.write("%s\n" % urlutils.unescape_for_display(
1610
1652
                if name == "":
1611
1653
                    continue
1612
1654
                active = (active_branch is not None and
1613
 
                          active_branch.user_url == branch.user_url)
 
1655
                          active_branch.base == branch.base)
1614
1656
                names[name] = active
1615
1657
            # Only mention the current branch explicitly if it's not
1616
1658
            # one of the colocated branches
1617
 
            if not any(names.values()) and active_branch is not None:
 
1659
            if not any(viewvalues(names)) and active_branch is not None:
1618
1660
                self.outf.write("* %s\n" % gettext("(default)"))
1619
1661
            for name in sorted(names):
1620
1662
                active = names[name]
1622
1664
                    prefix = "*"
1623
1665
                else:
1624
1666
                    prefix = " "
1625
 
                self.outf.write("%s %s\n" % (prefix, name))
 
1667
                self.outf.write("%s %s\n" % (
 
1668
                    prefix, name.encode(self.outf.encoding)))
1626
1669
 
1627
1670
 
1628
1671
class cmd_checkout(Command):
1629
1672
    __doc__ = """Create a new checkout of an existing branch.
1630
1673
 
1631
 
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree
1632
 
    for the branch found in '.'. This is useful if you have removed the working
1633
 
    tree or if it was never created - i.e. if you pushed the branch to its
1634
 
    current location using SFTP.
 
1674
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree for
 
1675
    the branch found in '.'. This is useful if you have removed the working tree
 
1676
    or if it was never created - i.e. if you pushed the branch to its current
 
1677
    location using SFTP.
1635
1678
 
1636
 
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION
1637
 
    will be used.  In other words, "checkout ../foo/bar" will attempt to create
1638
 
    ./bar.  If the BRANCH_LOCATION has no / or path separator embedded, the
1639
 
    TO_LOCATION is derived from the BRANCH_LOCATION by stripping a leading
1640
 
    scheme or drive identifier, if any. For example, "checkout lp:foo-bar" will
1641
 
    attempt to create ./foo-bar.
 
1679
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION will
 
1680
    be used.  In other words, "checkout ../foo/bar" will attempt to create ./bar.
 
1681
    If the BRANCH_LOCATION has no / or path separator embedded, the TO_LOCATION
 
1682
    is derived from the BRANCH_LOCATION by stripping a leading scheme or drive
 
1683
    identifier, if any. For example, "checkout lp:foo-bar" will attempt to
 
1684
    create ./foo-bar.
1642
1685
 
1643
1686
    To retrieve the branch as of a particular revision, supply the --revision
1644
 
    parameter, as in "checkout foo/bar -r 5". Note that this will be
1645
 
    immediately out of date [so you cannot commit] but it may be useful (i.e.
1646
 
    to examine old code.)
 
1687
    parameter, as in "checkout foo/bar -r 5". Note that this will be immediately
 
1688
    out of date [so you cannot commit] but it may be useful (i.e. to examine old
 
1689
    code.)
1647
1690
    """
1648
1691
 
1649
1692
    _see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
1656
1699
                                 "common operations like diff and status without "
1657
1700
                                 "such access, and also support local commits."
1658
1701
                            ),
1659
 
                     Option('files-from', type=str,
 
1702
                     Option('files-from', type=text_type,
1660
1703
                            help="Get file contents from this tree."),
1661
1704
                     Option('hardlink',
1662
1705
                            help='Hard-link working tree files where possible.'
1688
1731
        # if the source and to_location are the same,
1689
1732
        # and there is no working tree,
1690
1733
        # then reconstitute a branch
1691
 
        if osutils.abspath(to_location) == osutils.abspath(branch_location):
 
1734
        if (osutils.abspath(to_location) ==
 
1735
            osutils.abspath(branch_location)):
1692
1736
            try:
1693
1737
                source.controldir.open_workingtree()
1694
1738
            except errors.NoWorkingTree:
1698
1742
                               accelerator_tree, hardlink)
1699
1743
 
1700
1744
 
1701
 
class cmd_clone(Command):
1702
 
    __doc__ = """Clone a control directory.
1703
 
    """
1704
 
 
1705
 
    takes_args = ['from_location', 'to_location?']
1706
 
    takes_options = ['revision',
1707
 
                     Option('no-recurse-nested',
1708
 
                            help='Do not recursively check out nested trees.'),
1709
 
                     ]
1710
 
 
1711
 
    def run(self, from_location, to_location=None, revision=None, no_recurse_nested=False):
1712
 
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1713
 
            from_location)
1714
 
        if no_recurse_nested:
1715
 
            recurse = 'none'
1716
 
        else:
1717
 
            recurse = 'down'
1718
 
        revision = _get_one_revision('branch', revision)
1719
 
        self.enter_context(br_from.lock_read())
1720
 
        if revision is not None:
1721
 
            revision_id = revision.as_revision_id(br_from)
1722
 
        else:
1723
 
            # FIXME - wt.last_revision, fallback to branch, fall back to
1724
 
            # None or perhaps NULL_REVISION to mean copy nothing
1725
 
            # RBC 20060209
1726
 
            revision_id = br_from.last_revision()
1727
 
        if to_location is None:
1728
 
            to_location = urlutils.derive_to_location(from_location)
1729
 
        target_controldir = br_from.controldir.clone(to_location, revision_id=revision_id)
1730
 
        note(gettext('Created new control directory.'))
1731
 
 
1732
 
 
1733
1745
class cmd_renames(Command):
1734
1746
    __doc__ = """Show list of renamed files.
1735
1747
    """
1742
1754
    @display_command
1743
1755
    def run(self, dir=u'.'):
1744
1756
        tree = WorkingTree.open_containing(dir)[0]
1745
 
        self.enter_context(tree.lock_read())
 
1757
        self.add_cleanup(tree.lock_read().unlock)
1746
1758
        old_tree = tree.basis_tree()
1747
 
        self.enter_context(old_tree.lock_read())
 
1759
        self.add_cleanup(old_tree.lock_read().unlock)
1748
1760
        renames = []
1749
1761
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
1750
 
        for change in iterator:
1751
 
            if change.path[0] == change.path[1]:
1752
 
                continue
1753
 
            if None in change.path:
1754
 
                continue
1755
 
            renames.append(change.path)
 
1762
        for f, paths, c, v, p, n, k, e in iterator:
 
1763
            if paths[0] == paths[1]:
 
1764
                continue
 
1765
            if None in (paths):
 
1766
                continue
 
1767
            renames.append(paths)
1756
1768
        renames.sort()
1757
1769
        for old_name, new_name in renames:
1758
1770
            self.outf.write("%s => %s\n" % (old_name, new_name))
1763
1775
 
1764
1776
    This will perform a merge of the destination revision (the tip of the
1765
1777
    branch, or the specified revision) into the working tree, and then make
1766
 
    that revision the basis revision for the working tree.
 
1778
    that revision the basis revision for the working tree.  
1767
1779
 
1768
1780
    You can use this to visit an older revision, or to update a working tree
1769
1781
    that is out of date from its branch.
1770
 
 
 
1782
    
1771
1783
    If there are any uncommitted changes in the tree, they will be carried
1772
1784
    across and remain as uncommitted changes after the update.  To discard
1773
1785
    these changes, use 'brz revert'.  The uncommitted changes may conflict
1776
1788
    If the tree's branch is bound to a master branch, brz will also update
1777
1789
    the branch from the master.
1778
1790
 
1779
 
    You cannot update just a single file or directory, because each Breezy
 
1791
    You cannot update just a single file or directory, because each Bazaar
1780
1792
    working tree has just a single basis revision.  If you want to restore a
1781
1793
    file that has been removed locally, use 'brz revert' instead of 'brz
1782
1794
    update'.  If you want to restore a file to its state in a previous
1784
1796
    out the old content of that file to a new location.
1785
1797
 
1786
1798
    The 'dir' argument, if given, must be the location of the root of a
1787
 
    working tree to update.  By default, the working tree that contains the
 
1799
    working tree to update.  By default, the working tree that contains the 
1788
1800
    current working directory is used.
1789
1801
    """
1790
1802
 
1798
1810
 
1799
1811
    def run(self, dir=None, revision=None, show_base=None):
1800
1812
        if revision is not None and len(revision) != 1:
1801
 
            raise errors.CommandError(gettext(
 
1813
            raise errors.BzrCommandError(gettext(
1802
1814
                "brz update --revision takes exactly one revision"))
1803
1815
        if dir is None:
1804
1816
            tree = WorkingTree.open_containing('.')[0]
1806
1818
            tree, relpath = WorkingTree.open_containing(dir)
1807
1819
            if relpath:
1808
1820
                # See bug 557886.
1809
 
                raise errors.CommandError(gettext(
 
1821
                raise errors.BzrCommandError(gettext(
1810
1822
                    "brz update can only update a whole tree, "
1811
1823
                    "not a file or subdirectory"))
1812
1824
        branch = tree.branch
1815
1827
            possible_transports=possible_transports)
1816
1828
        if master is not None:
1817
1829
            branch_location = master.base
1818
 
            self.enter_context(tree.lock_write())
 
1830
            tree.lock_write()
1819
1831
        else:
1820
1832
            branch_location = tree.branch.base
1821
 
            self.enter_context(tree.lock_tree_write())
 
1833
            tree.lock_tree_write()
 
1834
        self.add_cleanup(tree.unlock)
1822
1835
        # get rid of the final '/' and be ready for display
1823
1836
        branch_location = urlutils.unescape_for_display(
1824
1837
            branch_location.rstrip('/'),
1838
1851
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1839
1852
            revno = branch.revision_id_to_dotted_revno(revision_id)
1840
1853
            note(gettext("Tree is up to date at revision {0} of branch {1}"
1841
 
                         ).format('.'.join(map(str, revno)), branch_location))
 
1854
                        ).format('.'.join(map(str, revno)), branch_location))
1842
1855
            return 0
1843
1856
        view_info = _get_view_info_for_change_reporter(tree)
1844
1857
        change_reporter = delta._ChangeReporter(
1852
1865
                old_tip=old_tip,
1853
1866
                show_base=show_base)
1854
1867
        except errors.NoSuchRevision as e:
1855
 
            raise errors.CommandError(gettext(
1856
 
                "branch has no revision %s\n"
1857
 
                "brz update --revision only works"
1858
 
                " for a revision in the branch history")
1859
 
                % (e.revision))
 
1868
            raise errors.BzrCommandError(gettext(
 
1869
                                  "branch has no revision %s\n"
 
1870
                                  "brz update --revision only works"
 
1871
                                  " for a revision in the branch history")
 
1872
                                  % (e.revision))
1860
1873
        revno = tree.branch.revision_id_to_dotted_revno(
1861
1874
            _mod_revision.ensure_null(tree.last_revision()))
1862
1875
        note(gettext('Updated to revision {0} of branch {1}').format(
1864
1877
        parent_ids = tree.get_parent_ids()
1865
1878
        if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
1866
1879
            note(gettext('Your local commits will now show as pending merges with '
1867
 
                         "'brz status', and can be committed with 'brz commit'."))
 
1880
                 "'brz status', and can be committed with 'brz commit'."))
1868
1881
        if conflicts != 0:
1869
1882
            return 1
1870
1883
        else:
1918
1931
class cmd_remove(Command):
1919
1932
    __doc__ = """Remove files or directories.
1920
1933
 
1921
 
    This makes Breezy stop tracking changes to the specified files. Breezy will
 
1934
    This makes Bazaar stop tracking changes to the specified files. Bazaar will
1922
1935
    delete them if they can easily be recovered using revert otherwise they
1923
1936
    will be backed up (adding an extension of the form .~#~). If no options or
1924
 
    parameters are given Breezy will scan for files that are being tracked by
1925
 
    Breezy but missing in your tree and stop tracking them for you.
 
1937
    parameters are given Bazaar will scan for files that are being tracked by
 
1938
    Bazaar but missing in your tree and stop tracking them for you.
1926
1939
    """
1927
1940
    takes_args = ['file*']
1928
1941
    takes_options = ['verbose',
1929
 
                     Option(
1930
 
                         'new', help='Only remove files that have never been committed.'),
1931
 
                     RegistryOption.from_kwargs('file-deletion-strategy',
1932
 
                                                'The file deletion mode to be used.',
1933
 
                                                title='Deletion Strategy', value_switches=True, enum_switch=False,
1934
 
                                                safe='Backup changed files (default).',
1935
 
                                                keep='Delete from brz but leave the working copy.',
1936
 
                                                no_backup='Don\'t backup changed files.'),
1937
 
                     ]
 
1942
        Option('new', help='Only remove files that have never been committed.'),
 
1943
        RegistryOption.from_kwargs('file-deletion-strategy',
 
1944
            'The file deletion mode to be used.',
 
1945
            title='Deletion Strategy', value_switches=True, enum_switch=False,
 
1946
            safe='Backup changed files (default).',
 
1947
            keep='Delete from brz but leave the working copy.',
 
1948
            no_backup='Don\'t backup changed files.'),
 
1949
        ]
1938
1950
    aliases = ['rm', 'del']
1939
1951
    encoding_type = 'replace'
1940
1952
 
1941
1953
    def run(self, file_list, verbose=False, new=False,
1942
 
            file_deletion_strategy='safe'):
 
1954
        file_deletion_strategy='safe'):
1943
1955
 
1944
1956
        tree, file_list = WorkingTree.open_containing_paths(file_list)
1945
1957
 
1946
1958
        if file_list is not None:
1947
1959
            file_list = [f for f in file_list]
1948
1960
 
1949
 
        self.enter_context(tree.lock_write())
 
1961
        self.add_cleanup(tree.lock_write().unlock)
1950
1962
        # Heuristics should probably all move into tree.remove_smart or
1951
1963
        # some such?
1952
1964
        if new:
1953
1965
            added = tree.changes_from(tree.basis_tree(),
1954
 
                                      specific_files=file_list).added
1955
 
            file_list = sorted([f.path[1] for f in added], reverse=True)
 
1966
                specific_files=file_list).added
 
1967
            file_list = sorted([f[0] for f in added], reverse=True)
1956
1968
            if len(file_list) == 0:
1957
 
                raise errors.CommandError(gettext('No matching files.'))
 
1969
                raise errors.BzrCommandError(gettext('No matching files.'))
1958
1970
        elif file_list is None:
1959
1971
            # missing files show up in iter_changes(basis) as
1960
1972
            # versioned-with-no-kind.
1961
1973
            missing = []
1962
1974
            for change in tree.iter_changes(tree.basis_tree()):
1963
1975
                # Find paths in the working tree that have no kind:
1964
 
                if change.path[1] is not None and change.kind[1] is None:
1965
 
                    missing.append(change.path[1])
 
1976
                if change[1][1] is not None and change[6][1] is None:
 
1977
                    missing.append(change[1][1])
1966
1978
            file_list = sorted(missing, reverse=True)
1967
1979
            file_deletion_strategy = 'keep'
1968
1980
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
1969
 
                    keep_files=file_deletion_strategy == 'keep',
1970
 
                    force=(file_deletion_strategy == 'no-backup'))
 
1981
            keep_files=file_deletion_strategy=='keep',
 
1982
            force=(file_deletion_strategy=='no-backup'))
 
1983
 
 
1984
 
 
1985
class cmd_file_id(Command):
 
1986
    __doc__ = """Print file_id of a particular file or directory.
 
1987
 
 
1988
    The file_id is assigned when the file is first added and remains the
 
1989
    same through all revisions where the file exists, even when it is
 
1990
    moved or renamed.
 
1991
    """
 
1992
 
 
1993
    hidden = True
 
1994
    _see_also = ['inventory', 'ls']
 
1995
    takes_args = ['filename']
 
1996
 
 
1997
    @display_command
 
1998
    def run(self, filename):
 
1999
        tree, relpath = WorkingTree.open_containing(filename)
 
2000
        i = tree.path2id(relpath)
 
2001
        if i is None:
 
2002
            raise errors.NotVersionedError(filename)
 
2003
        else:
 
2004
            self.outf.write(i + '\n')
 
2005
 
 
2006
 
 
2007
class cmd_file_path(Command):
 
2008
    __doc__ = """Print path of file_ids to a file or directory.
 
2009
 
 
2010
    This prints one line for each directory down to the target,
 
2011
    starting at the branch root.
 
2012
    """
 
2013
 
 
2014
    hidden = True
 
2015
    takes_args = ['filename']
 
2016
 
 
2017
    @display_command
 
2018
    def run(self, filename):
 
2019
        tree, relpath = WorkingTree.open_containing(filename)
 
2020
        fid = tree.path2id(relpath)
 
2021
        if fid is None:
 
2022
            raise errors.NotVersionedError(filename)
 
2023
        segments = osutils.splitpath(relpath)
 
2024
        for pos in range(1, len(segments) + 1):
 
2025
            path = osutils.joinpath(segments[:pos])
 
2026
            self.outf.write("%s\n" % tree.path2id(path))
1971
2027
 
1972
2028
 
1973
2029
class cmd_reconcile(Command):
2015
2071
    @display_command
2016
2072
    def run(self, location="."):
2017
2073
        branch = Branch.open_containing(location)[0]
2018
 
        self.enter_context(branch.lock_read())
 
2074
        self.add_cleanup(branch.lock_read().unlock)
2019
2075
        graph = branch.repository.get_graph()
2020
2076
        history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
2021
 
                                                    [_mod_revision.NULL_REVISION]))
 
2077
            [_mod_revision.NULL_REVISION]))
2022
2078
        for revid in reversed(history):
2023
2079
            self.outf.write(revid)
2024
2080
            self.outf.write('\n')
2043
2099
            b = wt.branch
2044
2100
            last_revision = wt.last_revision()
2045
2101
 
2046
 
        self.enter_context(b.repository.lock_read())
 
2102
        self.add_cleanup(b.repository.lock_read().unlock)
2047
2103
        graph = b.repository.get_graph()
2048
2104
        revisions = [revid for revid, parents in
2049
 
                     graph.iter_ancestry([last_revision])]
 
2105
            graph.iter_ancestry([last_revision])]
2050
2106
        for revision_id in reversed(revisions):
2051
2107
            if _mod_revision.is_null(revision_id):
2052
2108
                continue
2053
 
            self.outf.write(revision_id.decode('utf-8') + '\n')
 
2109
            self.outf.write(revision_id + '\n')
2054
2110
 
2055
2111
 
2056
2112
class cmd_init(Command):
2076
2132
        brz commit -m "imported project"
2077
2133
    """
2078
2134
 
2079
 
    _see_also = ['init-shared-repository', 'branch', 'checkout']
 
2135
    _see_also = ['init-repository', 'branch', 'checkout']
2080
2136
    takes_args = ['location?']
2081
2137
    takes_options = [
2082
2138
        Option('create-prefix',
2083
2139
               help='Create the path leading up to the branch '
2084
2140
                    'if it does not already exist.'),
2085
 
        RegistryOption('format',
2086
 
                       help='Specify a format for this branch. '
2087
 
                       'See "help formats" for a full list.',
2088
 
                       lazy_registry=('breezy.controldir', 'format_registry'),
2089
 
                       converter=lambda name: controldir.format_registry.make_controldir(
2090
 
                            name),
2091
 
                       value_switches=True,
2092
 
                       title="Branch format",
2093
 
                       ),
2094
 
        Option('append-revisions-only',
2095
 
               help='Never change revnos or the existing log.'
2096
 
               '  Append revisions to it only.'),
2097
 
        Option('no-tree',
2098
 
               'Create a branch without a working tree.')
2099
 
        ]
2100
 
 
 
2141
         RegistryOption('format',
 
2142
                help='Specify a format for this branch. '
 
2143
                'See "help formats".',
 
2144
                lazy_registry=('breezy.controldir', 'format_registry'),
 
2145
                converter=lambda name: controldir.format_registry.make_controldir(name),
 
2146
                value_switches=False,
 
2147
                title="Branch format",
 
2148
                ),
 
2149
         Option('append-revisions-only',
 
2150
                help='Never change revnos or the existing log.'
 
2151
                '  Append revisions to it only.'),
 
2152
         Option('no-tree',
 
2153
                'Create a branch without a working tree.')
 
2154
         ]
2101
2155
    def run(self, location=None, format=None, append_revisions_only=False,
2102
2156
            create_prefix=False, no_tree=False):
2103
2157
        if format is None:
2105
2159
        if location is None:
2106
2160
            location = u'.'
2107
2161
 
2108
 
        to_transport = transport.get_transport(location, purpose='write')
 
2162
        to_transport = transport.get_transport(location)
2109
2163
 
2110
2164
        # The path has to exist to initialize a
2111
2165
        # branch inside of it.
2116
2170
            to_transport.ensure_base()
2117
2171
        except errors.NoSuchFile:
2118
2172
            if not create_prefix:
2119
 
                raise errors.CommandError(gettext("Parent directory of %s"
2120
 
                                                     " does not exist."
2121
 
                                                     "\nYou may supply --create-prefix to create all"
2122
 
                                                     " leading parent directories.")
2123
 
                                             % location)
 
2173
                raise errors.BzrCommandError(gettext("Parent directory of %s"
 
2174
                    " does not exist."
 
2175
                    "\nYou may supply --create-prefix to create all"
 
2176
                    " leading parent directories.")
 
2177
                    % location)
2124
2178
            to_transport.create_prefix()
2125
2179
 
2126
2180
        try:
2127
 
            a_controldir = controldir.ControlDir.open_from_transport(
2128
 
                to_transport)
 
2181
            a_controldir = controldir.ControlDir.open_from_transport(to_transport)
2129
2182
        except errors.NotBranchError:
2130
2183
            # really a NotBzrDir error...
2131
2184
            create_branch = controldir.ControlDir.create_branch_convenience
2141
2194
            from .transport.local import LocalTransport
2142
2195
            if a_controldir.has_branch():
2143
2196
                if (isinstance(to_transport, LocalTransport)
2144
 
                        and not a_controldir.has_workingtree()):
2145
 
                    raise errors.BranchExistsWithoutWorkingTree(location)
 
2197
                    and not a_controldir.has_workingtree()):
 
2198
                        raise errors.BranchExistsWithoutWorkingTree(location)
2146
2199
                raise errors.AlreadyBranchError(location)
2147
2200
            branch = a_controldir.create_branch()
2148
2201
            if not no_tree and not a_controldir.has_workingtree():
2151
2204
            try:
2152
2205
                branch.set_append_revisions_only(True)
2153
2206
            except errors.UpgradeRequired:
2154
 
                raise errors.CommandError(gettext('This branch format cannot be set'
2155
 
                                                     ' to append-revisions-only.  Try --default.'))
 
2207
                raise errors.BzrCommandError(gettext('This branch format cannot be set'
 
2208
                    ' to append-revisions-only.  Try --default.'))
2156
2209
        if not is_quiet():
2157
2210
            from .info import describe_layout, describe_format
2158
2211
            try:
2163
2216
            layout = describe_layout(repository, branch, tree).lower()
2164
2217
            format = describe_format(a_controldir, repository, branch, tree)
2165
2218
            self.outf.write(gettext("Created a {0} (format: {1})\n").format(
2166
 
                layout, format))
 
2219
                  layout, format))
2167
2220
            if repository.is_shared():
2168
 
                # XXX: maybe this can be refactored into transport.path_or_url()
 
2221
                #XXX: maybe this can be refactored into transport.path_or_url()
2169
2222
                url = repository.controldir.root_transport.external_url()
2170
2223
                try:
2171
2224
                    url = urlutils.local_path_from_url(url)
2174
2227
                self.outf.write(gettext("Using shared repository: %s\n") % url)
2175
2228
 
2176
2229
 
2177
 
class cmd_init_shared_repository(Command):
 
2230
class cmd_init_repository(Command):
2178
2231
    __doc__ = """Create a shared repository for branches to share storage space.
2179
2232
 
2180
2233
    New branches created under the repository directory will store their
2181
2234
    revisions in the repository, not in the branch directory.  For branches
2182
 
    with shared history, this reduces the amount of storage needed and
 
2235
    with shared history, this reduces the amount of storage needed and 
2183
2236
    speeds up the creation of new branches.
2184
2237
 
2185
2238
    If the --no-trees option is given then the branches in the repository
2186
 
    will not have working trees by default.  They will still exist as
2187
 
    directories on disk, but they will not have separate copies of the
 
2239
    will not have working trees by default.  They will still exist as 
 
2240
    directories on disk, but they will not have separate copies of the 
2188
2241
    files at a certain revision.  This can be useful for repositories that
2189
2242
    store branches which are interacted with through checkouts or remote
2190
2243
    branches, such as on a server.
2192
2245
    :Examples:
2193
2246
        Create a shared repository holding just branches::
2194
2247
 
2195
 
            brz init-shared-repo --no-trees repo
 
2248
            brz init-repo --no-trees repo
2196
2249
            brz init repo/trunk
2197
2250
 
2198
2251
        Make a lightweight checkout elsewhere::
2205
2258
    _see_also = ['init', 'branch', 'checkout', 'repositories']
2206
2259
    takes_args = ["location"]
2207
2260
    takes_options = [RegistryOption('format',
2208
 
                                    help='Specify a format for this repository. See'
2209
 
                                    ' "brz help formats" for details.',
2210
 
                                    lazy_registry=(
2211
 
                                        'breezy.controldir', 'format_registry'),
2212
 
                                    converter=lambda name: controldir.format_registry.make_controldir(
2213
 
                                        name),
2214
 
                                    value_switches=True, title='Repository format'),
 
2261
                            help='Specify a format for this repository. See'
 
2262
                                 ' "brz help formats" for details.',
 
2263
                            lazy_registry=('breezy.controldir', 'format_registry'),
 
2264
                            converter=lambda name: controldir.format_registry.make_controldir(name),
 
2265
                            value_switches=False, title='Repository format'),
2215
2266
                     Option('no-trees',
2216
 
                            help='Branches in the repository will default to'
2217
 
                            ' not having a working tree.'),
2218
 
                     ]
2219
 
    aliases = ["init-shared-repo", "init-repo"]
 
2267
                             help='Branches in the repository will default to'
 
2268
                                  ' not having a working tree.'),
 
2269
                    ]
 
2270
    aliases = ["init-repo"]
2220
2271
 
2221
2272
    def run(self, location, format=None, no_trees=False):
2222
2273
        if format is None:
2225
2276
        if location is None:
2226
2277
            location = '.'
2227
2278
 
2228
 
        to_transport = transport.get_transport(location, purpose='write')
2229
 
 
2230
 
        if format.fixed_components:
2231
 
            repo_format_name = None
2232
 
        else:
2233
 
            repo_format_name = format.repository_format.get_format_string()
 
2279
        to_transport = transport.get_transport(location)
2234
2280
 
2235
2281
        (repo, newdir, require_stacking, repository_policy) = (
2236
2282
            format.initialize_on_transport_ex(to_transport,
2237
 
                                              create_prefix=True, make_working_trees=not no_trees,
2238
 
                                              shared_repo=True, force_new_repo=True,
2239
 
                                              use_existing_dir=True,
2240
 
                                              repo_format_name=repo_format_name))
 
2283
            create_prefix=True, make_working_trees=not no_trees,
 
2284
            shared_repo=True, force_new_repo=True,
 
2285
            use_existing_dir=True,
 
2286
            repo_format_name=format.repository_format.get_format_string()))
2241
2287
        if not is_quiet():
2242
2288
            from .info import show_bzrdir_info
2243
2289
            show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
2258
2304
 
2259
2305
    Note that when using the -r argument with a range of revisions, the
2260
2306
    differences are computed between the two specified revisions.  That
2261
 
    is, the command does not show the changes introduced by the first
2262
 
    revision in the range.  This differs from the interpretation of
 
2307
    is, the command does not show the changes introduced by the first 
 
2308
    revision in the range.  This differs from the interpretation of 
2263
2309
    revision ranges used by "brz log" which includes the first revision
2264
2310
    in the range.
2265
2311
 
2291
2337
            brz diff -c2
2292
2338
 
2293
2339
        To see the changes introduced by revision X::
2294
 
 
 
2340
        
2295
2341
            brz diff -cX
2296
2342
 
2297
2343
        Note that in the case of a merge, the -c option shows the changes
2332
2378
    _see_also = ['status']
2333
2379
    takes_args = ['file*']
2334
2380
    takes_options = [
2335
 
        Option('diff-options', type=str,
 
2381
        Option('diff-options', type=text_type,
2336
2382
               help='Pass these options to the external diff program.'),
2337
 
        Option('prefix', type=str,
 
2383
        Option('prefix', type=text_type,
2338
2384
               short_name='p',
2339
2385
               help='Set prefixes added to old and new filenames, as '
2340
2386
                    'two values separated by a colon. (eg "old/:new/").'),
2341
2387
        Option('old',
2342
 
               help='Branch/tree to compare from.',
2343
 
               type=str,
2344
 
               ),
 
2388
            help='Branch/tree to compare from.',
 
2389
            type=text_type,
 
2390
            ),
2345
2391
        Option('new',
2346
 
               help='Branch/tree to compare to.',
2347
 
               type=str,
2348
 
               ),
 
2392
            help='Branch/tree to compare to.',
 
2393
            type=text_type,
 
2394
            ),
2349
2395
        'revision',
2350
2396
        'change',
2351
2397
        Option('using',
2352
 
               help='Use this command to compare files.',
2353
 
               type=str,
2354
 
               ),
 
2398
            help='Use this command to compare files.',
 
2399
            type=text_type,
 
2400
            ),
2355
2401
        RegistryOption('format',
2356
 
                       short_name='F',
2357
 
                       help='Diff format to use.',
2358
 
                       lazy_registry=('breezy.diff', 'format_registry'),
2359
 
                       title='Diff format'),
 
2402
            short_name='F',
 
2403
            help='Diff format to use.',
 
2404
            lazy_registry=('breezy.diff', 'format_registry'),
 
2405
            title='Diff format'),
2360
2406
        Option('context',
2361
 
               help='How many lines of context to show.',
2362
 
               type=int,
2363
 
               ),
2364
 
        RegistryOption.from_kwargs(
2365
 
            'color',
2366
 
            help='Color mode to use.',
2367
 
            title='Color Mode', value_switches=False, enum_switch=True,
2368
 
            never='Never colorize output.',
2369
 
            auto='Only colorize output if terminal supports it and STDOUT is a'
2370
 
            ' TTY.',
2371
 
            always='Always colorize output (default).'),
2372
 
        Option(
2373
 
            'check-style',
2374
 
            help=('Warn if trailing whitespace or spurious changes have been'
2375
 
                  '  added.'))
 
2407
            help='How many lines of context to show.',
 
2408
            type=int,
 
2409
            ),
2376
2410
        ]
2377
 
 
2378
2411
    aliases = ['di', 'dif']
2379
2412
    encoding_type = 'exact'
2380
2413
 
2381
2414
    @display_command
2382
2415
    def run(self, revision=None, file_list=None, diff_options=None,
2383
2416
            prefix=None, old=None, new=None, using=None, format=None,
2384
 
            context=None, color='never'):
 
2417
            context=None):
2385
2418
        from .diff import (get_trees_and_branches_to_diff_locked,
2386
 
                           show_diff_trees)
 
2419
            show_diff_trees)
2387
2420
 
2388
2421
        if prefix == u'0':
2389
2422
            # diff -p0 format
2395
2428
        elif u':' in prefix:
2396
2429
            old_label, new_label = prefix.split(u":")
2397
2430
        else:
2398
 
            raise errors.CommandError(gettext(
 
2431
            raise errors.BzrCommandError(gettext(
2399
2432
                '--prefix expects two values separated by a colon'
2400
2433
                ' (eg "old/:new/")'))
2401
2434
 
2402
2435
        if revision and len(revision) > 2:
2403
 
            raise errors.CommandError(gettext('brz diff --revision takes exactly'
2404
 
                                                 ' one or two revision specifiers'))
 
2436
            raise errors.BzrCommandError(gettext('brz diff --revision takes exactly'
 
2437
                                         ' one or two revision specifiers'))
2405
2438
 
2406
2439
        if using is not None and format is not None:
2407
 
            raise errors.CommandError(gettext(
 
2440
            raise errors.BzrCommandError(gettext(
2408
2441
                '{0} and {1} are mutually exclusive').format(
2409
2442
                '--using', '--format'))
2410
2443
 
2411
2444
        (old_tree, new_tree,
2412
2445
         old_branch, new_branch,
2413
2446
         specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
2414
 
            file_list, revision, old, new, self._exit_stack, apply_view=True)
 
2447
            file_list, revision, old, new, self.add_cleanup, apply_view=True)
2415
2448
        # GNU diff on Windows uses ANSI encoding for filenames
2416
2449
        path_encoding = osutils.get_diff_header_encoding()
2417
 
        outf = self.outf
2418
 
        if color == 'auto':
2419
 
            from .terminal import has_ansi_colors
2420
 
            if has_ansi_colors():
2421
 
                color = 'always'
2422
 
            else:
2423
 
                color = 'never'
2424
 
        if 'always' == color:
2425
 
            from .colordiff import DiffWriter
2426
 
            outf = DiffWriter(outf)
2427
 
        return show_diff_trees(old_tree, new_tree, outf,
 
2450
        return show_diff_trees(old_tree, new_tree, self.outf,
2428
2451
                               specific_files=specific_files,
2429
2452
                               external_diff_options=diff_options,
2430
2453
                               old_label=old_label, new_label=new_label,
2449
2472
    @display_command
2450
2473
    def run(self, show_ids=False, directory=u'.'):
2451
2474
        tree = WorkingTree.open_containing(directory)[0]
2452
 
        self.enter_context(tree.lock_read())
 
2475
        self.add_cleanup(tree.lock_read().unlock)
2453
2476
        old = tree.basis_tree()
2454
 
        self.enter_context(old.lock_read())
2455
 
        delta = tree.changes_from(old)
2456
 
        for change in delta.removed:
2457
 
            self.outf.write(change.path[0])
2458
 
            if show_ids:
2459
 
                self.outf.write(' ')
2460
 
                self.outf.write(change.file_id)
2461
 
            self.outf.write('\n')
 
2477
        self.add_cleanup(old.lock_read().unlock)
 
2478
        for path, ie in old.iter_entries_by_dir():
 
2479
            if not tree.has_id(ie.file_id):
 
2480
                self.outf.write(path)
 
2481
                if show_ids:
 
2482
                    self.outf.write(' ')
 
2483
                    self.outf.write(ie.file_id)
 
2484
                self.outf.write('\n')
2462
2485
 
2463
2486
 
2464
2487
class cmd_modified(Command):
2472
2495
    @display_command
2473
2496
    def run(self, null=False, directory=u'.'):
2474
2497
        tree = WorkingTree.open_containing(directory)[0]
2475
 
        self.enter_context(tree.lock_read())
 
2498
        self.add_cleanup(tree.lock_read().unlock)
2476
2499
        td = tree.changes_from(tree.basis_tree())
2477
2500
        self.cleanup_now()
2478
 
        for change in td.modified:
 
2501
        for path, id, kind, text_modified, meta_modified in td.modified:
2479
2502
            if null:
2480
 
                self.outf.write(change.path[1] + '\0')
 
2503
                self.outf.write(path + '\0')
2481
2504
            else:
2482
 
                self.outf.write(osutils.quotefn(change.path[1]) + '\n')
 
2505
                self.outf.write(osutils.quotefn(path) + '\n')
2483
2506
 
2484
2507
 
2485
2508
class cmd_added(Command):
2493
2516
    @display_command
2494
2517
    def run(self, null=False, directory=u'.'):
2495
2518
        wt = WorkingTree.open_containing(directory)[0]
2496
 
        self.enter_context(wt.lock_read())
 
2519
        self.add_cleanup(wt.lock_read().unlock)
2497
2520
        basis = wt.basis_tree()
2498
 
        self.enter_context(basis.lock_read())
 
2521
        self.add_cleanup(basis.lock_read().unlock)
 
2522
        root_id = wt.get_root_id()
2499
2523
        for path in wt.all_versioned_paths():
2500
2524
            if basis.has_filename(path):
2501
2525
                continue
2512
2536
class cmd_root(Command):
2513
2537
    __doc__ = """Show the tree root directory.
2514
2538
 
2515
 
    The root is the nearest enclosing directory with a control
 
2539
    The root is the nearest enclosing directory with a .bzr control
2516
2540
    directory."""
2517
2541
 
2518
2542
    takes_args = ['filename?']
2519
 
 
2520
2543
    @display_command
2521
2544
    def run(self, filename=None):
2522
2545
        """Print the branch root."""
2529
2552
        return int(limitstring)
2530
2553
    except ValueError:
2531
2554
        msg = gettext("The limit argument must be an integer.")
2532
 
        raise errors.CommandError(msg)
 
2555
        raise errors.BzrCommandError(msg)
2533
2556
 
2534
2557
 
2535
2558
def _parse_levels(s):
2537
2560
        return int(s)
2538
2561
    except ValueError:
2539
2562
        msg = gettext("The levels argument must be an integer.")
2540
 
        raise errors.CommandError(msg)
 
2563
        raise errors.BzrCommandError(msg)
2541
2564
 
2542
2565
 
2543
2566
class cmd_log(Command):
2664
2687
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2665
2688
      bzr-explorer shell, or the Loggerhead web interface.  See the Bazaar
2666
2689
      Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2667
 
      <http://wiki.bazaar.canonical.com/IDEIntegration>.
 
2690
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
2668
2691
 
2669
2692
      You may find it useful to add the aliases below to ``breezy.conf``::
2670
2693
 
2698
2721
    takes_args = ['file*']
2699
2722
    _see_also = ['log-formats', 'revisionspec']
2700
2723
    takes_options = [
2701
 
        Option('forward',
2702
 
               help='Show from oldest to newest.'),
2703
 
        'timezone',
2704
 
        custom_help('verbose',
2705
 
                    help='Show files changed in each revision.'),
2706
 
        'show-ids',
2707
 
        'revision',
2708
 
        Option('change',
2709
 
               type=breezy.option._parse_revision_str,
2710
 
               short_name='c',
2711
 
               help='Show just the specified revision.'
2712
 
               ' See also "help revisionspec".'),
2713
 
        'log-format',
2714
 
        RegistryOption('authors',
2715
 
                       'What names to list as authors - first, all or committer.',
2716
 
                       title='Authors',
2717
 
                       lazy_registry=(
2718
 
                           'breezy.log', 'author_list_registry'),
2719
 
                       ),
2720
 
        Option('levels',
2721
 
               short_name='n',
2722
 
               help='Number of levels to display - 0 for all, 1 for flat.',
2723
 
               argname='N',
2724
 
               type=_parse_levels),
2725
 
        Option('message',
2726
 
               help='Show revisions whose message matches this '
2727
 
               'regular expression.',
2728
 
               type=str,
2729
 
               hidden=True),
2730
 
        Option('limit',
2731
 
               short_name='l',
2732
 
               help='Limit the output to the first N revisions.',
2733
 
               argname='N',
2734
 
               type=_parse_limit),
2735
 
        Option('show-diff',
2736
 
               short_name='p',
2737
 
               help='Show changes made in each revision as a patch.'),
2738
 
        Option('include-merged',
2739
 
               help='Show merged revisions like --levels 0 does.'),
2740
 
        Option('include-merges', hidden=True,
2741
 
               help='Historical alias for --include-merged.'),
2742
 
        Option('omit-merges',
2743
 
               help='Do not report commits with more than one parent.'),
2744
 
        Option('exclude-common-ancestry',
2745
 
               help='Display only the revisions that are not part'
2746
 
               ' of both ancestries (require -rX..Y).'
2747
 
               ),
2748
 
        Option('signatures',
2749
 
               help='Show digital signature validity.'),
2750
 
        ListOption('match',
2751
 
                   short_name='m',
2752
 
                   help='Show revisions whose properties match this '
2753
 
                   'expression.',
2754
 
                   type=str),
2755
 
        ListOption('match-message',
2756
 
                   help='Show revisions whose message matches this '
2757
 
                   'expression.',
2758
 
                   type=str),
2759
 
        ListOption('match-committer',
 
2724
            Option('forward',
 
2725
                   help='Show from oldest to newest.'),
 
2726
            'timezone',
 
2727
            custom_help('verbose',
 
2728
                   help='Show files changed in each revision.'),
 
2729
            'show-ids',
 
2730
            'revision',
 
2731
            Option('change',
 
2732
                   type=breezy.option._parse_revision_str,
 
2733
                   short_name='c',
 
2734
                   help='Show just the specified revision.'
 
2735
                   ' See also "help revisionspec".'),
 
2736
            'log-format',
 
2737
            RegistryOption('authors',
 
2738
                'What names to list as authors - first, all or committer.',
 
2739
                title='Authors',
 
2740
                lazy_registry=('breezy.log', 'author_list_registry'),
 
2741
            ),
 
2742
            Option('levels',
 
2743
                   short_name='n',
 
2744
                   help='Number of levels to display - 0 for all, 1 for flat.',
 
2745
                   argname='N',
 
2746
                   type=_parse_levels),
 
2747
            Option('message',
 
2748
                   help='Show revisions whose message matches this '
 
2749
                        'regular expression.',
 
2750
                   type=text_type,
 
2751
                   hidden=True),
 
2752
            Option('limit',
 
2753
                   short_name='l',
 
2754
                   help='Limit the output to the first N revisions.',
 
2755
                   argname='N',
 
2756
                   type=_parse_limit),
 
2757
            Option('show-diff',
 
2758
                   short_name='p',
 
2759
                   help='Show changes made in each revision as a patch.'),
 
2760
            Option('include-merged',
 
2761
                   help='Show merged revisions like --levels 0 does.'),
 
2762
            Option('include-merges', hidden=True,
 
2763
                   help='Historical alias for --include-merged.'),
 
2764
            Option('omit-merges',
 
2765
                   help='Do not report commits with more than one parent.'),
 
2766
            Option('exclude-common-ancestry',
 
2767
                   help='Display only the revisions that are not part'
 
2768
                   ' of both ancestries (require -rX..Y).'
 
2769
                   ),
 
2770
            Option('signatures',
 
2771
                   help='Show digital signature validity.'),
 
2772
            ListOption('match',
 
2773
                short_name='m',
 
2774
                help='Show revisions whose properties match this '
 
2775
                'expression.',
 
2776
                type=text_type),
 
2777
            ListOption('match-message',
 
2778
                   help='Show revisions whose message matches this '
 
2779
                   'expression.',
 
2780
                type=text_type),
 
2781
            ListOption('match-committer',
2760
2782
                   help='Show revisions whose committer matches this '
2761
2783
                   'expression.',
2762
 
                   type=str),
2763
 
        ListOption('match-author',
 
2784
                type=text_type),
 
2785
            ListOption('match-author',
2764
2786
                   help='Show revisions whose authors match this '
2765
2787
                   'expression.',
2766
 
                   type=str),
2767
 
        ListOption('match-bugs',
 
2788
                type=text_type),
 
2789
            ListOption('match-bugs',
2768
2790
                   help='Show revisions whose bugs match this '
2769
2791
                   'expression.',
2770
 
                   type=str)
2771
 
        ]
 
2792
                type=text_type)
 
2793
            ]
2772
2794
    encoding_type = 'replace'
2773
2795
 
2774
2796
    @display_command
2803
2825
        if include_merged is None:
2804
2826
            include_merged = False
2805
2827
        if (exclude_common_ancestry
2806
 
                and (revision is None or len(revision) != 2)):
2807
 
            raise errors.CommandError(gettext(
 
2828
            and (revision is None or len(revision) != 2)):
 
2829
            raise errors.BzrCommandError(gettext(
2808
2830
                '--exclude-common-ancestry requires -r with two revisions'))
2809
2831
        if include_merged:
2810
2832
            if levels is None:
2811
2833
                levels = 0
2812
2834
            else:
2813
 
                raise errors.CommandError(gettext(
 
2835
                raise errors.BzrCommandError(gettext(
2814
2836
                    '{0} and {1} are mutually exclusive').format(
2815
2837
                    '--levels', '--include-merged'))
2816
2838
 
2818
2840
            if len(change) > 1:
2819
2841
                raise errors.RangeInChangeOption()
2820
2842
            if revision is not None:
2821
 
                raise errors.CommandError(gettext(
 
2843
                raise errors.BzrCommandError(gettext(
2822
2844
                    '{0} and {1} are mutually exclusive').format(
2823
2845
                    '--revision', '--change'))
2824
2846
            else:
2825
2847
                revision = change
2826
2848
 
2827
 
        files = []
 
2849
        file_ids = []
2828
2850
        filter_by_dir = False
2829
2851
        if file_list:
2830
2852
            # find the file ids to log and check for directory filtering
2831
2853
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2832
 
                revision, file_list, self._exit_stack)
2833
 
            for relpath, kind in file_info_list:
2834
 
                if not kind:
2835
 
                    raise errors.CommandError(gettext(
 
2854
                revision, file_list, self.add_cleanup)
 
2855
            for relpath, file_id, kind in file_info_list:
 
2856
                if file_id is None:
 
2857
                    raise errors.BzrCommandError(gettext(
2836
2858
                        "Path unknown at end or start of revision range: %s") %
2837
2859
                        relpath)
2838
2860
                # If the relpath is the top of the tree, we log everything
2839
2861
                if relpath == '':
2840
 
                    files = []
 
2862
                    file_ids = []
2841
2863
                    break
2842
2864
                else:
2843
 
                    files.append(relpath)
 
2865
                    file_ids.append(file_id)
2844
2866
                filter_by_dir = filter_by_dir or (
2845
2867
                    kind in ['directory', 'tree-reference'])
2846
2868
        else:
2853
2875
                location = '.'
2854
2876
            dir, relpath = controldir.ControlDir.open_containing(location)
2855
2877
            b = dir.open_branch()
2856
 
            self.enter_context(b.lock_read())
 
2878
            self.add_cleanup(b.lock_read().unlock)
2857
2879
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2858
2880
 
2859
2881
        if b.get_config_stack().get('validate_signatures_in_log'):
2871
2893
            delta_type = 'full'
2872
2894
        if not show_diff:
2873
2895
            diff_type = None
2874
 
        elif files:
 
2896
        elif file_ids:
2875
2897
            diff_type = 'partial'
2876
2898
        else:
2877
2899
            diff_type = 'full'
2880
2902
        if log_format is None:
2881
2903
            log_format = log.log_formatter_registry.get_default(b)
2882
2904
        # Make a non-encoding output to include the diffs - bug 328007
2883
 
        unencoded_output = ui.ui_factory.make_output_stream(
2884
 
            encoding_type='exact')
 
2905
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2885
2906
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2886
2907
                        to_exact_file=unencoded_output,
2887
2908
                        show_timezone=timezone,
2903
2924
        # original algorithm - per-file-graph - for the "single
2904
2925
        # file that isn't a directory without showing a delta" case.
2905
2926
        partial_history = revision and b.repository._format.supports_chks
2906
 
        match_using_deltas = (len(files) != 1 or filter_by_dir
2907
 
                              or delta_type or partial_history)
 
2927
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2928
            or delta_type or partial_history)
2908
2929
 
2909
2930
        match_dict = {}
2910
2931
        if match:
2919
2940
            match_dict['bugs'] = match_bugs
2920
2941
 
2921
2942
        # Build the LogRequest and execute it
2922
 
        if len(files) == 0:
2923
 
            files = None
 
2943
        if len(file_ids) == 0:
 
2944
            file_ids = None
2924
2945
        rqst = make_log_request_dict(
2925
 
            direction=direction, specific_files=files,
 
2946
            direction=direction, specific_fileids=file_ids,
2926
2947
            start_revision=rev1, end_revision=rev2, limit=limit,
2927
2948
            message_search=message, delta_type=delta_type,
2928
2949
            diff_type=diff_type, _match_using_deltas=match_using_deltas,
2950
2971
            # b is taken from revision[0].get_branch(), and
2951
2972
            # show_log will use its revision_history. Having
2952
2973
            # different branches will lead to weird behaviors.
2953
 
            raise errors.CommandError(gettext(
 
2974
            raise errors.BzrCommandError(gettext(
2954
2975
                "brz %s doesn't accept two revisions in different"
2955
2976
                " branches.") % command_name)
2956
2977
        if start_spec.spec is None:
2966
2987
        else:
2967
2988
            rev2 = end_spec.in_history(branch)
2968
2989
    else:
2969
 
        raise errors.CommandError(gettext(
 
2990
        raise errors.BzrCommandError(gettext(
2970
2991
            'brz %s --revision takes one or two values.') % command_name)
2971
2992
    return rev1, rev2
2972
2993
 
2980
3001
        rev_id2 = revision_range[1].rev_id
2981
3002
    return rev_id1, rev_id2
2982
3003
 
2983
 
 
2984
3004
def get_log_format(long=False, short=False, line=False, default='long'):
2985
3005
    log_format = default
2986
3006
    if long:
3004
3024
    @display_command
3005
3025
    def run(self, filename):
3006
3026
        tree, relpath = WorkingTree.open_containing(filename)
3007
 
        with tree.lock_read():
3008
 
            touching_revs = log.find_touching_revisions(
3009
 
                tree.branch.repository, tree.branch.last_revision(), tree, relpath)
3010
 
            for revno, revision_id, what in reversed(list(touching_revs)):
3011
 
                self.outf.write("%6d %s\n" % (revno, what))
 
3027
        file_id = tree.path2id(relpath)
 
3028
        b = tree.branch
 
3029
        self.add_cleanup(b.lock_read().unlock)
 
3030
        touching_revs = log.find_touching_revisions(b, file_id)
 
3031
        for revno, revision_id, what in touching_revs:
 
3032
            self.outf.write("%6d %s\n" % (revno, what))
3012
3033
 
3013
3034
 
3014
3035
class cmd_ls(Command):
3018
3039
    _see_also = ['status', 'cat']
3019
3040
    takes_args = ['path?']
3020
3041
    takes_options = [
3021
 
        'verbose',
3022
 
        'revision',
3023
 
        Option('recursive', short_name='R',
3024
 
               help='Recurse into subdirectories.'),
3025
 
        Option('from-root',
3026
 
               help='Print paths relative to the root of the branch.'),
3027
 
        Option('unknown', short_name='u',
3028
 
               help='Print unknown files.'),
3029
 
        Option('versioned', help='Print versioned files.',
3030
 
               short_name='V'),
3031
 
        Option('ignored', short_name='i',
3032
 
               help='Print ignored files.'),
3033
 
        Option('kind', short_name='k',
3034
 
               help=('List entries of a particular kind: file, '
3035
 
                     'directory, symlink, tree-reference.'),
3036
 
               type=str),
3037
 
        'null',
3038
 
        'show-ids',
3039
 
        'directory',
3040
 
        ]
3041
 
 
 
3042
            'verbose',
 
3043
            'revision',
 
3044
            Option('recursive', short_name='R',
 
3045
                   help='Recurse into subdirectories.'),
 
3046
            Option('from-root',
 
3047
                   help='Print paths relative to the root of the branch.'),
 
3048
            Option('unknown', short_name='u',
 
3049
                help='Print unknown files.'),
 
3050
            Option('versioned', help='Print versioned files.',
 
3051
                   short_name='V'),
 
3052
            Option('ignored', short_name='i',
 
3053
                help='Print ignored files.'),
 
3054
            Option('kind', short_name='k',
 
3055
                   help='List entries of a particular kind: file, directory, symlink.',
 
3056
                   type=text_type),
 
3057
            'null',
 
3058
            'show-ids',
 
3059
            'directory',
 
3060
            ]
3042
3061
    @display_command
3043
3062
    def run(self, revision=None, verbose=False,
3044
3063
            recursive=False, from_root=False,
3045
3064
            unknown=False, versioned=False, ignored=False,
3046
3065
            null=False, kind=None, show_ids=False, path=None, directory=None):
3047
3066
 
3048
 
        if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
3049
 
            raise errors.CommandError(gettext('invalid kind specified'))
 
3067
        if kind and kind not in ('file', 'directory', 'symlink'):
 
3068
            raise errors.BzrCommandError(gettext('invalid kind specified'))
3050
3069
 
3051
3070
        if verbose and null:
3052
 
            raise errors.CommandError(
3053
 
                gettext('Cannot set both --verbose and --null'))
 
3071
            raise errors.BzrCommandError(gettext('Cannot set both --verbose and --null'))
3054
3072
        all = not (unknown or versioned or ignored)
3055
3073
 
3056
 
        selection = {'I': ignored, '?': unknown, 'V': versioned}
 
3074
        selection = {'I':ignored, '?':unknown, 'V':versioned}
3057
3075
 
3058
3076
        if path is None:
3059
3077
            fs_path = '.'
3060
3078
        else:
3061
3079
            if from_root:
3062
 
                raise errors.CommandError(gettext('cannot specify both --from-root'
3063
 
                                                     ' and PATH'))
 
3080
                raise errors.BzrCommandError(gettext('cannot specify both --from-root'
 
3081
                                             ' and PATH'))
3064
3082
            fs_path = path
3065
3083
        tree, branch, relpath = \
3066
3084
            _open_directory_or_containing_tree_or_branch(fs_path, directory)
3084
3102
                view_str = views.view_display_str(view_files)
3085
3103
                note(gettext("Ignoring files outside view. View is %s") % view_str)
3086
3104
 
3087
 
        self.enter_context(tree.lock_read())
3088
 
        for fp, fc, fkind, entry in tree.list_files(
3089
 
                include_root=False, from_dir=relpath, recursive=recursive):
 
3105
        self.add_cleanup(tree.lock_read().unlock)
 
3106
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
3107
            from_dir=relpath, recursive=recursive):
3090
3108
            # Apply additional masking
3091
3109
            if not all and not selection[fc]:
3092
3110
                continue
3110
3128
            ui.ui_factory.clear_term()
3111
3129
            if verbose:
3112
3130
                outstring = '%-8s %s' % (fc, outstring)
3113
 
                if show_ids and getattr(entry, 'file_id', None) is not None:
3114
 
                    outstring = "%-50s %s" % (outstring, entry.file_id.decode('utf-8'))
 
3131
                if show_ids and fid is not None:
 
3132
                    outstring = "%-50s %s" % (outstring, fid)
3115
3133
                self.outf.write(outstring + '\n')
3116
3134
            elif null:
3117
3135
                self.outf.write(fp + '\0')
3118
3136
                if show_ids:
3119
 
                    if getattr(entry, 'file_id', None) is not None:
3120
 
                        self.outf.write(entry.file_id.decode('utf-8'))
 
3137
                    if fid is not None:
 
3138
                        self.outf.write(fid)
3121
3139
                    self.outf.write('\0')
3122
3140
                self.outf.flush()
3123
3141
            else:
3124
3142
                if show_ids:
3125
 
                    if getattr(entry, 'file_id', None) is not None:
3126
 
                        my_id = entry.file_id.decode('utf-8')
 
3143
                    if fid is not None:
 
3144
                        my_id = fid
3127
3145
                    else:
3128
3146
                        my_id = ''
3129
3147
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
3152
3170
 
3153
3171
    If a .bzrignore file does not exist, the ignore command
3154
3172
    will create one and add the specified files or patterns to the newly
3155
 
    created file. The ignore command will also automatically add the
 
3173
    created file. The ignore command will also automatically add the 
3156
3174
    .bzrignore file to be versioned. Creating a .bzrignore file without
3157
3175
    the use of the ignore command will require an explicit add command.
3158
3176
 
3160
3178
    After adding, editing or deleting that file either indirectly by
3161
3179
    using this command or directly by using an editor, be sure to commit
3162
3180
    it.
3163
 
 
3164
 
    Breezy also supports a global ignore file ~/.config/breezy/ignore. On
3165
 
    Windows the global ignore file can be found in the application data
3166
 
    directory as
3167
 
    C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
 
3181
    
 
3182
    Bazaar also supports a global ignore file ~/.bazaar/ignore. On Windows
 
3183
    the global ignore file can be found in the application data directory as
 
3184
    C:\\Documents and Settings\\<user>\\Application Data\\Bazaar\\2.0\\ignore.
3168
3185
    Global ignores are not touched by this command. The global ignore file
3169
3186
    can be edited directly using an editor.
3170
3187
 
3171
3188
    Patterns prefixed with '!' are exceptions to ignore patterns and take
3172
3189
    precedence over regular ignores.  Such exceptions are used to specify
3173
3190
    files that should be versioned which would otherwise be ignored.
3174
 
 
 
3191
    
3175
3192
    Patterns prefixed with '!!' act as regular ignore patterns, but have
3176
3193
    precedence over the '!' exception patterns.
3177
3194
 
3178
 
    :Notes:
3179
 
 
 
3195
    :Notes: 
 
3196
        
3180
3197
    * Ignore patterns containing shell wildcards must be quoted from
3181
3198
      the shell on Unix.
3182
3199
 
3211
3228
        Ignore everything but the "debian" toplevel directory::
3212
3229
 
3213
3230
            brz ignore "RE:(?!debian/).*"
3214
 
 
 
3231
        
3215
3232
        Ignore everything except the "local" toplevel directory,
3216
3233
        but always ignore autosave files ending in ~, even under local/::
3217
 
 
 
3234
        
3218
3235
            brz ignore "*"
3219
3236
            brz ignore "!./local"
3220
3237
            brz ignore "!!*~"
3223
3240
    _see_also = ['status', 'ignored', 'patterns']
3224
3241
    takes_args = ['name_pattern*']
3225
3242
    takes_options = ['directory',
3226
 
                     Option('default-rules',
3227
 
                            help='Display the default ignore rules that brz uses.')
3228
 
                     ]
 
3243
        Option('default-rules',
 
3244
               help='Display the default ignore rules that brz uses.')
 
3245
        ]
3229
3246
 
3230
3247
    def run(self, name_pattern_list=None, default_rules=None,
3231
3248
            directory=u'.'):
3236
3253
                self.outf.write("%s\n" % pattern)
3237
3254
            return
3238
3255
        if not name_pattern_list:
3239
 
            raise errors.CommandError(gettext("ignore requires at least one "
3240
 
                                                 "NAME_PATTERN or --default-rules."))
 
3256
            raise errors.BzrCommandError(gettext("ignore requires at least one "
 
3257
                "NAME_PATTERN or --default-rules."))
3241
3258
        name_pattern_list = [globbing.normalize_pattern(p)
3242
3259
                             for p in name_pattern_list]
3243
3260
        bad_patterns = ''
3247
3264
                bad_patterns_count += 1
3248
3265
                bad_patterns += ('\n  %s' % p)
3249
3266
        if bad_patterns:
3250
 
            msg = (ngettext('Invalid ignore pattern found. %s',
 
3267
            msg = (ngettext('Invalid ignore pattern found. %s', 
3251
3268
                            'Invalid ignore patterns found. %s',
3252
3269
                            bad_patterns_count) % bad_patterns)
3253
3270
            ui.ui_factory.show_error(msg)
3254
3271
            raise lazy_regex.InvalidPattern('')
3255
3272
        for name_pattern in name_pattern_list:
3256
3273
            if (name_pattern[0] == '/' or
3257
 
                    (len(name_pattern) > 1 and name_pattern[1] == ':')):
3258
 
                raise errors.CommandError(gettext(
 
3274
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
 
3275
                raise errors.BzrCommandError(gettext(
3259
3276
                    "NAME_PATTERN should not be an absolute path"))
3260
3277
        tree, relpath = WorkingTree.open_containing(directory)
3261
3278
        ignores.tree_ignores_add_patterns(tree, name_pattern_list)
3262
3279
        ignored = globbing.Globster(name_pattern_list)
3263
3280
        matches = []
3264
 
        self.enter_context(tree.lock_read())
3265
 
        for filename, fc, fkind, entry in tree.list_files():
3266
 
            id = getattr(entry, 'file_id', None)
 
3281
        self.add_cleanup(tree.lock_read().unlock)
 
3282
        for entry in tree.list_files():
 
3283
            id = entry[3]
3267
3284
            if id is not None:
 
3285
                filename = entry[0]
3268
3286
                if ignored.match(filename):
3269
3287
                    matches.append(filename)
3270
3288
        if len(matches) > 0:
3271
3289
            self.outf.write(gettext("Warning: the following files are version "
3272
 
                                    "controlled and match your ignore pattern:\n%s"
3273
 
                                    "\nThese files will continue to be version controlled"
3274
 
                                    " unless you 'brz remove' them.\n") % ("\n".join(matches),))
 
3290
                  "controlled and match your ignore pattern:\n%s"
 
3291
                  "\nThese files will continue to be version controlled"
 
3292
                  " unless you 'brz remove' them.\n") % ("\n".join(matches),))
3275
3293
 
3276
3294
 
3277
3295
class cmd_ignored(Command):
3292
3310
    @display_command
3293
3311
    def run(self, directory=u'.'):
3294
3312
        tree = WorkingTree.open_containing(directory)[0]
3295
 
        self.enter_context(tree.lock_read())
3296
 
        for path, file_class, kind, entry in tree.list_files():
 
3313
        self.add_cleanup(tree.lock_read().unlock)
 
3314
        for path, file_class, kind, file_id, entry in tree.list_files():
3297
3315
            if file_class != 'I':
3298
3316
                continue
3299
 
            # XXX: Slightly inefficient since this was already calculated
 
3317
            ## XXX: Slightly inefficient since this was already calculated
3300
3318
            pat = tree.is_ignored(path)
3301
3319
            self.outf.write('%-50s %s\n' % (path, pat))
3302
3320
 
3316
3334
        try:
3317
3335
            revno = int(revno)
3318
3336
        except ValueError:
3319
 
            raise errors.CommandError(gettext("not a valid revision-number: %r")
 
3337
            raise errors.BzrCommandError(gettext("not a valid revision-number: %r")
3320
3338
                                         % revno)
3321
 
        revid = WorkingTree.open_containing(
3322
 
            directory)[0].branch.get_rev_id(revno)
3323
 
        self.outf.write("%s\n" % revid.decode('utf-8'))
 
3339
        revid = WorkingTree.open_containing(directory)[0].branch.get_rev_id(revno)
 
3340
        self.outf.write("%s\n" % revid)
3324
3341
 
3325
3342
 
3326
3343
class cmd_export(Command):
3352
3369
      =================       =========================
3353
3370
    """
3354
3371
    encoding = 'exact'
3355
 
    encoding_type = 'exact'
3356
3372
    takes_args = ['dest', 'branch_or_subdir?']
3357
3373
    takes_options = ['directory',
3358
 
                     Option('format',
3359
 
                            help="Type of file to export to.",
3360
 
                            type=str),
3361
 
                     'revision',
3362
 
                     Option('filters', help='Apply content filters to export the '
3363
 
                            'convenient form.'),
3364
 
                     Option('root',
3365
 
                            type=str,
3366
 
                            help="Name of the root directory inside the exported file."),
3367
 
                     Option('per-file-timestamps',
3368
 
                            help='Set modification time of files to that of the last '
3369
 
                            'revision in which it was changed.'),
3370
 
                     Option('uncommitted',
3371
 
                            help='Export the working tree contents rather than that of the '
3372
 
                            'last revision.'),
3373
 
                     ]
3374
 
 
 
3374
        Option('format',
 
3375
               help="Type of file to export to.",
 
3376
               type=text_type),
 
3377
        'revision',
 
3378
        Option('filters', help='Apply content filters to export the '
 
3379
                'convenient form.'),
 
3380
        Option('root',
 
3381
               type=text_type,
 
3382
               help="Name of the root directory inside the exported file."),
 
3383
        Option('per-file-timestamps',
 
3384
               help='Set modification time of files to that of the last '
 
3385
                    'revision in which it was changed.'),
 
3386
        Option('uncommitted',
 
3387
               help='Export the working tree contents rather than that of the '
 
3388
                    'last revision.'),
 
3389
        ]
3375
3390
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
3376
 
            root=None, filters=False, per_file_timestamps=False, uncommitted=False,
3377
 
            directory=u'.'):
3378
 
        from .export import export, guess_format, get_root_name
 
3391
        root=None, filters=False, per_file_timestamps=False, uncommitted=False,
 
3392
        directory=u'.'):
 
3393
        from .export import export
3379
3394
 
3380
3395
        if branch_or_subdir is None:
3381
3396
            branch_or_subdir = directory
3383
3398
        (tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
3384
3399
            branch_or_subdir)
3385
3400
        if tree is not None:
3386
 
            self.enter_context(tree.lock_read())
 
3401
            self.add_cleanup(tree.lock_read().unlock)
3387
3402
 
3388
3403
        if uncommitted:
3389
3404
            if tree is None:
3390
 
                raise errors.CommandError(
 
3405
                raise errors.BzrCommandError(
3391
3406
                    gettext("--uncommitted requires a working tree"))
3392
3407
            export_tree = tree
3393
3408
        else:
3394
 
            export_tree = _get_one_revision_tree(
3395
 
                'export', revision, branch=b,
3396
 
                tree=tree)
3397
 
 
3398
 
        if format is None:
3399
 
            format = guess_format(dest)
3400
 
 
3401
 
        if root is None:
3402
 
            root = get_root_name(dest)
3403
 
 
3404
 
        if not per_file_timestamps:
3405
 
            force_mtime = time.time()
3406
 
        else:
3407
 
            force_mtime = None
3408
 
 
3409
 
        if filters:
3410
 
            from breezy.filter_tree import ContentFilterTree
3411
 
            export_tree = ContentFilterTree(
3412
 
                export_tree, export_tree._content_filter_stack)
3413
 
 
 
3409
            export_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
3414
3410
        try:
3415
 
            export(export_tree, dest, format, root, subdir,
 
3411
            export(export_tree, dest, format, root, subdir, filtered=filters,
3416
3412
                   per_file_timestamps=per_file_timestamps)
3417
3413
        except errors.NoSuchExportFormat as e:
3418
 
            raise errors.CommandError(
 
3414
            raise errors.BzrCommandError(
3419
3415
                gettext('Unsupported export format: %s') % e.format)
3420
3416
 
3421
3417
 
3430
3426
 
3431
3427
    _see_also = ['ls']
3432
3428
    takes_options = ['directory',
3433
 
                     Option('name-from-revision',
3434
 
                            help='The path name in the old tree.'),
3435
 
                     Option('filters', help='Apply content filters to display the '
3436
 
                            'convenience form.'),
3437
 
                     'revision',
3438
 
                     ]
 
3429
        Option('name-from-revision', help='The path name in the old tree.'),
 
3430
        Option('filters', help='Apply content filters to display the '
 
3431
                'convenience form.'),
 
3432
        'revision',
 
3433
        ]
3439
3434
    takes_args = ['filename']
3440
3435
    encoding_type = 'exact'
3441
3436
 
3443
3438
    def run(self, filename, revision=None, name_from_revision=False,
3444
3439
            filters=False, directory=None):
3445
3440
        if revision is not None and len(revision) != 1:
3446
 
            raise errors.CommandError(gettext("brz cat --revision takes exactly"
3447
 
                                                 " one revision specifier"))
 
3441
            raise errors.BzrCommandError(gettext("brz cat --revision takes exactly"
 
3442
                                         " one revision specifier"))
3448
3443
        tree, branch, relpath = \
3449
3444
            _open_directory_or_containing_tree_or_branch(filename, directory)
3450
 
        self.enter_context(branch.lock_read())
 
3445
        self.add_cleanup(branch.lock_read().unlock)
3451
3446
        return self._run(tree, branch, relpath, filename, revision,
3452
3447
                         name_from_revision, filters)
3453
3448
 
3454
3449
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
3455
 
             filtered):
3456
 
        import shutil
 
3450
        filtered):
3457
3451
        if tree is None:
3458
3452
            tree = b.basis_tree()
3459
3453
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
3460
 
        self.enter_context(rev_tree.lock_read())
3461
 
 
 
3454
        self.add_cleanup(rev_tree.lock_read().unlock)
 
3455
 
 
3456
        old_file_id = rev_tree.path2id(relpath)
 
3457
 
 
3458
        # TODO: Split out this code to something that generically finds the
 
3459
        # best id for a path across one or more trees; it's like
 
3460
        # find_ids_across_trees but restricted to find just one. -- mbp
 
3461
        # 20110705.
3462
3462
        if name_from_revision:
3463
3463
            # Try in revision if requested
3464
 
            if not rev_tree.is_versioned(relpath):
3465
 
                raise errors.CommandError(gettext(
 
3464
            if old_file_id is None:
 
3465
                raise errors.BzrCommandError(gettext(
3466
3466
                    "{0!r} is not present in revision {1}").format(
3467
3467
                        filename, rev_tree.get_revision_id()))
3468
 
            rev_tree_path = relpath
 
3468
            else:
 
3469
                actual_file_id = old_file_id
3469
3470
        else:
3470
 
            try:
3471
 
                rev_tree_path = _mod_tree.find_previous_path(
3472
 
                    tree, rev_tree, relpath)
3473
 
            except errors.NoSuchFile:
3474
 
                rev_tree_path = None
3475
 
 
3476
 
            if rev_tree_path is None:
3477
 
                # Path didn't exist in working tree
3478
 
                if not rev_tree.is_versioned(relpath):
3479
 
                    raise errors.CommandError(gettext(
3480
 
                        "{0!r} is not present in revision {1}").format(
3481
 
                            filename, rev_tree.get_revision_id()))
3482
 
                else:
3483
 
                    # Fall back to the same path in the basis tree, if present.
3484
 
                    rev_tree_path = relpath
3485
 
 
 
3471
            cur_file_id = tree.path2id(relpath)
 
3472
            if cur_file_id is not None and rev_tree.has_id(cur_file_id):
 
3473
                actual_file_id = cur_file_id
 
3474
            elif old_file_id is not None:
 
3475
                actual_file_id = old_file_id
 
3476
            else:
 
3477
                raise errors.BzrCommandError(gettext(
 
3478
                    "{0!r} is not present in revision {1}").format(
 
3479
                        filename, rev_tree.get_revision_id()))
3486
3480
        if filtered:
3487
3481
            from .filter_tree import ContentFilterTree
3488
 
            filter_tree = ContentFilterTree(
3489
 
                rev_tree, rev_tree._content_filter_stack)
3490
 
            fileobj = filter_tree.get_file(rev_tree_path)
 
3482
            filter_tree = ContentFilterTree(rev_tree,
 
3483
                rev_tree._content_filter_stack)
 
3484
            content = filter_tree.get_file_text(relpath, actual_file_id)
3491
3485
        else:
3492
 
            fileobj = rev_tree.get_file(rev_tree_path)
3493
 
        shutil.copyfileobj(fileobj, self.outf)
 
3486
            content = rev_tree.get_file_text(relpath, actual_file_id)
3494
3487
        self.cleanup_now()
 
3488
        self.outf.write(content)
3495
3489
 
3496
3490
 
3497
3491
class cmd_local_time_offset(Command):
3498
3492
    __doc__ = """Show the offset in seconds from GMT to local time."""
3499
3493
    hidden = True
3500
 
 
3501
3494
    @display_command
3502
3495
    def run(self):
3503
3496
        self.outf.write("%s\n" % osutils.local_time_offset())
3504
3497
 
3505
3498
 
 
3499
 
3506
3500
class cmd_commit(Command):
3507
3501
    __doc__ = """Commit changes into a new revision.
3508
3502
 
3522
3516
      If selected files are specified, only changes to those files are
3523
3517
      committed.  If a directory is specified then the directory and
3524
3518
      everything within it is committed.
3525
 
 
 
3519
  
3526
3520
      When excludes are given, they take precedence over selected files.
3527
3521
      For example, to commit only changes within foo, but not changes
3528
3522
      within foo/bar::
3529
 
 
 
3523
  
3530
3524
        brz commit foo -x foo/bar
3531
 
 
 
3525
  
3532
3526
      A selective commit after a merge is not yet supported.
3533
3527
 
3534
3528
    :Custom authors:
3539
3533
      "John Doe <jdoe@example.com>". If there is more than one author of
3540
3534
      the change you can specify the option multiple times, once for each
3541
3535
      author.
3542
 
 
 
3536
  
3543
3537
    :Checks:
3544
3538
 
3545
3539
      A common mistake is to forget to add a new file or directory before
3550
3544
 
3551
3545
    :Things to note:
3552
3546
 
3553
 
      If you accidentally commit the wrong changes or make a spelling
 
3547
      If you accidentially commit the wrong changes or make a spelling
3554
3548
      mistake in the commit message say, you can use the uncommit command
3555
3549
      to undo it. See ``brz help uncommit`` for details.
3556
3550
 
3563
3557
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
3564
3558
    takes_args = ['selected*']
3565
3559
    takes_options = [
3566
 
        ListOption(
3567
 
            'exclude', type=str, short_name='x',
3568
 
            help="Do not consider changes made to a given path."),
3569
 
        Option('message', type=str,
3570
 
               short_name='m',
3571
 
               help="Description of the new revision."),
3572
 
        'verbose',
3573
 
        Option('unchanged',
3574
 
               help='Commit even if nothing has changed.'),
3575
 
        Option('file', type=str,
3576
 
               short_name='F',
3577
 
               argname='msgfile',
3578
 
               help='Take commit message from this file.'),
3579
 
        Option('strict',
3580
 
               help="Refuse to commit if there are unknown "
3581
 
               "files in the working tree."),
3582
 
        Option('commit-time', type=str,
3583
 
               help="Manually set a commit time using commit date "
3584
 
               "format, e.g. '2009-10-10 08:00:00 +0100'."),
3585
 
        ListOption(
3586
 
            'bugs', type=str,
3587
 
            help="Link to a related bug. (see \"brz help bugs\")."),
3588
 
        ListOption(
3589
 
            'fixes', type=str,
3590
 
            help="Mark a bug as being fixed by this revision "
3591
 
                 "(see \"brz help bugs\")."),
3592
 
        ListOption(
3593
 
            'author', type=str,
3594
 
            help="Set the author's name, if it's different "
3595
 
                 "from the committer."),
3596
 
        Option('local',
3597
 
               help="Perform a local commit in a bound "
3598
 
                    "branch.  Local commits are not pushed to "
3599
 
                    "the master branch until a normal commit "
3600
 
                    "is performed."
3601
 
               ),
3602
 
        Option('show-diff', short_name='p',
3603
 
               help='When no message is supplied, show the diff along'
3604
 
               ' with the status summary in the message editor.'),
3605
 
        Option('lossy',
3606
 
               help='When committing to a foreign version control '
3607
 
               'system do not push data that can not be natively '
3608
 
               'represented.'), ]
 
3560
            ListOption('exclude', type=text_type, short_name='x',
 
3561
                help="Do not consider changes made to a given path."),
 
3562
            Option('message', type=text_type,
 
3563
                   short_name='m',
 
3564
                   help="Description of the new revision."),
 
3565
            'verbose',
 
3566
             Option('unchanged',
 
3567
                    help='Commit even if nothing has changed.'),
 
3568
             Option('file', type=text_type,
 
3569
                    short_name='F',
 
3570
                    argname='msgfile',
 
3571
                    help='Take commit message from this file.'),
 
3572
             Option('strict',
 
3573
                    help="Refuse to commit if there are unknown "
 
3574
                    "files in the working tree."),
 
3575
             Option('commit-time', type=text_type,
 
3576
                    help="Manually set a commit time using commit date "
 
3577
                    "format, e.g. '2009-10-10 08:00:00 +0100'."),
 
3578
             ListOption('fixes', type=text_type,
 
3579
                    help="Mark a bug as being fixed by this revision "
 
3580
                         "(see \"brz help bugs\")."),
 
3581
             ListOption('author', type=text_type,
 
3582
                    help="Set the author's name, if it's different "
 
3583
                         "from the committer."),
 
3584
             Option('local',
 
3585
                    help="Perform a local commit in a bound "
 
3586
                         "branch.  Local commits are not pushed to "
 
3587
                         "the master branch until a normal commit "
 
3588
                         "is performed."
 
3589
                    ),
 
3590
             Option('show-diff', short_name='p',
 
3591
                    help='When no message is supplied, show the diff along'
 
3592
                    ' with the status summary in the message editor.'),
 
3593
             Option('lossy', 
 
3594
                    help='When committing to a foreign version control '
 
3595
                    'system do not push data that can not be natively '
 
3596
                    'represented.'),
 
3597
             ]
3609
3598
    aliases = ['ci', 'checkin']
3610
3599
 
3611
 
    def _iter_bug_urls(self, bugs, branch, status):
3612
 
        default_bugtracker = None
 
3600
    def _iter_bug_fix_urls(self, fixes, branch):
 
3601
        default_bugtracker  = None
3613
3602
        # Configure the properties for bug fixing attributes.
3614
 
        for bug in bugs:
3615
 
            tokens = bug.split(':')
 
3603
        for fixed_bug in fixes:
 
3604
            tokens = fixed_bug.split(':')
3616
3605
            if len(tokens) == 1:
3617
3606
                if default_bugtracker is None:
3618
3607
                    branch_config = branch.get_config_stack()
3619
3608
                    default_bugtracker = branch_config.get(
3620
3609
                        "bugtracker")
3621
3610
                if default_bugtracker is None:
3622
 
                    raise errors.CommandError(gettext(
 
3611
                    raise errors.BzrCommandError(gettext(
3623
3612
                        "No tracker specified for bug %s. Use the form "
3624
3613
                        "'tracker:id' or specify a default bug tracker "
3625
3614
                        "using the `bugtracker` option.\nSee "
3626
3615
                        "\"brz help bugs\" for more information on this "
3627
 
                        "feature. Commit refused.") % bug)
 
3616
                        "feature. Commit refused.") % fixed_bug)
3628
3617
                tag = default_bugtracker
3629
3618
                bug_id = tokens[0]
3630
3619
            elif len(tokens) != 2:
3631
 
                raise errors.CommandError(gettext(
 
3620
                raise errors.BzrCommandError(gettext(
3632
3621
                    "Invalid bug %s. Must be in the form of 'tracker:id'. "
3633
3622
                    "See \"brz help bugs\" for more information on this "
3634
 
                    "feature.\nCommit refused.") % bug)
 
3623
                    "feature.\nCommit refused.") % fixed_bug)
3635
3624
            else:
3636
3625
                tag, bug_id = tokens
3637
3626
            try:
3638
 
                yield bugtracker.get_bug_url(tag, branch, bug_id), status
 
3627
                yield bugtracker.get_bug_url(tag, branch, bug_id)
3639
3628
            except bugtracker.UnknownBugTrackerAbbreviation:
3640
 
                raise errors.CommandError(gettext(
3641
 
                    'Unrecognized bug %s. Commit refused.') % bug)
 
3629
                raise errors.BzrCommandError(gettext(
 
3630
                    'Unrecognized bug %s. Commit refused.') % fixed_bug)
3642
3631
            except bugtracker.MalformedBugIdentifier as e:
3643
 
                raise errors.CommandError(gettext(
 
3632
                raise errors.BzrCommandError(gettext(
3644
3633
                    u"%s\nCommit refused.") % (e,))
3645
3634
 
3646
3635
    def run(self, message=None, file=None, verbose=False, selected_list=None,
3647
 
            unchanged=False, strict=False, local=False, fixes=None, bugs=None,
 
3636
            unchanged=False, strict=False, local=False, fixes=None,
3648
3637
            author=None, show_diff=False, exclude=None, commit_time=None,
3649
3638
            lossy=False):
3650
 
        import itertools
3651
3639
        from .commit import (
3652
3640
            PointlessCommit,
3653
3641
            )
3667
3655
            try:
3668
3656
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
3669
3657
            except ValueError as e:
3670
 
                raise errors.CommandError(gettext(
 
3658
                raise errors.BzrCommandError(gettext(
3671
3659
                    "Could not parse --commit-time: " + str(e)))
3672
3660
 
3673
3661
        properties = {}
3681
3669
 
3682
3670
        if fixes is None:
3683
3671
            fixes = []
3684
 
        if bugs is None:
3685
 
            bugs = []
3686
3672
        bug_property = bugtracker.encode_fixes_bug_urls(
3687
 
            itertools.chain(
3688
 
                self._iter_bug_urls(bugs, tree.branch, bugtracker.RELATED),
3689
 
                self._iter_bug_urls(fixes, tree.branch, bugtracker.FIXED)))
 
3673
            self._iter_bug_fix_urls(fixes, tree.branch))
3690
3674
        if bug_property:
3691
 
            properties[u'bugs'] = bug_property
 
3675
            properties['bugs'] = bug_property
3692
3676
 
3693
3677
        if local and not tree.branch.get_bound_location():
3694
3678
            raise errors.LocalRequiresBoundBranch()
3705
3689
                warning_msg = (
3706
3690
                    'The commit message is a file name: "%(f)s".\n'
3707
3691
                    '(use --file "%(f)s" to take commit message from that file)'
3708
 
                    % {'f': message})
 
3692
                    % { 'f': message })
3709
3693
                ui.ui_factory.show_warning(warning_msg)
3710
3694
            if '\r' in message:
3711
3695
                message = message.replace('\r\n', '\n')
3712
3696
                message = message.replace('\r', '\n')
3713
3697
            if file:
3714
 
                raise errors.CommandError(gettext(
 
3698
                raise errors.BzrCommandError(gettext(
3715
3699
                    "please specify either --message or --file"))
3716
3700
 
3717
3701
        def get_message(commit_obj):
3718
3702
            """Callback to get commit message"""
3719
3703
            if file:
3720
 
                with open(file, 'rb') as f:
 
3704
                f = open(file)
 
3705
                try:
3721
3706
                    my_message = f.read().decode(osutils.get_user_encoding())
 
3707
                finally:
 
3708
                    f.close()
3722
3709
            elif message is not None:
3723
3710
                my_message = message
3724
3711
            else:
3725
3712
                # No message supplied: make one up.
3726
3713
                # text is the status of the tree
3727
3714
                text = make_commit_message_template_encoded(tree,
3728
 
                                                            selected_list, diff=show_diff,
3729
 
                                                            output_encoding=osutils.get_user_encoding())
 
3715
                        selected_list, diff=show_diff,
 
3716
                        output_encoding=osutils.get_user_encoding())
3730
3717
                # start_message is the template generated from hooks
3731
3718
                # XXX: Warning - looks like hooks return unicode,
3732
3719
                # make_commit_message_template_encoded returns user encoding.
3734
3721
                # avoid this.
3735
3722
                my_message = set_commit_message(commit_obj)
3736
3723
                if my_message is None:
3737
 
                    start_message = generate_commit_message_template(
3738
 
                        commit_obj)
3739
 
                    if start_message is not None:
3740
 
                        start_message = start_message.encode(
3741
 
                            osutils.get_user_encoding())
 
3724
                    start_message = generate_commit_message_template(commit_obj)
3742
3725
                    my_message = edit_commit_message_encoded(text,
3743
 
                                                             start_message=start_message)
 
3726
                        start_message=start_message)
3744
3727
                if my_message is None:
3745
 
                    raise errors.CommandError(gettext("please specify a commit"
3746
 
                                                         " message with either --message or --file"))
 
3728
                    raise errors.BzrCommandError(gettext("please specify a commit"
 
3729
                        " message with either --message or --file"))
3747
3730
                if my_message == "":
3748
 
                    raise errors.CommandError(gettext("Empty commit message specified."
3749
 
                                                         " Please specify a commit message with either"
3750
 
                                                         " --message or --file or leave a blank message"
3751
 
                                                         " with --message \"\"."))
 
3731
                    raise errors.BzrCommandError(gettext("Empty commit message specified."
 
3732
                            " Please specify a commit message with either"
 
3733
                            " --message or --file or leave a blank message"
 
3734
                            " with --message \"\"."))
3752
3735
            return my_message
3753
3736
 
3754
3737
        # The API permits a commit with a filter of [] to mean 'select nothing'
3765
3748
                        exclude=tree.safe_relpath_files(exclude),
3766
3749
                        lossy=lossy)
3767
3750
        except PointlessCommit:
3768
 
            raise errors.CommandError(gettext("No changes to commit."
3769
 
                                                 " Please 'brz add' the files you want to commit, or use"
3770
 
                                                 " --unchanged to force an empty commit."))
 
3751
            raise errors.BzrCommandError(gettext("No changes to commit."
 
3752
                " Please 'brz add' the files you want to commit, or use"
 
3753
                " --unchanged to force an empty commit."))
3771
3754
        except ConflictsInTree:
3772
 
            raise errors.CommandError(gettext('Conflicts detected in working '
3773
 
                                                 'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
3774
 
                                                 ' resolve.'))
 
3755
            raise errors.BzrCommandError(gettext('Conflicts detected in working '
 
3756
                'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
 
3757
                ' resolve.'))
3775
3758
        except StrictCommitFailed:
3776
 
            raise errors.CommandError(gettext("Commit refused because there are"
3777
 
                                                 " unknown files in the working tree."))
 
3759
            raise errors.BzrCommandError(gettext("Commit refused because there are"
 
3760
                              " unknown files in the working tree."))
3778
3761
        except errors.BoundBranchOutOfDate as e:
3779
3762
            e.extra_help = (gettext("\n"
3780
 
                                    'To commit to master branch, run update and then commit.\n'
3781
 
                                    'You can also pass --local to commit to continue working '
3782
 
                                    'disconnected.'))
 
3763
                'To commit to master branch, run update and then commit.\n'
 
3764
                'You can also pass --local to commit to continue working '
 
3765
                'disconnected.'))
3783
3766
            raise
3784
3767
 
3785
3768
 
3803
3786
    unreferenced ancestors
3804
3787
        Texts that are ancestors of other texts, but
3805
3788
        are not properly referenced by the revision ancestry.  This is a
3806
 
        subtle problem that Breezy can work around.
 
3789
        subtle problem that Bazaar can work around.
3807
3790
 
3808
3791
    unique file texts
3809
3792
        This is the total number of unique file contents
3815
3798
        entries are modified, but the file contents are not.  It does not
3816
3799
        indicate a problem.
3817
3800
 
3818
 
    If no restrictions are specified, all data that is found at the given
 
3801
    If no restrictions are specified, all Bazaar data that is found at the given
3819
3802
    location will be checked.
3820
3803
 
3821
3804
    :Examples:
3857
3840
    __doc__ = """Upgrade a repository, branch or working tree to a newer format.
3858
3841
 
3859
3842
    When the default format has changed after a major new release of
3860
 
    Bazaar/Breezy, you may be informed during certain operations that you
 
3843
    Bazaar, you may be informed during certain operations that you
3861
3844
    should upgrade. Upgrading to a newer format may improve performance
3862
3845
    or make new features available. It may however limit interoperability
3863
 
    with older repositories or with older versions of Bazaar or Breezy.
 
3846
    with older repositories or with older versions of Bazaar.
3864
3847
 
3865
3848
    If you wish to upgrade to a particular format rather than the
3866
3849
    current default, that can be specified using the --format option.
3882
3865
    If the conversion of a branch fails, remaining branches are still
3883
3866
    tried.
3884
3867
 
3885
 
    For more information on upgrades, see the Breezy Upgrade Guide,
3886
 
    https://www.breezy-vcs.org/doc/en/upgrade-guide/.
 
3868
    For more information on upgrades, see the Bazaar Upgrade Guide,
 
3869
    http://doc.bazaar.canonical.com/latest/en/upgrade-guide/.
3887
3870
    """
3888
3871
 
3889
3872
    _see_also = ['check', 'reconcile', 'formats']
3890
3873
    takes_args = ['url?']
3891
3874
    takes_options = [
3892
3875
        RegistryOption('format',
3893
 
                       help='Upgrade to a specific format.  See "brz help'
3894
 
                       ' formats" for details.',
3895
 
                       lazy_registry=('breezy.controldir', 'format_registry'),
3896
 
                       converter=lambda name: controldir.format_registry.make_controldir(
3897
 
                           name),
3898
 
                       value_switches=True, title='Branch format'),
 
3876
            help='Upgrade to a specific format.  See "brz help'
 
3877
                 ' formats" for details.',
 
3878
            lazy_registry=('breezy.controldir', 'format_registry'),
 
3879
            converter=lambda name: controldir.format_registry.make_controldir(name),
 
3880
            value_switches=True, title='Branch format'),
3899
3881
        Option('clean',
3900
 
               help='Remove the backup.bzr directory if successful.'),
 
3882
            help='Remove the backup.bzr directory if successful.'),
3901
3883
        Option('dry-run',
3902
 
               help="Show what would be done, but don't actually do anything."),
 
3884
            help="Show what would be done, but don't actually do anything."),
3903
3885
    ]
3904
3886
 
3905
3887
    def run(self, url='.', format=None, clean=False, dry_run=False):
3925
3907
 
3926
3908
            brz whoami "Frank Chu <fchu@example.com>"
3927
3909
    """
3928
 
    takes_options = ['directory',
3929
 
                     Option('email',
3930
 
                            help='Display email address only.'),
3931
 
                     Option('branch',
3932
 
                            help='Set identity for the current branch instead of '
3933
 
                            'globally.'),
3934
 
                     ]
 
3910
    takes_options = [ 'directory',
 
3911
                      Option('email',
 
3912
                             help='Display email address only.'),
 
3913
                      Option('branch',
 
3914
                             help='Set identity for the current branch instead of '
 
3915
                                  'globally.'),
 
3916
                    ]
3935
3917
    takes_args = ['name?']
3936
3918
    encoding_type = 'replace'
3937
3919
 
3955
3937
            return
3956
3938
 
3957
3939
        if email:
3958
 
            raise errors.CommandError(gettext("--email can only be used to display existing "
3959
 
                                                 "identity"))
 
3940
            raise errors.BzrCommandError(gettext("--email can only be used to display existing "
 
3941
                                         "identity"))
3960
3942
 
3961
3943
        # display a warning if an email address isn't included in the given name.
3962
3944
        try:
3963
3945
            _mod_config.extract_email_address(name)
3964
 
        except _mod_config.NoEmailInUsername:
 
3946
        except _mod_config.NoEmailInUsername as e:
3965
3947
            warning('"%s" does not seem to contain an email address.  '
3966
3948
                    'This is allowed, but not recommended.', name)
3967
3949
 
3971
3953
                c = Branch.open_containing(u'.')[0].get_config_stack()
3972
3954
            else:
3973
3955
                b = Branch.open(directory)
3974
 
                self.enter_context(b.lock_write())
 
3956
                self.add_cleanup(b.lock_write().unlock)
3975
3957
                c = b.get_config_stack()
3976
3958
        else:
3977
3959
            c = _mod_config.GlobalStack()
3992
3974
    _see_also = ['info']
3993
3975
    takes_args = ['nickname?']
3994
3976
    takes_options = ['directory']
3995
 
 
3996
3977
    def run(self, nickname=None, directory=u'.'):
3997
3978
        branch = Branch.open_containing(directory)[0]
3998
3979
        if nickname is None:
4041
4022
            if equal_pos == -1:
4042
4023
                self.print_alias(name)
4043
4024
            else:
4044
 
                self.set_alias(name[:equal_pos], name[equal_pos + 1:])
 
4025
                self.set_alias(name[:equal_pos], name[equal_pos+1:])
4045
4026
 
4046
4027
    def remove_alias(self, alias_name):
4047
4028
        if alias_name is None:
4048
 
            raise errors.CommandError(gettext(
 
4029
            raise errors.BzrCommandError(gettext(
4049
4030
                'brz alias --remove expects an alias to remove.'))
4050
4031
        # If alias is not found, print something like:
4051
4032
        # unalias: foo: not found
4056
4037
    def print_aliases(self):
4057
4038
        """Print out the defined aliases in a similar format to bash."""
4058
4039
        aliases = _mod_config.GlobalConfig().get_aliases()
4059
 
        for key, value in sorted(aliases.items()):
 
4040
        for key, value in sorted(viewitems(aliases)):
4060
4041
            self.outf.write('brz alias %s="%s"\n' % (key, value))
4061
4042
 
4062
4043
    @display_command
4126
4107
    """
4127
4108
    # NB: this is used from the class without creating an instance, which is
4128
4109
    # why it does not have a self parameter.
4129
 
 
4130
4110
    def get_transport_type(typestring):
4131
4111
        """Parse and return a transport specifier."""
4132
4112
        if typestring == "sftp":
4140
4120
            return test_server.FakeNFSServer
4141
4121
        msg = "No known transport type %s. Supported types are: sftp\n" %\
4142
4122
            (typestring)
4143
 
        raise errors.CommandError(msg)
 
4123
        raise errors.BzrCommandError(msg)
4144
4124
 
4145
4125
    hidden = True
4146
4126
    takes_args = ['testspecs*']
4147
4127
    takes_options = ['verbose',
4148
4128
                     Option('one',
4149
 
                            help='Stop when one test fails.',
4150
 
                            short_name='1',
4151
 
                            ),
 
4129
                             help='Stop when one test fails.',
 
4130
                             short_name='1',
 
4131
                             ),
4152
4132
                     Option('transport',
4153
4133
                            help='Use a different transport by default '
4154
4134
                                 'throughout the test suite.',
4168
4148
                     Option('list-only',
4169
4149
                            help='List the tests instead of running them.'),
4170
4150
                     RegistryOption('parallel',
4171
 
                                    help="Run the test suite in parallel.",
4172
 
                                    lazy_registry=(
4173
 
                                        'breezy.tests', 'parallel_registry'),
4174
 
                                    value_switches=False,
4175
 
                                    ),
4176
 
                     Option('randomize', type=str, argname="SEED",
 
4151
                        help="Run the test suite in parallel.",
 
4152
                        lazy_registry=('breezy.tests', 'parallel_registry'),
 
4153
                        value_switches=False,
 
4154
                        ),
 
4155
                     Option('randomize', type=text_type, argname="SEED",
4177
4156
                            help='Randomize the order of tests using the given'
4178
4157
                                 ' seed or "now" for the current time.'),
4179
 
                     ListOption('exclude', type=str, argname="PATTERN",
 
4158
                     ListOption('exclude', type=text_type, argname="PATTERN",
4180
4159
                                short_name='x',
4181
4160
                                help='Exclude tests that match this regular'
4182
4161
                                ' expression.'),
4186
4165
                            help='Output test progress via subunit v2.'),
4187
4166
                     Option('strict', help='Fail on missing dependencies or '
4188
4167
                            'known failures.'),
4189
 
                     Option('load-list', type=str, argname='TESTLISTFILE',
 
4168
                     Option('load-list', type=text_type, argname='TESTLISTFILE',
4190
4169
                            help='Load a test id list from a text file.'),
4191
 
                     ListOption('debugflag', type=str, short_name='E',
 
4170
                     ListOption('debugflag', type=text_type, short_name='E',
4192
4171
                                help='Turn on a selftest debug flag.'),
4193
 
                     ListOption('starting-with', type=str, argname='TESTID',
 
4172
                     ListOption('starting-with', type=text_type, argname='TESTID',
4194
4173
                                param_name='starting_with', short_name='s',
4195
 
                                help='Load only the tests starting with TESTID.'),
 
4174
                                help=
 
4175
                                'Load only the tests starting with TESTID.'),
4196
4176
                     Option('sync',
4197
4177
                            help="By default we disable fsync and fdatasync"
4198
4178
                                 " while running the test suite.")
4217
4197
        # too heavily. The call should be as early as possible, as
4218
4198
        # error reporting for past duplicate imports won't have useful
4219
4199
        # backtraces.
4220
 
        if sys.version_info[0] < 3:
4221
 
            # TODO(pad.lv/1696545): Allow proxying on Python 3, since
4222
 
            # disallowing it currently leads to failures in many places.
4223
 
            lazy_import.disallow_proxying()
 
4200
        lazy_import.disallow_proxying()
4224
4201
 
4225
 
        try:
4226
 
            from . import tests
4227
 
        except ImportError as e:
4228
 
            raise errors.CommandError("tests not available. Install the "
4229
 
                                         "breezy tests to run the breezy testsuite.")
 
4202
        from . import tests
4230
4203
 
4231
4204
        if testspecs_list is not None:
4232
4205
            pattern = '|'.join(testspecs_list)
4236
4209
            try:
4237
4210
                from .tests import SubUnitBzrRunnerv1
4238
4211
            except ImportError:
4239
 
                raise errors.CommandError(gettext(
 
4212
                raise errors.BzrCommandError(gettext(
4240
4213
                    "subunit not available. subunit needs to be installed "
4241
4214
                    "to use --subunit."))
4242
4215
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
4243
4216
            # On Windows, disable automatic conversion of '\n' to '\r\n' in
4244
 
            # stdout, which would corrupt the subunit stream.
 
4217
            # stdout, which would corrupt the subunit stream. 
4245
4218
            # FIXME: This has been fixed in subunit trunk (>0.0.5) so the
4246
4219
            # following code can be deleted when it's sufficiently deployed
4247
4220
            # -- vila/mgz 20100514
4248
4221
            if (sys.platform == "win32"
4249
 
                    and getattr(sys.stdout, 'fileno', None) is not None):
 
4222
                and getattr(sys.stdout, 'fileno', None) is not None):
4250
4223
                import msvcrt
4251
4224
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4252
4225
        if subunit2:
4253
4226
            try:
4254
4227
                from .tests import SubUnitBzrRunnerv2
4255
4228
            except ImportError:
4256
 
                raise errors.CommandError(gettext(
 
4229
                raise errors.BzrCommandError(gettext(
4257
4230
                    "subunit not available. subunit "
4258
4231
                    "needs to be installed to use --subunit2."))
4259
4232
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv2
4262
4235
            self.additional_selftest_args.setdefault(
4263
4236
                'suite_decorators', []).append(parallel)
4264
4237
        if benchmark:
4265
 
            raise errors.CommandError(gettext(
 
4238
            raise errors.BzrCommandError(gettext(
4266
4239
                "--benchmark is no longer supported from brz 2.2; "
4267
4240
                "use bzr-usertest instead"))
4268
4241
        test_suite_factory = None
4273
4246
        if not sync:
4274
4247
            self._disable_fsync()
4275
4248
        selftest_kwargs = {"verbose": verbose,
4276
 
                           "pattern": pattern,
4277
 
                           "stop_on_failure": one,
4278
 
                           "transport": transport,
4279
 
                           "test_suite_factory": test_suite_factory,
4280
 
                           "lsprof_timed": lsprof_timed,
4281
 
                           "lsprof_tests": lsprof_tests,
4282
 
                           "matching_tests_first": first,
4283
 
                           "list_only": list_only,
4284
 
                           "random_seed": randomize,
4285
 
                           "exclude_pattern": exclude_pattern,
4286
 
                           "strict": strict,
4287
 
                           "load_list": load_list,
4288
 
                           "debug_flags": debugflag,
4289
 
                           "starting_with": starting_with
4290
 
                           }
 
4249
                          "pattern": pattern,
 
4250
                          "stop_on_failure": one,
 
4251
                          "transport": transport,
 
4252
                          "test_suite_factory": test_suite_factory,
 
4253
                          "lsprof_timed": lsprof_timed,
 
4254
                          "lsprof_tests": lsprof_tests,
 
4255
                          "matching_tests_first": first,
 
4256
                          "list_only": list_only,
 
4257
                          "random_seed": randomize,
 
4258
                          "exclude_pattern": exclude_pattern,
 
4259
                          "strict": strict,
 
4260
                          "load_list": load_list,
 
4261
                          "debug_flags": debugflag,
 
4262
                          "starting_with": starting_with
 
4263
                          }
4291
4264
        selftest_kwargs.update(self.additional_selftest_args)
4292
4265
 
4293
4266
        # Make deprecation warnings visible, unless -Werror is set
4349
4322
 
4350
4323
        branch1 = Branch.open_containing(branch)[0]
4351
4324
        branch2 = Branch.open_containing(other)[0]
4352
 
        self.enter_context(branch1.lock_read())
4353
 
        self.enter_context(branch2.lock_read())
 
4325
        self.add_cleanup(branch1.lock_read().unlock)
 
4326
        self.add_cleanup(branch2.lock_read().unlock)
4354
4327
        last1 = ensure_null(branch1.last_revision())
4355
4328
        last2 = ensure_null(branch2.last_revision())
4356
4329
 
4357
4330
        graph = branch1.repository.get_graph(branch2.repository)
4358
4331
        base_rev_id = graph.find_unique_lca(last1, last2)
4359
4332
 
4360
 
        self.outf.write(gettext('merge base is revision %s\n') %
4361
 
                        base_rev_id.decode('utf-8'))
 
4333
        self.outf.write(gettext('merge base is revision %s\n') % base_rev_id)
4362
4334
 
4363
4335
 
4364
4336
class cmd_merge(Command):
4383
4355
    through OTHER, excluding BASE but including OTHER, will be merged.  If this
4384
4356
    causes some revisions to be skipped, i.e. if the destination branch does
4385
4357
    not already contain revision BASE, such a merge is commonly referred to as
4386
 
    a "cherrypick". Unlike a normal merge, Breezy does not currently track
 
4358
    a "cherrypick". Unlike a normal merge, Bazaar does not currently track
4387
4359
    cherrypicks. The changes look like a normal commit, and the history of the
4388
4360
    changes from the other branch is not stored in the commit.
4389
4361
 
4406
4378
    committed to record the result of the merge.
4407
4379
 
4408
4380
    merge refuses to run if there are any uncommitted changes, unless
4409
 
    --force is given.  If --force is given, then the changes from the source
 
4381
    --force is given.  If --force is given, then the changes from the source 
4410
4382
    will be merged with the current working tree, including any uncommitted
4411
4383
    changes in the tree.  The --force option can also be used to create a
4412
4384
    merge revision which has more than two parents.
4459
4431
        Option('uncommitted', help='Apply uncommitted changes'
4460
4432
               ' from a working copy, instead of branch changes.'),
4461
4433
        Option('pull', help='If the destination is already'
4462
 
               ' completely merged into the source, pull from the'
4463
 
               ' source rather than merging.  When this happens,'
4464
 
               ' you do not need to commit the result.'),
 
4434
                ' completely merged into the source, pull from the'
 
4435
                ' source rather than merging.  When this happens,'
 
4436
                ' you do not need to commit the result.'),
4465
4437
        custom_help('directory',
4466
 
                    help='Branch to merge into, '
 
4438
               help='Branch to merge into, '
4467
4439
                    'rather than the one containing the working directory.'),
4468
4440
        Option('preview', help='Instead of merging, show a diff of the'
4469
4441
               ' merge.'),
4470
4442
        Option('interactive', help='Select changes interactively.',
4471
 
               short_name='i')
 
4443
            short_name='i')
4472
4444
    ]
4473
4445
 
4474
4446
    def run(self, location=None, revision=None, force=False,
4481
4453
        if merge_type is None:
4482
4454
            merge_type = _mod_merge.Merge3Merger
4483
4455
 
4484
 
        if directory is None:
4485
 
            directory = u'.'
 
4456
        if directory is None: directory = u'.'
4486
4457
        possible_transports = []
4487
4458
        merger = None
4488
4459
        allow_pending = True
4489
4460
        verified = 'inapplicable'
4490
4461
 
4491
4462
        tree = WorkingTree.open_containing(directory)[0]
4492
 
        if tree.branch.last_revision() == _mod_revision.NULL_REVISION:
4493
 
            raise errors.CommandError(gettext('Merging into empty branches not currently supported, '
4494
 
                                                 'https://bugs.launchpad.net/bzr/+bug/308562'))
 
4463
        if tree.branch.revno() == 0:
 
4464
            raise errors.BzrCommandError(gettext('Merging into empty branches not currently supported, '
 
4465
                                         'https://bugs.launchpad.net/bzr/+bug/308562'))
 
4466
 
 
4467
        try:
 
4468
            basis_tree = tree.revision_tree(tree.last_revision())
 
4469
        except errors.NoSuchRevision:
 
4470
            basis_tree = tree.basis_tree()
4495
4471
 
4496
4472
        # die as quickly as possible if there are uncommitted changes
4497
4473
        if not force:
4502
4478
        change_reporter = delta._ChangeReporter(
4503
4479
            unversioned_filter=tree.is_ignored, view_info=view_info)
4504
4480
        pb = ui.ui_factory.nested_progress_bar()
4505
 
        self.enter_context(pb)
4506
 
        self.enter_context(tree.lock_write())
 
4481
        self.add_cleanup(pb.finished)
 
4482
        self.add_cleanup(tree.lock_write().unlock)
4507
4483
        if location is not None:
4508
4484
            try:
4509
 
                mergeable = _mod_mergeable.read_mergeable_from_url(
4510
 
                    location, possible_transports=possible_transports)
 
4485
                mergeable = bundle.read_mergeable_from_url(location,
 
4486
                    possible_transports=possible_transports)
4511
4487
            except errors.NotABundle:
4512
4488
                mergeable = None
4513
4489
            else:
4514
4490
                if uncommitted:
4515
 
                    raise errors.CommandError(gettext('Cannot use --uncommitted'
4516
 
                                                         ' with bundles or merge directives.'))
 
4491
                    raise errors.BzrCommandError(gettext('Cannot use --uncommitted'
 
4492
                        ' with bundles or merge directives.'))
4517
4493
 
4518
4494
                if revision is not None:
4519
 
                    raise errors.CommandError(gettext(
 
4495
                    raise errors.BzrCommandError(gettext(
4520
4496
                        'Cannot use -r with merge directives or bundles'))
4521
4497
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
4522
 
                                                                    mergeable)
 
4498
                   mergeable)
4523
4499
 
4524
4500
        if merger is None and uncommitted:
4525
4501
            if revision is not None and len(revision) > 0:
4526
 
                raise errors.CommandError(gettext('Cannot use --uncommitted and'
4527
 
                                                     ' --revision at the same time.'))
 
4502
                raise errors.BzrCommandError(gettext('Cannot use --uncommitted and'
 
4503
                    ' --revision at the same time.'))
4528
4504
            merger = self.get_merger_from_uncommitted(tree, location, None)
4529
4505
            allow_pending = False
4530
4506
 
4531
4507
        if merger is None:
4532
4508
            merger, allow_pending = self._get_merger_from_branch(tree,
4533
 
                                                                 location, revision, remember, possible_transports, None)
 
4509
                location, revision, remember, possible_transports, None)
4534
4510
 
4535
4511
        merger.merge_type = merge_type
4536
4512
        merger.reprocess = reprocess
4537
4513
        merger.show_base = show_base
4538
4514
        self.sanity_check_merger(merger)
4539
4515
        if (merger.base_rev_id == merger.other_rev_id and
4540
 
                merger.other_rev_id is not None):
 
4516
            merger.other_rev_id is not None):
4541
4517
            # check if location is a nonexistent file (and not a branch) to
4542
4518
            # disambiguate the 'Nothing to do'
4543
4519
            if merger.interesting_files:
4544
4520
                if not merger.other_tree.has_filename(
4545
 
                        merger.interesting_files[0]):
 
4521
                    merger.interesting_files[0]):
4546
4522
                    note(gettext("merger: ") + str(merger))
4547
4523
                    raise errors.PathsDoNotExist([location])
4548
4524
            note(gettext('Nothing to do.'))
4549
4525
            return 0
4550
4526
        if pull and not preview:
4551
4527
            if merger.interesting_files is not None:
4552
 
                raise errors.CommandError(
4553
 
                    gettext('Cannot pull individual files'))
 
4528
                raise errors.BzrCommandError(gettext('Cannot pull individual files'))
4554
4529
            if (merger.base_rev_id == tree.last_revision()):
4555
4530
                result = tree.pull(merger.other_branch, False,
4556
4531
                                   merger.other_rev_id)
4557
4532
                result.report(self.outf)
4558
4533
                return 0
4559
4534
        if merger.this_basis is None:
4560
 
            raise errors.CommandError(gettext(
 
4535
            raise errors.BzrCommandError(gettext(
4561
4536
                "This branch has no commits."
4562
4537
                " (perhaps you would prefer 'brz pull')"))
4563
4538
        if preview:
4571
4546
    def _get_preview(self, merger):
4572
4547
        tree_merger = merger.make_merger()
4573
4548
        tt = tree_merger.make_preview_transform()
4574
 
        self.enter_context(tt)
 
4549
        self.add_cleanup(tt.finalize)
4575
4550
        result_tree = tt.get_preview_tree()
4576
4551
        return result_tree
4577
4552
 
4615
4590
 
4616
4591
    def sanity_check_merger(self, merger):
4617
4592
        if (merger.show_base and
4618
 
                merger.merge_type is not _mod_merge.Merge3Merger):
4619
 
            raise errors.CommandError(gettext("Show-base is not supported for this"
4620
 
                                                 " merge type. %s") % merger.merge_type)
 
4593
            not merger.merge_type is _mod_merge.Merge3Merger):
 
4594
            raise errors.BzrCommandError(gettext("Show-base is not supported for this"
 
4595
                                         " merge type. %s") % merger.merge_type)
4621
4596
        if merger.reprocess is None:
4622
4597
            if merger.show_base:
4623
4598
                merger.reprocess = False
4625
4600
                # Use reprocess if the merger supports it
4626
4601
                merger.reprocess = merger.merge_type.supports_reprocess
4627
4602
        if merger.reprocess and not merger.merge_type.supports_reprocess:
4628
 
            raise errors.CommandError(gettext("Conflict reduction is not supported"
4629
 
                                                 " for merge type %s.") %
 
4603
            raise errors.BzrCommandError(gettext("Conflict reduction is not supported"
 
4604
                                         " for merge type %s.") %
4630
4605
                                         merger.merge_type)
4631
4606
        if merger.reprocess and merger.show_base:
4632
 
            raise errors.CommandError(gettext("Cannot do conflict reduction and"
4633
 
                                                 " show base."))
4634
 
 
4635
 
        if (merger.merge_type.requires_file_merge_plan and
4636
 
            (not getattr(merger.this_tree, 'plan_file_merge', None) or
4637
 
             not getattr(merger.other_tree, 'plan_file_merge', None) or
4638
 
             (merger.base_tree is not None and
4639
 
                 not getattr(merger.base_tree, 'plan_file_merge', None)))):
4640
 
            raise errors.CommandError(
4641
 
                gettext('Plan file merge unsupported: '
4642
 
                        'Merge type incompatible with tree formats.'))
 
4607
            raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
 
4608
                                         " show base."))
4643
4609
 
4644
4610
    def _get_merger_from_branch(self, tree, location, revision, remember,
4645
4611
                                possible_transports, pb):
4646
4612
        """Produce a merger from a location, assuming it refers to a branch."""
4647
4613
        # find the branch locations
4648
4614
        other_loc, user_location = self._select_branch_location(tree, location,
4649
 
                                                                revision, -1)
 
4615
            revision, -1)
4650
4616
        if revision is not None and len(revision) == 2:
4651
4617
            base_loc, _unused = self._select_branch_location(tree,
4652
 
                                                             location, revision, 0)
 
4618
                location, revision, 0)
4653
4619
        else:
4654
4620
            base_loc = other_loc
4655
4621
        # Open the branches
4656
4622
        other_branch, other_path = Branch.open_containing(other_loc,
4657
 
                                                          possible_transports)
 
4623
            possible_transports)
4658
4624
        if base_loc == other_loc:
4659
4625
            base_branch = other_branch
4660
4626
        else:
4661
4627
            base_branch, base_path = Branch.open_containing(base_loc,
4662
 
                                                            possible_transports)
 
4628
                possible_transports)
4663
4629
        # Find the revision ids
4664
4630
        other_revision_id = None
4665
4631
        base_revision_id = None
4677
4643
        # - user ask to remember or there is no previous location set to merge
4678
4644
        #   from and user didn't ask to *not* remember
4679
4645
        if (user_location is not None
4680
 
            and ((remember or
4681
 
                 (remember is None and
4682
 
                  tree.branch.get_submit_branch() is None)))):
 
4646
            and ((remember
 
4647
                  or (remember is None
 
4648
                      and tree.branch.get_submit_branch() is None)))):
4683
4649
            tree.branch.set_submit_branch(other_branch.base)
4684
4650
        # Merge tags (but don't set them in the master branch yet, the user
4685
4651
        # might revert this merge).  Commit will propagate them.
4686
4652
        other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
4687
4653
        merger = _mod_merge.Merger.from_revision_ids(tree,
4688
 
                                                     other_revision_id, base_revision_id, other_branch, base_branch)
 
4654
            other_revision_id, base_revision_id, other_branch, base_branch)
4689
4655
        if other_path != '':
4690
4656
            allow_pending = False
4691
4657
            merger.interesting_files = [other_path]
4726
4692
            will be the user-entered location.
4727
4693
        """
4728
4694
        if (revision is not None and index is not None
4729
 
                and revision[index] is not None):
 
4695
            and revision[index] is not None):
4730
4696
            branch = revision[index].get_branch()
4731
4697
            if branch is not None:
4732
4698
                return branch, branch
4748
4714
            stored_location_type = "parent"
4749
4715
        mutter("%s", stored_location)
4750
4716
        if stored_location is None:
4751
 
            raise errors.CommandError(
4752
 
                gettext("No location specified or remembered"))
 
4717
            raise errors.BzrCommandError(gettext("No location specified or remembered"))
4753
4718
        display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
4754
4719
        note(gettext("{0} remembered {1} location {2}").format(verb_string,
4755
 
                                                               stored_location_type, display_url))
 
4720
                stored_location_type, display_url))
4756
4721
        return stored_location
4757
4722
 
4758
4723
 
4780
4745
    """
4781
4746
    takes_args = ['file*']
4782
4747
    takes_options = [
4783
 
        'merge-type',
4784
 
        'reprocess',
4785
 
        Option('show-base',
4786
 
               help="Show base revision text in conflicts."),
4787
 
        ]
 
4748
            'merge-type',
 
4749
            'reprocess',
 
4750
            Option('show-base',
 
4751
                   help="Show base revision text in conflicts."),
 
4752
            ]
4788
4753
 
4789
4754
    def run(self, file_list=None, merge_type=None, show_base=False,
4790
4755
            reprocess=False):
4792
4757
        if merge_type is None:
4793
4758
            merge_type = _mod_merge.Merge3Merger
4794
4759
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4795
 
        self.enter_context(tree.lock_write())
 
4760
        self.add_cleanup(tree.lock_write().unlock)
4796
4761
        parents = tree.get_parent_ids()
4797
4762
        if len(parents) != 2:
4798
 
            raise errors.CommandError(
4799
 
                gettext("Sorry, remerge only works after normal"
4800
 
                        " merges.  Not cherrypicking or multi-merges."))
4801
 
        interesting_files = None
 
4763
            raise errors.BzrCommandError(gettext("Sorry, remerge only works after normal"
 
4764
                                         " merges.  Not cherrypicking or"
 
4765
                                         " multi-merges."))
 
4766
        repository = tree.branch.repository
 
4767
        interesting_ids = None
4802
4768
        new_conflicts = []
4803
4769
        conflicts = tree.conflicts()
4804
4770
        if file_list is not None:
4805
 
            interesting_files = set()
 
4771
            interesting_ids = set()
4806
4772
            for filename in file_list:
4807
 
                if not tree.is_versioned(filename):
 
4773
                file_id = tree.path2id(filename)
 
4774
                if file_id is None:
4808
4775
                    raise errors.NotVersionedError(filename)
4809
 
                interesting_files.add(filename)
4810
 
                if tree.kind(filename) != "directory":
 
4776
                interesting_ids.add(file_id)
 
4777
                if tree.kind(filename, file_id) != "directory":
4811
4778
                    continue
4812
4779
 
4813
 
                for path, ie in tree.iter_entries_by_dir(
4814
 
                        specific_files=[filename]):
4815
 
                    interesting_files.add(path)
 
4780
                # FIXME: Support nested trees
 
4781
                for name, ie in tree.root_inventory.iter_entries(file_id):
 
4782
                    interesting_ids.add(ie.file_id)
4816
4783
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4817
4784
        else:
4818
4785
            # Remerge only supports resolving contents conflicts
4819
4786
            allowed_conflicts = ('text conflict', 'contents conflict')
4820
4787
            restore_files = [c.path for c in conflicts
4821
4788
                             if c.typestring in allowed_conflicts]
4822
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
 
4789
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4823
4790
        tree.set_conflicts(ConflictList(new_conflicts))
4824
4791
        if file_list is not None:
4825
4792
            restore_files = file_list
4836
4803
        tree.set_parent_ids(parents[:1])
4837
4804
        try:
4838
4805
            merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
4839
 
            merger.interesting_files = interesting_files
 
4806
            merger.interesting_ids = interesting_ids
4840
4807
            merger.merge_type = merge_type
4841
4808
            merger.show_base = show_base
4842
4809
            merger.reprocess = reprocess
4869
4836
    update command.
4870
4837
 
4871
4838
    Uncommitted changes to files that are reverted will be discarded.
4872
 
    However, by default, any files that have been manually changed will be
 
4839
    Howver, by default, any files that have been manually changed will be
4873
4840
    backed up first.  (Files changed only by merge are not backed up.)  Backup
4874
4841
    files have '.~#~' appended to their name, where # is a number.
4875
4842
 
4915
4882
    def run(self, revision=None, no_backup=False, file_list=None,
4916
4883
            forget_merges=None):
4917
4884
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4918
 
        self.enter_context(tree.lock_tree_write())
 
4885
        self.add_cleanup(tree.lock_tree_write().unlock)
4919
4886
        if forget_merges:
4920
4887
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4921
4888
        else:
4925
4892
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4926
4893
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4927
4894
        tree.revert(file_list, rev_tree, not no_backup, None,
4928
 
                    report_changes=True)
 
4895
            report_changes=True)
4929
4896
 
4930
4897
 
4931
4898
class cmd_assert_fail(Command):
4944
4911
 
4945
4912
    _see_also = ['topics']
4946
4913
    takes_options = [
4947
 
        Option('long', 'Show help on all commands.'),
4948
 
        ]
 
4914
            Option('long', 'Show help on all commands.'),
 
4915
            ]
4949
4916
    takes_args = ['topic?']
4950
4917
    aliases = ['?', '--help', '-?', '-h']
4951
4918
 
4980
4947
    To filter on a range of revisions, you can use the command -r begin..end
4981
4948
    -r revision requests a specific revision, -r ..end or -r begin.. are
4982
4949
    also valid.
4983
 
 
 
4950
            
4984
4951
    :Exit values:
4985
4952
        1 - some missing revisions
4986
4953
        0 - no missing revisions
5022
4989
        'show-ids',
5023
4990
        'verbose',
5024
4991
        custom_help('revision',
5025
 
                    help='Filter on other branch revisions (inclusive). '
5026
 
                    'See "help revisionspec" for details.'),
 
4992
             help='Filter on other branch revisions (inclusive). '
 
4993
                'See "help revisionspec" for details.'),
5027
4994
        Option('my-revision',
5028
 
               type=_parse_revision_str,
5029
 
               help='Filter on local branch revisions (inclusive). '
5030
 
               'See "help revisionspec" for details.'),
 
4995
            type=_parse_revision_str,
 
4996
            help='Filter on local branch revisions (inclusive). '
 
4997
                'See "help revisionspec" for details.'),
5031
4998
        Option('include-merged',
5032
4999
               'Show all revisions in addition to the mainline ones.'),
5033
5000
        Option('include-merges', hidden=True,
5043
5010
            include_merged=None, revision=None, my_revision=None,
5044
5011
            directory=u'.'):
5045
5012
        from breezy.missing import find_unmerged, iter_log_revisions
5046
 
 
5047
5013
        def message(s):
5048
5014
            if not is_quiet():
5049
5015
                self.outf.write(s)
5064
5030
            restrict = 'remote'
5065
5031
 
5066
5032
        local_branch = Branch.open_containing(directory)[0]
5067
 
        self.enter_context(local_branch.lock_read())
 
5033
        self.add_cleanup(local_branch.lock_read().unlock)
5068
5034
 
5069
5035
        parent = local_branch.get_parent()
5070
5036
        if other_branch is None:
5071
5037
            other_branch = parent
5072
5038
            if other_branch is None:
5073
 
                raise errors.CommandError(gettext("No peer location known"
5074
 
                                                     " or specified."))
 
5039
                raise errors.BzrCommandError(gettext("No peer location known"
 
5040
                                             " or specified."))
5075
5041
            display_url = urlutils.unescape_for_display(parent,
5076
5042
                                                        self.outf.encoding)
5077
5043
            message(gettext("Using saved parent location: {0}\n").format(
5081
5047
        if remote_branch.base == local_branch.base:
5082
5048
            remote_branch = local_branch
5083
5049
        else:
5084
 
            self.enter_context(remote_branch.lock_read())
 
5050
            self.add_cleanup(remote_branch.lock_read().unlock)
5085
5051
 
5086
5052
        local_revid_range = _revision_range_to_revid_range(
5087
5053
            _get_revision_range(my_revision, local_branch,
5088
 
                                self.name()))
 
5054
                self.name()))
5089
5055
 
5090
5056
        remote_revid_range = _revision_range_to_revid_range(
5091
5057
            _get_revision_range(revision,
5092
 
                                remote_branch, self.name()))
 
5058
                remote_branch, self.name()))
5093
5059
 
5094
5060
        local_extra, remote_extra = find_unmerged(
5095
5061
            local_branch, remote_branch, restrict,
5108
5074
        status_code = 0
5109
5075
        if local_extra and not theirs_only:
5110
5076
            message(ngettext("You have %d extra revision:\n",
5111
 
                             "You have %d extra revisions:\n",
 
5077
                             "You have %d extra revisions:\n", 
5112
5078
                             len(local_extra)) %
5113
 
                    len(local_extra))
 
5079
                len(local_extra))
5114
5080
            rev_tag_dict = {}
5115
5081
            if local_branch.supports_tags():
5116
5082
                rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5117
5083
            for revision in iter_log_revisions(local_extra,
5118
 
                                               local_branch.repository,
5119
 
                                               verbose,
5120
 
                                               rev_tag_dict):
 
5084
                                local_branch.repository,
 
5085
                                verbose,
 
5086
                                rev_tag_dict):
5121
5087
                lf.log_revision(revision)
5122
5088
            printed_local = True
5123
5089
            status_code = 1
5130
5096
            message(ngettext("You are missing %d revision:\n",
5131
5097
                             "You are missing %d revisions:\n",
5132
5098
                             len(remote_extra)) %
5133
 
                    len(remote_extra))
 
5099
                len(remote_extra))
5134
5100
            if remote_branch.supports_tags():
5135
5101
                rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5136
5102
            for revision in iter_log_revisions(remote_extra,
5137
 
                                               remote_branch.repository,
5138
 
                                               verbose,
5139
 
                                               rev_tag_dict):
 
5103
                                remote_branch.repository,
 
5104
                                verbose,
 
5105
                                rev_tag_dict):
5140
5106
                lf.log_revision(revision)
5141
5107
            status_code = 1
5142
5108
 
5153
5119
            message(gettext("Branches are up to date.\n"))
5154
5120
        self.cleanup_now()
5155
5121
        if not status_code and parent is None and other_branch is not None:
5156
 
            self.enter_context(local_branch.lock_write())
 
5122
            self.add_cleanup(local_branch.lock_write().unlock)
5157
5123
            # handle race conditions - a parent might be set while we run.
5158
5124
            if local_branch.get_parent() is None:
5159
5125
                local_branch.set_parent(remote_branch.base)
5182
5148
    _see_also = ['repositories']
5183
5149
    takes_args = ['branch_or_repo?']
5184
5150
    takes_options = [
5185
 
        Option('clean-obsolete-packs',
5186
 
               'Delete obsolete packs to save disk space.'),
 
5151
        Option('clean-obsolete-packs', 'Delete obsolete packs to save disk space.'),
5187
5152
        ]
5188
5153
 
5189
5154
    def run(self, branch_or_repo='.', clean_obsolete_packs=False):
5204
5169
 
5205
5170
    --verbose shows the path where each plugin is located.
5206
5171
 
5207
 
    A plugin is an external component for Breezy that extends the
5208
 
    revision control system, by adding or replacing code in Breezy.
 
5172
    A plugin is an external component for Bazaar that extends the
 
5173
    revision control system, by adding or replacing code in Bazaar.
5209
5174
    Plugins can do a variety of things, including overriding commands,
5210
5175
    adding new commands, providing additional network transports and
5211
5176
    customizing log output.
5228
5193
class cmd_testament(Command):
5229
5194
    __doc__ = """Show testament (signing-form) of a revision."""
5230
5195
    takes_options = [
5231
 
        'revision',
5232
 
        Option('long', help='Produce long-format testament.'),
5233
 
        Option('strict',
5234
 
               help='Produce a strict-format testament.')]
 
5196
            'revision',
 
5197
            Option('long', help='Produce long-format testament.'),
 
5198
            Option('strict',
 
5199
                   help='Produce a strict-format testament.')]
5235
5200
    takes_args = ['branch?']
5236
5201
    encoding_type = 'exact'
5237
 
 
5238
5202
    @display_command
5239
5203
    def run(self, branch=u'.', revision=None, long=False, strict=False):
5240
 
        from .bzr.testament import Testament, StrictTestament
 
5204
        from .testament import Testament, StrictTestament
5241
5205
        if strict is True:
5242
5206
            testament_class = StrictTestament
5243
5207
        else:
5246
5210
            b = Branch.open_containing(branch)[0]
5247
5211
        else:
5248
5212
            b = Branch.open(branch)
5249
 
        self.enter_context(b.lock_read())
 
5213
        self.add_cleanup(b.lock_read().unlock)
5250
5214
        if revision is None:
5251
5215
            rev_id = b.last_revision()
5252
5216
        else:
5289
5253
        wt, branch, relpath = \
5290
5254
            _open_directory_or_containing_tree_or_branch(filename, directory)
5291
5255
        if wt is not None:
5292
 
            self.enter_context(wt.lock_read())
 
5256
            self.add_cleanup(wt.lock_read().unlock)
5293
5257
        else:
5294
 
            self.enter_context(branch.lock_read())
 
5258
            self.add_cleanup(branch.lock_read().unlock)
5295
5259
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
5296
 
        self.enter_context(tree.lock_read())
5297
 
        if wt is not None and revision is None:
5298
 
            if not wt.is_versioned(relpath):
5299
 
                raise errors.NotVersionedError(relpath)
 
5260
        self.add_cleanup(tree.lock_read().unlock)
 
5261
        if wt is not None and revision is None:
 
5262
            file_id = wt.path2id(relpath)
 
5263
        else:
 
5264
            file_id = tree.path2id(relpath)
 
5265
        if file_id is None:
 
5266
            raise errors.NotVersionedError(filename)
 
5267
        if wt is not None and revision is None:
5300
5268
            # If there is a tree and we're not annotating historical
5301
5269
            # versions, annotate the working tree's content.
5302
5270
            annotate_file_tree(wt, relpath, self.outf, long, all,
5303
 
                               show_ids=show_ids)
 
5271
                show_ids=show_ids, file_id=file_id)
5304
5272
        else:
5305
 
            if not tree.is_versioned(relpath):
5306
 
                raise errors.NotVersionedError(relpath)
5307
5273
            annotate_file_tree(tree, relpath, self.outf, long, all,
5308
 
                               show_ids=show_ids, branch=branch)
 
5274
                show_ids=show_ids, branch=branch, file_id=file_id)
5309
5275
 
5310
5276
 
5311
5277
class cmd_re_sign(Command):
5312
5278
    __doc__ = """Create a digital signature for an existing revision."""
5313
5279
    # TODO be able to replace existing ones.
5314
5280
 
5315
 
    hidden = True  # is this right ?
 
5281
    hidden = True # is this right ?
5316
5282
    takes_args = ['revision_id*']
5317
5283
    takes_options = ['directory', 'revision']
5318
5284
 
5319
5285
    def run(self, revision_id_list=None, revision=None, directory=u'.'):
5320
5286
        if revision_id_list is not None and revision is not None:
5321
 
            raise errors.CommandError(
5322
 
                gettext('You can only supply one of revision_id or --revision'))
 
5287
            raise errors.BzrCommandError(gettext('You can only supply one of revision_id or --revision'))
5323
5288
        if revision_id_list is None and revision is None:
5324
 
            raise errors.CommandError(
5325
 
                gettext('You must supply either --revision or a revision_id'))
 
5289
            raise errors.BzrCommandError(gettext('You must supply either --revision or a revision_id'))
5326
5290
        b = WorkingTree.open_containing(directory)[0].branch
5327
 
        self.enter_context(b.lock_write())
 
5291
        self.add_cleanup(b.lock_write().unlock)
5328
5292
        return self._run(b, revision_id_list, revision)
5329
5293
 
5330
5294
    def _run(self, b, revision_id_list, revision):
5331
 
        from .repository import WriteGroup
 
5295
        from . import gpg
5332
5296
        gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
5333
5297
        if revision_id_list is not None:
5334
 
            with WriteGroup(b.repository):
 
5298
            b.repository.start_write_group()
 
5299
            try:
5335
5300
                for revision_id in revision_id_list:
5336
5301
                    revision_id = cache_utf8.encode(revision_id)
5337
5302
                    b.repository.sign_revision(revision_id, gpg_strategy)
 
5303
            except:
 
5304
                b.repository.abort_write_group()
 
5305
                raise
 
5306
            else:
 
5307
                b.repository.commit_write_group()
5338
5308
        elif revision is not None:
5339
5309
            if len(revision) == 1:
5340
5310
                revno, rev_id = revision[0].in_history(b)
5341
 
                with WriteGroup(b.repository):
 
5311
                b.repository.start_write_group()
 
5312
                try:
5342
5313
                    b.repository.sign_revision(rev_id, gpg_strategy)
 
5314
                except:
 
5315
                    b.repository.abort_write_group()
 
5316
                    raise
 
5317
                else:
 
5318
                    b.repository.commit_write_group()
5343
5319
            elif len(revision) == 2:
5344
5320
                # are they both on rh- if so we can walk between them
5345
5321
                # might be nice to have a range helper for arbitrary
5349
5325
                if to_revid is None:
5350
5326
                    to_revno = b.revno()
5351
5327
                if from_revno is None or to_revno is None:
5352
 
                    raise errors.CommandError(
5353
 
                        gettext('Cannot sign a range of non-revision-history revisions'))
5354
 
                with WriteGroup(b.repository):
 
5328
                    raise errors.BzrCommandError(gettext('Cannot sign a range of non-revision-history revisions'))
 
5329
                b.repository.start_write_group()
 
5330
                try:
5355
5331
                    for revno in range(from_revno, to_revno + 1):
5356
5332
                        b.repository.sign_revision(b.get_rev_id(revno),
5357
5333
                                                   gpg_strategy)
 
5334
                except:
 
5335
                    b.repository.abort_write_group()
 
5336
                    raise
 
5337
                else:
 
5338
                    b.repository.commit_write_group()
5358
5339
            else:
5359
 
                raise errors.CommandError(
5360
 
                    gettext('Please supply either one revision, or a range.'))
 
5340
                raise errors.BzrCommandError(gettext('Please supply either one revision, or a range.'))
5361
5341
 
5362
5342
 
5363
5343
class cmd_bind(Command):
5382
5362
            try:
5383
5363
                location = b.get_old_bound_location()
5384
5364
            except errors.UpgradeRequired:
5385
 
                raise errors.CommandError(
5386
 
                    gettext('No location supplied.  '
5387
 
                            'This format does not remember old locations.'))
 
5365
                raise errors.BzrCommandError(gettext('No location supplied.  '
 
5366
                    'This format does not remember old locations.'))
5388
5367
            else:
5389
5368
                if location is None:
5390
5369
                    if b.get_bound_location() is not None:
5391
 
                        raise errors.CommandError(
 
5370
                        raise errors.BzrCommandError(
5392
5371
                            gettext('Branch is already bound'))
5393
5372
                    else:
5394
 
                        raise errors.CommandError(
 
5373
                        raise errors.BzrCommandError(
5395
5374
                            gettext('No location supplied'
5396
5375
                                    ' and no previous location known'))
5397
5376
        b_other = Branch.open(location)
5398
5377
        try:
5399
5378
            b.bind(b_other)
5400
5379
        except errors.DivergedBranches:
5401
 
            raise errors.CommandError(
5402
 
                gettext('These branches have diverged.'
5403
 
                        ' Try merging, and then bind again.'))
 
5380
            raise errors.BzrCommandError(gettext('These branches have diverged.'
 
5381
                                         ' Try merging, and then bind again.'))
5404
5382
        if b.get_config().has_explicit_nickname():
5405
5383
            b.nick = b_other.nick
5406
5384
 
5419
5397
    def run(self, directory=u'.'):
5420
5398
        b, relpath = Branch.open_containing(directory)
5421
5399
        if not b.unbind():
5422
 
            raise errors.CommandError(gettext('Local branch is not bound'))
 
5400
            raise errors.BzrCommandError(gettext('Local branch is not bound'))
5423
5401
 
5424
5402
 
5425
5403
class cmd_uncommit(Command):
5444
5422
    # information in shared branches as well.
5445
5423
    _see_also = ['commit']
5446
5424
    takes_options = ['verbose', 'revision',
5447
 
                     Option('dry-run', help='Don\'t actually make changes.'),
5448
 
                     Option('force', help='Say yes to all questions.'),
5449
 
                     Option('keep-tags',
5450
 
                            help='Keep tags that point to removed revisions.'),
5451
 
                     Option('local',
5452
 
                            help="Only remove the commits from the local "
5453
 
                            "branch when in a checkout."
5454
 
                            ),
5455
 
                     ]
 
5425
                    Option('dry-run', help='Don\'t actually make changes.'),
 
5426
                    Option('force', help='Say yes to all questions.'),
 
5427
                    Option('keep-tags',
 
5428
                           help='Keep tags that point to removed revisions.'),
 
5429
                    Option('local',
 
5430
                           help="Only remove the commits from the local branch"
 
5431
                                " when in a checkout."
 
5432
                           ),
 
5433
                    ]
5456
5434
    takes_args = ['location?']
5457
5435
    aliases = []
5458
5436
    encoding_type = 'replace'
5470
5448
            b = control.open_branch()
5471
5449
 
5472
5450
        if tree is not None:
5473
 
            self.enter_context(tree.lock_write())
 
5451
            self.add_cleanup(tree.lock_write().unlock)
5474
5452
        else:
5475
 
            self.enter_context(b.lock_write())
 
5453
            self.add_cleanup(b.lock_write().unlock)
5476
5454
        return self._run(b, tree, dry_run, verbose, revision, force,
5477
 
                         local, keep_tags, location)
 
5455
                         local, keep_tags)
5478
5456
 
5479
5457
    def _run(self, b, tree, dry_run, verbose, revision, force, local,
5480
 
             keep_tags, location):
 
5458
             keep_tags):
5481
5459
        from .log import log_formatter, show_log
5482
5460
        from .uncommit import uncommit
5483
5461
 
5514
5492
 
5515
5493
        if dry_run:
5516
5494
            self.outf.write(gettext('Dry-run, pretending to remove'
5517
 
                                    ' the above revisions.\n'))
 
5495
                            ' the above revisions.\n'))
5518
5496
        else:
5519
 
            self.outf.write(
5520
 
                gettext('The above revision(s) will be removed.\n'))
 
5497
            self.outf.write(gettext('The above revision(s) will be removed.\n'))
5521
5498
 
5522
5499
        if not force:
5523
5500
            if not ui.ui_factory.confirm_action(
5531
5508
               last_rev_id, rev_id)
5532
5509
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
5533
5510
                 revno=revno, local=local, keep_tags=keep_tags)
5534
 
        if location != '.':
5535
 
            self.outf.write(
5536
 
                gettext('You can restore the old tip by running:\n'
5537
 
                        '  brz pull -d %s %s -r revid:%s\n')
5538
 
                % (location, location, last_rev_id.decode('utf-8')))
5539
 
        else:
5540
 
            self.outf.write(
5541
 
                gettext('You can restore the old tip by running:\n'
5542
 
                        '  brz pull . -r revid:%s\n')
5543
 
                % last_rev_id.decode('utf-8'))
 
5511
        self.outf.write(gettext('You can restore the old tip by running:\n'
 
5512
             '  brz pull . -r revid:%s\n') % last_rev_id)
5544
5513
 
5545
5514
 
5546
5515
class cmd_break_lock(Command):
5558
5527
    :Examples:
5559
5528
        brz break-lock
5560
5529
        brz break-lock brz+ssh://example.com/brz/foo
5561
 
        brz break-lock --conf ~/.config/breezy
 
5530
        brz break-lock --conf ~/.bazaar
5562
5531
    """
5563
5532
 
5564
5533
    takes_args = ['location?']
5566
5535
        Option('config',
5567
5536
               help='LOCATION is the directory where the config lock is.'),
5568
5537
        Option('force',
5569
 
               help='Do not ask for confirmation before breaking the lock.'),
 
5538
            help='Do not ask for confirmation before breaking the lock.'),
5570
5539
        ]
5571
5540
 
5572
5541
    def run(self, location=None, config=False, force=False):
5574
5543
            location = u'.'
5575
5544
        if force:
5576
5545
            ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
5577
 
                                                               None,
5578
 
                                                               {'breezy.lockdir.break': True})
 
5546
                None,
 
5547
                {'breezy.lockdir.break': True})
5579
5548
        if config:
5580
5549
            conf = _mod_config.LockableConfig(file_name=location)
5581
5550
            conf.break_lock()
5610
5579
        Option('inet',
5611
5580
               help='Serve on stdin/out for use from inetd or sshd.'),
5612
5581
        RegistryOption('protocol',
5613
 
                       help="Protocol to serve.",
5614
 
                       lazy_registry=('breezy.transport',
5615
 
                                      'transport_server_registry'),
5616
 
                       value_switches=True),
 
5582
               help="Protocol to serve.",
 
5583
               lazy_registry=('breezy.transport', 'transport_server_registry'),
 
5584
               value_switches=True),
5617
5585
        Option('listen',
5618
 
               help='Listen for connections on nominated address.',
5619
 
               type=str),
 
5586
               help='Listen for connections on nominated address.', type=text_type),
5620
5587
        Option('port',
5621
5588
               help='Listen for connections on nominated port.  Passing 0 as '
5622
5589
                    'the port number will result in a dynamically allocated '
5623
5590
                    'port.  The default port depends on the protocol.',
5624
5591
               type=int),
5625
5592
        custom_help('directory',
5626
 
                    help='Serve contents of this directory.'),
 
5593
               help='Serve contents of this directory.'),
5627
5594
        Option('allow-writes',
5628
5595
               help='By default the server is a readonly server.  Supplying '
5629
5596
                    '--allow-writes enables write access to the contents of '
5632
5599
                    'external authentication is arranged supplying this '
5633
5600
                    'option leads to global uncontrolled write access to your '
5634
5601
                    'file system.'
5635
 
               ),
 
5602
                ),
5636
5603
        Option('client-timeout', type=float,
5637
5604
               help='Override the default idle client timeout (5min).'),
5638
5605
        ]
5639
5606
 
5640
5607
    def run(self, listen=None, port=None, inet=False, directory=None,
5641
5608
            allow_writes=False, protocol=None, client_timeout=None):
5642
 
        from . import location, transport
 
5609
        from . import transport
5643
5610
        if directory is None:
5644
5611
            directory = osutils.getcwd()
5645
5612
        if protocol is None:
5646
5613
            protocol = transport.transport_server_registry.get()
5647
 
        url = location.location_to_url(directory)
 
5614
        url = transport.location_to_url(directory)
5648
5615
        if not allow_writes:
5649
5616
            url = 'readonly+' + url
5650
5617
        t = transport.get_transport_from_url(url)
5668
5635
    _see_also = ['split']
5669
5636
    takes_args = ['tree']
5670
5637
    takes_options = [
5671
 
        Option('reference', help='Join by reference.', hidden=True),
5672
 
        ]
 
5638
            Option('reference', help='Join by reference.', hidden=True),
 
5639
            ]
5673
5640
 
5674
5641
    def run(self, tree, reference=False):
5675
5642
        from breezy.mutabletree import BadReferenceTarget
5678
5645
        containing_tree = WorkingTree.open_containing(parent_dir)[0]
5679
5646
        repo = containing_tree.branch.repository
5680
5647
        if not repo.supports_rich_root():
5681
 
            raise errors.CommandError(gettext(
 
5648
            raise errors.BzrCommandError(gettext(
5682
5649
                "Can't join trees because %s doesn't support rich root data.\n"
5683
5650
                "You can use brz upgrade on the repository.")
5684
5651
                % (repo,))
5688
5655
            except BadReferenceTarget as e:
5689
5656
                # XXX: Would be better to just raise a nicely printable
5690
5657
                # exception from the real origin.  Also below.  mbp 20070306
5691
 
                raise errors.CommandError(
5692
 
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5658
                raise errors.BzrCommandError(
 
5659
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5693
5660
        else:
5694
5661
            try:
5695
5662
                containing_tree.subsume(sub_tree)
5696
5663
            except errors.BadSubsumeSource as e:
5697
 
                raise errors.CommandError(
5698
 
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5664
                raise errors.BzrCommandError(
 
5665
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5699
5666
 
5700
5667
 
5701
5668
class cmd_split(Command):
5715
5682
 
5716
5683
    def run(self, tree):
5717
5684
        containing_tree, subdir = WorkingTree.open_containing(tree)
5718
 
        if not containing_tree.is_versioned(subdir):
 
5685
        sub_id = containing_tree.path2id(subdir)
 
5686
        if sub_id is None:
5719
5687
            raise errors.NotVersionedError(subdir)
5720
5688
        try:
5721
 
            containing_tree.extract(subdir)
 
5689
            containing_tree.extract(subdir, sub_id)
5722
5690
        except errors.RootNotRich:
5723
5691
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
5724
5692
 
5748
5716
 
5749
5717
    takes_options = [
5750
5718
        'directory',
5751
 
        RegistryOption.from_kwargs(
5752
 
            'patch-type',
 
5719
        RegistryOption.from_kwargs('patch-type',
5753
5720
            'The type of patch to include in the directive.',
5754
5721
            title='Patch type',
5755
5722
            value_switches=True,
5758
5725
            diff='Normal unified diff.',
5759
5726
            plain='No patch, just directive.'),
5760
5727
        Option('sign', help='GPG-sign the directive.'), 'revision',
5761
 
        Option('mail-to', type=str,
5762
 
               help='Instead of printing the directive, email to this '
5763
 
               'address.'),
5764
 
        Option('message', type=str, short_name='m',
5765
 
               help='Message to use when committing this merge.')
 
5728
        Option('mail-to', type=text_type,
 
5729
            help='Instead of printing the directive, email to this address.'),
 
5730
        Option('message', type=text_type, short_name='m',
 
5731
            help='Message to use when committing this merge.')
5766
5732
        ]
5767
5733
 
5768
5734
    encoding_type = 'exact'
5786
5752
        if submit_branch is None:
5787
5753
            submit_branch = branch.get_parent()
5788
5754
        if submit_branch is None:
5789
 
            raise errors.CommandError(
5790
 
                gettext('No submit branch specified or known'))
 
5755
            raise errors.BzrCommandError(gettext('No submit branch specified or known'))
5791
5756
 
5792
5757
        stored_public_branch = branch.get_public_branch()
5793
5758
        if public_branch is None:
5796
5761
            # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5797
5762
            branch.set_public_branch(public_branch)
5798
5763
        if not include_bundle and public_branch is None:
5799
 
            raise errors.CommandError(
5800
 
                gettext('No public branch specified or known'))
 
5764
            raise errors.BzrCommandError(gettext('No public branch specified or'
 
5765
                                         ' known'))
5801
5766
        base_revision_id = None
5802
5767
        if revision is not None:
5803
5768
            if len(revision) > 2:
5804
 
                raise errors.CommandError(
5805
 
                    gettext('brz merge-directive takes '
5806
 
                            'at most two one revision identifiers'))
 
5769
                raise errors.BzrCommandError(gettext('brz merge-directive takes '
 
5770
                    'at most two one revision identifiers'))
5807
5771
            revision_id = revision[-1].as_revision_id(branch)
5808
5772
            if len(revision) == 2:
5809
5773
                base_revision_id = revision[0].as_revision_id(branch)
5811
5775
            revision_id = branch.last_revision()
5812
5776
        revision_id = ensure_null(revision_id)
5813
5777
        if revision_id == NULL_REVISION:
5814
 
            raise errors.CommandError(gettext('No revisions to bundle.'))
 
5778
            raise errors.BzrCommandError(gettext('No revisions to bundle.'))
5815
5779
        directive = merge_directive.MergeDirective2.from_objects(
5816
5780
            branch.repository, revision_id, time.time(),
5817
5781
            osutils.local_time_offset(), submit_branch,
5843
5807
      branch.
5844
5808
 
5845
5809
    `brz send` creates a compact data set that, when applied using brz
5846
 
    merge, has the same effect as merging from the source branch.
5847
 
 
 
5810
    merge, has the same effect as merging from the source branch.  
 
5811
    
5848
5812
    By default the merge directive is self-contained and can be applied to any
5849
5813
    branch containing submit_branch in its ancestory without needing access to
5850
5814
    the source branch.
5851
 
 
5852
 
    If --no-bundle is specified, then Breezy doesn't send the contents of the
 
5815
    
 
5816
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
5853
5817
    revisions, but only a structured request to merge from the
5854
5818
    public_location.  In that case the public_branch is needed and it must be
5855
5819
    up-to-date and accessible to the recipient.  The public_branch is always
5880
5844
    If the preferred client can't be found (or used), your editor will be used.
5881
5845
 
5882
5846
    To use a specific mail program, set the mail_client configuration option.
5883
 
    Supported values for specific clients are "claws", "evolution", "kmail",
5884
 
    "mail.app" (MacOS X's Mail.app), "mutt", and "thunderbird"; generic options
5885
 
    are "default", "editor", "emacsclient", "mapi", and "xdg-email".  Plugins
5886
 
    may also add supported clients.
 
5847
    (For Thunderbird 1.5, this works around some bugs.)  Supported values for
 
5848
    specific clients are "claws", "evolution", "kmail", "mail.app" (MacOS X's
 
5849
    Mail.app), "mutt", and "thunderbird"; generic options are "default",
 
5850
    "editor", "emacsclient", "mapi", and "xdg-email".  Plugins may also add
 
5851
    supported clients.
5887
5852
 
5888
5853
    If mail is being sent, a to address is required.  This can be supplied
5889
5854
    either on the commandline, by setting the submit_to configuration
5890
5855
    option in the branch itself or the child_submit_to configuration option
5891
5856
    in the submit branch.
5892
5857
 
 
5858
    Two formats are currently supported: "4" uses revision bundle format 4 and
 
5859
    merge directive format 2.  It is significantly faster and smaller than
 
5860
    older formats.  It is compatible with Bazaar 0.19 and later.  It is the
 
5861
    default.  "0.9" uses revision bundle format 0.9 and merge directive
 
5862
    format 1.  It is compatible with Bazaar 0.12 - 0.18.
 
5863
 
5893
5864
    The merge directives created by brz send may be applied using brz merge or
5894
5865
    brz pull by specifying a file containing a merge directive as the location.
5895
5866
 
5915
5886
               help='Branch to generate the submission from, '
5916
5887
               'rather than the one containing the working directory.',
5917
5888
               short_name='f',
5918
 
               type=str),
 
5889
               type=text_type),
5919
5890
        Option('output', short_name='o',
5920
5891
               help='Write merge directive to this file or directory; '
5921
5892
                    'use - for stdout.',
5922
 
               type=str),
 
5893
               type=text_type),
5923
5894
        Option('strict',
5924
5895
               help='Refuse to send if there are uncommitted changes in'
5925
5896
               ' the working tree, --no-strict disables the check.'),
5926
5897
        Option('mail-to', help='Mail the request to this address.',
5927
 
               type=str),
 
5898
               type=text_type),
5928
5899
        'revision',
5929
5900
        'message',
5930
 
        Option('body', help='Body for the email.', type=str),
 
5901
        Option('body', help='Body for the email.', type=text_type),
5931
5902
        RegistryOption('format',
5932
5903
                       help='Use the specified output format.',
5933
5904
                       lazy_registry=('breezy.send', 'format_registry')),
5970
5941
    branch is used in the merge instructions.  This means that a local mirror
5971
5942
    can be used as your actual submit branch, once you have set public_branch
5972
5943
    for that mirror.
 
5944
 
 
5945
    Two formats are currently supported: "4" uses revision bundle format 4 and
 
5946
    merge directive format 2.  It is significantly faster and smaller than
 
5947
    older formats.  It is compatible with Bazaar 0.19 and later.  It is the
 
5948
    default.  "0.9" uses revision bundle format 0.9 and merge directive
 
5949
    format 1.  It is compatible with Bazaar 0.12 - 0.18.
5973
5950
    """
5974
5951
 
5975
5952
    takes_options = [
5983
5960
               help='Branch to generate the submission from, '
5984
5961
               'rather than the one containing the working directory.',
5985
5962
               short_name='f',
5986
 
               type=str),
 
5963
               type=text_type),
5987
5964
        Option('output', short_name='o', help='Write directive to this file.',
5988
 
               type=str),
 
5965
               type=text_type),
5989
5966
        Option('strict',
5990
5967
               help='Refuse to bundle revisions if there are uncommitted'
5991
5968
               ' changes in the working tree, --no-strict disables the check.'),
6007
5984
            output = '-'
6008
5985
        from .send import send
6009
5986
        return send(submit_branch, revision, public_branch, remember,
6010
 
                    format, no_bundle, no_patch, output,
6011
 
                    kwargs.get('from', '.'), None, None, None,
6012
 
                    self.outf, strict=strict)
 
5987
                         format, no_bundle, no_patch, output,
 
5988
                         kwargs.get('from', '.'), None, None, None,
 
5989
                         self.outf, strict=strict)
6013
5990
 
6014
5991
 
6015
5992
class cmd_tag(Command):
6028
6005
    To rename a tag (change the name but keep it on the same revsion), run ``brz
6029
6006
    tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
6030
6007
 
6031
 
    If no tag name is specified it will be determined through the
 
6008
    If no tag name is specified it will be determined through the 
6032
6009
    'automatic_tag_name' hook. This can e.g. be used to automatically tag
6033
6010
    upstream releases by reading configure.ac. See ``brz help hooks`` for
6034
6011
    details.
6038
6015
    takes_args = ['tag_name?']
6039
6016
    takes_options = [
6040
6017
        Option('delete',
6041
 
               help='Delete this tag rather than placing it.',
6042
 
               ),
 
6018
            help='Delete this tag rather than placing it.',
 
6019
            ),
6043
6020
        custom_help('directory',
6044
 
                    help='Branch in which to place the tag.'),
 
6021
            help='Branch in which to place the tag.'),
6045
6022
        Option('force',
6046
 
               help='Replace existing tags.',
6047
 
               ),
 
6023
            help='Replace existing tags.',
 
6024
            ),
6048
6025
        'revision',
6049
6026
        ]
6050
6027
 
6055
6032
            revision=None,
6056
6033
            ):
6057
6034
        branch, relpath = Branch.open_containing(directory)
6058
 
        self.enter_context(branch.lock_write())
 
6035
        self.add_cleanup(branch.lock_write().unlock)
6059
6036
        if delete:
6060
6037
            if tag_name is None:
6061
 
                raise errors.CommandError(
6062
 
                    gettext("No tag specified to delete."))
 
6038
                raise errors.BzrCommandError(gettext("No tag specified to delete."))
6063
6039
            branch.tags.delete_tag(tag_name)
6064
6040
            note(gettext('Deleted tag %s.') % tag_name)
6065
6041
        else:
6066
6042
            if revision:
6067
6043
                if len(revision) != 1:
6068
 
                    raise errors.CommandError(gettext(
 
6044
                    raise errors.BzrCommandError(gettext(
6069
6045
                        "Tags can only be placed on a single revision, "
6070
6046
                        "not on a range"))
6071
6047
                revision_id = revision[0].as_revision_id(branch)
6074
6050
            if tag_name is None:
6075
6051
                tag_name = branch.automatic_tag_name(revision_id)
6076
6052
                if tag_name is None:
6077
 
                    raise errors.CommandError(gettext(
 
6053
                    raise errors.BzrCommandError(gettext(
6078
6054
                        "Please specify a tag name."))
6079
6055
            try:
6080
6056
                existing_target = branch.tags.lookup_tag(tag_name)
6101
6077
    _see_also = ['tag']
6102
6078
    takes_options = [
6103
6079
        custom_help('directory',
6104
 
                    help='Branch whose tags should be displayed.'),
 
6080
            help='Branch whose tags should be displayed.'),
6105
6081
        RegistryOption('sort',
6106
 
                       'Sort tags by different criteria.', title='Sorting',
6107
 
                       lazy_registry=('breezy.tag', 'tag_sort_methods')
6108
 
                       ),
 
6082
            'Sort tags by different criteria.', title='Sorting',
 
6083
            lazy_registry=('breezy.tag', 'tag_sort_methods')
 
6084
            ),
6109
6085
        'show-ids',
6110
6086
        'revision',
6111
6087
    ]
6115
6091
        from .tag import tag_sort_methods
6116
6092
        branch, relpath = Branch.open_containing(directory)
6117
6093
 
6118
 
        tags = list(branch.tags.get_tag_dict().items())
 
6094
        tags = list(viewitems(branch.tags.get_tag_dict()))
6119
6095
        if not tags:
6120
6096
            return
6121
6097
 
6122
 
        self.enter_context(branch.lock_read())
 
6098
        self.add_cleanup(branch.lock_read().unlock)
6123
6099
        if revision:
6124
6100
            # Restrict to the specified range
6125
6101
            tags = self._tags_for_range(branch, revision)
6140
6116
                    # which are not in this branch. Fail gracefully ...
6141
6117
                    revno = '?'
6142
6118
                tags[index] = (tag, revno)
6143
 
        else:
6144
 
            tags = [(tag, revid.decode('utf-8')) for (tag, revid) in tags]
6145
6119
        self.cleanup_now()
6146
6120
        for tag, revspec in tags:
6147
6121
            self.outf.write('%-20s %s\n' % (tag, revspec))
6148
6122
 
6149
6123
    def _tags_for_range(self, branch, revision):
 
6124
        range_valid = True
6150
6125
        rev1, rev2 = _get_revision_range(revision, branch, self.name())
6151
6126
        revid1, revid2 = rev1.rev_id, rev2.rev_id
6152
6127
        # _get_revision_range will always set revid2 if it's not specified.
6164
6139
        tagged_revids = branch.tags.get_reverse_tag_dict()
6165
6140
        found = []
6166
6141
        for r in branch.iter_merge_sorted_revisions(
6167
 
                start_revision_id=revid2, stop_revision_id=revid1,
6168
 
                stop_rule='include'):
 
6142
            start_revision_id=revid2, stop_revision_id=revid1,
 
6143
            stop_rule='include'):
6169
6144
            revid_tags = tagged_revids.get(r[0], None)
6170
6145
            if revid_tags:
6171
6146
                found.extend([(tag, r[0]) for tag in revid_tags])
6198
6173
            tree='Reconfigure to be an unbound branch with a working tree.',
6199
6174
            checkout='Reconfigure to be a bound branch with a working tree.',
6200
6175
            lightweight_checkout='Reconfigure to be a lightweight'
6201
 
            ' checkout (with no local history).',
 
6176
                ' checkout (with no local history).',
6202
6177
            ),
6203
6178
        RegistryOption.from_kwargs(
6204
6179
            'repository_type',
6206
6181
            help='Location fo the repository.',
6207
6182
            value_switches=True, enum_switch=False,
6208
6183
            standalone='Reconfigure to be a standalone branch '
6209
 
            '(i.e. stop using shared repository).',
 
6184
                '(i.e. stop using shared repository).',
6210
6185
            use_shared='Reconfigure to use a shared repository.',
6211
6186
            ),
6212
6187
        RegistryOption.from_kwargs(
6215
6190
            help='Whether new branches in the repository have trees.',
6216
6191
            value_switches=True, enum_switch=False,
6217
6192
            with_trees='Reconfigure repository to create '
6218
 
            'working trees on branches by default.',
 
6193
                'working trees on branches by default.',
6219
6194
            with_no_trees='Reconfigure repository to not create '
6220
 
            'working trees on branches by default.'
 
6195
                'working trees on branches by default.'
6221
6196
            ),
6222
 
        Option('bind-to', help='Branch to bind checkout to.', type=str),
 
6197
        Option('bind-to', help='Branch to bind checkout to.', type=text_type),
6223
6198
        Option('force',
6224
 
               help='Perform reconfiguration even if local changes'
6225
 
               ' will be lost.'),
 
6199
            help='Perform reconfiguration even if local changes'
 
6200
            ' will be lost.'),
6226
6201
        Option('stacked-on',
6227
 
               help='Reconfigure a branch to be stacked on another branch.',
6228
 
               type=str,
6229
 
               ),
 
6202
            help='Reconfigure a branch to be stacked on another branch.',
 
6203
            type=text_type,
 
6204
            ),
6230
6205
        Option('unstacked',
6231
 
               help='Reconfigure a branch to be unstacked.  This '
6232
 
               'may require copying substantial data into it.',
6233
 
               ),
 
6206
            help='Reconfigure a branch to be unstacked.  This '
 
6207
                'may require copying substantial data into it.',
 
6208
            ),
6234
6209
        ]
6235
6210
 
6236
6211
    def run(self, location=None, bind_to=None, force=False,
6238
6213
            stacked_on=None, unstacked=None):
6239
6214
        directory = controldir.ControlDir.open(location)
6240
6215
        if stacked_on and unstacked:
6241
 
            raise errors.CommandError(
6242
 
                gettext("Can't use both --stacked-on and --unstacked"))
 
6216
            raise errors.BzrCommandError(gettext("Can't use both --stacked-on and --unstacked"))
6243
6217
        elif stacked_on is not None:
6244
6218
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
6245
6219
        elif unstacked:
6249
6223
        # to ban it.
6250
6224
        if (tree_type is None and
6251
6225
            repository_type is None and
6252
 
                repository_trees is None):
 
6226
            repository_trees is None):
6253
6227
            if stacked_on or unstacked:
6254
6228
                return
6255
6229
            else:
6256
 
                raise errors.CommandError(gettext('No target configuration '
6257
 
                                                     'specified'))
 
6230
                raise errors.BzrCommandError(gettext('No target configuration '
 
6231
                    'specified'))
6258
6232
        reconfiguration = None
6259
6233
        if tree_type == 'branch':
6260
6234
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
6313
6287
    takes_args = ['to_location?']
6314
6288
    takes_options = ['directory',
6315
6289
                     Option('force',
6316
 
                            help='Switch even if local commits will be lost.'),
 
6290
                        help='Switch even if local commits will be lost.'),
6317
6291
                     'revision',
6318
6292
                     Option('create-branch', short_name='b',
6319
 
                            help='Create the target branch from this one before'
6320
 
                            ' switching to it.'),
 
6293
                        help='Create the target branch from this one before'
 
6294
                             ' switching to it.'),
6321
6295
                     Option('store',
6322
 
                            help='Store and restore uncommitted changes in the'
6323
 
                            ' branch.'),
6324
 
                     ]
 
6296
                        help='Store and restore uncommitted changes in the'
 
6297
                             ' branch.'),
 
6298
                    ]
6325
6299
 
6326
6300
    def run(self, to_location=None, force=False, create_branch=False,
6327
6301
            revision=None, directory=u'.', store=False):
6328
6302
        from . import switch
6329
6303
        tree_location = directory
6330
6304
        revision = _get_one_revision('switch', revision)
6331
 
        control_dir = controldir.ControlDir.open_containing(tree_location)[0]
6332
 
        possible_transports = [control_dir.root_transport]
 
6305
        possible_transports = []
 
6306
        control_dir = controldir.ControlDir.open_containing(tree_location,
 
6307
            possible_transports=possible_transports)[0]
6333
6308
        if to_location is None:
6334
6309
            if revision is None:
6335
 
                raise errors.CommandError(gettext('You must supply either a'
6336
 
                                                     ' revision or a location'))
 
6310
                raise errors.BzrCommandError(gettext('You must supply either a'
 
6311
                                             ' revision or a location'))
6337
6312
            to_location = tree_location
6338
6313
        try:
6339
6314
            branch = control_dir.open_branch(
6342
6317
        except errors.NotBranchError:
6343
6318
            branch = None
6344
6319
            had_explicit_nick = False
6345
 
        else:
6346
 
            possible_transports.append(branch.user_transport)
6347
6320
        if create_branch:
6348
6321
            if branch is None:
6349
 
                raise errors.CommandError(
 
6322
                raise errors.BzrCommandError(
6350
6323
                    gettext('cannot create branch without source branch'))
6351
 
            to_location = lookup_new_sibling_branch(
6352
 
                control_dir, to_location,
6353
 
                possible_transports=possible_transports)
6354
 
            if revision is not None:
6355
 
                revision = revision.as_revision_id(branch)
6356
 
            to_branch = branch.controldir.sprout(
6357
 
                to_location,
6358
 
                possible_transports=possible_transports,
6359
 
                revision_id=revision,
6360
 
                source_branch=branch).open_branch()
 
6324
            to_location = lookup_new_sibling_branch(control_dir, to_location,
 
6325
                 possible_transports=possible_transports)
 
6326
            to_branch = branch.controldir.sprout(to_location,
 
6327
                 possible_transports=possible_transports,
 
6328
                 source_branch=branch).open_branch()
6361
6329
        else:
6362
6330
            try:
6363
 
                to_branch = Branch.open(
6364
 
                    to_location, possible_transports=possible_transports)
 
6331
                to_branch = Branch.open(to_location,
 
6332
                    possible_transports=possible_transports)
6365
6333
            except errors.NotBranchError:
6366
 
                to_branch = open_sibling_branch(
6367
 
                    control_dir, to_location,
 
6334
                to_branch = open_sibling_branch(control_dir, to_location,
6368
6335
                    possible_transports=possible_transports)
6369
 
            if revision is not None:
6370
 
                revision = revision.as_revision_id(to_branch)
6371
 
        possible_transports.append(to_branch.user_transport)
6372
 
        try:
6373
 
            switch.switch(control_dir, to_branch, force, revision_id=revision,
6374
 
                          store_uncommitted=store,
6375
 
                          possible_transports=possible_transports)
6376
 
        except controldir.BranchReferenceLoop:
6377
 
            raise errors.CommandError(
6378
 
                gettext('switching would create a branch reference loop. '
6379
 
                        'Use the "bzr up" command to switch to a '
6380
 
                        'different revision.'))
 
6336
        if revision is not None:
 
6337
            revision = revision.as_revision_id(to_branch)
 
6338
        switch.switch(control_dir, to_branch, force, revision_id=revision,
 
6339
                      store_uncommitted=store)
6381
6340
        if had_explicit_nick:
6382
 
            branch = control_dir.open_branch()  # get the new branch!
 
6341
            branch = control_dir.open_branch() #get the new branch!
6383
6342
            branch.nick = to_branch.nick
6384
 
        if to_branch.name:
6385
 
            if to_branch.controldir.control_url != control_dir.control_url:
6386
 
                note(gettext('Switched to branch %s at %s'),
6387
 
                     to_branch.name, urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6388
 
            else:
6389
 
                note(gettext('Switched to branch %s'), to_branch.name)
6390
 
        else:
6391
 
            note(gettext('Switched to branch at %s'),
6392
 
                 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6343
        note(gettext('Switched to branch: %s'),
 
6344
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6345
 
6393
6346
 
6394
6347
 
6395
6348
class cmd_view(Command):
6458
6411
    takes_args = ['file*']
6459
6412
    takes_options = [
6460
6413
        Option('all',
6461
 
               help='Apply list or delete action to all views.',
6462
 
               ),
 
6414
            help='Apply list or delete action to all views.',
 
6415
            ),
6463
6416
        Option('delete',
6464
 
               help='Delete the view.',
6465
 
               ),
 
6417
            help='Delete the view.',
 
6418
            ),
6466
6419
        Option('name',
6467
 
               help='Name of the view to define, list or delete.',
6468
 
               type=str,
6469
 
               ),
 
6420
            help='Name of the view to define, list or delete.',
 
6421
            type=text_type,
 
6422
            ),
6470
6423
        Option('switch',
6471
 
               help='Name of the view to switch to.',
6472
 
               type=str,
6473
 
               ),
 
6424
            help='Name of the view to switch to.',
 
6425
            type=text_type,
 
6426
            ),
6474
6427
        ]
6475
6428
 
6476
6429
    def run(self, file_list,
6480
6433
            switch=None,
6481
6434
            ):
6482
6435
        tree, file_list = WorkingTree.open_containing_paths(file_list,
6483
 
                                                            apply_view=False)
 
6436
            apply_view=False)
6484
6437
        current_view, view_dict = tree.views.get_view_info()
6485
6438
        if name is None:
6486
6439
            name = current_view
6487
6440
        if delete:
6488
6441
            if file_list:
6489
 
                raise errors.CommandError(gettext(
 
6442
                raise errors.BzrCommandError(gettext(
6490
6443
                    "Both --delete and a file list specified"))
6491
6444
            elif switch:
6492
 
                raise errors.CommandError(gettext(
 
6445
                raise errors.BzrCommandError(gettext(
6493
6446
                    "Both --delete and --switch specified"))
6494
6447
            elif all:
6495
6448
                tree.views.set_view_info(None, {})
6496
6449
                self.outf.write(gettext("Deleted all views.\n"))
6497
6450
            elif name is None:
6498
 
                raise errors.CommandError(
6499
 
                    gettext("No current view to delete"))
 
6451
                raise errors.BzrCommandError(gettext("No current view to delete"))
6500
6452
            else:
6501
6453
                tree.views.delete_view(name)
6502
6454
                self.outf.write(gettext("Deleted '%s' view.\n") % name)
6503
6455
        elif switch:
6504
6456
            if file_list:
6505
 
                raise errors.CommandError(gettext(
 
6457
                raise errors.BzrCommandError(gettext(
6506
6458
                    "Both --switch and a file list specified"))
6507
6459
            elif all:
6508
 
                raise errors.CommandError(gettext(
 
6460
                raise errors.BzrCommandError(gettext(
6509
6461
                    "Both --switch and --all specified"))
6510
6462
            elif switch == 'off':
6511
6463
                if current_view is None:
6512
 
                    raise errors.CommandError(
6513
 
                        gettext("No current view to disable"))
 
6464
                    raise errors.BzrCommandError(gettext("No current view to disable"))
6514
6465
                tree.views.set_view_info(None, view_dict)
6515
 
                self.outf.write(gettext("Disabled '%s' view.\n") %
6516
 
                                (current_view))
 
6466
                self.outf.write(gettext("Disabled '%s' view.\n") % (current_view))
6517
6467
            else:
6518
6468
                tree.views.set_view_info(switch, view_dict)
6519
6469
                view_str = views.view_display_str(tree.views.lookup_view())
6520
 
                self.outf.write(
6521
 
                    gettext("Using '{0}' view: {1}\n").format(switch, view_str))
 
6470
                self.outf.write(gettext("Using '{0}' view: {1}\n").format(switch, view_str))
6522
6471
        elif all:
6523
6472
            if view_dict:
6524
6473
                self.outf.write(gettext('Views defined:\n'))
6536
6485
                # No name given and no current view set
6537
6486
                name = 'my'
6538
6487
            elif name == 'off':
6539
 
                raise errors.CommandError(gettext(
 
6488
                raise errors.BzrCommandError(gettext(
6540
6489
                    "Cannot change the 'off' pseudo view"))
6541
6490
            tree.views.set_view(name, sorted(file_list))
6542
6491
            view_str = views.view_display_str(tree.views.lookup_view())
6543
 
            self.outf.write(
6544
 
                gettext("Using '{0}' view: {1}\n").format(name, view_str))
 
6492
            self.outf.write(gettext("Using '{0}' view: {1}\n").format(name, view_str))
6545
6493
        else:
6546
6494
            # list the files
6547
6495
            if name is None:
6549
6497
                self.outf.write(gettext('No current view.\n'))
6550
6498
            else:
6551
6499
                view_str = views.view_display_str(tree.views.lookup_view(name))
6552
 
                self.outf.write(
6553
 
                    gettext("'{0}' view is: {1}\n").format(name, view_str))
 
6500
                self.outf.write(gettext("'{0}' view is: {1}\n").format(name, view_str))
6554
6501
 
6555
6502
 
6556
6503
class cmd_hooks(Command):
6576
6523
class cmd_remove_branch(Command):
6577
6524
    __doc__ = """Remove a branch.
6578
6525
 
6579
 
    This will remove the branch from the specified location but
 
6526
    This will remove the branch from the specified location but 
6580
6527
    will keep any working tree or repository in place.
6581
6528
 
6582
6529
    :Examples:
6590
6537
    takes_args = ["location?"]
6591
6538
 
6592
6539
    takes_options = ['directory',
6593
 
                     Option('force', help='Remove branch even if it is the active branch.')]
 
6540
        Option('force', help='Remove branch even if it is the active branch.')]
6594
6541
 
6595
6542
    aliases = ["rmbranch"]
6596
6543
 
6602
6549
            except errors.NotBranchError:
6603
6550
                active_branch = None
6604
6551
            if (active_branch is not None and
6605
 
                    br.control_url == active_branch.control_url):
6606
 
                raise errors.CommandError(
 
6552
                br.control_url == active_branch.control_url):
 
6553
                raise errors.BzrCommandError(
6607
6554
                    gettext("Branch is active. Use --force to remove it."))
6608
6555
        br.controldir.destroy_branch(br.name)
6609
6556
 
6636
6583
    editor program to decide what the file remaining in the working copy
6637
6584
    should look like.  To do this, add the configuration option
6638
6585
 
6639
 
        change_editor = PROGRAM {new_path} {old_path}
 
6586
        change_editor = PROGRAM @new_path @old_path
6640
6587
 
6641
 
    where {new_path} is replaced with the path of the new version of the
6642
 
    file and {old_path} is replaced with the path of the old version of
6643
 
    the file.  The PROGRAM should save the new file with the desired
 
6588
    where @new_path is replaced with the path of the new version of the 
 
6589
    file and @old_path is replaced with the path of the old version of 
 
6590
    the file.  The PROGRAM should save the new file with the desired 
6644
6591
    contents of the file in the working tree.
6645
 
 
 
6592
        
6646
6593
    """
6647
6594
 
6648
6595
    takes_args = ['file*']
6671
6618
            writer = breezy.option.diff_writer_registry.get()
6672
6619
        try:
6673
6620
            shelver = Shelver.from_args(writer(self.outf), revision, all,
6674
 
                                        file_list, message, destroy=destroy, directory=directory)
 
6621
                file_list, message, destroy=destroy, directory=directory)
6675
6622
            try:
6676
6623
                shelver.run()
6677
6624
            finally:
6683
6630
        if directory is None:
6684
6631
            directory = u'.'
6685
6632
        tree = WorkingTree.open_containing(directory)[0]
6686
 
        self.enter_context(tree.lock_read())
 
6633
        self.add_cleanup(tree.lock_read().unlock)
6687
6634
        manager = tree.get_shelf_manager()
6688
6635
        shelves = manager.active_shelves()
6689
6636
        if len(shelves) == 0:
6690
6637
            note(gettext('No shelved changes.'))
6691
6638
            return 0
6692
6639
        for shelf_id in reversed(shelves):
6693
 
            message = manager.get_metadata(shelf_id).get(b'message')
 
6640
            message = manager.get_metadata(shelf_id).get('message')
6694
6641
            if message is None:
6695
6642
                message = '<no message>'
6696
6643
            self.outf.write('%3d: %s\n' % (shelf_id, message))
6754
6701
                     Option('dry-run', help='Show files to delete instead of'
6755
6702
                            ' deleting them.'),
6756
6703
                     Option('force', help='Do not prompt before deleting.')]
6757
 
 
6758
6704
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
6759
6705
            force=False, directory=u'.'):
6760
6706
        from .clean_tree import clean_tree
6777
6723
    hidden = True
6778
6724
 
6779
6725
    takes_args = ['path?', 'location?']
6780
 
    takes_options = [
6781
 
        'directory',
6782
 
        Option('force-unversioned',
6783
 
               help='Set reference even if path is not versioned.'),
6784
 
        ]
6785
6726
 
6786
 
    def run(self, path=None, directory='.', location=None, force_unversioned=False):
6787
 
        tree, branch, relpath = (
6788
 
            controldir.ControlDir.open_containing_tree_or_branch(directory))
 
6727
    def run(self, path=None, location=None):
 
6728
        branchdir = '.'
 
6729
        if path is not None:
 
6730
            branchdir = path
 
6731
        tree, branch, relpath =(
 
6732
            controldir.ControlDir.open_containing_tree_or_branch(branchdir))
 
6733
        if path is not None:
 
6734
            path = relpath
6789
6735
        if tree is None:
6790
6736
            tree = branch.basis_tree()
6791
6737
        if path is None:
6792
 
            with tree.lock_read():
6793
 
                info = [
6794
 
                    (path, tree.get_reference_info(path, branch))
6795
 
                    for path in tree.iter_references()]
6796
 
                self._display_reference_info(tree, branch, info)
 
6738
            info = viewitems(branch._get_all_reference_info())
 
6739
            self._display_reference_info(tree, branch, info)
6797
6740
        else:
6798
 
            if not tree.is_versioned(path) and not force_unversioned:
 
6741
            file_id = tree.path2id(path)
 
6742
            if file_id is None:
6799
6743
                raise errors.NotVersionedError(path)
6800
6744
            if location is None:
6801
 
                info = [(path, tree.get_reference_info(path, branch))]
 
6745
                info = [(file_id, branch.get_reference_info(file_id))]
6802
6746
                self._display_reference_info(tree, branch, info)
6803
6747
            else:
6804
 
                tree.set_reference_info(path, location)
 
6748
                branch.set_reference_info(file_id, path, location)
6805
6749
 
6806
6750
    def _display_reference_info(self, tree, branch, info):
6807
6751
        ref_list = []
6808
 
        for path, location in info:
 
6752
        for file_id, (path, location) in info:
 
6753
            try:
 
6754
                path = tree.id2path(file_id)
 
6755
            except errors.NoSuchId:
 
6756
                pass
6809
6757
            ref_list.append((path, location))
6810
6758
        for path, location in sorted(ref_list):
6811
6759
            self.outf.write('%s %s\n' % (path, location))
6815
6763
    __doc__ = """Export command helps and error messages in po format."""
6816
6764
 
6817
6765
    hidden = True
6818
 
    takes_options = [Option('plugin',
6819
 
                            help='Export help text from named command '
 
6766
    takes_options = [Option('plugin', 
 
6767
                            help='Export help text from named command '\
6820
6768
                                 '(defaults to all built in commands).',
6821
 
                            type=str),
 
6769
                            type=text_type),
6822
6770
                     Option('include-duplicates',
6823
6771
                            help='Output multiple copies of the same msgid '
6824
6772
                                 'string if it appears more than once.'),
6825
 
                     ]
 
6773
                            ]
6826
6774
 
6827
6775
    def run(self, plugin=None, include_duplicates=False):
6828
6776
        from .export_pot import export_pot
6862
6810
        from .transform import link_tree
6863
6811
        target_tree = WorkingTree.open_containing(".")[0]
6864
6812
        source_tree = WorkingTree.open(location)
6865
 
        with target_tree.lock_write(), source_tree.lock_read():
6866
 
            link_tree(target_tree, source_tree)
 
6813
        target_tree.lock_write()
 
6814
        try:
 
6815
            source_tree.lock_read()
 
6816
            try:
 
6817
                link_tree(target_tree, source_tree)
 
6818
            finally:
 
6819
                source_tree.unlock()
 
6820
        finally:
 
6821
            target_tree.unlock()
6867
6822
 
6868
6823
 
6869
6824
class cmd_fetch_ghosts(Command):
6883
6838
        if len(installed) > 0:
6884
6839
            self.outf.write("Installed:\n")
6885
6840
            for rev in installed:
6886
 
                self.outf.write(rev.decode('utf-8') + "\n")
 
6841
                self.outf.write(rev + "\n")
6887
6842
        if len(failed) > 0:
6888
6843
            self.outf.write("Still missing:\n")
6889
6844
            for rev in failed:
6890
 
                self.outf.write(rev.decode('utf-8') + "\n")
 
6845
                self.outf.write(rev + "\n")
6891
6846
        if not no_fix and len(installed) > 0:
6892
6847
            cmd_reconcile().run(".")
6893
6848
 
6894
6849
 
6895
 
class cmd_grep(Command):
6896
 
    """Print lines matching PATTERN for specified files and revisions.
6897
 
 
6898
 
    This command searches the specified files and revisions for a given
6899
 
    pattern.  The pattern is specified as a Python regular expressions[1].
6900
 
 
6901
 
    If the file name is not specified, the revisions starting with the
6902
 
    current directory are searched recursively. If the revision number is
6903
 
    not specified, the working copy is searched. To search the last committed
6904
 
    revision, use the '-r -1' or '-r last:1' option.
6905
 
 
6906
 
    Unversioned files are not searched unless explicitly specified on the
6907
 
    command line. Unversioned directores are not searched.
6908
 
 
6909
 
    When searching a pattern, the output is shown in the 'filepath:string'
6910
 
    format. If a revision is explicitly searched, the output is shown as
6911
 
    'filepath~N:string', where N is the revision number.
6912
 
 
6913
 
    --include and --exclude options can be used to search only (or exclude
6914
 
    from search) files with base name matches the specified Unix style GLOB
6915
 
    pattern.  The GLOB pattern an use *, ?, and [...] as wildcards, and \\
6916
 
    to quote wildcard or backslash character literally. Note that the glob
6917
 
    pattern is not a regular expression.
6918
 
 
6919
 
    [1] http://docs.python.org/library/re.html#regular-expression-syntax
6920
 
    """
6921
 
 
6922
 
    encoding_type = 'replace'
6923
 
    takes_args = ['pattern', 'path*']
6924
 
    takes_options = [
6925
 
        'verbose',
6926
 
        'revision',
6927
 
        Option('color', type=str, argname='when',
6928
 
               help='Show match in color. WHEN is never, always or auto.'),
6929
 
        Option('diff', short_name='p',
6930
 
               help='Grep for pattern in changeset for each revision.'),
6931
 
        ListOption('exclude', type=str, argname='glob', short_name='X',
6932
 
                   help="Skip files whose base name matches GLOB."),
6933
 
        ListOption('include', type=str, argname='glob', short_name='I',
6934
 
                   help="Search only files whose base name matches GLOB."),
6935
 
        Option('files-with-matches', short_name='l',
6936
 
               help='Print only the name of each input file in '
6937
 
               'which PATTERN is found.'),
6938
 
        Option('files-without-match', short_name='L',
6939
 
               help='Print only the name of each input file in '
6940
 
               'which PATTERN is not found.'),
6941
 
        Option('fixed-string', short_name='F',
6942
 
               help='Interpret PATTERN is a single fixed string (not regex).'),
6943
 
        Option('from-root',
6944
 
               help='Search for pattern starting from the root of the branch. '
6945
 
               '(implies --recursive)'),
6946
 
        Option('ignore-case', short_name='i',
6947
 
               help='Ignore case distinctions while matching.'),
6948
 
        Option('levels',
6949
 
               help='Number of levels to display - 0 for all, 1 for collapsed '
6950
 
               '(1 is default).',
6951
 
               argname='N',
6952
 
               type=_parse_levels),
6953
 
        Option('line-number', short_name='n',
6954
 
               help='Show 1-based line number.'),
6955
 
        Option('no-recursive',
6956
 
               help="Don't recurse into subdirectories. (default is --recursive)"),
6957
 
        Option('null', short_name='Z',
6958
 
               help='Write an ASCII NUL (\\0) separator '
6959
 
               'between output lines rather than a newline.'),
6960
 
        ]
6961
 
 
6962
 
    @display_command
6963
 
    def run(self, verbose=False, ignore_case=False, no_recursive=False,
6964
 
            from_root=False, null=False, levels=None, line_number=False,
6965
 
            path_list=None, revision=None, pattern=None, include=None,
6966
 
            exclude=None, fixed_string=False, files_with_matches=False,
6967
 
            files_without_match=False, color=None, diff=False):
6968
 
        from breezy import _termcolor
6969
 
        from . import grep
6970
 
        import re
6971
 
        if path_list is None:
6972
 
            path_list = ['.']
6973
 
        else:
6974
 
            if from_root:
6975
 
                raise errors.CommandError(
6976
 
                    'cannot specify both --from-root and PATH.')
6977
 
 
6978
 
        if files_with_matches and files_without_match:
6979
 
            raise errors.CommandError(
6980
 
                'cannot specify both '
6981
 
                '-l/--files-with-matches and -L/--files-without-matches.')
6982
 
 
6983
 
        global_config = _mod_config.GlobalConfig()
6984
 
 
6985
 
        if color is None:
6986
 
            color = global_config.get_user_option('grep_color')
6987
 
 
6988
 
        if color is None:
6989
 
            color = 'never'
6990
 
 
6991
 
        if color not in ['always', 'never', 'auto']:
6992
 
            raise errors.CommandError('Valid values for --color are '
6993
 
                                         '"always", "never" or "auto".')
6994
 
 
6995
 
        if levels is None:
6996
 
            levels = 1
6997
 
 
6998
 
        print_revno = False
6999
 
        if revision is not None or levels == 0:
7000
 
            # print revision numbers as we may be showing multiple revisions
7001
 
            print_revno = True
7002
 
 
7003
 
        eol_marker = '\n'
7004
 
        if null:
7005
 
            eol_marker = '\0'
7006
 
 
7007
 
        if not ignore_case and grep.is_fixed_string(pattern):
7008
 
            # if the pattern isalnum, implicitly use to -F for faster grep
7009
 
            fixed_string = True
7010
 
        elif ignore_case and fixed_string:
7011
 
            # GZ 2010-06-02: Fall back to regexp rather than lowercasing
7012
 
            #                pattern and text which will cause pain later
7013
 
            fixed_string = False
7014
 
            pattern = re.escape(pattern)
7015
 
 
7016
 
        patternc = None
7017
 
        re_flags = re.MULTILINE
7018
 
        if ignore_case:
7019
 
            re_flags |= re.IGNORECASE
7020
 
 
7021
 
        if not fixed_string:
7022
 
            patternc = grep.compile_pattern(
7023
 
                pattern.encode(grep._user_encoding), re_flags)
7024
 
 
7025
 
        if color == 'always':
7026
 
            show_color = True
7027
 
        elif color == 'never':
7028
 
            show_color = False
7029
 
        elif color == 'auto':
7030
 
            show_color = _termcolor.allow_color()
7031
 
 
7032
 
        opts = grep.GrepOptions()
7033
 
 
7034
 
        opts.verbose = verbose
7035
 
        opts.ignore_case = ignore_case
7036
 
        opts.no_recursive = no_recursive
7037
 
        opts.from_root = from_root
7038
 
        opts.null = null
7039
 
        opts.levels = levels
7040
 
        opts.line_number = line_number
7041
 
        opts.path_list = path_list
7042
 
        opts.revision = revision
7043
 
        opts.pattern = pattern
7044
 
        opts.include = include
7045
 
        opts.exclude = exclude
7046
 
        opts.fixed_string = fixed_string
7047
 
        opts.files_with_matches = files_with_matches
7048
 
        opts.files_without_match = files_without_match
7049
 
        opts.color = color
7050
 
        opts.diff = False
7051
 
 
7052
 
        opts.eol_marker = eol_marker
7053
 
        opts.print_revno = print_revno
7054
 
        opts.patternc = patternc
7055
 
        opts.recursive = not no_recursive
7056
 
        opts.fixed_string = fixed_string
7057
 
        opts.outf = self.outf
7058
 
        opts.show_color = show_color
7059
 
 
7060
 
        if diff:
7061
 
            # options not used:
7062
 
            # files_with_matches, files_without_match
7063
 
            # levels(?), line_number, from_root
7064
 
            # include, exclude
7065
 
            # These are silently ignored.
7066
 
            grep.grep_diff(opts)
7067
 
        elif revision is None:
7068
 
            grep.workingtree_grep(opts)
7069
 
        else:
7070
 
            grep.versioned_grep(opts)
7071
 
 
7072
 
 
7073
 
class cmd_patch(Command):
7074
 
    """Apply a named patch to the current tree.
7075
 
 
7076
 
    """
7077
 
 
7078
 
    takes_args = ['filename?']
7079
 
    takes_options = [Option('strip', type=int, short_name='p',
7080
 
                            help=("Strip the smallest prefix containing num "
7081
 
                                  "leading slashes from filenames.")),
7082
 
                     Option('silent', help='Suppress chatter.')]
7083
 
 
7084
 
    def run(self, filename=None, strip=None, silent=False):
7085
 
        from .patch import patch_tree
7086
 
        wt = WorkingTree.open_containing('.')[0]
7087
 
        if strip is None:
7088
 
            strip = 1
7089
 
        my_file = None
7090
 
        if filename is None:
7091
 
            my_file = getattr(sys.stdin, 'buffer', sys.stdin)
7092
 
        else:
7093
 
            my_file = open(filename, 'rb')
7094
 
        patches = [my_file.read()]
7095
 
        return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
7096
 
 
7097
 
 
7098
 
class cmd_resolve_location(Command):
7099
 
    __doc__ = """Expand a location to a full URL.
7100
 
 
7101
 
    :Examples:
7102
 
        Look up a Launchpad URL.
7103
 
 
7104
 
            brz resolve-location lp:brz
7105
 
    """
7106
 
    takes_args = ['location']
7107
 
    hidden = True
7108
 
 
7109
 
    def run(self, location):
7110
 
        from .location import location_to_url
7111
 
        url = location_to_url(location)
7112
 
        display_url = urlutils.unescape_for_display(url, self.outf.encoding)
7113
 
        self.outf.write('%s\n' % display_url)
7114
 
 
7115
 
 
7116
6850
def _register_lazy_builtins():
7117
6851
    # register lazy builtins from other modules; called at startup and should
7118
6852
    # be only called once.
7119
6853
    for (name, aliases, module_name) in [
7120
 
            ('cmd_bisect', [], 'breezy.bisect'),
7121
 
            ('cmd_bundle_info', [], 'breezy.bzr.bundle.commands'),
7122
 
            ('cmd_config', [], 'breezy.config'),
7123
 
            ('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
7124
 
            ('cmd_file_id', [], 'breezy.bzr.debug_commands'),
7125
 
            ('cmd_file_path', [], 'breezy.bzr.debug_commands'),
7126
 
            ('cmd_version_info', [], 'breezy.cmd_version_info'),
7127
 
            ('cmd_resolve', ['resolved'], 'breezy.conflicts'),
7128
 
            ('cmd_conflicts', [], 'breezy.conflicts'),
7129
 
            ('cmd_ping', [], 'breezy.bzr.smart.ping'),
7130
 
            ('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
7131
 
            ('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
7132
 
            ('cmd_test_script', [], 'breezy.cmd_test_script'),
7133
 
            ]:
 
6854
        ('cmd_bisect', [], 'breezy.bisect'),
 
6855
        ('cmd_bundle_info', [], 'breezy.bundle.commands'),
 
6856
        ('cmd_config', [], 'breezy.config'),
 
6857
        ('cmd_dpush', [], 'breezy.foreign'),
 
6858
        ('cmd_version_info', [], 'breezy.cmd_version_info'),
 
6859
        ('cmd_resolve', ['resolved'], 'breezy.conflicts'),
 
6860
        ('cmd_conflicts', [], 'breezy.conflicts'),
 
6861
        ('cmd_ping', [], 'breezy.bzr.smart.ping'),
 
6862
        ('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
 
6863
        ('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
 
6864
        ('cmd_test_script', [], 'breezy.cmd_test_script'),
 
6865
        ]:
7134
6866
        builtin_command_registry.register_lazy(name, aliases, module_name)