/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: 2020-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

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
 
 
21
19
import errno
22
20
import os
23
21
import sys
24
22
 
25
23
import breezy.bzr
 
24
import breezy.git
 
25
 
 
26
from . import (
 
27
    errors,
 
28
    )
26
29
 
27
30
from . import lazy_import
28
31
lazy_import.lazy_import(globals(), """
32
35
from breezy import (
33
36
    branch as _mod_branch,
34
37
    bugtracker,
35
 
    bundle,
36
38
    cache_utf8,
37
39
    controldir,
38
40
    directory_service,
39
41
    delta,
40
42
    config as _mod_config,
41
 
    errors,
42
43
    globbing,
 
44
    gpg,
43
45
    hooks,
44
46
    lazy_regex,
45
47
    log,
46
48
    merge as _mod_merge,
 
49
    mergeable as _mod_mergeable,
47
50
    merge_directive,
48
51
    osutils,
49
52
    reconfigure,
50
53
    rename_map,
51
54
    revision as _mod_revision,
52
 
    static_tuple,
53
55
    symbol_versioning,
54
56
    timestamp,
55
57
    transport,
 
58
    tree as _mod_tree,
56
59
    ui,
57
60
    urlutils,
58
61
    views,
59
 
    gpg,
60
 
    )
61
 
from breezy.bzr import (
62
 
    btree_index,
63
62
    )
64
63
from breezy.branch import Branch
65
64
from breezy.conflicts import ConflictList
85
84
    RevisionSpec,
86
85
    RevisionInfo,
87
86
    )
88
 
from .sixish import (
89
 
    BytesIO,
90
 
    text_type,
91
 
    viewitems,
92
 
    viewvalues,
93
 
)
94
87
from .trace import mutter, note, warning, is_quiet, get_verbosity_level
95
88
 
96
89
 
120
113
        and the full URL to the actual branch
121
114
    """
122
115
    # This path is meant to be relative to the existing branch
123
 
    this_url = _get_branch_location(control_dir,
124
 
        possible_transports=possible_transports)
 
116
    this_url = _get_branch_location(
 
117
        control_dir, possible_transports=possible_transports)
125
118
    # Perhaps the target control dir supports colocated branches?
126
119
    try:
127
 
        root = controldir.ControlDir.open(this_url,
128
 
            possible_transports=possible_transports)
 
120
        root = controldir.ControlDir.open(
 
121
            this_url, possible_transports=possible_transports)
129
122
    except errors.NotBranchError:
130
123
        return (False, this_url)
131
124
    else:
132
125
        try:
133
 
            wt = control_dir.open_workingtree()
 
126
            control_dir.open_workingtree()
134
127
        except (errors.NoWorkingTree, errors.NotLocalUrl):
135
128
            return (False, this_url)
136
129
        else:
152
145
        (colocated, this_url) = _is_colocated(control_dir, possible_transports)
153
146
 
154
147
        if colocated:
155
 
            return urlutils.join_segment_parameters(this_url,
156
 
                {"branch": urlutils.escape(location)})
 
148
            return urlutils.join_segment_parameters(
 
149
                this_url, {"branch": urlutils.escape(location)})
157
150
        else:
158
151
            return urlutils.join(this_url, '..', urlutils.escape(location))
159
152
    return location
169
162
    """
170
163
    try:
171
164
        # Perhaps it's a colocated branch?
172
 
        return control_dir.open_branch(location, 
173
 
            possible_transports=possible_transports)
 
165
        return control_dir.open_branch(
 
166
            location, possible_transports=possible_transports)
174
167
    except (errors.NotBranchError, errors.NoColocatedBranchSupport):
175
168
        this_url = _get_branch_location(control_dir)
176
169
        return Branch.open(
189
182
        if location is None:
190
183
            location = "."
191
184
        try:
192
 
            return Branch.open(location,
193
 
                possible_transports=possible_transports)
 
185
            return Branch.open(
 
186
                location, possible_transports=possible_transports)
194
187
        except errors.NotBranchError:
195
188
            near = "."
196
 
    cdir = controldir.ControlDir.open(near,
197
 
        possible_transports=possible_transports)
198
 
    return open_sibling_branch(cdir, location,
199
 
        possible_transports=possible_transports)
 
189
    cdir = controldir.ControlDir.open(
 
190
        near, possible_transports=possible_transports)
 
191
    return open_sibling_branch(
 
192
        cdir, location, possible_transports=possible_transports)
200
193
 
201
194
 
202
195
def iter_sibling_branches(control_dir, possible_transports=None):
205
198
    :param control_dir: Control directory for which to look up the siblings
206
199
    :return: Iterator over tuples with branch name and branch object
207
200
    """
208
 
    seen_urls = set()
209
201
    try:
210
202
        reference = control_dir.get_branch_reference()
211
203
    except errors.NotBranchError:
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
 
204
        reference = None
216
205
    if reference is not None:
217
 
        ref_branch = Branch.open(reference,
218
 
            possible_transports=possible_transports)
 
206
        try:
 
207
            ref_branch = Branch.open(
 
208
                reference, possible_transports=possible_transports)
 
209
        except errors.NotBranchError:
 
210
            ref_branch = None
219
211
    else:
220
212
        ref_branch = None
221
213
    if ref_branch is None or ref_branch.name:
222
214
        if ref_branch is not None:
223
215
            control_dir = ref_branch.controldir
224
 
        for name, branch in viewitems(control_dir.get_branches()):
 
216
        for name, branch in control_dir.get_branches().items():
225
217
            yield name, branch
226
218
    else:
227
219
        repo = ref_branch.controldir.find_repository()
228
220
        for branch in repo.find_branches(using=True):
229
 
            name = urlutils.relative_url(repo.user_url,
230
 
                branch.user_url).rstrip("/")
 
221
            name = urlutils.relative_url(
 
222
                repo.user_url, branch.user_url).rstrip("/")
231
223
            yield name, branch
232
224
 
233
225
 
259
251
            if view_files:
260
252
                file_list = view_files
261
253
                view_str = views.view_display_str(view_files)
262
 
                note(gettext("Ignoring files outside view. View is %s") % view_str)
 
254
                note(gettext("Ignoring files outside view. View is %s"),
 
255
                     view_str)
263
256
    return tree, file_list
264
257
 
265
258
 
275
268
 
276
269
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
277
270
    """Get a revision tree. Not suitable for commands that change the tree.
278
 
    
 
271
 
279
272
    Specifically, the basis tree in dirstate trees is coupled to the dirstate
280
273
    and doing a commit/uncommit/pull will at best fail due to changing the
281
274
    basis revision data.
350
343
        Not versioned and not matching an ignore pattern.
351
344
 
352
345
    Additionally for directories, symlinks and files with a changed
353
 
    executable bit, Bazaar indicates their type using a trailing
 
346
    executable bit, Breezy indicates their type using a trailing
354
347
    character: '/', '@' or '*' respectively. These decorations can be
355
348
    disabled using the '--no-classify' option.
356
349
 
387
380
                            short_name='S'),
388
381
                     Option('versioned', help='Only show versioned files.',
389
382
                            short_name='V'),
390
 
                     Option('no-pending', help='Don\'t show pending merges.',
391
 
                           ),
 
383
                     Option('no-pending', help='Don\'t show pending merges.'),
392
384
                     Option('no-classify',
393
 
                            help='Do not mark object type using indicator.',
394
 
                           ),
 
385
                            help='Do not mark object type using indicator.'),
395
386
                     ]
396
387
    aliases = ['st', 'stat']
397
388
 
405
396
        from .status import show_tree_status
406
397
 
407
398
        if revision and len(revision) > 2:
408
 
            raise errors.BzrCommandError(gettext('brz status --revision takes exactly'
409
 
                                         ' one or two revision specifiers'))
 
399
            raise errors.BzrCommandError(
 
400
                gettext('brz status --revision takes exactly'
 
401
                        ' one or two revision specifiers'))
410
402
 
411
403
        tree, relfile_list = WorkingTree.open_containing_paths(file_list)
412
404
        # Avoid asking for specific files when that is not needed.
450
442
    def run(self, revision_id=None, revision=None, directory=u'.'):
451
443
        if revision_id is not None and revision is not None:
452
444
            raise errors.BzrCommandError(gettext('You can only supply one of'
453
 
                                         ' revision_id or --revision'))
 
445
                                                 ' revision_id or --revision'))
454
446
        if revision_id is None and revision is None:
455
 
            raise errors.BzrCommandError(gettext('You must supply either'
456
 
                                         ' --revision or a revision_id'))
 
447
            raise errors.BzrCommandError(
 
448
                gettext('You must supply either --revision or a revision_id'))
457
449
 
458
450
        b = controldir.ControlDir.open_containing_tree_or_branch(directory)[1]
459
451
 
460
 
        revisions = b.repository.revisions
 
452
        revisions = getattr(b.repository, "revisions", None)
461
453
        if revisions is None:
462
 
            raise errors.BzrCommandError(gettext('Repository %r does not support '
463
 
                'access to raw revision texts'))
 
454
            raise errors.BzrCommandError(
 
455
                gettext('Repository %r does not support '
 
456
                        'access to raw revision texts') % b.repository)
464
457
 
465
458
        with b.repository.lock_read():
466
459
            # TODO: jam 20060112 should cat-revision always output utf-8?
469
462
                try:
470
463
                    self.print_revision(revisions, revision_id)
471
464
                except errors.NoSuchRevision:
472
 
                    msg = gettext("The repository {0} contains no revision {1}.").format(
473
 
                        b.repository.base, revision_id)
 
465
                    msg = gettext(
 
466
                        "The repository {0} contains no revision {1}.").format(
 
467
                            b.repository.base, revision_id.decode('utf-8'))
474
468
                    raise errors.BzrCommandError(msg)
475
469
            elif revision is not None:
476
470
                for rev in revision:
481
475
                    self.print_revision(revisions, rev_id)
482
476
 
483
477
 
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
 
 
568
478
class cmd_remove_tree(Command):
569
479
    __doc__ = """Remove the working tree from a given branch/checkout.
570
480
 
583
493
 
584
494
    def run(self, location_list, force=False):
585
495
        if not location_list:
586
 
            location_list=['.']
 
496
            location_list = ['.']
587
497
 
588
498
        for location in location_list:
589
499
            d = controldir.ControlDir.open(location)
591
501
            try:
592
502
                working = d.open_workingtree()
593
503
            except errors.NoWorkingTree:
594
 
                raise errors.BzrCommandError(gettext("No working tree to remove"))
 
504
                raise errors.BzrCommandError(
 
505
                    gettext("No working tree to remove"))
595
506
            except errors.NotLocalUrl:
596
 
                raise errors.BzrCommandError(gettext("You cannot remove the working tree"
597
 
                                             " of a remote path"))
 
507
                raise errors.BzrCommandError(
 
508
                    gettext("You cannot remove the working tree"
 
509
                            " of a remote path"))
598
510
            if not force:
599
511
                if (working.has_changes()):
600
512
                    raise errors.UncommittedChanges(working)
602
514
                    raise errors.ShelvedChanges(working)
603
515
 
604
516
            if working.user_url != working.branch.user_url:
605
 
                raise errors.BzrCommandError(gettext("You cannot remove the working tree"
606
 
                                             " from a lightweight checkout"))
 
517
                raise errors.BzrCommandError(
 
518
                    gettext("You cannot remove the working tree"
 
519
                            " from a lightweight checkout"))
607
520
 
608
521
            d.destroy_workingtree()
609
522
 
624
537
    that, you can supply --revision to force the state of the tree.
625
538
    """
626
539
 
627
 
    takes_options = ['revision', 'directory',
 
540
    takes_options = [
 
541
        'revision', 'directory',
628
542
        Option('force',
629
543
               help='Reset the tree even if it doesn\'t appear to be'
630
544
                    ' corrupted.'),
633
547
 
634
548
    def run(self, revision=None, directory='.', force=False):
635
549
        tree, _ = WorkingTree.open_containing(directory)
636
 
        self.add_cleanup(tree.lock_tree_write().unlock)
 
550
        self.enter_context(tree.lock_tree_write())
637
551
        if not force:
638
552
            try:
639
553
                tree.check_state()
640
554
            except errors.BzrError:
641
 
                pass # There seems to be a real error here, so we'll reset
 
555
                pass  # There seems to be a real error here, so we'll reset
642
556
            else:
643
557
                # Refuse
644
558
                raise errors.BzrCommandError(gettext(
651
565
            revision_ids = [r.as_revision_id(tree.branch) for r in revision]
652
566
        try:
653
567
            tree.reset_state(revision_ids)
654
 
        except errors.BzrError as e:
 
568
        except errors.BzrError:
655
569
            if revision_ids is None:
656
 
                extra = (gettext(', the header appears corrupt, try passing -r -1'
657
 
                         ' to set the state to the last commit'))
 
570
                extra = gettext(', the header appears corrupt, try passing '
 
571
                                '-r -1 to set the state to the last commit')
658
572
            else:
659
573
                extra = ''
660
 
            raise errors.BzrCommandError(gettext('failed to reset the tree state{0}').format(extra))
 
574
            raise errors.BzrCommandError(
 
575
                gettext('failed to reset the tree state{0}').format(extra))
661
576
 
662
577
 
663
578
class cmd_revno(Command):
676
591
    @display_command
677
592
    def run(self, tree=False, location=u'.', revision=None):
678
593
        if revision is not None and tree:
679
 
            raise errors.BzrCommandError(gettext("--tree and --revision can "
680
 
                "not be used together"))
 
594
            raise errors.BzrCommandError(
 
595
                gettext("--tree and --revision can not be used together"))
681
596
 
682
597
        if tree:
683
598
            try:
684
599
                wt = WorkingTree.open_containing(location)[0]
685
 
                self.add_cleanup(wt.lock_read().unlock)
 
600
                self.enter_context(wt.lock_read())
686
601
            except (errors.NoWorkingTree, errors.NotLocalUrl):
687
602
                raise errors.NoWorkingTree(location)
688
603
            b = wt.branch
689
604
            revid = wt.last_revision()
690
605
        else:
691
606
            b = Branch.open_containing(location)[0]
692
 
            self.add_cleanup(b.lock_read().unlock)
 
607
            self.enter_context(b.lock_read())
693
608
            if revision:
694
609
                if len(revision) != 1:
695
610
                    raise errors.BzrCommandError(gettext(
700
615
                revid = b.last_revision()
701
616
        try:
702
617
            revno_t = b.revision_id_to_dotted_revno(revid)
703
 
        except errors.NoSuchRevision:
 
618
        except (errors.NoSuchRevision, errors.GhostRevisionsHaveNoRevno):
704
619
            revno_t = ('???',)
705
620
        revno = ".".join(str(n) for n in revno_t)
706
621
        self.cleanup_now()
714
629
    takes_args = ['revision_info*']
715
630
    takes_options = [
716
631
        'revision',
717
 
        custom_help('directory',
718
 
            help='Branch to examine, '
719
 
                 'rather than the one containing the working directory.'),
 
632
        custom_help('directory', help='Branch to examine, '
 
633
                    'rather than the one containing the working directory.'),
720
634
        Option('tree', help='Show revno of working tree.'),
721
635
        ]
722
636
 
727
641
        try:
728
642
            wt = WorkingTree.open_containing(directory)[0]
729
643
            b = wt.branch
730
 
            self.add_cleanup(wt.lock_read().unlock)
 
644
            self.enter_context(wt.lock_read())
731
645
        except (errors.NoWorkingTree, errors.NotLocalUrl):
732
646
            wt = None
733
647
            b = Branch.open_containing(directory)[0]
734
 
            self.add_cleanup(b.lock_read().unlock)
 
648
            self.enter_context(b.lock_read())
735
649
        revision_ids = []
736
650
        if revision is not None:
737
651
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
757
671
            except errors.NoSuchRevision:
758
672
                revno = '???'
759
673
            maxlen = max(maxlen, len(revno))
760
 
            revinfos.append([revno, revision_id])
 
674
            revinfos.append((revno, revision_id))
761
675
 
762
676
        self.cleanup_now()
763
 
        for ri in revinfos:
764
 
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
 
677
        for revno, revid in revinfos:
 
678
            self.outf.write(
 
679
                '%*s %s\n' % (maxlen, revno, revid.decode('utf-8')))
765
680
 
766
681
 
767
682
class cmd_add(Command):
800
715
    branches that will be merged later (without showing the two different
801
716
    adds as a conflict). It is also useful when merging another project
802
717
    into a subdirectory of this one.
803
 
    
 
718
 
804
719
    Any files matching patterns in the ignore list will not be added
805
720
    unless they are explicitly mentioned.
806
 
    
807
 
    In recursive mode, files larger than the configuration option 
 
721
 
 
722
    In recursive mode, files larger than the configuration option
808
723
    add.maximum_file_size will be skipped. Named items are never skipped due
809
724
    to file size.
810
725
    """
814
729
               help="Don't recursively add the contents of directories.",
815
730
               short_name='N'),
816
731
        Option('dry-run',
817
 
               help="Show what would be done, but don't actually do anything."),
 
732
               help="Show what would be done, but don't actually do "
 
733
                    "anything."),
818
734
        'verbose',
819
735
        Option('file-ids-from',
820
 
               type=text_type,
 
736
               type=str,
821
737
               help='Lookup file ids from this tree.'),
822
738
        ]
823
739
    encoding_type = 'replace'
826
742
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
827
743
            file_ids_from=None):
828
744
        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
829
752
 
830
753
        base_tree = None
831
754
        if file_ids_from is not None:
832
755
            try:
833
756
                base_tree, base_path = WorkingTree.open_containing(
834
 
                                            file_ids_from)
 
757
                    file_ids_from)
835
758
            except errors.NoWorkingTree:
836
759
                base_branch, base_path = Branch.open_containing(
837
 
                                            file_ids_from)
 
760
                    file_ids_from)
838
761
                base_tree = base_branch.basis_tree()
839
762
 
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,
 
763
            action = breezy.add.AddFromBaseAction(
 
764
                base_tree, base_path, to_file=self.outf,
844
765
                should_print=(not is_quiet()))
 
766
        else:
 
767
            action = breezy.add.AddWithSkipLargeAction(
 
768
                to_file=self.outf, should_print=(not is_quiet()))
845
769
 
846
770
        if base_tree:
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)
 
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)
851
774
        self.cleanup_now()
852
775
        if len(ignored) > 0:
853
776
            if verbose:
854
777
                for glob in sorted(ignored):
855
778
                    for path in ignored[glob]:
856
779
                        self.outf.write(
857
 
                         gettext("ignored {0} matching \"{1}\"\n").format(
858
 
                         path, glob))
 
780
                            gettext("ignored {0} matching \"{1}\"\n").format(
 
781
                                path, glob))
859
782
 
860
783
 
861
784
class cmd_mkdir(Command):
935
858
    takes_options = [
936
859
        'revision',
937
860
        'show-ids',
 
861
        Option('include-root',
 
862
               help='Include the entry for the root of the tree, if any.'),
938
863
        Option('kind',
939
 
               help='List entries of a particular kind: file, directory, symlink.',
940
 
               type=text_type),
 
864
               help='List entries of a particular kind: file, directory, '
 
865
                    'symlink.',
 
866
               type=str),
941
867
        ]
942
868
    takes_args = ['file*']
943
869
 
944
870
    @display_command
945
 
    def run(self, revision=None, show_ids=False, kind=None, file_list=None):
 
871
    def run(self, revision=None, show_ids=False, kind=None, include_root=False,
 
872
            file_list=None):
946
873
        if kind and kind not in ['file', 'directory', 'symlink']:
947
 
            raise errors.BzrCommandError(gettext('invalid kind %r specified') % (kind,))
 
874
            raise errors.BzrCommandError(
 
875
                gettext('invalid kind %r specified') % (kind,))
948
876
 
949
877
        revision = _get_one_revision('inventory', revision)
950
878
        work_tree, file_list = WorkingTree.open_containing_paths(file_list)
951
 
        self.add_cleanup(work_tree.lock_read().unlock)
 
879
        self.enter_context(work_tree.lock_read())
952
880
        if revision is not None:
953
881
            tree = revision.as_tree(work_tree.branch)
954
882
 
955
883
            extra_trees = [work_tree]
956
 
            self.add_cleanup(tree.lock_read().unlock)
 
884
            self.enter_context(tree.lock_read())
957
885
        else:
958
886
            tree = work_tree
959
887
            extra_trees = []
960
888
 
961
 
        self.add_cleanup(tree.lock_read().unlock)
 
889
        self.enter_context(tree.lock_read())
962
890
        if file_list is not None:
963
 
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
964
 
                                      require_versioned=True)
 
891
            paths = tree.find_related_paths_across_trees(
 
892
                file_list, extra_trees, require_versioned=True)
965
893
            # find_ids_across_trees may include some paths that don't
966
894
            # exist in 'tree'.
967
 
            entries = tree.iter_entries_by_dir(specific_file_ids=file_ids)
 
895
            entries = tree.iter_entries_by_dir(specific_files=paths)
968
896
        else:
969
897
            entries = tree.iter_entries_by_dir()
970
898
 
971
899
        for path, entry in sorted(entries):
972
900
            if kind and kind != entry.kind:
973
901
                continue
974
 
            if path == "":
 
902
            if path == "" and not include_root:
975
903
                continue
976
904
            if show_ids:
977
 
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
 
905
                self.outf.write('%-50s %s\n' % (
 
906
                    path, entry.file_id.decode('utf-8')))
978
907
            else:
979
908
                self.outf.write(path)
980
909
                self.outf.write('\n')
1002
931
    encoding_type = 'replace'
1003
932
 
1004
933
    def run(self, names_list):
1005
 
        import shutil
1006
934
        if names_list is None:
1007
935
            names_list = []
1008
936
        if len(names_list) < 2:
1009
937
            raise errors.BzrCommandError(gettext("missing file argument"))
1010
 
        tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
 
938
        tree, rel_names = WorkingTree.open_containing_paths(
 
939
            names_list, canonicalize=False)
1011
940
        for file_name in rel_names[0:-1]:
1012
941
            if file_name == '':
1013
 
                raise errors.BzrCommandError(gettext("can not copy root of branch"))
1014
 
        self.add_cleanup(tree.lock_tree_write().unlock)
 
942
                raise errors.BzrCommandError(
 
943
                    gettext("can not copy root of branch"))
 
944
        self.enter_context(tree.lock_tree_write())
1015
945
        into_existing = osutils.isdir(names_list[-1])
1016
946
        if not into_existing:
1017
947
            try:
1018
948
                (src, dst) = rel_names
1019
949
            except IndexError:
1020
 
                raise errors.BzrCommandError(gettext('to copy multiple files the'
1021
 
                                                     ' destination must be a versioned'
1022
 
                                                     ' directory'))
 
950
                raise errors.BzrCommandError(
 
951
                    gettext('to copy multiple files the'
 
952
                            ' destination must be a versioned'
 
953
                            ' directory'))
1023
954
            pairs = [(src, dst)]
1024
955
        else:
1025
 
            pairs = [(n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
1026
 
                     for n in rel_names[:-1]]
 
956
            pairs = [
 
957
                (n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
 
958
                for n in rel_names[:-1]]
1027
959
 
1028
960
        for src, dst in pairs:
1029
961
            try:
1030
962
                src_kind = tree.stored_kind(src)
1031
963
            except errors.NoSuchFile:
1032
964
                raise errors.BzrCommandError(
1033
 
                        gettext('Could not copy %s => %s: %s is not versioned.')
1034
 
                        % (src, dst, src))
 
965
                    gettext('Could not copy %s => %s: %s is not versioned.')
 
966
                    % (src, dst, src))
1035
967
            if src_kind is None:
1036
968
                raise errors.BzrCommandError(
1037
 
                    gettext('Could not copy %s => %s . %s is not versioned\.'
1038
 
                        % (src, dst, src)))
 
969
                    gettext('Could not copy %s => %s . %s is not versioned\\.'
 
970
                            % (src, dst, src)))
1039
971
            if src_kind == 'directory':
1040
972
                raise errors.BzrCommandError(
1041
973
                    gettext('Could not copy %s => %s . %s is a directory.'
1042
 
                        % (src, dst, src)))
 
974
                            % (src, dst, src)))
1043
975
            dst_parent = osutils.split(dst)[0]
1044
976
            if dst_parent != '':
1045
977
                try:
1046
978
                    dst_parent_kind = tree.stored_kind(dst_parent)
1047
979
                except errors.NoSuchFile:
1048
980
                    raise errors.BzrCommandError(
1049
 
                            gettext('Could not copy %s => %s: %s is not versioned.')
1050
 
                            % (src, dst, dst_parent))
 
981
                        gettext('Could not copy %s => %s: %s is not versioned.')
 
982
                        % (src, dst, dst_parent))
1051
983
                if dst_parent_kind != 'directory':
1052
984
                    raise errors.BzrCommandError(
1053
 
                            gettext('Could not copy to %s: %s is not a directory.')
1054
 
                            % (dst_parent, dst_parent))
 
985
                        gettext('Could not copy to %s: %s is not a directory.')
 
986
                        % (dst_parent, dst_parent))
1055
987
 
1056
988
            tree.copy_one(src, dst)
1057
989
 
1079
1011
 
1080
1012
    takes_args = ['names*']
1081
1013
    takes_options = [Option("after", help="Move only the brz identifier"
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
 
        ]
 
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
                     ]
1086
1019
    aliases = ['move', 'rename']
1087
1020
    encoding_type = 'replace'
1088
1021
 
1095
1028
            names_list = []
1096
1029
        if len(names_list) < 2:
1097
1030
            raise errors.BzrCommandError(gettext("missing file argument"))
1098
 
        tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
 
1031
        tree, rel_names = WorkingTree.open_containing_paths(
 
1032
            names_list, canonicalize=False)
1099
1033
        for file_name in rel_names[0:-1]:
1100
1034
            if file_name == '':
1101
 
                raise errors.BzrCommandError(gettext("can not move root of branch"))
1102
 
        self.add_cleanup(tree.lock_tree_write().unlock)
 
1035
                raise errors.BzrCommandError(
 
1036
                    gettext("can not move root of branch"))
 
1037
        self.enter_context(tree.lock_tree_write())
1103
1038
        self._run(tree, names_list, rel_names, after)
1104
1039
 
1105
1040
    def run_auto(self, names_list, after, dry_run):
1106
1041
        if names_list is not None and len(names_list) > 1:
1107
 
            raise errors.BzrCommandError(gettext('Only one path may be specified to'
1108
 
                                         ' --auto.'))
 
1042
            raise errors.BzrCommandError(
 
1043
                gettext('Only one path may be specified to --auto.'))
1109
1044
        if after:
1110
 
            raise errors.BzrCommandError(gettext('--after cannot be specified with'
1111
 
                                         ' --auto.'))
 
1045
            raise errors.BzrCommandError(
 
1046
                gettext('--after cannot be specified with --auto.'))
1112
1047
        work_tree, file_list = WorkingTree.open_containing_paths(
1113
1048
            names_list, default_directory='.')
1114
 
        self.add_cleanup(work_tree.lock_tree_write().unlock)
1115
 
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
1049
        self.enter_context(work_tree.lock_tree_write())
 
1050
        rename_map.RenameMap.guess_renames(
 
1051
            work_tree.basis_tree(), work_tree, dry_run)
1116
1052
 
1117
1053
    def _run(self, tree, names_list, rel_names, after):
1118
1054
        into_existing = osutils.isdir(names_list[-1])
1123
1059
            #    a directory, but now doesn't exist in the working tree
1124
1060
            #    and the target is an existing directory, just rename it)
1125
1061
            if (not tree.case_sensitive
1126
 
                and rel_names[0].lower() == rel_names[1].lower()):
 
1062
                    and rel_names[0].lower() == rel_names[1].lower()):
1127
1063
                into_existing = False
1128
1064
            else:
1129
1065
                # 'fix' the case of a potential 'from'
1130
 
                from_path = tree.get_canonical_inventory_path(rel_names[0])
1131
 
                from_id = tree.path2id(from_path)
 
1066
                from_path = tree.get_canonical_path(rel_names[0])
1132
1067
                if (not osutils.lexists(names_list[0]) and
1133
 
                    from_id and
1134
 
                    tree.stored_kind(from_path, from_id) == "directory"):
 
1068
                    tree.is_versioned(from_path) and
 
1069
                        tree.stored_kind(from_path) == "directory"):
1135
1070
                    into_existing = False
1136
1071
        # move/rename
1137
1072
        if into_existing:
1138
1073
            # move into existing directory
1139
1074
            # All entries reference existing inventory items, so fix them up
1140
1075
            # for cicp file-systems.
1141
 
            rel_names = tree.get_canonical_inventory_paths(rel_names)
 
1076
            rel_names = list(tree.get_canonical_paths(rel_names))
1142
1077
            for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
1143
1078
                if not is_quiet():
1144
1079
                    self.outf.write("%s => %s\n" % (src, dest))
1145
1080
        else:
1146
1081
            if len(names_list) != 2:
1147
1082
                raise errors.BzrCommandError(gettext('to mv multiple files the'
1148
 
                                             ' destination must be a versioned'
1149
 
                                             ' directory'))
 
1083
                                                     ' destination must be a versioned'
 
1084
                                                     ' directory'))
1150
1085
 
1151
1086
            # for cicp file-systems: the src references an existing inventory
1152
1087
            # item:
1153
 
            src = tree.get_canonical_inventory_path(rel_names[0])
 
1088
            src = tree.get_canonical_path(rel_names[0])
1154
1089
            # Find the canonical version of the destination:  In all cases, the
1155
1090
            # parent of the target must be in the inventory, so we fetch the
1156
1091
            # canonical version from there (we do not always *use* the
1157
1092
            # canonicalized tail portion - we may be attempting to rename the
1158
1093
            # case of the tail)
1159
 
            canon_dest = tree.get_canonical_inventory_path(rel_names[1])
 
1094
            canon_dest = tree.get_canonical_path(rel_names[1])
1160
1095
            dest_parent = osutils.dirname(canon_dest)
1161
1096
            spec_tail = osutils.basename(rel_names[1])
1162
1097
            # For a CICP file-system, we need to avoid creating 2 inventory
1173
1108
                if after:
1174
1109
                    # If 'after' is specified, the tail must refer to a file on disk.
1175
1110
                    if dest_parent:
1176
 
                        dest_parent_fq = osutils.pathjoin(tree.basedir, dest_parent)
 
1111
                        dest_parent_fq = osutils.pathjoin(
 
1112
                            tree.basedir, dest_parent)
1177
1113
                    else:
1178
1114
                        # pathjoin with an empty tail adds a slash, which breaks
1179
1115
                        # relpath :(
1180
1116
                        dest_parent_fq = tree.basedir
1181
1117
 
1182
1118
                    dest_tail = osutils.canonical_relpath(
1183
 
                                    dest_parent_fq,
1184
 
                                    osutils.pathjoin(dest_parent_fq, spec_tail))
 
1119
                        dest_parent_fq,
 
1120
                        osutils.pathjoin(dest_parent_fq, spec_tail))
1185
1121
                else:
1186
1122
                    # not 'after', so case as specified is used
1187
1123
                    dest_tail = spec_tail
1199
1135
    __doc__ = """Turn this branch into a mirror of another branch.
1200
1136
 
1201
1137
    By default, this command only works on branches that have not diverged.
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 
 
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
1204
1140
    parent.
1205
1141
 
1206
1142
    If branches have diverged, you can use 'brz merge' to integrate the changes
1227
1163
 
1228
1164
    _see_also = ['push', 'update', 'status-flags', 'send']
1229
1165
    takes_options = ['remember', 'overwrite', 'revision',
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
 
        ]
 
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
                     ]
1245
1181
    takes_args = ['location?']
1246
1182
    encoding_type = 'replace'
1247
1183
 
1264
1200
        try:
1265
1201
            tree_to = WorkingTree.open_containing(directory)[0]
1266
1202
            branch_to = tree_to.branch
1267
 
            self.add_cleanup(tree_to.lock_write().unlock)
 
1203
            self.enter_context(tree_to.lock_write())
1268
1204
        except errors.NoWorkingTree:
1269
1205
            tree_to = None
1270
1206
            branch_to = Branch.open_containing(directory)[0]
1271
 
            self.add_cleanup(branch_to.lock_write().unlock)
 
1207
            self.enter_context(branch_to.lock_write())
1272
1208
            if show_base:
1273
1209
                warning(gettext("No working tree, ignoring --show-base"))
1274
1210
 
1278
1214
        possible_transports = []
1279
1215
        if location is not None:
1280
1216
            try:
1281
 
                mergeable = bundle.read_mergeable_from_url(location,
1282
 
                    possible_transports=possible_transports)
 
1217
                mergeable = _mod_mergeable.read_mergeable_from_url(
 
1218
                    location, possible_transports=possible_transports)
1283
1219
            except errors.NotABundle:
1284
1220
                mergeable = None
1285
1221
 
1287
1223
        if location is None:
1288
1224
            if stored_loc is None:
1289
1225
                raise errors.BzrCommandError(gettext("No pull location known or"
1290
 
                                             " specified."))
 
1226
                                                     " specified."))
1291
1227
            else:
1292
1228
                display_url = urlutils.unescape_for_display(stored_loc,
1293
 
                        self.outf.encoding)
 
1229
                                                            self.outf.encoding)
1294
1230
                if not is_quiet():
1295
 
                    self.outf.write(gettext("Using saved parent location: %s\n") % display_url)
 
1231
                    self.outf.write(
 
1232
                        gettext("Using saved parent location: %s\n") % display_url)
1296
1233
                location = stored_loc
1297
1234
 
1298
1235
        revision = _get_one_revision('pull', revision)
1306
1243
            branch_from = branch_to
1307
1244
        else:
1308
1245
            branch_from = Branch.open(location,
1309
 
                possible_transports=possible_transports)
1310
 
            self.add_cleanup(branch_from.lock_read().unlock)
 
1246
                                      possible_transports=possible_transports)
 
1247
            self.enter_context(branch_from.lock_read())
1311
1248
            # Remembers if asked explicitly or no previous location is set
1312
1249
            if (remember
1313
 
                or (remember is None and branch_to.get_parent() is None)):
 
1250
                    or (remember is None and branch_to.get_parent() is None)):
1314
1251
                # FIXME: This shouldn't be done before the pull
1315
1252
                # succeeds... -- vila 2012-01-02
1316
1253
                branch_to.set_parent(branch_from.base)
1373
1310
 
1374
1311
    _see_also = ['pull', 'update', 'working-trees']
1375
1312
    takes_options = ['remember', 'overwrite', 'verbose', 'revision',
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
 
        ]
 
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
                     ]
1404
1343
    takes_args = ['location?']
1405
1344
    encoding_type = 'replace'
1406
1345
 
1407
1346
    def run(self, location=None, remember=None, overwrite=False,
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):
 
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
1412
1352
        from .push import _show_push_branch
1413
1353
 
1414
1354
        if overwrite:
1436
1376
                more_warning='Uncommitted changes will not be pushed.')
1437
1377
        # Get the stacked_on branch, if any
1438
1378
        if stacked_on is not None:
 
1379
            stacked_on = location_to_url(stacked_on, 'read')
1439
1380
            stacked_on = urlutils.normalize_url(stacked_on)
1440
1381
        elif stacked:
1441
1382
            parent_url = br_from.get_parent()
1462
1403
                        "No push location known or specified. To push to the "
1463
1404
                        "parent branch (at %s), use 'brz push :parent'." %
1464
1405
                        urlutils.unescape_for_display(parent_loc,
1465
 
                            self.outf.encoding)))
 
1406
                                                      self.outf.encoding)))
1466
1407
                else:
1467
1408
                    raise errors.BzrCommandError(gettext(
1468
1409
                        "No push location known or specified."))
1469
1410
            else:
1470
1411
                display_url = urlutils.unescape_for_display(stored_loc,
1471
 
                        self.outf.encoding)
 
1412
                                                            self.outf.encoding)
1472
1413
                note(gettext("Using saved push location: %s") % display_url)
1473
1414
                location = stored_loc
1474
1415
 
1475
1416
        _show_push_branch(br_from, revision_id, location, self.outf,
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)
 
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)
1479
1421
 
1480
1422
 
1481
1423
class cmd_branch(Command):
1495
1437
    _see_also = ['checkout']
1496
1438
    takes_args = ['from_location', 'to_location?']
1497
1439
    takes_options = ['revision',
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
 
        ]
 
1440
                     Option(
 
1441
                         'hardlink', help='Hard-link working tree files where possible.'),
 
1442
                     Option('files-from', type=str,
 
1443
                            help="Get file contents from this tree."),
 
1444
                     Option('no-tree',
 
1445
                            help="Create a branch without a working-tree."),
 
1446
                     Option('switch',
 
1447
                            help="Switch the checkout in the current directory "
 
1448
                            "to the new branch."),
 
1449
                     Option('stacked',
 
1450
                            help='Create a stacked branch referring to the source branch. '
 
1451
                            'The new branch will depend on the availability of the source '
 
1452
                            'branch for all operations.'),
 
1453
                     Option('standalone',
 
1454
                            help='Do not use a shared repository, even if available.'),
 
1455
                     Option('use-existing-dir',
 
1456
                            help='By default branch will fail if the target'
 
1457
                            ' directory exists, but does not already'
 
1458
                            ' have a control directory.  This flag will'
 
1459
                            ' allow branch to proceed.'),
 
1460
                     Option('bind',
 
1461
                            help="Bind new branch to from location."),
 
1462
                     Option('no-recurse-nested',
 
1463
                            help='Do not recursively check out nested trees.'),
 
1464
                     ]
1520
1465
 
1521
1466
    def run(self, from_location, to_location=None, revision=None,
1522
1467
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1523
1468
            use_existing_dir=False, switch=False, bind=False,
1524
 
            files_from=None):
 
1469
            files_from=None, no_recurse_nested=False):
1525
1470
        from breezy import switch as _mod_switch
1526
1471
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1527
1472
            from_location)
 
1473
        if no_recurse_nested:
 
1474
            recurse = 'none'
 
1475
        else:
 
1476
            recurse = 'down'
1528
1477
        if not (hardlink or files_from):
1529
1478
            # accelerator_tree is usually slower because you have to read N
1530
1479
            # files (no readahead, lots of seeks, etc), but allow the user to
1533
1482
        if files_from is not None and files_from != from_location:
1534
1483
            accelerator_tree = WorkingTree.open(files_from)
1535
1484
        revision = _get_one_revision('branch', revision)
1536
 
        self.add_cleanup(br_from.lock_read().unlock)
 
1485
        self.enter_context(br_from.lock_read())
1537
1486
        if revision is not None:
1538
1487
            revision_id = revision.as_revision_id(br_from)
1539
1488
        else:
1542
1491
            # RBC 20060209
1543
1492
            revision_id = br_from.last_revision()
1544
1493
        if to_location is None:
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)
 
1494
            to_location = urlutils.derive_to_location(from_location)
 
1495
        to_transport = transport.get_transport(to_location, purpose='write')
1549
1496
        try:
1550
1497
            to_transport.mkdir('.')
1551
1498
        except errors.FileExists:
1555
1502
            except errors.NotBranchError:
1556
1503
                if not use_existing_dir:
1557
1504
                    raise errors.BzrCommandError(gettext('Target directory "%s" '
1558
 
                        'already exists.') % to_location)
 
1505
                                                         'already exists.') % to_location)
1559
1506
                else:
1560
1507
                    to_dir = None
1561
1508
            else:
1578
1525
                    possible_transports=[to_transport],
1579
1526
                    accelerator_tree=accelerator_tree, hardlink=hardlink,
1580
1527
                    stacked=stacked, force_new_repo=standalone,
1581
 
                    create_tree_if_local=not no_tree, source_branch=br_from)
 
1528
                    create_tree_if_local=not no_tree, source_branch=br_from,
 
1529
                    recurse=recurse)
1582
1530
                branch = to_dir.open_branch(
1583
1531
                    possible_transports=[
1584
1532
                        br_from.controldir.root_transport, to_transport])
1593
1541
            except errors.NoRepositoryPresent:
1594
1542
                to_repo = to_dir.create_repository()
1595
1543
            to_repo.fetch(br_from.repository, revision_id=revision_id)
1596
 
            branch = br_from.sprout(to_dir, revision_id=revision_id)
 
1544
            branch = br_from.sprout(
 
1545
                to_dir, revision_id=revision_id)
1597
1546
        br_from.tags.merge_to(branch.tags)
1598
1547
 
1599
1548
        # If the source branch is stacked, the new branch may
1601
1550
        # We therefore need a try/except here and not just 'if stacked:'
1602
1551
        try:
1603
1552
            note(gettext('Created new stacked branch referring to %s.') %
1604
 
                branch.get_stacked_on_url())
 
1553
                 branch.get_stacked_on_url())
1605
1554
        except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
1606
 
            errors.UnstackableRepositoryFormat) as e:
1607
 
            note(ngettext('Branched %d revision.', 'Branched %d revisions.', branch.revno()) % branch.revno())
 
1555
                errors.UnstackableRepositoryFormat) as e:
 
1556
            revno = branch.revno()
 
1557
            if revno is not None:
 
1558
                note(ngettext('Branched %d revision.',
 
1559
                              'Branched %d revisions.',
 
1560
                              branch.revno()) % revno)
 
1561
            else:
 
1562
                note(gettext('Created new branch.'))
1608
1563
        if bind:
1609
1564
            # Bind to the parent
1610
1565
            parent_branch = Branch.open(from_location)
1615
1570
            wt, _ = WorkingTree.open_containing('.')
1616
1571
            _mod_switch.switch(wt.controldir, branch)
1617
1572
            note(gettext('Switched to branch: %s'),
1618
 
                urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1573
                 urlutils.unescape_for_display(branch.base, 'utf-8'))
1619
1574
 
1620
1575
 
1621
1576
class cmd_branches(Command):
1627
1582
 
1628
1583
    takes_args = ['location?']
1629
1584
    takes_options = [
1630
 
                  Option('recursive', short_name='R',
1631
 
                         help='Recursively scan for branches rather than '
1632
 
                              'just looking in the specified location.')]
 
1585
        Option('recursive', short_name='R',
 
1586
               help='Recursively scan for branches rather than '
 
1587
               'just looking in the specified location.')]
1633
1588
 
1634
1589
    def run(self, location=".", recursive=False):
1635
1590
        if recursive:
1636
 
            t = transport.get_transport(location)
 
1591
            t = transport.get_transport(location, purpose='read')
1637
1592
            if not t.listable():
1638
1593
                raise errors.BzrCommandError(
1639
1594
                    "Can't scan this type of location.")
1652
1607
                if name == "":
1653
1608
                    continue
1654
1609
                active = (active_branch is not None and
1655
 
                          active_branch.base == branch.base)
 
1610
                          active_branch.user_url == branch.user_url)
1656
1611
                names[name] = active
1657
1612
            # Only mention the current branch explicitly if it's not
1658
1613
            # one of the colocated branches
1659
 
            if not any(viewvalues(names)) and active_branch is not None:
 
1614
            if not any(names.values()) and active_branch is not None:
1660
1615
                self.outf.write("* %s\n" % gettext("(default)"))
1661
1616
            for name in sorted(names):
1662
1617
                active = names[name]
1664
1619
                    prefix = "*"
1665
1620
                else:
1666
1621
                    prefix = " "
1667
 
                self.outf.write("%s %s\n" % (
1668
 
                    prefix, name.encode(self.outf.encoding)))
 
1622
                self.outf.write("%s %s\n" % (prefix, name))
1669
1623
 
1670
1624
 
1671
1625
class cmd_checkout(Command):
1672
1626
    __doc__ = """Create a new checkout of an existing branch.
1673
1627
 
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.
 
1628
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree
 
1629
    for the branch found in '.'. This is useful if you have removed the working
 
1630
    tree or if it was never created - i.e. if you pushed the branch to its
 
1631
    current location using SFTP.
1678
1632
 
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.
 
1633
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION
 
1634
    will be used.  In other words, "checkout ../foo/bar" will attempt to create
 
1635
    ./bar.  If the BRANCH_LOCATION has no / or path separator embedded, the
 
1636
    TO_LOCATION is derived from the BRANCH_LOCATION by stripping a leading
 
1637
    scheme or drive identifier, if any. For example, "checkout lp:foo-bar" will
 
1638
    attempt to create ./foo-bar.
1685
1639
 
1686
1640
    To retrieve the branch as of a particular revision, supply the --revision
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.)
 
1641
    parameter, as in "checkout foo/bar -r 5". Note that this will be
 
1642
    immediately out of date [so you cannot commit] but it may be useful (i.e.
 
1643
    to examine old code.)
1690
1644
    """
1691
1645
 
1692
1646
    _see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
1699
1653
                                 "common operations like diff and status without "
1700
1654
                                 "such access, and also support local commits."
1701
1655
                            ),
1702
 
                     Option('files-from', type=text_type,
 
1656
                     Option('files-from', type=str,
1703
1657
                            help="Get file contents from this tree."),
1704
1658
                     Option('hardlink',
1705
1659
                            help='Hard-link working tree files where possible.'
1731
1685
        # if the source and to_location are the same,
1732
1686
        # and there is no working tree,
1733
1687
        # then reconstitute a branch
1734
 
        if (osutils.abspath(to_location) ==
1735
 
            osutils.abspath(branch_location)):
 
1688
        if osutils.abspath(to_location) == osutils.abspath(branch_location):
1736
1689
            try:
1737
1690
                source.controldir.open_workingtree()
1738
1691
            except errors.NoWorkingTree:
1754
1707
    @display_command
1755
1708
    def run(self, dir=u'.'):
1756
1709
        tree = WorkingTree.open_containing(dir)[0]
1757
 
        self.add_cleanup(tree.lock_read().unlock)
 
1710
        self.enter_context(tree.lock_read())
1758
1711
        old_tree = tree.basis_tree()
1759
 
        self.add_cleanup(old_tree.lock_read().unlock)
 
1712
        self.enter_context(old_tree.lock_read())
1760
1713
        renames = []
1761
1714
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
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)
 
1715
        for change in iterator:
 
1716
            if change.path[0] == change.path[1]:
 
1717
                continue
 
1718
            if None in change.path:
 
1719
                continue
 
1720
            renames.append(change.path)
1768
1721
        renames.sort()
1769
1722
        for old_name, new_name in renames:
1770
1723
            self.outf.write("%s => %s\n" % (old_name, new_name))
1775
1728
 
1776
1729
    This will perform a merge of the destination revision (the tip of the
1777
1730
    branch, or the specified revision) into the working tree, and then make
1778
 
    that revision the basis revision for the working tree.  
 
1731
    that revision the basis revision for the working tree.
1779
1732
 
1780
1733
    You can use this to visit an older revision, or to update a working tree
1781
1734
    that is out of date from its branch.
1782
 
    
 
1735
 
1783
1736
    If there are any uncommitted changes in the tree, they will be carried
1784
1737
    across and remain as uncommitted changes after the update.  To discard
1785
1738
    these changes, use 'brz revert'.  The uncommitted changes may conflict
1788
1741
    If the tree's branch is bound to a master branch, brz will also update
1789
1742
    the branch from the master.
1790
1743
 
1791
 
    You cannot update just a single file or directory, because each Bazaar
 
1744
    You cannot update just a single file or directory, because each Breezy
1792
1745
    working tree has just a single basis revision.  If you want to restore a
1793
1746
    file that has been removed locally, use 'brz revert' instead of 'brz
1794
1747
    update'.  If you want to restore a file to its state in a previous
1796
1749
    out the old content of that file to a new location.
1797
1750
 
1798
1751
    The 'dir' argument, if given, must be the location of the root of a
1799
 
    working tree to update.  By default, the working tree that contains the 
 
1752
    working tree to update.  By default, the working tree that contains the
1800
1753
    current working directory is used.
1801
1754
    """
1802
1755
 
1827
1780
            possible_transports=possible_transports)
1828
1781
        if master is not None:
1829
1782
            branch_location = master.base
1830
 
            tree.lock_write()
 
1783
            self.enter_context(tree.lock_write())
1831
1784
        else:
1832
1785
            branch_location = tree.branch.base
1833
 
            tree.lock_tree_write()
1834
 
        self.add_cleanup(tree.unlock)
 
1786
            self.enter_context(tree.lock_tree_write())
1835
1787
        # get rid of the final '/' and be ready for display
1836
1788
        branch_location = urlutils.unescape_for_display(
1837
1789
            branch_location.rstrip('/'),
1851
1803
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1852
1804
            revno = branch.revision_id_to_dotted_revno(revision_id)
1853
1805
            note(gettext("Tree is up to date at revision {0} of branch {1}"
1854
 
                        ).format('.'.join(map(str, revno)), branch_location))
 
1806
                         ).format('.'.join(map(str, revno)), branch_location))
1855
1807
            return 0
1856
1808
        view_info = _get_view_info_for_change_reporter(tree)
1857
1809
        change_reporter = delta._ChangeReporter(
1866
1818
                show_base=show_base)
1867
1819
        except errors.NoSuchRevision as e:
1868
1820
            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))
 
1821
                "branch has no revision %s\n"
 
1822
                "brz update --revision only works"
 
1823
                " for a revision in the branch history")
 
1824
                % (e.revision))
1873
1825
        revno = tree.branch.revision_id_to_dotted_revno(
1874
1826
            _mod_revision.ensure_null(tree.last_revision()))
1875
1827
        note(gettext('Updated to revision {0} of branch {1}').format(
1877
1829
        parent_ids = tree.get_parent_ids()
1878
1830
        if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
1879
1831
            note(gettext('Your local commits will now show as pending merges with '
1880
 
                 "'brz status', and can be committed with 'brz commit'."))
 
1832
                         "'brz status', and can be committed with 'brz commit'."))
1881
1833
        if conflicts != 0:
1882
1834
            return 1
1883
1835
        else:
1931
1883
class cmd_remove(Command):
1932
1884
    __doc__ = """Remove files or directories.
1933
1885
 
1934
 
    This makes Bazaar stop tracking changes to the specified files. Bazaar will
 
1886
    This makes Breezy stop tracking changes to the specified files. Breezy will
1935
1887
    delete them if they can easily be recovered using revert otherwise they
1936
1888
    will be backed up (adding an extension of the form .~#~). If no options or
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.
 
1889
    parameters are given Breezy will scan for files that are being tracked by
 
1890
    Breezy but missing in your tree and stop tracking them for you.
1939
1891
    """
1940
1892
    takes_args = ['file*']
1941
1893
    takes_options = ['verbose',
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
 
        ]
 
1894
                     Option(
 
1895
                         'new', help='Only remove files that have never been committed.'),
 
1896
                     RegistryOption.from_kwargs('file-deletion-strategy',
 
1897
                                                'The file deletion mode to be used.',
 
1898
                                                title='Deletion Strategy', value_switches=True, enum_switch=False,
 
1899
                                                safe='Backup changed files (default).',
 
1900
                                                keep='Delete from brz but leave the working copy.',
 
1901
                                                no_backup='Don\'t backup changed files.'),
 
1902
                     ]
1950
1903
    aliases = ['rm', 'del']
1951
1904
    encoding_type = 'replace'
1952
1905
 
1953
1906
    def run(self, file_list, verbose=False, new=False,
1954
 
        file_deletion_strategy='safe'):
 
1907
            file_deletion_strategy='safe'):
1955
1908
 
1956
1909
        tree, file_list = WorkingTree.open_containing_paths(file_list)
1957
1910
 
1958
1911
        if file_list is not None:
1959
1912
            file_list = [f for f in file_list]
1960
1913
 
1961
 
        self.add_cleanup(tree.lock_write().unlock)
 
1914
        self.enter_context(tree.lock_write())
1962
1915
        # Heuristics should probably all move into tree.remove_smart or
1963
1916
        # some such?
1964
1917
        if new:
1965
1918
            added = tree.changes_from(tree.basis_tree(),
1966
 
                specific_files=file_list).added
1967
 
            file_list = sorted([f[0] for f in added], reverse=True)
 
1919
                                      specific_files=file_list).added
 
1920
            file_list = sorted([f.path[1] for f in added], reverse=True)
1968
1921
            if len(file_list) == 0:
1969
1922
                raise errors.BzrCommandError(gettext('No matching files.'))
1970
1923
        elif file_list is None:
1973
1926
            missing = []
1974
1927
            for change in tree.iter_changes(tree.basis_tree()):
1975
1928
                # Find paths in the working tree that have no kind:
1976
 
                if change[1][1] is not None and change[6][1] is None:
1977
 
                    missing.append(change[1][1])
 
1929
                if change.path[1] is not None and change.kind[1] is None:
 
1930
                    missing.append(change.path[1])
1978
1931
            file_list = sorted(missing, reverse=True)
1979
1932
            file_deletion_strategy = 'keep'
1980
1933
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
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))
 
1934
                    keep_files=file_deletion_strategy == 'keep',
 
1935
                    force=(file_deletion_strategy == 'no-backup'))
2027
1936
 
2028
1937
 
2029
1938
class cmd_reconcile(Command):
2071
1980
    @display_command
2072
1981
    def run(self, location="."):
2073
1982
        branch = Branch.open_containing(location)[0]
2074
 
        self.add_cleanup(branch.lock_read().unlock)
 
1983
        self.enter_context(branch.lock_read())
2075
1984
        graph = branch.repository.get_graph()
2076
1985
        history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
2077
 
            [_mod_revision.NULL_REVISION]))
 
1986
                                                    [_mod_revision.NULL_REVISION]))
2078
1987
        for revid in reversed(history):
2079
1988
            self.outf.write(revid)
2080
1989
            self.outf.write('\n')
2099
2008
            b = wt.branch
2100
2009
            last_revision = wt.last_revision()
2101
2010
 
2102
 
        self.add_cleanup(b.repository.lock_read().unlock)
 
2011
        self.enter_context(b.repository.lock_read())
2103
2012
        graph = b.repository.get_graph()
2104
2013
        revisions = [revid for revid, parents in
2105
 
            graph.iter_ancestry([last_revision])]
 
2014
                     graph.iter_ancestry([last_revision])]
2106
2015
        for revision_id in reversed(revisions):
2107
2016
            if _mod_revision.is_null(revision_id):
2108
2017
                continue
2109
 
            self.outf.write(revision_id + '\n')
 
2018
            self.outf.write(revision_id.decode('utf-8') + '\n')
2110
2019
 
2111
2020
 
2112
2021
class cmd_init(Command):
2132
2041
        brz commit -m "imported project"
2133
2042
    """
2134
2043
 
2135
 
    _see_also = ['init-repository', 'branch', 'checkout']
 
2044
    _see_also = ['init-shared-repository', 'branch', 'checkout']
2136
2045
    takes_args = ['location?']
2137
2046
    takes_options = [
2138
2047
        Option('create-prefix',
2139
2048
               help='Create the path leading up to the branch '
2140
2049
                    'if it does not already exist.'),
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
 
         ]
 
2050
        RegistryOption('format',
 
2051
                       help='Specify a format for this branch. '
 
2052
                       'See "help formats" for a full list.',
 
2053
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
2054
                       converter=lambda name: controldir.format_registry.make_controldir(
 
2055
                            name),
 
2056
                       value_switches=True,
 
2057
                       title="Branch format",
 
2058
                       ),
 
2059
        Option('append-revisions-only',
 
2060
               help='Never change revnos or the existing log.'
 
2061
               '  Append revisions to it only.'),
 
2062
        Option('no-tree',
 
2063
               'Create a branch without a working tree.')
 
2064
        ]
 
2065
 
2155
2066
    def run(self, location=None, format=None, append_revisions_only=False,
2156
2067
            create_prefix=False, no_tree=False):
2157
2068
        if format is None:
2159
2070
        if location is None:
2160
2071
            location = u'.'
2161
2072
 
2162
 
        to_transport = transport.get_transport(location)
 
2073
        to_transport = transport.get_transport(location, purpose='write')
2163
2074
 
2164
2075
        # The path has to exist to initialize a
2165
2076
        # branch inside of it.
2171
2082
        except errors.NoSuchFile:
2172
2083
            if not create_prefix:
2173
2084
                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)
 
2085
                                                     " does not exist."
 
2086
                                                     "\nYou may supply --create-prefix to create all"
 
2087
                                                     " leading parent directories.")
 
2088
                                             % location)
2178
2089
            to_transport.create_prefix()
2179
2090
 
2180
2091
        try:
2181
 
            a_controldir = controldir.ControlDir.open_from_transport(to_transport)
 
2092
            a_controldir = controldir.ControlDir.open_from_transport(
 
2093
                to_transport)
2182
2094
        except errors.NotBranchError:
2183
2095
            # really a NotBzrDir error...
2184
2096
            create_branch = controldir.ControlDir.create_branch_convenience
2194
2106
            from .transport.local import LocalTransport
2195
2107
            if a_controldir.has_branch():
2196
2108
                if (isinstance(to_transport, LocalTransport)
2197
 
                    and not a_controldir.has_workingtree()):
2198
 
                        raise errors.BranchExistsWithoutWorkingTree(location)
 
2109
                        and not a_controldir.has_workingtree()):
 
2110
                    raise errors.BranchExistsWithoutWorkingTree(location)
2199
2111
                raise errors.AlreadyBranchError(location)
2200
2112
            branch = a_controldir.create_branch()
2201
2113
            if not no_tree and not a_controldir.has_workingtree():
2205
2117
                branch.set_append_revisions_only(True)
2206
2118
            except errors.UpgradeRequired:
2207
2119
                raise errors.BzrCommandError(gettext('This branch format cannot be set'
2208
 
                    ' to append-revisions-only.  Try --default.'))
 
2120
                                                     ' to append-revisions-only.  Try --default.'))
2209
2121
        if not is_quiet():
2210
2122
            from .info import describe_layout, describe_format
2211
2123
            try:
2216
2128
            layout = describe_layout(repository, branch, tree).lower()
2217
2129
            format = describe_format(a_controldir, repository, branch, tree)
2218
2130
            self.outf.write(gettext("Created a {0} (format: {1})\n").format(
2219
 
                  layout, format))
 
2131
                layout, format))
2220
2132
            if repository.is_shared():
2221
 
                #XXX: maybe this can be refactored into transport.path_or_url()
 
2133
                # XXX: maybe this can be refactored into transport.path_or_url()
2222
2134
                url = repository.controldir.root_transport.external_url()
2223
2135
                try:
2224
2136
                    url = urlutils.local_path_from_url(url)
2227
2139
                self.outf.write(gettext("Using shared repository: %s\n") % url)
2228
2140
 
2229
2141
 
2230
 
class cmd_init_repository(Command):
 
2142
class cmd_init_shared_repository(Command):
2231
2143
    __doc__ = """Create a shared repository for branches to share storage space.
2232
2144
 
2233
2145
    New branches created under the repository directory will store their
2234
2146
    revisions in the repository, not in the branch directory.  For branches
2235
 
    with shared history, this reduces the amount of storage needed and 
 
2147
    with shared history, this reduces the amount of storage needed and
2236
2148
    speeds up the creation of new branches.
2237
2149
 
2238
2150
    If the --no-trees option is given then the branches in the repository
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 
 
2151
    will not have working trees by default.  They will still exist as
 
2152
    directories on disk, but they will not have separate copies of the
2241
2153
    files at a certain revision.  This can be useful for repositories that
2242
2154
    store branches which are interacted with through checkouts or remote
2243
2155
    branches, such as on a server.
2245
2157
    :Examples:
2246
2158
        Create a shared repository holding just branches::
2247
2159
 
2248
 
            brz init-repo --no-trees repo
 
2160
            brz init-shared-repo --no-trees repo
2249
2161
            brz init repo/trunk
2250
2162
 
2251
2163
        Make a lightweight checkout elsewhere::
2258
2170
    _see_also = ['init', 'branch', 'checkout', 'repositories']
2259
2171
    takes_args = ["location"]
2260
2172
    takes_options = [RegistryOption('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'),
 
2173
                                    help='Specify a format for this repository. See'
 
2174
                                    ' "brz help formats" for details.',
 
2175
                                    lazy_registry=(
 
2176
                                        'breezy.controldir', 'format_registry'),
 
2177
                                    converter=lambda name: controldir.format_registry.make_controldir(
 
2178
                                        name),
 
2179
                                    value_switches=True, title='Repository format'),
2266
2180
                     Option('no-trees',
2267
 
                             help='Branches in the repository will default to'
2268
 
                                  ' not having a working tree.'),
2269
 
                    ]
2270
 
    aliases = ["init-repo"]
 
2181
                            help='Branches in the repository will default to'
 
2182
                            ' not having a working tree.'),
 
2183
                     ]
 
2184
    aliases = ["init-shared-repo", "init-repo"]
2271
2185
 
2272
2186
    def run(self, location, format=None, no_trees=False):
2273
2187
        if format is None:
2276
2190
        if location is None:
2277
2191
            location = '.'
2278
2192
 
2279
 
        to_transport = transport.get_transport(location)
 
2193
        to_transport = transport.get_transport(location, purpose='write')
 
2194
 
 
2195
        if format.fixed_components:
 
2196
            repo_format_name = None
 
2197
        else:
 
2198
            repo_format_name = format.repository_format.get_format_string()
2280
2199
 
2281
2200
        (repo, newdir, require_stacking, repository_policy) = (
2282
2201
            format.initialize_on_transport_ex(to_transport,
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()))
 
2202
                                              create_prefix=True, make_working_trees=not no_trees,
 
2203
                                              shared_repo=True, force_new_repo=True,
 
2204
                                              use_existing_dir=True,
 
2205
                                              repo_format_name=repo_format_name))
2287
2206
        if not is_quiet():
2288
2207
            from .info import show_bzrdir_info
2289
2208
            show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
2304
2223
 
2305
2224
    Note that when using the -r argument with a range of revisions, the
2306
2225
    differences are computed between the two specified revisions.  That
2307
 
    is, the command does not show the changes introduced by the first 
2308
 
    revision in the range.  This differs from the interpretation of 
 
2226
    is, the command does not show the changes introduced by the first
 
2227
    revision in the range.  This differs from the interpretation of
2309
2228
    revision ranges used by "brz log" which includes the first revision
2310
2229
    in the range.
2311
2230
 
2337
2256
            brz diff -c2
2338
2257
 
2339
2258
        To see the changes introduced by revision X::
2340
 
        
 
2259
 
2341
2260
            brz diff -cX
2342
2261
 
2343
2262
        Note that in the case of a merge, the -c option shows the changes
2378
2297
    _see_also = ['status']
2379
2298
    takes_args = ['file*']
2380
2299
    takes_options = [
2381
 
        Option('diff-options', type=text_type,
 
2300
        Option('diff-options', type=str,
2382
2301
               help='Pass these options to the external diff program.'),
2383
 
        Option('prefix', type=text_type,
 
2302
        Option('prefix', type=str,
2384
2303
               short_name='p',
2385
2304
               help='Set prefixes added to old and new filenames, as '
2386
2305
                    'two values separated by a colon. (eg "old/:new/").'),
2387
2306
        Option('old',
2388
 
            help='Branch/tree to compare from.',
2389
 
            type=text_type,
2390
 
            ),
 
2307
               help='Branch/tree to compare from.',
 
2308
               type=str,
 
2309
               ),
2391
2310
        Option('new',
2392
 
            help='Branch/tree to compare to.',
2393
 
            type=text_type,
2394
 
            ),
 
2311
               help='Branch/tree to compare to.',
 
2312
               type=str,
 
2313
               ),
2395
2314
        'revision',
2396
2315
        'change',
2397
2316
        Option('using',
2398
 
            help='Use this command to compare files.',
2399
 
            type=text_type,
2400
 
            ),
 
2317
               help='Use this command to compare files.',
 
2318
               type=str,
 
2319
               ),
2401
2320
        RegistryOption('format',
2402
 
            short_name='F',
2403
 
            help='Diff format to use.',
2404
 
            lazy_registry=('breezy.diff', 'format_registry'),
2405
 
            title='Diff format'),
 
2321
                       short_name='F',
 
2322
                       help='Diff format to use.',
 
2323
                       lazy_registry=('breezy.diff', 'format_registry'),
 
2324
                       title='Diff format'),
2406
2325
        Option('context',
2407
 
            help='How many lines of context to show.',
2408
 
            type=int,
2409
 
            ),
 
2326
               help='How many lines of context to show.',
 
2327
               type=int,
 
2328
               ),
 
2329
        RegistryOption.from_kwargs(
 
2330
            'color',
 
2331
            help='Color mode to use.',
 
2332
            title='Color Mode', value_switches=False, enum_switch=True,
 
2333
            never='Never colorize output.',
 
2334
            auto='Only colorize output if terminal supports it and STDOUT is a'
 
2335
            ' TTY.',
 
2336
            always='Always colorize output (default).'),
 
2337
        Option(
 
2338
            'check-style',
 
2339
            help=('Warn if trailing whitespace or spurious changes have been'
 
2340
                  '  added.'))
2410
2341
        ]
 
2342
 
2411
2343
    aliases = ['di', 'dif']
2412
2344
    encoding_type = 'exact'
2413
2345
 
2414
2346
    @display_command
2415
2347
    def run(self, revision=None, file_list=None, diff_options=None,
2416
2348
            prefix=None, old=None, new=None, using=None, format=None,
2417
 
            context=None):
 
2349
            context=None, color='never'):
2418
2350
        from .diff import (get_trees_and_branches_to_diff_locked,
2419
 
            show_diff_trees)
 
2351
                           show_diff_trees)
2420
2352
 
2421
2353
        if prefix == u'0':
2422
2354
            # diff -p0 format
2434
2366
 
2435
2367
        if revision and len(revision) > 2:
2436
2368
            raise errors.BzrCommandError(gettext('brz diff --revision takes exactly'
2437
 
                                         ' one or two revision specifiers'))
 
2369
                                                 ' one or two revision specifiers'))
2438
2370
 
2439
2371
        if using is not None and format is not None:
2440
2372
            raise errors.BzrCommandError(gettext(
2444
2376
        (old_tree, new_tree,
2445
2377
         old_branch, new_branch,
2446
2378
         specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
2447
 
            file_list, revision, old, new, self.add_cleanup, apply_view=True)
 
2379
            file_list, revision, old, new, self._exit_stack, apply_view=True)
2448
2380
        # GNU diff on Windows uses ANSI encoding for filenames
2449
2381
        path_encoding = osutils.get_diff_header_encoding()
2450
 
        return show_diff_trees(old_tree, new_tree, self.outf,
 
2382
        outf = self.outf
 
2383
        if color == 'auto':
 
2384
            from .terminal import has_ansi_colors
 
2385
            if has_ansi_colors():
 
2386
                color = 'always'
 
2387
            else:
 
2388
                color = 'never'
 
2389
        if 'always' == color:
 
2390
            from .colordiff import DiffWriter
 
2391
            outf = DiffWriter(outf)
 
2392
        return show_diff_trees(old_tree, new_tree, outf,
2451
2393
                               specific_files=specific_files,
2452
2394
                               external_diff_options=diff_options,
2453
2395
                               old_label=old_label, new_label=new_label,
2472
2414
    @display_command
2473
2415
    def run(self, show_ids=False, directory=u'.'):
2474
2416
        tree = WorkingTree.open_containing(directory)[0]
2475
 
        self.add_cleanup(tree.lock_read().unlock)
 
2417
        self.enter_context(tree.lock_read())
2476
2418
        old = tree.basis_tree()
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')
 
2419
        self.enter_context(old.lock_read())
 
2420
        delta = tree.changes_from(old)
 
2421
        for change in delta.removed:
 
2422
            self.outf.write(change.path[0])
 
2423
            if show_ids:
 
2424
                self.outf.write(' ')
 
2425
                self.outf.write(change.file_id)
 
2426
            self.outf.write('\n')
2485
2427
 
2486
2428
 
2487
2429
class cmd_modified(Command):
2495
2437
    @display_command
2496
2438
    def run(self, null=False, directory=u'.'):
2497
2439
        tree = WorkingTree.open_containing(directory)[0]
2498
 
        self.add_cleanup(tree.lock_read().unlock)
 
2440
        self.enter_context(tree.lock_read())
2499
2441
        td = tree.changes_from(tree.basis_tree())
2500
2442
        self.cleanup_now()
2501
 
        for path, id, kind, text_modified, meta_modified in td.modified:
 
2443
        for change in td.modified:
2502
2444
            if null:
2503
 
                self.outf.write(path + '\0')
 
2445
                self.outf.write(change.path[1] + '\0')
2504
2446
            else:
2505
 
                self.outf.write(osutils.quotefn(path) + '\n')
 
2447
                self.outf.write(osutils.quotefn(change.path[1]) + '\n')
2506
2448
 
2507
2449
 
2508
2450
class cmd_added(Command):
2516
2458
    @display_command
2517
2459
    def run(self, null=False, directory=u'.'):
2518
2460
        wt = WorkingTree.open_containing(directory)[0]
2519
 
        self.add_cleanup(wt.lock_read().unlock)
 
2461
        self.enter_context(wt.lock_read())
2520
2462
        basis = wt.basis_tree()
2521
 
        self.add_cleanup(basis.lock_read().unlock)
2522
 
        root_id = wt.get_root_id()
 
2463
        self.enter_context(basis.lock_read())
2523
2464
        for path in wt.all_versioned_paths():
2524
2465
            if basis.has_filename(path):
2525
2466
                continue
2536
2477
class cmd_root(Command):
2537
2478
    __doc__ = """Show the tree root directory.
2538
2479
 
2539
 
    The root is the nearest enclosing directory with a .bzr control
 
2480
    The root is the nearest enclosing directory with a control
2540
2481
    directory."""
2541
2482
 
2542
2483
    takes_args = ['filename?']
 
2484
 
2543
2485
    @display_command
2544
2486
    def run(self, filename=None):
2545
2487
        """Print the branch root."""
2687
2629
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2688
2630
      bzr-explorer shell, or the Loggerhead web interface.  See the Bazaar
2689
2631
      Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2690
 
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
 
2632
      <http://wiki.bazaar.canonical.com/IDEIntegration>.
2691
2633
 
2692
2634
      You may find it useful to add the aliases below to ``breezy.conf``::
2693
2635
 
2721
2663
    takes_args = ['file*']
2722
2664
    _see_also = ['log-formats', 'revisionspec']
2723
2665
    takes_options = [
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',
 
2666
        Option('forward',
 
2667
               help='Show from oldest to newest.'),
 
2668
        'timezone',
 
2669
        custom_help('verbose',
 
2670
                    help='Show files changed in each revision.'),
 
2671
        'show-ids',
 
2672
        'revision',
 
2673
        Option('change',
 
2674
               type=breezy.option._parse_revision_str,
 
2675
               short_name='c',
 
2676
               help='Show just the specified revision.'
 
2677
               ' See also "help revisionspec".'),
 
2678
        'log-format',
 
2679
        RegistryOption('authors',
 
2680
                       'What names to list as authors - first, all or committer.',
 
2681
                       title='Authors',
 
2682
                       lazy_registry=(
 
2683
                           'breezy.log', 'author_list_registry'),
 
2684
                       ),
 
2685
        Option('levels',
 
2686
               short_name='n',
 
2687
               help='Number of levels to display - 0 for all, 1 for flat.',
 
2688
               argname='N',
 
2689
               type=_parse_levels),
 
2690
        Option('message',
 
2691
               help='Show revisions whose message matches this '
 
2692
               'regular expression.',
 
2693
               type=str,
 
2694
               hidden=True),
 
2695
        Option('limit',
 
2696
               short_name='l',
 
2697
               help='Limit the output to the first N revisions.',
 
2698
               argname='N',
 
2699
               type=_parse_limit),
 
2700
        Option('show-diff',
 
2701
               short_name='p',
 
2702
               help='Show changes made in each revision as a patch.'),
 
2703
        Option('include-merged',
 
2704
               help='Show merged revisions like --levels 0 does.'),
 
2705
        Option('include-merges', hidden=True,
 
2706
               help='Historical alias for --include-merged.'),
 
2707
        Option('omit-merges',
 
2708
               help='Do not report commits with more than one parent.'),
 
2709
        Option('exclude-common-ancestry',
 
2710
               help='Display only the revisions that are not part'
 
2711
               ' of both ancestries (require -rX..Y).'
 
2712
               ),
 
2713
        Option('signatures',
 
2714
               help='Show digital signature validity.'),
 
2715
        ListOption('match',
 
2716
                   short_name='m',
 
2717
                   help='Show revisions whose properties match this '
 
2718
                   'expression.',
 
2719
                   type=str),
 
2720
        ListOption('match-message',
 
2721
                   help='Show revisions whose message matches this '
 
2722
                   'expression.',
 
2723
                   type=str),
 
2724
        ListOption('match-committer',
2782
2725
                   help='Show revisions whose committer matches this '
2783
2726
                   'expression.',
2784
 
                type=text_type),
2785
 
            ListOption('match-author',
 
2727
                   type=str),
 
2728
        ListOption('match-author',
2786
2729
                   help='Show revisions whose authors match this '
2787
2730
                   'expression.',
2788
 
                type=text_type),
2789
 
            ListOption('match-bugs',
 
2731
                   type=str),
 
2732
        ListOption('match-bugs',
2790
2733
                   help='Show revisions whose bugs match this '
2791
2734
                   'expression.',
2792
 
                type=text_type)
2793
 
            ]
 
2735
                   type=str)
 
2736
        ]
2794
2737
    encoding_type = 'replace'
2795
2738
 
2796
2739
    @display_command
2825
2768
        if include_merged is None:
2826
2769
            include_merged = False
2827
2770
        if (exclude_common_ancestry
2828
 
            and (revision is None or len(revision) != 2)):
 
2771
                and (revision is None or len(revision) != 2)):
2829
2772
            raise errors.BzrCommandError(gettext(
2830
2773
                '--exclude-common-ancestry requires -r with two revisions'))
2831
2774
        if include_merged:
2851
2794
        if file_list:
2852
2795
            # find the file ids to log and check for directory filtering
2853
2796
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2854
 
                revision, file_list, self.add_cleanup)
 
2797
                revision, file_list, self._exit_stack)
2855
2798
            for relpath, file_id, kind in file_info_list:
2856
2799
                if file_id is None:
2857
2800
                    raise errors.BzrCommandError(gettext(
2875
2818
                location = '.'
2876
2819
            dir, relpath = controldir.ControlDir.open_containing(location)
2877
2820
            b = dir.open_branch()
2878
 
            self.add_cleanup(b.lock_read().unlock)
 
2821
            self.enter_context(b.lock_read())
2879
2822
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2880
2823
 
2881
2824
        if b.get_config_stack().get('validate_signatures_in_log'):
2902
2845
        if log_format is None:
2903
2846
            log_format = log.log_formatter_registry.get_default(b)
2904
2847
        # Make a non-encoding output to include the diffs - bug 328007
2905
 
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2848
        unencoded_output = ui.ui_factory.make_output_stream(
 
2849
            encoding_type='exact')
2906
2850
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2907
2851
                        to_exact_file=unencoded_output,
2908
2852
                        show_timezone=timezone,
2925
2869
        # file that isn't a directory without showing a delta" case.
2926
2870
        partial_history = revision and b.repository._format.supports_chks
2927
2871
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2928
 
            or delta_type or partial_history)
 
2872
                              or delta_type or partial_history)
2929
2873
 
2930
2874
        match_dict = {}
2931
2875
        if match:
3001
2945
        rev_id2 = revision_range[1].rev_id
3002
2946
    return rev_id1, rev_id2
3003
2947
 
 
2948
 
3004
2949
def get_log_format(long=False, short=False, line=False, default='long'):
3005
2950
    log_format = default
3006
2951
    if long:
3024
2969
    @display_command
3025
2970
    def run(self, filename):
3026
2971
        tree, relpath = WorkingTree.open_containing(filename)
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))
 
2972
        with tree.lock_read():
 
2973
            touching_revs = log.find_touching_revisions(
 
2974
                tree.branch.repository, tree.branch.last_revision(), tree, relpath)
 
2975
            for revno, revision_id, what in reversed(list(touching_revs)):
 
2976
                self.outf.write("%6d %s\n" % (revno, what))
3033
2977
 
3034
2978
 
3035
2979
class cmd_ls(Command):
3039
2983
    _see_also = ['status', 'cat']
3040
2984
    takes_args = ['path?']
3041
2985
    takes_options = [
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
 
            ]
 
2986
        'verbose',
 
2987
        'revision',
 
2988
        Option('recursive', short_name='R',
 
2989
               help='Recurse into subdirectories.'),
 
2990
        Option('from-root',
 
2991
               help='Print paths relative to the root of the branch.'),
 
2992
        Option('unknown', short_name='u',
 
2993
               help='Print unknown files.'),
 
2994
        Option('versioned', help='Print versioned files.',
 
2995
               short_name='V'),
 
2996
        Option('ignored', short_name='i',
 
2997
               help='Print ignored files.'),
 
2998
        Option('kind', short_name='k',
 
2999
               help=('List entries of a particular kind: file, '
 
3000
                     'directory, symlink, tree-reference.'),
 
3001
               type=str),
 
3002
        'null',
 
3003
        'show-ids',
 
3004
        'directory',
 
3005
        ]
 
3006
 
3061
3007
    @display_command
3062
3008
    def run(self, revision=None, verbose=False,
3063
3009
            recursive=False, from_root=False,
3064
3010
            unknown=False, versioned=False, ignored=False,
3065
3011
            null=False, kind=None, show_ids=False, path=None, directory=None):
3066
3012
 
3067
 
        if kind and kind not in ('file', 'directory', 'symlink'):
 
3013
        if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
3068
3014
            raise errors.BzrCommandError(gettext('invalid kind specified'))
3069
3015
 
3070
3016
        if verbose and null:
3071
 
            raise errors.BzrCommandError(gettext('Cannot set both --verbose and --null'))
 
3017
            raise errors.BzrCommandError(
 
3018
                gettext('Cannot set both --verbose and --null'))
3072
3019
        all = not (unknown or versioned or ignored)
3073
3020
 
3074
 
        selection = {'I':ignored, '?':unknown, 'V':versioned}
 
3021
        selection = {'I': ignored, '?': unknown, 'V': versioned}
3075
3022
 
3076
3023
        if path is None:
3077
3024
            fs_path = '.'
3078
3025
        else:
3079
3026
            if from_root:
3080
3027
                raise errors.BzrCommandError(gettext('cannot specify both --from-root'
3081
 
                                             ' and PATH'))
 
3028
                                                     ' and PATH'))
3082
3029
            fs_path = path
3083
3030
        tree, branch, relpath = \
3084
3031
            _open_directory_or_containing_tree_or_branch(fs_path, directory)
3102
3049
                view_str = views.view_display_str(view_files)
3103
3050
                note(gettext("Ignoring files outside view. View is %s") % view_str)
3104
3051
 
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):
 
3052
        self.enter_context(tree.lock_read())
 
3053
        for fp, fc, fkind, entry in tree.list_files(
 
3054
                include_root=False, from_dir=relpath, recursive=recursive):
3108
3055
            # Apply additional masking
3109
3056
            if not all and not selection[fc]:
3110
3057
                continue
3128
3075
            ui.ui_factory.clear_term()
3129
3076
            if verbose:
3130
3077
                outstring = '%-8s %s' % (fc, outstring)
3131
 
                if show_ids and fid is not None:
3132
 
                    outstring = "%-50s %s" % (outstring, fid)
 
3078
                if show_ids and getattr(entry, 'file_id', None) is not None:
 
3079
                    outstring = "%-50s %s" % (outstring, entry.file_id.decode('utf-8'))
3133
3080
                self.outf.write(outstring + '\n')
3134
3081
            elif null:
3135
3082
                self.outf.write(fp + '\0')
3136
3083
                if show_ids:
3137
 
                    if fid is not None:
3138
 
                        self.outf.write(fid)
 
3084
                    if getattr(entry, 'file_id', None) is not None:
 
3085
                        self.outf.write(entry.file_id.decode('utf-8'))
3139
3086
                    self.outf.write('\0')
3140
3087
                self.outf.flush()
3141
3088
            else:
3142
3089
                if show_ids:
3143
 
                    if fid is not None:
3144
 
                        my_id = fid
 
3090
                    if getattr(entry, 'file_id', None) is not None:
 
3091
                        my_id = entry.file_id.decode('utf-8')
3145
3092
                    else:
3146
3093
                        my_id = ''
3147
3094
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
3170
3117
 
3171
3118
    If a .bzrignore file does not exist, the ignore command
3172
3119
    will create one and add the specified files or patterns to the newly
3173
 
    created file. The ignore command will also automatically add the 
 
3120
    created file. The ignore command will also automatically add the
3174
3121
    .bzrignore file to be versioned. Creating a .bzrignore file without
3175
3122
    the use of the ignore command will require an explicit add command.
3176
3123
 
3178
3125
    After adding, editing or deleting that file either indirectly by
3179
3126
    using this command or directly by using an editor, be sure to commit
3180
3127
    it.
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.
 
3128
 
 
3129
    Breezy also supports a global ignore file ~/.config/breezy/ignore. On
 
3130
    Windows the global ignore file can be found in the application data
 
3131
    directory as
 
3132
    C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
3185
3133
    Global ignores are not touched by this command. The global ignore file
3186
3134
    can be edited directly using an editor.
3187
3135
 
3188
3136
    Patterns prefixed with '!' are exceptions to ignore patterns and take
3189
3137
    precedence over regular ignores.  Such exceptions are used to specify
3190
3138
    files that should be versioned which would otherwise be ignored.
3191
 
    
 
3139
 
3192
3140
    Patterns prefixed with '!!' act as regular ignore patterns, but have
3193
3141
    precedence over the '!' exception patterns.
3194
3142
 
3195
 
    :Notes: 
3196
 
        
 
3143
    :Notes:
 
3144
 
3197
3145
    * Ignore patterns containing shell wildcards must be quoted from
3198
3146
      the shell on Unix.
3199
3147
 
3228
3176
        Ignore everything but the "debian" toplevel directory::
3229
3177
 
3230
3178
            brz ignore "RE:(?!debian/).*"
3231
 
        
 
3179
 
3232
3180
        Ignore everything except the "local" toplevel directory,
3233
3181
        but always ignore autosave files ending in ~, even under local/::
3234
 
        
 
3182
 
3235
3183
            brz ignore "*"
3236
3184
            brz ignore "!./local"
3237
3185
            brz ignore "!!*~"
3240
3188
    _see_also = ['status', 'ignored', 'patterns']
3241
3189
    takes_args = ['name_pattern*']
3242
3190
    takes_options = ['directory',
3243
 
        Option('default-rules',
3244
 
               help='Display the default ignore rules that brz uses.')
3245
 
        ]
 
3191
                     Option('default-rules',
 
3192
                            help='Display the default ignore rules that brz uses.')
 
3193
                     ]
3246
3194
 
3247
3195
    def run(self, name_pattern_list=None, default_rules=None,
3248
3196
            directory=u'.'):
3254
3202
            return
3255
3203
        if not name_pattern_list:
3256
3204
            raise errors.BzrCommandError(gettext("ignore requires at least one "
3257
 
                "NAME_PATTERN or --default-rules."))
 
3205
                                                 "NAME_PATTERN or --default-rules."))
3258
3206
        name_pattern_list = [globbing.normalize_pattern(p)
3259
3207
                             for p in name_pattern_list]
3260
3208
        bad_patterns = ''
3264
3212
                bad_patterns_count += 1
3265
3213
                bad_patterns += ('\n  %s' % p)
3266
3214
        if bad_patterns:
3267
 
            msg = (ngettext('Invalid ignore pattern found. %s', 
 
3215
            msg = (ngettext('Invalid ignore pattern found. %s',
3268
3216
                            'Invalid ignore patterns found. %s',
3269
3217
                            bad_patterns_count) % bad_patterns)
3270
3218
            ui.ui_factory.show_error(msg)
3271
3219
            raise lazy_regex.InvalidPattern('')
3272
3220
        for name_pattern in name_pattern_list:
3273
3221
            if (name_pattern[0] == '/' or
3274
 
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
 
3222
                    (len(name_pattern) > 1 and name_pattern[1] == ':')):
3275
3223
                raise errors.BzrCommandError(gettext(
3276
3224
                    "NAME_PATTERN should not be an absolute path"))
3277
3225
        tree, relpath = WorkingTree.open_containing(directory)
3278
3226
        ignores.tree_ignores_add_patterns(tree, name_pattern_list)
3279
3227
        ignored = globbing.Globster(name_pattern_list)
3280
3228
        matches = []
3281
 
        self.add_cleanup(tree.lock_read().unlock)
3282
 
        for entry in tree.list_files():
3283
 
            id = entry[3]
 
3229
        self.enter_context(tree.lock_read())
 
3230
        for filename, fc, fkind, entry in tree.list_files():
 
3231
            id = getattr(entry, 'file_id', None)
3284
3232
            if id is not None:
3285
 
                filename = entry[0]
3286
3233
                if ignored.match(filename):
3287
3234
                    matches.append(filename)
3288
3235
        if len(matches) > 0:
3289
3236
            self.outf.write(gettext("Warning: the following files are version "
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),))
 
3237
                                    "controlled and match your ignore pattern:\n%s"
 
3238
                                    "\nThese files will continue to be version controlled"
 
3239
                                    " unless you 'brz remove' them.\n") % ("\n".join(matches),))
3293
3240
 
3294
3241
 
3295
3242
class cmd_ignored(Command):
3310
3257
    @display_command
3311
3258
    def run(self, directory=u'.'):
3312
3259
        tree = WorkingTree.open_containing(directory)[0]
3313
 
        self.add_cleanup(tree.lock_read().unlock)
3314
 
        for path, file_class, kind, file_id, entry in tree.list_files():
 
3260
        self.enter_context(tree.lock_read())
 
3261
        for path, file_class, kind, entry in tree.list_files():
3315
3262
            if file_class != 'I':
3316
3263
                continue
3317
 
            ## XXX: Slightly inefficient since this was already calculated
 
3264
            # XXX: Slightly inefficient since this was already calculated
3318
3265
            pat = tree.is_ignored(path)
3319
3266
            self.outf.write('%-50s %s\n' % (path, pat))
3320
3267
 
3336
3283
        except ValueError:
3337
3284
            raise errors.BzrCommandError(gettext("not a valid revision-number: %r")
3338
3285
                                         % revno)
3339
 
        revid = WorkingTree.open_containing(directory)[0].branch.get_rev_id(revno)
3340
 
        self.outf.write("%s\n" % revid)
 
3286
        revid = WorkingTree.open_containing(
 
3287
            directory)[0].branch.get_rev_id(revno)
 
3288
        self.outf.write("%s\n" % revid.decode('utf-8'))
3341
3289
 
3342
3290
 
3343
3291
class cmd_export(Command):
3369
3317
      =================       =========================
3370
3318
    """
3371
3319
    encoding = 'exact'
 
3320
    encoding_type = 'exact'
3372
3321
    takes_args = ['dest', 'branch_or_subdir?']
3373
3322
    takes_options = ['directory',
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
 
        ]
 
3323
                     Option('format',
 
3324
                            help="Type of file to export to.",
 
3325
                            type=str),
 
3326
                     'revision',
 
3327
                     Option('filters', help='Apply content filters to export the '
 
3328
                            'convenient form.'),
 
3329
                     Option('root',
 
3330
                            type=str,
 
3331
                            help="Name of the root directory inside the exported file."),
 
3332
                     Option('per-file-timestamps',
 
3333
                            help='Set modification time of files to that of the last '
 
3334
                            'revision in which it was changed.'),
 
3335
                     Option('uncommitted',
 
3336
                            help='Export the working tree contents rather than that of the '
 
3337
                            'last revision.'),
 
3338
                     ]
 
3339
 
3390
3340
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
3391
 
        root=None, filters=False, per_file_timestamps=False, uncommitted=False,
3392
 
        directory=u'.'):
3393
 
        from .export import export
 
3341
            root=None, filters=False, per_file_timestamps=False, uncommitted=False,
 
3342
            directory=u'.'):
 
3343
        from .export import export, guess_format, get_root_name
3394
3344
 
3395
3345
        if branch_or_subdir is None:
3396
3346
            branch_or_subdir = directory
3398
3348
        (tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
3399
3349
            branch_or_subdir)
3400
3350
        if tree is not None:
3401
 
            self.add_cleanup(tree.lock_read().unlock)
 
3351
            self.enter_context(tree.lock_read())
3402
3352
 
3403
3353
        if uncommitted:
3404
3354
            if tree is None:
3406
3356
                    gettext("--uncommitted requires a working tree"))
3407
3357
            export_tree = tree
3408
3358
        else:
3409
 
            export_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
 
3359
            export_tree = _get_one_revision_tree(
 
3360
                'export', revision, branch=b,
 
3361
                tree=tree)
 
3362
 
 
3363
        if format is None:
 
3364
            format = guess_format(dest)
 
3365
 
 
3366
        if root is None:
 
3367
            root = get_root_name(dest)
 
3368
 
 
3369
        if not per_file_timestamps:
 
3370
            force_mtime = time.time()
 
3371
        else:
 
3372
            force_mtime = None
 
3373
 
 
3374
        if filters:
 
3375
            from breezy.filter_tree import ContentFilterTree
 
3376
            export_tree = ContentFilterTree(
 
3377
                export_tree, export_tree._content_filter_stack)
 
3378
 
3410
3379
        try:
3411
 
            export(export_tree, dest, format, root, subdir, filtered=filters,
 
3380
            export(export_tree, dest, format, root, subdir,
3412
3381
                   per_file_timestamps=per_file_timestamps)
3413
3382
        except errors.NoSuchExportFormat as e:
3414
3383
            raise errors.BzrCommandError(
3426
3395
 
3427
3396
    _see_also = ['ls']
3428
3397
    takes_options = ['directory',
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
 
        ]
 
3398
                     Option('name-from-revision',
 
3399
                            help='The path name in the old tree.'),
 
3400
                     Option('filters', help='Apply content filters to display the '
 
3401
                            'convenience form.'),
 
3402
                     'revision',
 
3403
                     ]
3434
3404
    takes_args = ['filename']
3435
3405
    encoding_type = 'exact'
3436
3406
 
3439
3409
            filters=False, directory=None):
3440
3410
        if revision is not None and len(revision) != 1:
3441
3411
            raise errors.BzrCommandError(gettext("brz cat --revision takes exactly"
3442
 
                                         " one revision specifier"))
 
3412
                                                 " one revision specifier"))
3443
3413
        tree, branch, relpath = \
3444
3414
            _open_directory_or_containing_tree_or_branch(filename, directory)
3445
 
        self.add_cleanup(branch.lock_read().unlock)
 
3415
        self.enter_context(branch.lock_read())
3446
3416
        return self._run(tree, branch, relpath, filename, revision,
3447
3417
                         name_from_revision, filters)
3448
3418
 
3449
3419
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
3450
 
        filtered):
 
3420
             filtered):
 
3421
        import shutil
3451
3422
        if tree is None:
3452
3423
            tree = b.basis_tree()
3453
3424
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
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.
 
3425
        self.enter_context(rev_tree.lock_read())
 
3426
 
3462
3427
        if name_from_revision:
3463
3428
            # Try in revision if requested
3464
 
            if old_file_id is None:
 
3429
            if not rev_tree.is_versioned(relpath):
3465
3430
                raise errors.BzrCommandError(gettext(
3466
3431
                    "{0!r} is not present in revision {1}").format(
3467
3432
                        filename, rev_tree.get_revision_id()))
3468
 
            else:
3469
 
                actual_file_id = old_file_id
 
3433
            rev_tree_path = relpath
3470
3434
        else:
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()))
 
3435
            try:
 
3436
                rev_tree_path = _mod_tree.find_previous_path(
 
3437
                    tree, rev_tree, relpath)
 
3438
            except errors.NoSuchFile:
 
3439
                rev_tree_path = None
 
3440
 
 
3441
            if rev_tree_path is None:
 
3442
                # Path didn't exist in working tree
 
3443
                if not rev_tree.is_versioned(relpath):
 
3444
                    raise errors.BzrCommandError(gettext(
 
3445
                        "{0!r} is not present in revision {1}").format(
 
3446
                            filename, rev_tree.get_revision_id()))
 
3447
                else:
 
3448
                    # Fall back to the same path in the basis tree, if present.
 
3449
                    rev_tree_path = relpath
 
3450
 
3480
3451
        if filtered:
3481
3452
            from .filter_tree import ContentFilterTree
3482
 
            filter_tree = ContentFilterTree(rev_tree,
3483
 
                rev_tree._content_filter_stack)
3484
 
            content = filter_tree.get_file_text(relpath, actual_file_id)
 
3453
            filter_tree = ContentFilterTree(
 
3454
                rev_tree, rev_tree._content_filter_stack)
 
3455
            fileobj = filter_tree.get_file(rev_tree_path)
3485
3456
        else:
3486
 
            content = rev_tree.get_file_text(relpath, actual_file_id)
 
3457
            fileobj = rev_tree.get_file(rev_tree_path)
 
3458
        shutil.copyfileobj(fileobj, self.outf)
3487
3459
        self.cleanup_now()
3488
 
        self.outf.write(content)
3489
3460
 
3490
3461
 
3491
3462
class cmd_local_time_offset(Command):
3492
3463
    __doc__ = """Show the offset in seconds from GMT to local time."""
3493
3464
    hidden = True
 
3465
 
3494
3466
    @display_command
3495
3467
    def run(self):
3496
3468
        self.outf.write("%s\n" % osutils.local_time_offset())
3497
3469
 
3498
3470
 
3499
 
 
3500
3471
class cmd_commit(Command):
3501
3472
    __doc__ = """Commit changes into a new revision.
3502
3473
 
3516
3487
      If selected files are specified, only changes to those files are
3517
3488
      committed.  If a directory is specified then the directory and
3518
3489
      everything within it is committed.
3519
 
  
 
3490
 
3520
3491
      When excludes are given, they take precedence over selected files.
3521
3492
      For example, to commit only changes within foo, but not changes
3522
3493
      within foo/bar::
3523
 
  
 
3494
 
3524
3495
        brz commit foo -x foo/bar
3525
 
  
 
3496
 
3526
3497
      A selective commit after a merge is not yet supported.
3527
3498
 
3528
3499
    :Custom authors:
3533
3504
      "John Doe <jdoe@example.com>". If there is more than one author of
3534
3505
      the change you can specify the option multiple times, once for each
3535
3506
      author.
3536
 
  
 
3507
 
3537
3508
    :Checks:
3538
3509
 
3539
3510
      A common mistake is to forget to add a new file or directory before
3544
3515
 
3545
3516
    :Things to note:
3546
3517
 
3547
 
      If you accidentially commit the wrong changes or make a spelling
 
3518
      If you accidentally commit the wrong changes or make a spelling
3548
3519
      mistake in the commit message say, you can use the uncommit command
3549
3520
      to undo it. See ``brz help uncommit`` for details.
3550
3521
 
3557
3528
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
3558
3529
    takes_args = ['selected*']
3559
3530
    takes_options = [
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
 
             ]
 
3531
        ListOption(
 
3532
            'exclude', type=str, short_name='x',
 
3533
            help="Do not consider changes made to a given path."),
 
3534
        Option('message', type=str,
 
3535
               short_name='m',
 
3536
               help="Description of the new revision."),
 
3537
        'verbose',
 
3538
        Option('unchanged',
 
3539
               help='Commit even if nothing has changed.'),
 
3540
        Option('file', type=str,
 
3541
               short_name='F',
 
3542
               argname='msgfile',
 
3543
               help='Take commit message from this file.'),
 
3544
        Option('strict',
 
3545
               help="Refuse to commit if there are unknown "
 
3546
               "files in the working tree."),
 
3547
        Option('commit-time', type=str,
 
3548
               help="Manually set a commit time using commit date "
 
3549
               "format, e.g. '2009-10-10 08:00:00 +0100'."),
 
3550
        ListOption(
 
3551
            'bugs', type=str,
 
3552
            help="Link to a related bug. (see \"brz help bugs\")."),
 
3553
        ListOption(
 
3554
            'fixes', type=str,
 
3555
            help="Mark a bug as being fixed by this revision "
 
3556
                 "(see \"brz help bugs\")."),
 
3557
        ListOption(
 
3558
            'author', type=str,
 
3559
            help="Set the author's name, if it's different "
 
3560
                 "from the committer."),
 
3561
        Option('local',
 
3562
               help="Perform a local commit in a bound "
 
3563
                    "branch.  Local commits are not pushed to "
 
3564
                    "the master branch until a normal commit "
 
3565
                    "is performed."
 
3566
               ),
 
3567
        Option('show-diff', short_name='p',
 
3568
               help='When no message is supplied, show the diff along'
 
3569
               ' with the status summary in the message editor.'),
 
3570
        Option('lossy',
 
3571
               help='When committing to a foreign version control '
 
3572
               'system do not push data that can not be natively '
 
3573
               'represented.'), ]
3598
3574
    aliases = ['ci', 'checkin']
3599
3575
 
3600
 
    def _iter_bug_fix_urls(self, fixes, branch):
3601
 
        default_bugtracker  = None
 
3576
    def _iter_bug_urls(self, bugs, branch, status):
 
3577
        default_bugtracker = None
3602
3578
        # Configure the properties for bug fixing attributes.
3603
 
        for fixed_bug in fixes:
3604
 
            tokens = fixed_bug.split(':')
 
3579
        for bug in bugs:
 
3580
            tokens = bug.split(':')
3605
3581
            if len(tokens) == 1:
3606
3582
                if default_bugtracker is None:
3607
3583
                    branch_config = branch.get_config_stack()
3613
3589
                        "'tracker:id' or specify a default bug tracker "
3614
3590
                        "using the `bugtracker` option.\nSee "
3615
3591
                        "\"brz help bugs\" for more information on this "
3616
 
                        "feature. Commit refused.") % fixed_bug)
 
3592
                        "feature. Commit refused.") % bug)
3617
3593
                tag = default_bugtracker
3618
3594
                bug_id = tokens[0]
3619
3595
            elif len(tokens) != 2:
3620
3596
                raise errors.BzrCommandError(gettext(
3621
3597
                    "Invalid bug %s. Must be in the form of 'tracker:id'. "
3622
3598
                    "See \"brz help bugs\" for more information on this "
3623
 
                    "feature.\nCommit refused.") % fixed_bug)
 
3599
                    "feature.\nCommit refused.") % bug)
3624
3600
            else:
3625
3601
                tag, bug_id = tokens
3626
3602
            try:
3627
 
                yield bugtracker.get_bug_url(tag, branch, bug_id)
 
3603
                yield bugtracker.get_bug_url(tag, branch, bug_id), status
3628
3604
            except bugtracker.UnknownBugTrackerAbbreviation:
3629
3605
                raise errors.BzrCommandError(gettext(
3630
 
                    'Unrecognized bug %s. Commit refused.') % fixed_bug)
 
3606
                    'Unrecognized bug %s. Commit refused.') % bug)
3631
3607
            except bugtracker.MalformedBugIdentifier as e:
3632
3608
                raise errors.BzrCommandError(gettext(
3633
3609
                    u"%s\nCommit refused.") % (e,))
3634
3610
 
3635
3611
    def run(self, message=None, file=None, verbose=False, selected_list=None,
3636
 
            unchanged=False, strict=False, local=False, fixes=None,
 
3612
            unchanged=False, strict=False, local=False, fixes=None, bugs=None,
3637
3613
            author=None, show_diff=False, exclude=None, commit_time=None,
3638
3614
            lossy=False):
 
3615
        import itertools
3639
3616
        from .commit import (
3640
3617
            PointlessCommit,
3641
3618
            )
3669
3646
 
3670
3647
        if fixes is None:
3671
3648
            fixes = []
 
3649
        if bugs is None:
 
3650
            bugs = []
3672
3651
        bug_property = bugtracker.encode_fixes_bug_urls(
3673
 
            self._iter_bug_fix_urls(fixes, tree.branch))
 
3652
            itertools.chain(
 
3653
                self._iter_bug_urls(bugs, tree.branch, bugtracker.RELATED),
 
3654
                self._iter_bug_urls(fixes, tree.branch, bugtracker.FIXED)))
3674
3655
        if bug_property:
3675
 
            properties['bugs'] = bug_property
 
3656
            properties[u'bugs'] = bug_property
3676
3657
 
3677
3658
        if local and not tree.branch.get_bound_location():
3678
3659
            raise errors.LocalRequiresBoundBranch()
3689
3670
                warning_msg = (
3690
3671
                    'The commit message is a file name: "%(f)s".\n'
3691
3672
                    '(use --file "%(f)s" to take commit message from that file)'
3692
 
                    % { 'f': message })
 
3673
                    % {'f': message})
3693
3674
                ui.ui_factory.show_warning(warning_msg)
3694
3675
            if '\r' in message:
3695
3676
                message = message.replace('\r\n', '\n')
3701
3682
        def get_message(commit_obj):
3702
3683
            """Callback to get commit message"""
3703
3684
            if file:
3704
 
                f = open(file)
3705
 
                try:
 
3685
                with open(file, 'rb') as f:
3706
3686
                    my_message = f.read().decode(osutils.get_user_encoding())
3707
 
                finally:
3708
 
                    f.close()
3709
3687
            elif message is not None:
3710
3688
                my_message = message
3711
3689
            else:
3712
3690
                # No message supplied: make one up.
3713
3691
                # text is the status of the tree
3714
3692
                text = make_commit_message_template_encoded(tree,
3715
 
                        selected_list, diff=show_diff,
3716
 
                        output_encoding=osutils.get_user_encoding())
 
3693
                                                            selected_list, diff=show_diff,
 
3694
                                                            output_encoding=osutils.get_user_encoding())
3717
3695
                # start_message is the template generated from hooks
3718
3696
                # XXX: Warning - looks like hooks return unicode,
3719
3697
                # make_commit_message_template_encoded returns user encoding.
3721
3699
                # avoid this.
3722
3700
                my_message = set_commit_message(commit_obj)
3723
3701
                if my_message is None:
3724
 
                    start_message = generate_commit_message_template(commit_obj)
 
3702
                    start_message = generate_commit_message_template(
 
3703
                        commit_obj)
 
3704
                    if start_message is not None:
 
3705
                        start_message = start_message.encode(
 
3706
                            osutils.get_user_encoding())
3725
3707
                    my_message = edit_commit_message_encoded(text,
3726
 
                        start_message=start_message)
 
3708
                                                             start_message=start_message)
3727
3709
                if my_message is None:
3728
3710
                    raise errors.BzrCommandError(gettext("please specify a commit"
3729
 
                        " message with either --message or --file"))
 
3711
                                                         " message with either --message or --file"))
3730
3712
                if my_message == "":
3731
3713
                    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 \"\"."))
 
3714
                                                         " Please specify a commit message with either"
 
3715
                                                         " --message or --file or leave a blank message"
 
3716
                                                         " with --message \"\"."))
3735
3717
            return my_message
3736
3718
 
3737
3719
        # The API permits a commit with a filter of [] to mean 'select nothing'
3749
3731
                        lossy=lossy)
3750
3732
        except PointlessCommit:
3751
3733
            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."))
 
3734
                                                 " Please 'brz add' the files you want to commit, or use"
 
3735
                                                 " --unchanged to force an empty commit."))
3754
3736
        except ConflictsInTree:
3755
3737
            raise errors.BzrCommandError(gettext('Conflicts detected in working '
3756
 
                'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
3757
 
                ' resolve.'))
 
3738
                                                 'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
 
3739
                                                 ' resolve.'))
3758
3740
        except StrictCommitFailed:
3759
3741
            raise errors.BzrCommandError(gettext("Commit refused because there are"
3760
 
                              " unknown files in the working tree."))
 
3742
                                                 " unknown files in the working tree."))
3761
3743
        except errors.BoundBranchOutOfDate as e:
3762
3744
            e.extra_help = (gettext("\n"
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.'))
 
3745
                                    'To commit to master branch, run update and then commit.\n'
 
3746
                                    'You can also pass --local to commit to continue working '
 
3747
                                    'disconnected.'))
3766
3748
            raise
3767
3749
 
3768
3750
 
3786
3768
    unreferenced ancestors
3787
3769
        Texts that are ancestors of other texts, but
3788
3770
        are not properly referenced by the revision ancestry.  This is a
3789
 
        subtle problem that Bazaar can work around.
 
3771
        subtle problem that Breezy can work around.
3790
3772
 
3791
3773
    unique file texts
3792
3774
        This is the total number of unique file contents
3798
3780
        entries are modified, but the file contents are not.  It does not
3799
3781
        indicate a problem.
3800
3782
 
3801
 
    If no restrictions are specified, all Bazaar data that is found at the given
 
3783
    If no restrictions are specified, all data that is found at the given
3802
3784
    location will be checked.
3803
3785
 
3804
3786
    :Examples:
3840
3822
    __doc__ = """Upgrade a repository, branch or working tree to a newer format.
3841
3823
 
3842
3824
    When the default format has changed after a major new release of
3843
 
    Bazaar, you may be informed during certain operations that you
 
3825
    Bazaar/Breezy, you may be informed during certain operations that you
3844
3826
    should upgrade. Upgrading to a newer format may improve performance
3845
3827
    or make new features available. It may however limit interoperability
3846
 
    with older repositories or with older versions of Bazaar.
 
3828
    with older repositories or with older versions of Bazaar or Breezy.
3847
3829
 
3848
3830
    If you wish to upgrade to a particular format rather than the
3849
3831
    current default, that can be specified using the --format option.
3865
3847
    If the conversion of a branch fails, remaining branches are still
3866
3848
    tried.
3867
3849
 
3868
 
    For more information on upgrades, see the Bazaar Upgrade Guide,
3869
 
    http://doc.bazaar.canonical.com/latest/en/upgrade-guide/.
 
3850
    For more information on upgrades, see the Breezy Upgrade Guide,
 
3851
    https://www.breezy-vcs.org/doc/en/upgrade-guide/.
3870
3852
    """
3871
3853
 
3872
3854
    _see_also = ['check', 'reconcile', 'formats']
3873
3855
    takes_args = ['url?']
3874
3856
    takes_options = [
3875
3857
        RegistryOption('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'),
 
3858
                       help='Upgrade to a specific format.  See "brz help'
 
3859
                       ' formats" for details.',
 
3860
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
3861
                       converter=lambda name: controldir.format_registry.make_controldir(
 
3862
                           name),
 
3863
                       value_switches=True, title='Branch format'),
3881
3864
        Option('clean',
3882
 
            help='Remove the backup.bzr directory if successful.'),
 
3865
               help='Remove the backup.bzr directory if successful.'),
3883
3866
        Option('dry-run',
3884
 
            help="Show what would be done, but don't actually do anything."),
 
3867
               help="Show what would be done, but don't actually do anything."),
3885
3868
    ]
3886
3869
 
3887
3870
    def run(self, url='.', format=None, clean=False, dry_run=False):
3907
3890
 
3908
3891
            brz whoami "Frank Chu <fchu@example.com>"
3909
3892
    """
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
 
                    ]
 
3893
    takes_options = ['directory',
 
3894
                     Option('email',
 
3895
                            help='Display email address only.'),
 
3896
                     Option('branch',
 
3897
                            help='Set identity for the current branch instead of '
 
3898
                            'globally.'),
 
3899
                     ]
3917
3900
    takes_args = ['name?']
3918
3901
    encoding_type = 'replace'
3919
3902
 
3938
3921
 
3939
3922
        if email:
3940
3923
            raise errors.BzrCommandError(gettext("--email can only be used to display existing "
3941
 
                                         "identity"))
 
3924
                                                 "identity"))
3942
3925
 
3943
3926
        # display a warning if an email address isn't included in the given name.
3944
3927
        try:
3945
3928
            _mod_config.extract_email_address(name)
3946
 
        except _mod_config.NoEmailInUsername as e:
 
3929
        except _mod_config.NoEmailInUsername:
3947
3930
            warning('"%s" does not seem to contain an email address.  '
3948
3931
                    'This is allowed, but not recommended.', name)
3949
3932
 
3953
3936
                c = Branch.open_containing(u'.')[0].get_config_stack()
3954
3937
            else:
3955
3938
                b = Branch.open(directory)
3956
 
                self.add_cleanup(b.lock_write().unlock)
 
3939
                self.enter_context(b.lock_write())
3957
3940
                c = b.get_config_stack()
3958
3941
        else:
3959
3942
            c = _mod_config.GlobalStack()
3974
3957
    _see_also = ['info']
3975
3958
    takes_args = ['nickname?']
3976
3959
    takes_options = ['directory']
 
3960
 
3977
3961
    def run(self, nickname=None, directory=u'.'):
3978
3962
        branch = Branch.open_containing(directory)[0]
3979
3963
        if nickname is None:
4022
4006
            if equal_pos == -1:
4023
4007
                self.print_alias(name)
4024
4008
            else:
4025
 
                self.set_alias(name[:equal_pos], name[equal_pos+1:])
 
4009
                self.set_alias(name[:equal_pos], name[equal_pos + 1:])
4026
4010
 
4027
4011
    def remove_alias(self, alias_name):
4028
4012
        if alias_name is None:
4037
4021
    def print_aliases(self):
4038
4022
        """Print out the defined aliases in a similar format to bash."""
4039
4023
        aliases = _mod_config.GlobalConfig().get_aliases()
4040
 
        for key, value in sorted(viewitems(aliases)):
 
4024
        for key, value in sorted(aliases.items()):
4041
4025
            self.outf.write('brz alias %s="%s"\n' % (key, value))
4042
4026
 
4043
4027
    @display_command
4107
4091
    """
4108
4092
    # NB: this is used from the class without creating an instance, which is
4109
4093
    # why it does not have a self parameter.
 
4094
 
4110
4095
    def get_transport_type(typestring):
4111
4096
        """Parse and return a transport specifier."""
4112
4097
        if typestring == "sftp":
4126
4111
    takes_args = ['testspecs*']
4127
4112
    takes_options = ['verbose',
4128
4113
                     Option('one',
4129
 
                             help='Stop when one test fails.',
4130
 
                             short_name='1',
4131
 
                             ),
 
4114
                            help='Stop when one test fails.',
 
4115
                            short_name='1',
 
4116
                            ),
4132
4117
                     Option('transport',
4133
4118
                            help='Use a different transport by default '
4134
4119
                                 'throughout the test suite.',
4148
4133
                     Option('list-only',
4149
4134
                            help='List the tests instead of running them.'),
4150
4135
                     RegistryOption('parallel',
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",
 
4136
                                    help="Run the test suite in parallel.",
 
4137
                                    lazy_registry=(
 
4138
                                        'breezy.tests', 'parallel_registry'),
 
4139
                                    value_switches=False,
 
4140
                                    ),
 
4141
                     Option('randomize', type=str, argname="SEED",
4156
4142
                            help='Randomize the order of tests using the given'
4157
4143
                                 ' seed or "now" for the current time.'),
4158
 
                     ListOption('exclude', type=text_type, argname="PATTERN",
 
4144
                     ListOption('exclude', type=str, argname="PATTERN",
4159
4145
                                short_name='x',
4160
4146
                                help='Exclude tests that match this regular'
4161
4147
                                ' expression.'),
4165
4151
                            help='Output test progress via subunit v2.'),
4166
4152
                     Option('strict', help='Fail on missing dependencies or '
4167
4153
                            'known failures.'),
4168
 
                     Option('load-list', type=text_type, argname='TESTLISTFILE',
 
4154
                     Option('load-list', type=str, argname='TESTLISTFILE',
4169
4155
                            help='Load a test id list from a text file.'),
4170
 
                     ListOption('debugflag', type=text_type, short_name='E',
 
4156
                     ListOption('debugflag', type=str, short_name='E',
4171
4157
                                help='Turn on a selftest debug flag.'),
4172
 
                     ListOption('starting-with', type=text_type, argname='TESTID',
 
4158
                     ListOption('starting-with', type=str, argname='TESTID',
4173
4159
                                param_name='starting_with', short_name='s',
4174
 
                                help=
4175
 
                                'Load only the tests starting with TESTID.'),
 
4160
                                help='Load only the tests starting with TESTID.'),
4176
4161
                     Option('sync',
4177
4162
                            help="By default we disable fsync and fdatasync"
4178
4163
                                 " while running the test suite.")
4197
4182
        # too heavily. The call should be as early as possible, as
4198
4183
        # error reporting for past duplicate imports won't have useful
4199
4184
        # backtraces.
4200
 
        lazy_import.disallow_proxying()
 
4185
        if sys.version_info[0] < 3:
 
4186
            # TODO(pad.lv/1696545): Allow proxying on Python 3, since
 
4187
            # disallowing it currently leads to failures in many places.
 
4188
            lazy_import.disallow_proxying()
4201
4189
 
4202
 
        from . import tests
 
4190
        try:
 
4191
            from . import tests
 
4192
        except ImportError:
 
4193
            raise errors.BzrCommandError("tests not available. Install the "
 
4194
                                         "breezy tests to run the breezy testsuite.")
4203
4195
 
4204
4196
        if testspecs_list is not None:
4205
4197
            pattern = '|'.join(testspecs_list)
4214
4206
                    "to use --subunit."))
4215
4207
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
4216
4208
            # On Windows, disable automatic conversion of '\n' to '\r\n' in
4217
 
            # stdout, which would corrupt the subunit stream. 
 
4209
            # stdout, which would corrupt the subunit stream.
4218
4210
            # FIXME: This has been fixed in subunit trunk (>0.0.5) so the
4219
4211
            # following code can be deleted when it's sufficiently deployed
4220
4212
            # -- vila/mgz 20100514
4221
4213
            if (sys.platform == "win32"
4222
 
                and getattr(sys.stdout, 'fileno', None) is not None):
 
4214
                    and getattr(sys.stdout, 'fileno', None) is not None):
4223
4215
                import msvcrt
4224
4216
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4225
4217
        if subunit2:
4246
4238
        if not sync:
4247
4239
            self._disable_fsync()
4248
4240
        selftest_kwargs = {"verbose": verbose,
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
 
                          }
 
4241
                           "pattern": pattern,
 
4242
                           "stop_on_failure": one,
 
4243
                           "transport": transport,
 
4244
                           "test_suite_factory": test_suite_factory,
 
4245
                           "lsprof_timed": lsprof_timed,
 
4246
                           "lsprof_tests": lsprof_tests,
 
4247
                           "matching_tests_first": first,
 
4248
                           "list_only": list_only,
 
4249
                           "random_seed": randomize,
 
4250
                           "exclude_pattern": exclude_pattern,
 
4251
                           "strict": strict,
 
4252
                           "load_list": load_list,
 
4253
                           "debug_flags": debugflag,
 
4254
                           "starting_with": starting_with
 
4255
                           }
4264
4256
        selftest_kwargs.update(self.additional_selftest_args)
4265
4257
 
4266
4258
        # Make deprecation warnings visible, unless -Werror is set
4322
4314
 
4323
4315
        branch1 = Branch.open_containing(branch)[0]
4324
4316
        branch2 = Branch.open_containing(other)[0]
4325
 
        self.add_cleanup(branch1.lock_read().unlock)
4326
 
        self.add_cleanup(branch2.lock_read().unlock)
 
4317
        self.enter_context(branch1.lock_read())
 
4318
        self.enter_context(branch2.lock_read())
4327
4319
        last1 = ensure_null(branch1.last_revision())
4328
4320
        last2 = ensure_null(branch2.last_revision())
4329
4321
 
4330
4322
        graph = branch1.repository.get_graph(branch2.repository)
4331
4323
        base_rev_id = graph.find_unique_lca(last1, last2)
4332
4324
 
4333
 
        self.outf.write(gettext('merge base is revision %s\n') % base_rev_id)
 
4325
        self.outf.write(gettext('merge base is revision %s\n') %
 
4326
                        base_rev_id.decode('utf-8'))
4334
4327
 
4335
4328
 
4336
4329
class cmd_merge(Command):
4355
4348
    through OTHER, excluding BASE but including OTHER, will be merged.  If this
4356
4349
    causes some revisions to be skipped, i.e. if the destination branch does
4357
4350
    not already contain revision BASE, such a merge is commonly referred to as
4358
 
    a "cherrypick". Unlike a normal merge, Bazaar does not currently track
 
4351
    a "cherrypick". Unlike a normal merge, Breezy does not currently track
4359
4352
    cherrypicks. The changes look like a normal commit, and the history of the
4360
4353
    changes from the other branch is not stored in the commit.
4361
4354
 
4378
4371
    committed to record the result of the merge.
4379
4372
 
4380
4373
    merge refuses to run if there are any uncommitted changes, unless
4381
 
    --force is given.  If --force is given, then the changes from the source 
 
4374
    --force is given.  If --force is given, then the changes from the source
4382
4375
    will be merged with the current working tree, including any uncommitted
4383
4376
    changes in the tree.  The --force option can also be used to create a
4384
4377
    merge revision which has more than two parents.
4431
4424
        Option('uncommitted', help='Apply uncommitted changes'
4432
4425
               ' from a working copy, instead of branch changes.'),
4433
4426
        Option('pull', help='If the destination is already'
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.'),
 
4427
               ' completely merged into the source, pull from the'
 
4428
               ' source rather than merging.  When this happens,'
 
4429
               ' you do not need to commit the result.'),
4437
4430
        custom_help('directory',
4438
 
               help='Branch to merge into, '
 
4431
                    help='Branch to merge into, '
4439
4432
                    'rather than the one containing the working directory.'),
4440
4433
        Option('preview', help='Instead of merging, show a diff of the'
4441
4434
               ' merge.'),
4442
4435
        Option('interactive', help='Select changes interactively.',
4443
 
            short_name='i')
 
4436
               short_name='i')
4444
4437
    ]
4445
4438
 
4446
4439
    def run(self, location=None, revision=None, force=False,
4453
4446
        if merge_type is None:
4454
4447
            merge_type = _mod_merge.Merge3Merger
4455
4448
 
4456
 
        if directory is None: directory = u'.'
 
4449
        if directory is None:
 
4450
            directory = u'.'
4457
4451
        possible_transports = []
4458
4452
        merger = None
4459
4453
        allow_pending = True
4460
4454
        verified = 'inapplicable'
4461
4455
 
4462
4456
        tree = WorkingTree.open_containing(directory)[0]
4463
 
        if tree.branch.revno() == 0:
 
4457
        if tree.branch.last_revision() == _mod_revision.NULL_REVISION:
4464
4458
            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()
 
4459
                                                 'https://bugs.launchpad.net/bzr/+bug/308562'))
4471
4460
 
4472
4461
        # die as quickly as possible if there are uncommitted changes
4473
4462
        if not force:
4478
4467
        change_reporter = delta._ChangeReporter(
4479
4468
            unversioned_filter=tree.is_ignored, view_info=view_info)
4480
4469
        pb = ui.ui_factory.nested_progress_bar()
4481
 
        self.add_cleanup(pb.finished)
4482
 
        self.add_cleanup(tree.lock_write().unlock)
 
4470
        self.enter_context(pb)
 
4471
        self.enter_context(tree.lock_write())
4483
4472
        if location is not None:
4484
4473
            try:
4485
 
                mergeable = bundle.read_mergeable_from_url(location,
4486
 
                    possible_transports=possible_transports)
 
4474
                mergeable = _mod_mergeable.read_mergeable_from_url(
 
4475
                    location, possible_transports=possible_transports)
4487
4476
            except errors.NotABundle:
4488
4477
                mergeable = None
4489
4478
            else:
4490
4479
                if uncommitted:
4491
4480
                    raise errors.BzrCommandError(gettext('Cannot use --uncommitted'
4492
 
                        ' with bundles or merge directives.'))
 
4481
                                                         ' with bundles or merge directives.'))
4493
4482
 
4494
4483
                if revision is not None:
4495
4484
                    raise errors.BzrCommandError(gettext(
4496
4485
                        'Cannot use -r with merge directives or bundles'))
4497
4486
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
4498
 
                   mergeable)
 
4487
                                                                    mergeable)
4499
4488
 
4500
4489
        if merger is None and uncommitted:
4501
4490
            if revision is not None and len(revision) > 0:
4502
4491
                raise errors.BzrCommandError(gettext('Cannot use --uncommitted and'
4503
 
                    ' --revision at the same time.'))
 
4492
                                                     ' --revision at the same time.'))
4504
4493
            merger = self.get_merger_from_uncommitted(tree, location, None)
4505
4494
            allow_pending = False
4506
4495
 
4507
4496
        if merger is None:
4508
4497
            merger, allow_pending = self._get_merger_from_branch(tree,
4509
 
                location, revision, remember, possible_transports, None)
 
4498
                                                                 location, revision, remember, possible_transports, None)
4510
4499
 
4511
4500
        merger.merge_type = merge_type
4512
4501
        merger.reprocess = reprocess
4513
4502
        merger.show_base = show_base
4514
4503
        self.sanity_check_merger(merger)
4515
4504
        if (merger.base_rev_id == merger.other_rev_id and
4516
 
            merger.other_rev_id is not None):
 
4505
                merger.other_rev_id is not None):
4517
4506
            # check if location is a nonexistent file (and not a branch) to
4518
4507
            # disambiguate the 'Nothing to do'
4519
4508
            if merger.interesting_files:
4520
4509
                if not merger.other_tree.has_filename(
4521
 
                    merger.interesting_files[0]):
 
4510
                        merger.interesting_files[0]):
4522
4511
                    note(gettext("merger: ") + str(merger))
4523
4512
                    raise errors.PathsDoNotExist([location])
4524
4513
            note(gettext('Nothing to do.'))
4525
4514
            return 0
4526
4515
        if pull and not preview:
4527
4516
            if merger.interesting_files is not None:
4528
 
                raise errors.BzrCommandError(gettext('Cannot pull individual files'))
 
4517
                raise errors.BzrCommandError(
 
4518
                    gettext('Cannot pull individual files'))
4529
4519
            if (merger.base_rev_id == tree.last_revision()):
4530
4520
                result = tree.pull(merger.other_branch, False,
4531
4521
                                   merger.other_rev_id)
4546
4536
    def _get_preview(self, merger):
4547
4537
        tree_merger = merger.make_merger()
4548
4538
        tt = tree_merger.make_preview_transform()
4549
 
        self.add_cleanup(tt.finalize)
 
4539
        self.enter_context(tt)
4550
4540
        result_tree = tt.get_preview_tree()
4551
4541
        return result_tree
4552
4542
 
4590
4580
 
4591
4581
    def sanity_check_merger(self, merger):
4592
4582
        if (merger.show_base and
4593
 
            not merger.merge_type is _mod_merge.Merge3Merger):
 
4583
                merger.merge_type is not _mod_merge.Merge3Merger):
4594
4584
            raise errors.BzrCommandError(gettext("Show-base is not supported for this"
4595
 
                                         " merge type. %s") % merger.merge_type)
 
4585
                                                 " merge type. %s") % merger.merge_type)
4596
4586
        if merger.reprocess is None:
4597
4587
            if merger.show_base:
4598
4588
                merger.reprocess = False
4601
4591
                merger.reprocess = merger.merge_type.supports_reprocess
4602
4592
        if merger.reprocess and not merger.merge_type.supports_reprocess:
4603
4593
            raise errors.BzrCommandError(gettext("Conflict reduction is not supported"
4604
 
                                         " for merge type %s.") %
 
4594
                                                 " for merge type %s.") %
4605
4595
                                         merger.merge_type)
4606
4596
        if merger.reprocess and merger.show_base:
4607
4597
            raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
4608
 
                                         " show base."))
 
4598
                                                 " show base."))
 
4599
 
 
4600
        if (merger.merge_type.requires_file_merge_plan and
 
4601
            (not getattr(merger.this_tree, 'plan_file_merge', None) or
 
4602
             not getattr(merger.other_tree, 'plan_file_merge', None) or
 
4603
             (merger.base_tree is not None and
 
4604
                 not getattr(merger.base_tree, 'plan_file_merge', None)))):
 
4605
            raise errors.BzrCommandError(
 
4606
                gettext('Plan file merge unsupported: '
 
4607
                        'Merge type incompatible with tree formats.'))
4609
4608
 
4610
4609
    def _get_merger_from_branch(self, tree, location, revision, remember,
4611
4610
                                possible_transports, pb):
4612
4611
        """Produce a merger from a location, assuming it refers to a branch."""
4613
4612
        # find the branch locations
4614
4613
        other_loc, user_location = self._select_branch_location(tree, location,
4615
 
            revision, -1)
 
4614
                                                                revision, -1)
4616
4615
        if revision is not None and len(revision) == 2:
4617
4616
            base_loc, _unused = self._select_branch_location(tree,
4618
 
                location, revision, 0)
 
4617
                                                             location, revision, 0)
4619
4618
        else:
4620
4619
            base_loc = other_loc
4621
4620
        # Open the branches
4622
4621
        other_branch, other_path = Branch.open_containing(other_loc,
4623
 
            possible_transports)
 
4622
                                                          possible_transports)
4624
4623
        if base_loc == other_loc:
4625
4624
            base_branch = other_branch
4626
4625
        else:
4627
4626
            base_branch, base_path = Branch.open_containing(base_loc,
4628
 
                possible_transports)
 
4627
                                                            possible_transports)
4629
4628
        # Find the revision ids
4630
4629
        other_revision_id = None
4631
4630
        base_revision_id = None
4643
4642
        # - user ask to remember or there is no previous location set to merge
4644
4643
        #   from and user didn't ask to *not* remember
4645
4644
        if (user_location is not None
4646
 
            and ((remember
4647
 
                  or (remember is None
4648
 
                      and tree.branch.get_submit_branch() is None)))):
 
4645
            and ((remember or
 
4646
                 (remember is None and
 
4647
                  tree.branch.get_submit_branch() is None)))):
4649
4648
            tree.branch.set_submit_branch(other_branch.base)
4650
4649
        # Merge tags (but don't set them in the master branch yet, the user
4651
4650
        # might revert this merge).  Commit will propagate them.
4652
4651
        other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
4653
4652
        merger = _mod_merge.Merger.from_revision_ids(tree,
4654
 
            other_revision_id, base_revision_id, other_branch, base_branch)
 
4653
                                                     other_revision_id, base_revision_id, other_branch, base_branch)
4655
4654
        if other_path != '':
4656
4655
            allow_pending = False
4657
4656
            merger.interesting_files = [other_path]
4692
4691
            will be the user-entered location.
4693
4692
        """
4694
4693
        if (revision is not None and index is not None
4695
 
            and revision[index] is not None):
 
4694
                and revision[index] is not None):
4696
4695
            branch = revision[index].get_branch()
4697
4696
            if branch is not None:
4698
4697
                return branch, branch
4714
4713
            stored_location_type = "parent"
4715
4714
        mutter("%s", stored_location)
4716
4715
        if stored_location is None:
4717
 
            raise errors.BzrCommandError(gettext("No location specified or remembered"))
 
4716
            raise errors.BzrCommandError(
 
4717
                gettext("No location specified or remembered"))
4718
4718
        display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
4719
4719
        note(gettext("{0} remembered {1} location {2}").format(verb_string,
4720
 
                stored_location_type, display_url))
 
4720
                                                               stored_location_type, display_url))
4721
4721
        return stored_location
4722
4722
 
4723
4723
 
4745
4745
    """
4746
4746
    takes_args = ['file*']
4747
4747
    takes_options = [
4748
 
            'merge-type',
4749
 
            'reprocess',
4750
 
            Option('show-base',
4751
 
                   help="Show base revision text in conflicts."),
4752
 
            ]
 
4748
        'merge-type',
 
4749
        'reprocess',
 
4750
        Option('show-base',
 
4751
               help="Show base revision text in conflicts."),
 
4752
        ]
4753
4753
 
4754
4754
    def run(self, file_list=None, merge_type=None, show_base=False,
4755
4755
            reprocess=False):
4757
4757
        if merge_type is None:
4758
4758
            merge_type = _mod_merge.Merge3Merger
4759
4759
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4760
 
        self.add_cleanup(tree.lock_write().unlock)
 
4760
        self.enter_context(tree.lock_write())
4761
4761
        parents = tree.get_parent_ids()
4762
4762
        if len(parents) != 2:
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
 
4763
            raise errors.BzrCommandError(
 
4764
                gettext("Sorry, remerge only works after normal"
 
4765
                        " merges.  Not cherrypicking or multi-merges."))
 
4766
        interesting_files = None
4768
4767
        new_conflicts = []
4769
4768
        conflicts = tree.conflicts()
4770
4769
        if file_list is not None:
4771
 
            interesting_ids = set()
 
4770
            interesting_files = set()
4772
4771
            for filename in file_list:
4773
 
                file_id = tree.path2id(filename)
4774
 
                if file_id is None:
 
4772
                if not tree.is_versioned(filename):
4775
4773
                    raise errors.NotVersionedError(filename)
4776
 
                interesting_ids.add(file_id)
4777
 
                if tree.kind(filename, file_id) != "directory":
 
4774
                interesting_files.add(filename)
 
4775
                if tree.kind(filename) != "directory":
4778
4776
                    continue
4779
4777
 
4780
 
                # FIXME: Support nested trees
4781
 
                for name, ie in tree.root_inventory.iter_entries(file_id):
4782
 
                    interesting_ids.add(ie.file_id)
 
4778
                for path, ie in tree.iter_entries_by_dir(
 
4779
                        specific_files=[filename]):
 
4780
                    interesting_files.add(path)
4783
4781
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4784
4782
        else:
4785
4783
            # Remerge only supports resolving contents conflicts
4786
4784
            allowed_conflicts = ('text conflict', 'contents conflict')
4787
4785
            restore_files = [c.path for c in conflicts
4788
4786
                             if c.typestring in allowed_conflicts]
4789
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4787
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
4790
4788
        tree.set_conflicts(ConflictList(new_conflicts))
4791
4789
        if file_list is not None:
4792
4790
            restore_files = file_list
4803
4801
        tree.set_parent_ids(parents[:1])
4804
4802
        try:
4805
4803
            merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
4806
 
            merger.interesting_ids = interesting_ids
 
4804
            merger.interesting_files = interesting_files
4807
4805
            merger.merge_type = merge_type
4808
4806
            merger.show_base = show_base
4809
4807
            merger.reprocess = reprocess
4836
4834
    update command.
4837
4835
 
4838
4836
    Uncommitted changes to files that are reverted will be discarded.
4839
 
    Howver, by default, any files that have been manually changed will be
 
4837
    However, by default, any files that have been manually changed will be
4840
4838
    backed up first.  (Files changed only by merge are not backed up.)  Backup
4841
4839
    files have '.~#~' appended to their name, where # is a number.
4842
4840
 
4882
4880
    def run(self, revision=None, no_backup=False, file_list=None,
4883
4881
            forget_merges=None):
4884
4882
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4885
 
        self.add_cleanup(tree.lock_tree_write().unlock)
 
4883
        self.enter_context(tree.lock_tree_write())
4886
4884
        if forget_merges:
4887
4885
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4888
4886
        else:
4892
4890
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4893
4891
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4894
4892
        tree.revert(file_list, rev_tree, not no_backup, None,
4895
 
            report_changes=True)
 
4893
                    report_changes=True)
4896
4894
 
4897
4895
 
4898
4896
class cmd_assert_fail(Command):
4911
4909
 
4912
4910
    _see_also = ['topics']
4913
4911
    takes_options = [
4914
 
            Option('long', 'Show help on all commands.'),
4915
 
            ]
 
4912
        Option('long', 'Show help on all commands.'),
 
4913
        ]
4916
4914
    takes_args = ['topic?']
4917
4915
    aliases = ['?', '--help', '-?', '-h']
4918
4916
 
4947
4945
    To filter on a range of revisions, you can use the command -r begin..end
4948
4946
    -r revision requests a specific revision, -r ..end or -r begin.. are
4949
4947
    also valid.
4950
 
            
 
4948
 
4951
4949
    :Exit values:
4952
4950
        1 - some missing revisions
4953
4951
        0 - no missing revisions
4989
4987
        'show-ids',
4990
4988
        'verbose',
4991
4989
        custom_help('revision',
4992
 
             help='Filter on other branch revisions (inclusive). '
4993
 
                'See "help revisionspec" for details.'),
 
4990
                    help='Filter on other branch revisions (inclusive). '
 
4991
                    'See "help revisionspec" for details.'),
4994
4992
        Option('my-revision',
4995
 
            type=_parse_revision_str,
4996
 
            help='Filter on local branch revisions (inclusive). '
4997
 
                'See "help revisionspec" for details.'),
 
4993
               type=_parse_revision_str,
 
4994
               help='Filter on local branch revisions (inclusive). '
 
4995
               'See "help revisionspec" for details.'),
4998
4996
        Option('include-merged',
4999
4997
               'Show all revisions in addition to the mainline ones.'),
5000
4998
        Option('include-merges', hidden=True,
5010
5008
            include_merged=None, revision=None, my_revision=None,
5011
5009
            directory=u'.'):
5012
5010
        from breezy.missing import find_unmerged, iter_log_revisions
 
5011
 
5013
5012
        def message(s):
5014
5013
            if not is_quiet():
5015
5014
                self.outf.write(s)
5030
5029
            restrict = 'remote'
5031
5030
 
5032
5031
        local_branch = Branch.open_containing(directory)[0]
5033
 
        self.add_cleanup(local_branch.lock_read().unlock)
 
5032
        self.enter_context(local_branch.lock_read())
5034
5033
 
5035
5034
        parent = local_branch.get_parent()
5036
5035
        if other_branch is None:
5037
5036
            other_branch = parent
5038
5037
            if other_branch is None:
5039
5038
                raise errors.BzrCommandError(gettext("No peer location known"
5040
 
                                             " or specified."))
 
5039
                                                     " or specified."))
5041
5040
            display_url = urlutils.unescape_for_display(parent,
5042
5041
                                                        self.outf.encoding)
5043
5042
            message(gettext("Using saved parent location: {0}\n").format(
5047
5046
        if remote_branch.base == local_branch.base:
5048
5047
            remote_branch = local_branch
5049
5048
        else:
5050
 
            self.add_cleanup(remote_branch.lock_read().unlock)
 
5049
            self.enter_context(remote_branch.lock_read())
5051
5050
 
5052
5051
        local_revid_range = _revision_range_to_revid_range(
5053
5052
            _get_revision_range(my_revision, local_branch,
5054
 
                self.name()))
 
5053
                                self.name()))
5055
5054
 
5056
5055
        remote_revid_range = _revision_range_to_revid_range(
5057
5056
            _get_revision_range(revision,
5058
 
                remote_branch, self.name()))
 
5057
                                remote_branch, self.name()))
5059
5058
 
5060
5059
        local_extra, remote_extra = find_unmerged(
5061
5060
            local_branch, remote_branch, restrict,
5074
5073
        status_code = 0
5075
5074
        if local_extra and not theirs_only:
5076
5075
            message(ngettext("You have %d extra revision:\n",
5077
 
                             "You have %d extra revisions:\n", 
 
5076
                             "You have %d extra revisions:\n",
5078
5077
                             len(local_extra)) %
5079
 
                len(local_extra))
 
5078
                    len(local_extra))
5080
5079
            rev_tag_dict = {}
5081
5080
            if local_branch.supports_tags():
5082
5081
                rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5083
5082
            for revision in iter_log_revisions(local_extra,
5084
 
                                local_branch.repository,
5085
 
                                verbose,
5086
 
                                rev_tag_dict):
 
5083
                                               local_branch.repository,
 
5084
                                               verbose,
 
5085
                                               rev_tag_dict):
5087
5086
                lf.log_revision(revision)
5088
5087
            printed_local = True
5089
5088
            status_code = 1
5096
5095
            message(ngettext("You are missing %d revision:\n",
5097
5096
                             "You are missing %d revisions:\n",
5098
5097
                             len(remote_extra)) %
5099
 
                len(remote_extra))
 
5098
                    len(remote_extra))
5100
5099
            if remote_branch.supports_tags():
5101
5100
                rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5102
5101
            for revision in iter_log_revisions(remote_extra,
5103
 
                                remote_branch.repository,
5104
 
                                verbose,
5105
 
                                rev_tag_dict):
 
5102
                                               remote_branch.repository,
 
5103
                                               verbose,
 
5104
                                               rev_tag_dict):
5106
5105
                lf.log_revision(revision)
5107
5106
            status_code = 1
5108
5107
 
5119
5118
            message(gettext("Branches are up to date.\n"))
5120
5119
        self.cleanup_now()
5121
5120
        if not status_code and parent is None and other_branch is not None:
5122
 
            self.add_cleanup(local_branch.lock_write().unlock)
 
5121
            self.enter_context(local_branch.lock_write())
5123
5122
            # handle race conditions - a parent might be set while we run.
5124
5123
            if local_branch.get_parent() is None:
5125
5124
                local_branch.set_parent(remote_branch.base)
5148
5147
    _see_also = ['repositories']
5149
5148
    takes_args = ['branch_or_repo?']
5150
5149
    takes_options = [
5151
 
        Option('clean-obsolete-packs', 'Delete obsolete packs to save disk space.'),
 
5150
        Option('clean-obsolete-packs',
 
5151
               'Delete obsolete packs to save disk space.'),
5152
5152
        ]
5153
5153
 
5154
5154
    def run(self, branch_or_repo='.', clean_obsolete_packs=False):
5169
5169
 
5170
5170
    --verbose shows the path where each plugin is located.
5171
5171
 
5172
 
    A plugin is an external component for Bazaar that extends the
5173
 
    revision control system, by adding or replacing code in Bazaar.
 
5172
    A plugin is an external component for Breezy that extends the
 
5173
    revision control system, by adding or replacing code in Breezy.
5174
5174
    Plugins can do a variety of things, including overriding commands,
5175
5175
    adding new commands, providing additional network transports and
5176
5176
    customizing log output.
5193
5193
class cmd_testament(Command):
5194
5194
    __doc__ = """Show testament (signing-form) of a revision."""
5195
5195
    takes_options = [
5196
 
            'revision',
5197
 
            Option('long', help='Produce long-format testament.'),
5198
 
            Option('strict',
5199
 
                   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.')]
5200
5200
    takes_args = ['branch?']
5201
5201
    encoding_type = 'exact'
 
5202
 
5202
5203
    @display_command
5203
5204
    def run(self, branch=u'.', revision=None, long=False, strict=False):
5204
 
        from .testament import Testament, StrictTestament
 
5205
        from .bzr.testament import Testament, StrictTestament
5205
5206
        if strict is True:
5206
5207
            testament_class = StrictTestament
5207
5208
        else:
5210
5211
            b = Branch.open_containing(branch)[0]
5211
5212
        else:
5212
5213
            b = Branch.open(branch)
5213
 
        self.add_cleanup(b.lock_read().unlock)
 
5214
        self.enter_context(b.lock_read())
5214
5215
        if revision is None:
5215
5216
            rev_id = b.last_revision()
5216
5217
        else:
5253
5254
        wt, branch, relpath = \
5254
5255
            _open_directory_or_containing_tree_or_branch(filename, directory)
5255
5256
        if wt is not None:
5256
 
            self.add_cleanup(wt.lock_read().unlock)
 
5257
            self.enter_context(wt.lock_read())
5257
5258
        else:
5258
 
            self.add_cleanup(branch.lock_read().unlock)
 
5259
            self.enter_context(branch.lock_read())
5259
5260
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
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:
 
5261
        self.enter_context(tree.lock_read())
 
5262
        if wt is not None and revision is None:
 
5263
            if not wt.is_versioned(relpath):
 
5264
                raise errors.NotVersionedError(relpath)
5268
5265
            # If there is a tree and we're not annotating historical
5269
5266
            # versions, annotate the working tree's content.
5270
5267
            annotate_file_tree(wt, relpath, self.outf, long, all,
5271
 
                show_ids=show_ids, file_id=file_id)
 
5268
                               show_ids=show_ids)
5272
5269
        else:
 
5270
            if not tree.is_versioned(relpath):
 
5271
                raise errors.NotVersionedError(relpath)
5273
5272
            annotate_file_tree(tree, relpath, self.outf, long, all,
5274
 
                show_ids=show_ids, branch=branch, file_id=file_id)
 
5273
                               show_ids=show_ids, branch=branch)
5275
5274
 
5276
5275
 
5277
5276
class cmd_re_sign(Command):
5278
5277
    __doc__ = """Create a digital signature for an existing revision."""
5279
5278
    # TODO be able to replace existing ones.
5280
5279
 
5281
 
    hidden = True # is this right ?
 
5280
    hidden = True  # is this right ?
5282
5281
    takes_args = ['revision_id*']
5283
5282
    takes_options = ['directory', 'revision']
5284
5283
 
5285
5284
    def run(self, revision_id_list=None, revision=None, directory=u'.'):
5286
5285
        if revision_id_list is not None and revision is not None:
5287
 
            raise errors.BzrCommandError(gettext('You can only supply one of revision_id or --revision'))
 
5286
            raise errors.BzrCommandError(
 
5287
                gettext('You can only supply one of revision_id or --revision'))
5288
5288
        if revision_id_list is None and revision is None:
5289
 
            raise errors.BzrCommandError(gettext('You must supply either --revision or a revision_id'))
 
5289
            raise errors.BzrCommandError(
 
5290
                gettext('You must supply either --revision or a revision_id'))
5290
5291
        b = WorkingTree.open_containing(directory)[0].branch
5291
 
        self.add_cleanup(b.lock_write().unlock)
 
5292
        self.enter_context(b.lock_write())
5292
5293
        return self._run(b, revision_id_list, revision)
5293
5294
 
5294
5295
    def _run(self, b, revision_id_list, revision):
5295
 
        from . import gpg
 
5296
        from .repository import WriteGroup
5296
5297
        gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
5297
5298
        if revision_id_list is not None:
5298
 
            b.repository.start_write_group()
5299
 
            try:
 
5299
            with WriteGroup(b.repository):
5300
5300
                for revision_id in revision_id_list:
5301
5301
                    revision_id = cache_utf8.encode(revision_id)
5302
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()
5308
5303
        elif revision is not None:
5309
5304
            if len(revision) == 1:
5310
5305
                revno, rev_id = revision[0].in_history(b)
5311
 
                b.repository.start_write_group()
5312
 
                try:
 
5306
                with WriteGroup(b.repository):
5313
5307
                    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()
5319
5308
            elif len(revision) == 2:
5320
5309
                # are they both on rh- if so we can walk between them
5321
5310
                # might be nice to have a range helper for arbitrary
5325
5314
                if to_revid is None:
5326
5315
                    to_revno = b.revno()
5327
5316
                if from_revno is None or to_revno is None:
5328
 
                    raise errors.BzrCommandError(gettext('Cannot sign a range of non-revision-history revisions'))
5329
 
                b.repository.start_write_group()
5330
 
                try:
 
5317
                    raise errors.BzrCommandError(
 
5318
                        gettext('Cannot sign a range of non-revision-history revisions'))
 
5319
                with WriteGroup(b.repository):
5331
5320
                    for revno in range(from_revno, to_revno + 1):
5332
5321
                        b.repository.sign_revision(b.get_rev_id(revno),
5333
5322
                                                   gpg_strategy)
5334
 
                except:
5335
 
                    b.repository.abort_write_group()
5336
 
                    raise
5337
 
                else:
5338
 
                    b.repository.commit_write_group()
5339
5323
            else:
5340
 
                raise errors.BzrCommandError(gettext('Please supply either one revision, or a range.'))
 
5324
                raise errors.BzrCommandError(
 
5325
                    gettext('Please supply either one revision, or a range.'))
5341
5326
 
5342
5327
 
5343
5328
class cmd_bind(Command):
5362
5347
            try:
5363
5348
                location = b.get_old_bound_location()
5364
5349
            except errors.UpgradeRequired:
5365
 
                raise errors.BzrCommandError(gettext('No location supplied.  '
5366
 
                    'This format does not remember old locations.'))
 
5350
                raise errors.BzrCommandError(
 
5351
                    gettext('No location supplied.  '
 
5352
                            'This format does not remember old locations.'))
5367
5353
            else:
5368
5354
                if location is None:
5369
5355
                    if b.get_bound_location() is not None:
5377
5363
        try:
5378
5364
            b.bind(b_other)
5379
5365
        except errors.DivergedBranches:
5380
 
            raise errors.BzrCommandError(gettext('These branches have diverged.'
5381
 
                                         ' Try merging, and then bind again.'))
 
5366
            raise errors.BzrCommandError(
 
5367
                gettext('These branches have diverged.'
 
5368
                        ' Try merging, and then bind again.'))
5382
5369
        if b.get_config().has_explicit_nickname():
5383
5370
            b.nick = b_other.nick
5384
5371
 
5422
5409
    # information in shared branches as well.
5423
5410
    _see_also = ['commit']
5424
5411
    takes_options = ['verbose', 'revision',
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
 
                    ]
 
5412
                     Option('dry-run', help='Don\'t actually make changes.'),
 
5413
                     Option('force', help='Say yes to all questions.'),
 
5414
                     Option('keep-tags',
 
5415
                            help='Keep tags that point to removed revisions.'),
 
5416
                     Option('local',
 
5417
                            help="Only remove the commits from the local "
 
5418
                            "branch when in a checkout."
 
5419
                            ),
 
5420
                     ]
5434
5421
    takes_args = ['location?']
5435
5422
    aliases = []
5436
5423
    encoding_type = 'replace'
5448
5435
            b = control.open_branch()
5449
5436
 
5450
5437
        if tree is not None:
5451
 
            self.add_cleanup(tree.lock_write().unlock)
 
5438
            self.enter_context(tree.lock_write())
5452
5439
        else:
5453
 
            self.add_cleanup(b.lock_write().unlock)
 
5440
            self.enter_context(b.lock_write())
5454
5441
        return self._run(b, tree, dry_run, verbose, revision, force,
5455
 
                         local, keep_tags)
 
5442
                         local, keep_tags, location)
5456
5443
 
5457
5444
    def _run(self, b, tree, dry_run, verbose, revision, force, local,
5458
 
             keep_tags):
 
5445
             keep_tags, location):
5459
5446
        from .log import log_formatter, show_log
5460
5447
        from .uncommit import uncommit
5461
5448
 
5492
5479
 
5493
5480
        if dry_run:
5494
5481
            self.outf.write(gettext('Dry-run, pretending to remove'
5495
 
                            ' the above revisions.\n'))
 
5482
                                    ' the above revisions.\n'))
5496
5483
        else:
5497
 
            self.outf.write(gettext('The above revision(s) will be removed.\n'))
 
5484
            self.outf.write(
 
5485
                gettext('The above revision(s) will be removed.\n'))
5498
5486
 
5499
5487
        if not force:
5500
5488
            if not ui.ui_factory.confirm_action(
5508
5496
               last_rev_id, rev_id)
5509
5497
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
5510
5498
                 revno=revno, local=local, keep_tags=keep_tags)
5511
 
        self.outf.write(gettext('You can restore the old tip by running:\n'
5512
 
             '  brz pull . -r revid:%s\n') % last_rev_id)
 
5499
        if location != '.':
 
5500
            self.outf.write(
 
5501
                gettext('You can restore the old tip by running:\n'
 
5502
                        '  brz pull -d %s %s -r revid:%s\n')
 
5503
                % (location, location, last_rev_id.decode('utf-8')))
 
5504
        else:
 
5505
            self.outf.write(
 
5506
                gettext('You can restore the old tip by running:\n'
 
5507
                        '  brz pull . -r revid:%s\n')
 
5508
                % last_rev_id.decode('utf-8'))
5513
5509
 
5514
5510
 
5515
5511
class cmd_break_lock(Command):
5527
5523
    :Examples:
5528
5524
        brz break-lock
5529
5525
        brz break-lock brz+ssh://example.com/brz/foo
5530
 
        brz break-lock --conf ~/.bazaar
 
5526
        brz break-lock --conf ~/.config/breezy
5531
5527
    """
5532
5528
 
5533
5529
    takes_args = ['location?']
5535
5531
        Option('config',
5536
5532
               help='LOCATION is the directory where the config lock is.'),
5537
5533
        Option('force',
5538
 
            help='Do not ask for confirmation before breaking the lock.'),
 
5534
               help='Do not ask for confirmation before breaking the lock.'),
5539
5535
        ]
5540
5536
 
5541
5537
    def run(self, location=None, config=False, force=False):
5543
5539
            location = u'.'
5544
5540
        if force:
5545
5541
            ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
5546
 
                None,
5547
 
                {'breezy.lockdir.break': True})
 
5542
                                                               None,
 
5543
                                                               {'breezy.lockdir.break': True})
5548
5544
        if config:
5549
5545
            conf = _mod_config.LockableConfig(file_name=location)
5550
5546
            conf.break_lock()
5579
5575
        Option('inet',
5580
5576
               help='Serve on stdin/out for use from inetd or sshd.'),
5581
5577
        RegistryOption('protocol',
5582
 
               help="Protocol to serve.",
5583
 
               lazy_registry=('breezy.transport', 'transport_server_registry'),
5584
 
               value_switches=True),
 
5578
                       help="Protocol to serve.",
 
5579
                       lazy_registry=('breezy.transport',
 
5580
                                      'transport_server_registry'),
 
5581
                       value_switches=True),
5585
5582
        Option('listen',
5586
 
               help='Listen for connections on nominated address.', type=text_type),
 
5583
               help='Listen for connections on nominated address.',
 
5584
               type=str),
5587
5585
        Option('port',
5588
5586
               help='Listen for connections on nominated port.  Passing 0 as '
5589
5587
                    'the port number will result in a dynamically allocated '
5590
5588
                    'port.  The default port depends on the protocol.',
5591
5589
               type=int),
5592
5590
        custom_help('directory',
5593
 
               help='Serve contents of this directory.'),
 
5591
                    help='Serve contents of this directory.'),
5594
5592
        Option('allow-writes',
5595
5593
               help='By default the server is a readonly server.  Supplying '
5596
5594
                    '--allow-writes enables write access to the contents of '
5599
5597
                    'external authentication is arranged supplying this '
5600
5598
                    'option leads to global uncontrolled write access to your '
5601
5599
                    'file system.'
5602
 
                ),
 
5600
               ),
5603
5601
        Option('client-timeout', type=float,
5604
5602
               help='Override the default idle client timeout (5min).'),
5605
5603
        ]
5606
5604
 
5607
5605
    def run(self, listen=None, port=None, inet=False, directory=None,
5608
5606
            allow_writes=False, protocol=None, client_timeout=None):
5609
 
        from . import transport
 
5607
        from . import location, transport
5610
5608
        if directory is None:
5611
5609
            directory = osutils.getcwd()
5612
5610
        if protocol is None:
5613
5611
            protocol = transport.transport_server_registry.get()
5614
 
        url = transport.location_to_url(directory)
 
5612
        url = location.location_to_url(directory)
5615
5613
        if not allow_writes:
5616
5614
            url = 'readonly+' + url
5617
5615
        t = transport.get_transport_from_url(url)
5635
5633
    _see_also = ['split']
5636
5634
    takes_args = ['tree']
5637
5635
    takes_options = [
5638
 
            Option('reference', help='Join by reference.', hidden=True),
5639
 
            ]
 
5636
        Option('reference', help='Join by reference.', hidden=True),
 
5637
        ]
5640
5638
 
5641
5639
    def run(self, tree, reference=False):
5642
5640
        from breezy.mutabletree import BadReferenceTarget
5656
5654
                # XXX: Would be better to just raise a nicely printable
5657
5655
                # exception from the real origin.  Also below.  mbp 20070306
5658
5656
                raise errors.BzrCommandError(
5659
 
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5657
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5660
5658
        else:
5661
5659
            try:
5662
5660
                containing_tree.subsume(sub_tree)
5663
5661
            except errors.BadSubsumeSource as e:
5664
5662
                raise errors.BzrCommandError(
5665
 
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5663
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5666
5664
 
5667
5665
 
5668
5666
class cmd_split(Command):
5682
5680
 
5683
5681
    def run(self, tree):
5684
5682
        containing_tree, subdir = WorkingTree.open_containing(tree)
5685
 
        sub_id = containing_tree.path2id(subdir)
5686
 
        if sub_id is None:
 
5683
        if not containing_tree.is_versioned(subdir):
5687
5684
            raise errors.NotVersionedError(subdir)
5688
5685
        try:
5689
 
            containing_tree.extract(subdir, sub_id)
 
5686
            containing_tree.extract(subdir)
5690
5687
        except errors.RootNotRich:
5691
5688
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
5692
5689
 
5716
5713
 
5717
5714
    takes_options = [
5718
5715
        'directory',
5719
 
        RegistryOption.from_kwargs('patch-type',
 
5716
        RegistryOption.from_kwargs(
 
5717
            'patch-type',
5720
5718
            'The type of patch to include in the directive.',
5721
5719
            title='Patch type',
5722
5720
            value_switches=True,
5725
5723
            diff='Normal unified diff.',
5726
5724
            plain='No patch, just directive.'),
5727
5725
        Option('sign', help='GPG-sign the directive.'), 'revision',
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.')
 
5726
        Option('mail-to', type=str,
 
5727
               help='Instead of printing the directive, email to this '
 
5728
               'address.'),
 
5729
        Option('message', type=str, short_name='m',
 
5730
               help='Message to use when committing this merge.')
5732
5731
        ]
5733
5732
 
5734
5733
    encoding_type = 'exact'
5752
5751
        if submit_branch is None:
5753
5752
            submit_branch = branch.get_parent()
5754
5753
        if submit_branch is None:
5755
 
            raise errors.BzrCommandError(gettext('No submit branch specified or known'))
 
5754
            raise errors.BzrCommandError(
 
5755
                gettext('No submit branch specified or known'))
5756
5756
 
5757
5757
        stored_public_branch = branch.get_public_branch()
5758
5758
        if public_branch is None:
5761
5761
            # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5762
5762
            branch.set_public_branch(public_branch)
5763
5763
        if not include_bundle and public_branch is None:
5764
 
            raise errors.BzrCommandError(gettext('No public branch specified or'
5765
 
                                         ' known'))
 
5764
            raise errors.BzrCommandError(
 
5765
                gettext('No public branch specified or known'))
5766
5766
        base_revision_id = None
5767
5767
        if revision is not None:
5768
5768
            if len(revision) > 2:
5769
 
                raise errors.BzrCommandError(gettext('brz merge-directive takes '
5770
 
                    'at most two one revision identifiers'))
 
5769
                raise errors.BzrCommandError(
 
5770
                    gettext('brz merge-directive takes '
 
5771
                            'at most two one revision identifiers'))
5771
5772
            revision_id = revision[-1].as_revision_id(branch)
5772
5773
            if len(revision) == 2:
5773
5774
                base_revision_id = revision[0].as_revision_id(branch)
5807
5808
      branch.
5808
5809
 
5809
5810
    `brz send` creates a compact data set that, when applied using brz
5810
 
    merge, has the same effect as merging from the source branch.  
5811
 
    
 
5811
    merge, has the same effect as merging from the source branch.
 
5812
 
5812
5813
    By default the merge directive is self-contained and can be applied to any
5813
5814
    branch containing submit_branch in its ancestory without needing access to
5814
5815
    the source branch.
5815
 
    
5816
 
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5816
 
 
5817
    If --no-bundle is specified, then Breezy doesn't send the contents of the
5817
5818
    revisions, but only a structured request to merge from the
5818
5819
    public_location.  In that case the public_branch is needed and it must be
5819
5820
    up-to-date and accessible to the recipient.  The public_branch is always
5844
5845
    If the preferred client can't be found (or used), your editor will be used.
5845
5846
 
5846
5847
    To use a specific mail program, set the mail_client configuration option.
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.
 
5848
    Supported values for specific clients are "claws", "evolution", "kmail",
 
5849
    "mail.app" (MacOS X's Mail.app), "mutt", and "thunderbird"; generic options
 
5850
    are "default", "editor", "emacsclient", "mapi", and "xdg-email".  Plugins
 
5851
    may also add supported clients.
5852
5852
 
5853
5853
    If mail is being sent, a to address is required.  This can be supplied
5854
5854
    either on the commandline, by setting the submit_to configuration
5855
5855
    option in the branch itself or the child_submit_to configuration option
5856
5856
    in the submit branch.
5857
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
 
 
5864
5858
    The merge directives created by brz send may be applied using brz merge or
5865
5859
    brz pull by specifying a file containing a merge directive as the location.
5866
5860
 
5886
5880
               help='Branch to generate the submission from, '
5887
5881
               'rather than the one containing the working directory.',
5888
5882
               short_name='f',
5889
 
               type=text_type),
 
5883
               type=str),
5890
5884
        Option('output', short_name='o',
5891
5885
               help='Write merge directive to this file or directory; '
5892
5886
                    'use - for stdout.',
5893
 
               type=text_type),
 
5887
               type=str),
5894
5888
        Option('strict',
5895
5889
               help='Refuse to send if there are uncommitted changes in'
5896
5890
               ' the working tree, --no-strict disables the check.'),
5897
5891
        Option('mail-to', help='Mail the request to this address.',
5898
 
               type=text_type),
 
5892
               type=str),
5899
5893
        'revision',
5900
5894
        'message',
5901
 
        Option('body', help='Body for the email.', type=text_type),
 
5895
        Option('body', help='Body for the email.', type=str),
5902
5896
        RegistryOption('format',
5903
5897
                       help='Use the specified output format.',
5904
5898
                       lazy_registry=('breezy.send', 'format_registry')),
5941
5935
    branch is used in the merge instructions.  This means that a local mirror
5942
5936
    can be used as your actual submit branch, once you have set public_branch
5943
5937
    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.
5950
5938
    """
5951
5939
 
5952
5940
    takes_options = [
5960
5948
               help='Branch to generate the submission from, '
5961
5949
               'rather than the one containing the working directory.',
5962
5950
               short_name='f',
5963
 
               type=text_type),
 
5951
               type=str),
5964
5952
        Option('output', short_name='o', help='Write directive to this file.',
5965
 
               type=text_type),
 
5953
               type=str),
5966
5954
        Option('strict',
5967
5955
               help='Refuse to bundle revisions if there are uncommitted'
5968
5956
               ' changes in the working tree, --no-strict disables the check.'),
5984
5972
            output = '-'
5985
5973
        from .send import send
5986
5974
        return send(submit_branch, revision, public_branch, remember,
5987
 
                         format, no_bundle, no_patch, output,
5988
 
                         kwargs.get('from', '.'), None, None, None,
5989
 
                         self.outf, strict=strict)
 
5975
                    format, no_bundle, no_patch, output,
 
5976
                    kwargs.get('from', '.'), None, None, None,
 
5977
                    self.outf, strict=strict)
5990
5978
 
5991
5979
 
5992
5980
class cmd_tag(Command):
6005
5993
    To rename a tag (change the name but keep it on the same revsion), run ``brz
6006
5994
    tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
6007
5995
 
6008
 
    If no tag name is specified it will be determined through the 
 
5996
    If no tag name is specified it will be determined through the
6009
5997
    'automatic_tag_name' hook. This can e.g. be used to automatically tag
6010
5998
    upstream releases by reading configure.ac. See ``brz help hooks`` for
6011
5999
    details.
6015
6003
    takes_args = ['tag_name?']
6016
6004
    takes_options = [
6017
6005
        Option('delete',
6018
 
            help='Delete this tag rather than placing it.',
6019
 
            ),
 
6006
               help='Delete this tag rather than placing it.',
 
6007
               ),
6020
6008
        custom_help('directory',
6021
 
            help='Branch in which to place the tag.'),
 
6009
                    help='Branch in which to place the tag.'),
6022
6010
        Option('force',
6023
 
            help='Replace existing tags.',
6024
 
            ),
 
6011
               help='Replace existing tags.',
 
6012
               ),
6025
6013
        'revision',
6026
6014
        ]
6027
6015
 
6032
6020
            revision=None,
6033
6021
            ):
6034
6022
        branch, relpath = Branch.open_containing(directory)
6035
 
        self.add_cleanup(branch.lock_write().unlock)
 
6023
        self.enter_context(branch.lock_write())
6036
6024
        if delete:
6037
6025
            if tag_name is None:
6038
 
                raise errors.BzrCommandError(gettext("No tag specified to delete."))
 
6026
                raise errors.BzrCommandError(
 
6027
                    gettext("No tag specified to delete."))
6039
6028
            branch.tags.delete_tag(tag_name)
6040
6029
            note(gettext('Deleted tag %s.') % tag_name)
6041
6030
        else:
6077
6066
    _see_also = ['tag']
6078
6067
    takes_options = [
6079
6068
        custom_help('directory',
6080
 
            help='Branch whose tags should be displayed.'),
 
6069
                    help='Branch whose tags should be displayed.'),
6081
6070
        RegistryOption('sort',
6082
 
            'Sort tags by different criteria.', title='Sorting',
6083
 
            lazy_registry=('breezy.tag', 'tag_sort_methods')
6084
 
            ),
 
6071
                       'Sort tags by different criteria.', title='Sorting',
 
6072
                       lazy_registry=('breezy.tag', 'tag_sort_methods')
 
6073
                       ),
6085
6074
        'show-ids',
6086
6075
        'revision',
6087
6076
    ]
6091
6080
        from .tag import tag_sort_methods
6092
6081
        branch, relpath = Branch.open_containing(directory)
6093
6082
 
6094
 
        tags = list(viewitems(branch.tags.get_tag_dict()))
 
6083
        tags = list(branch.tags.get_tag_dict().items())
6095
6084
        if not tags:
6096
6085
            return
6097
6086
 
6098
 
        self.add_cleanup(branch.lock_read().unlock)
 
6087
        self.enter_context(branch.lock_read())
6099
6088
        if revision:
6100
6089
            # Restrict to the specified range
6101
6090
            tags = self._tags_for_range(branch, revision)
6116
6105
                    # which are not in this branch. Fail gracefully ...
6117
6106
                    revno = '?'
6118
6107
                tags[index] = (tag, revno)
 
6108
        else:
 
6109
            tags = [(tag, revid.decode('utf-8')) for (tag, revid) in tags]
6119
6110
        self.cleanup_now()
6120
6111
        for tag, revspec in tags:
6121
6112
            self.outf.write('%-20s %s\n' % (tag, revspec))
6122
6113
 
6123
6114
    def _tags_for_range(self, branch, revision):
6124
 
        range_valid = True
6125
6115
        rev1, rev2 = _get_revision_range(revision, branch, self.name())
6126
6116
        revid1, revid2 = rev1.rev_id, rev2.rev_id
6127
6117
        # _get_revision_range will always set revid2 if it's not specified.
6139
6129
        tagged_revids = branch.tags.get_reverse_tag_dict()
6140
6130
        found = []
6141
6131
        for r in branch.iter_merge_sorted_revisions(
6142
 
            start_revision_id=revid2, stop_revision_id=revid1,
6143
 
            stop_rule='include'):
 
6132
                start_revision_id=revid2, stop_revision_id=revid1,
 
6133
                stop_rule='include'):
6144
6134
            revid_tags = tagged_revids.get(r[0], None)
6145
6135
            if revid_tags:
6146
6136
                found.extend([(tag, r[0]) for tag in revid_tags])
6173
6163
            tree='Reconfigure to be an unbound branch with a working tree.',
6174
6164
            checkout='Reconfigure to be a bound branch with a working tree.',
6175
6165
            lightweight_checkout='Reconfigure to be a lightweight'
6176
 
                ' checkout (with no local history).',
 
6166
            ' checkout (with no local history).',
6177
6167
            ),
6178
6168
        RegistryOption.from_kwargs(
6179
6169
            'repository_type',
6181
6171
            help='Location fo the repository.',
6182
6172
            value_switches=True, enum_switch=False,
6183
6173
            standalone='Reconfigure to be a standalone branch '
6184
 
                '(i.e. stop using shared repository).',
 
6174
            '(i.e. stop using shared repository).',
6185
6175
            use_shared='Reconfigure to use a shared repository.',
6186
6176
            ),
6187
6177
        RegistryOption.from_kwargs(
6190
6180
            help='Whether new branches in the repository have trees.',
6191
6181
            value_switches=True, enum_switch=False,
6192
6182
            with_trees='Reconfigure repository to create '
6193
 
                'working trees on branches by default.',
 
6183
            'working trees on branches by default.',
6194
6184
            with_no_trees='Reconfigure repository to not create '
6195
 
                'working trees on branches by default.'
 
6185
            'working trees on branches by default.'
6196
6186
            ),
6197
 
        Option('bind-to', help='Branch to bind checkout to.', type=text_type),
 
6187
        Option('bind-to', help='Branch to bind checkout to.', type=str),
6198
6188
        Option('force',
6199
 
            help='Perform reconfiguration even if local changes'
6200
 
            ' will be lost.'),
 
6189
               help='Perform reconfiguration even if local changes'
 
6190
               ' will be lost.'),
6201
6191
        Option('stacked-on',
6202
 
            help='Reconfigure a branch to be stacked on another branch.',
6203
 
            type=text_type,
6204
 
            ),
 
6192
               help='Reconfigure a branch to be stacked on another branch.',
 
6193
               type=str,
 
6194
               ),
6205
6195
        Option('unstacked',
6206
 
            help='Reconfigure a branch to be unstacked.  This '
6207
 
                'may require copying substantial data into it.',
6208
 
            ),
 
6196
               help='Reconfigure a branch to be unstacked.  This '
 
6197
               'may require copying substantial data into it.',
 
6198
               ),
6209
6199
        ]
6210
6200
 
6211
6201
    def run(self, location=None, bind_to=None, force=False,
6213
6203
            stacked_on=None, unstacked=None):
6214
6204
        directory = controldir.ControlDir.open(location)
6215
6205
        if stacked_on and unstacked:
6216
 
            raise errors.BzrCommandError(gettext("Can't use both --stacked-on and --unstacked"))
 
6206
            raise errors.BzrCommandError(
 
6207
                gettext("Can't use both --stacked-on and --unstacked"))
6217
6208
        elif stacked_on is not None:
6218
6209
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
6219
6210
        elif unstacked:
6223
6214
        # to ban it.
6224
6215
        if (tree_type is None and
6225
6216
            repository_type is None and
6226
 
            repository_trees is None):
 
6217
                repository_trees is None):
6227
6218
            if stacked_on or unstacked:
6228
6219
                return
6229
6220
            else:
6230
6221
                raise errors.BzrCommandError(gettext('No target configuration '
6231
 
                    'specified'))
 
6222
                                                     'specified'))
6232
6223
        reconfiguration = None
6233
6224
        if tree_type == 'branch':
6234
6225
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
6287
6278
    takes_args = ['to_location?']
6288
6279
    takes_options = ['directory',
6289
6280
                     Option('force',
6290
 
                        help='Switch even if local commits will be lost.'),
 
6281
                            help='Switch even if local commits will be lost.'),
6291
6282
                     'revision',
6292
6283
                     Option('create-branch', short_name='b',
6293
 
                        help='Create the target branch from this one before'
6294
 
                             ' switching to it.'),
 
6284
                            help='Create the target branch from this one before'
 
6285
                            ' switching to it.'),
6295
6286
                     Option('store',
6296
 
                        help='Store and restore uncommitted changes in the'
6297
 
                             ' branch.'),
6298
 
                    ]
 
6287
                            help='Store and restore uncommitted changes in the'
 
6288
                            ' branch.'),
 
6289
                     ]
6299
6290
 
6300
6291
    def run(self, to_location=None, force=False, create_branch=False,
6301
6292
            revision=None, directory=u'.', store=False):
6302
6293
        from . import switch
6303
6294
        tree_location = directory
6304
6295
        revision = _get_one_revision('switch', revision)
6305
 
        possible_transports = []
6306
 
        control_dir = controldir.ControlDir.open_containing(tree_location,
6307
 
            possible_transports=possible_transports)[0]
 
6296
        control_dir = controldir.ControlDir.open_containing(tree_location)[0]
 
6297
        possible_transports = [control_dir.root_transport]
6308
6298
        if to_location is None:
6309
6299
            if revision is None:
6310
6300
                raise errors.BzrCommandError(gettext('You must supply either a'
6311
 
                                             ' revision or a location'))
 
6301
                                                     ' revision or a location'))
6312
6302
            to_location = tree_location
6313
6303
        try:
6314
6304
            branch = control_dir.open_branch(
6317
6307
        except errors.NotBranchError:
6318
6308
            branch = None
6319
6309
            had_explicit_nick = False
 
6310
        else:
 
6311
            possible_transports.append(branch.user_transport)
6320
6312
        if create_branch:
6321
6313
            if branch is None:
6322
6314
                raise errors.BzrCommandError(
6323
6315
                    gettext('cannot create branch without source 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()
 
6316
            to_location = lookup_new_sibling_branch(
 
6317
                control_dir, to_location,
 
6318
                possible_transports=possible_transports)
 
6319
            if revision is not None:
 
6320
                revision = revision.as_revision_id(branch)
 
6321
            to_branch = branch.controldir.sprout(
 
6322
                to_location,
 
6323
                possible_transports=possible_transports,
 
6324
                revision_id=revision,
 
6325
                source_branch=branch).open_branch()
6329
6326
        else:
6330
6327
            try:
6331
 
                to_branch = Branch.open(to_location,
6332
 
                    possible_transports=possible_transports)
 
6328
                to_branch = Branch.open(
 
6329
                    to_location, possible_transports=possible_transports)
6333
6330
            except errors.NotBranchError:
6334
 
                to_branch = open_sibling_branch(control_dir, to_location,
 
6331
                to_branch = open_sibling_branch(
 
6332
                    control_dir, to_location,
6335
6333
                    possible_transports=possible_transports)
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)
 
6334
            if revision is not None:
 
6335
                revision = revision.as_revision_id(to_branch)
 
6336
        possible_transports.append(to_branch.user_transport)
 
6337
        try:
 
6338
            switch.switch(control_dir, to_branch, force, revision_id=revision,
 
6339
                          store_uncommitted=store,
 
6340
                          possible_transports=possible_transports)
 
6341
        except controldir.BranchReferenceLoop:
 
6342
            raise errors.BzrCommandError(
 
6343
                gettext('switching would create a branch reference loop. '
 
6344
                        'Use the "bzr up" command to switch to a '
 
6345
                        'different revision.'))
6340
6346
        if had_explicit_nick:
6341
 
            branch = control_dir.open_branch() #get the new branch!
 
6347
            branch = control_dir.open_branch()  # get the new branch!
6342
6348
            branch.nick = to_branch.nick
6343
 
        note(gettext('Switched to branch: %s'),
6344
 
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6345
 
 
 
6349
        if to_branch.name:
 
6350
            if to_branch.controldir.control_url != control_dir.control_url:
 
6351
                note(gettext('Switched to branch %s at %s'),
 
6352
                     to_branch.name, urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6353
            else:
 
6354
                note(gettext('Switched to branch %s'), to_branch.name)
 
6355
        else:
 
6356
            note(gettext('Switched to branch at %s'),
 
6357
                 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6346
6358
 
6347
6359
 
6348
6360
class cmd_view(Command):
6411
6423
    takes_args = ['file*']
6412
6424
    takes_options = [
6413
6425
        Option('all',
6414
 
            help='Apply list or delete action to all views.',
6415
 
            ),
 
6426
               help='Apply list or delete action to all views.',
 
6427
               ),
6416
6428
        Option('delete',
6417
 
            help='Delete the view.',
6418
 
            ),
 
6429
               help='Delete the view.',
 
6430
               ),
6419
6431
        Option('name',
6420
 
            help='Name of the view to define, list or delete.',
6421
 
            type=text_type,
6422
 
            ),
 
6432
               help='Name of the view to define, list or delete.',
 
6433
               type=str,
 
6434
               ),
6423
6435
        Option('switch',
6424
 
            help='Name of the view to switch to.',
6425
 
            type=text_type,
6426
 
            ),
 
6436
               help='Name of the view to switch to.',
 
6437
               type=str,
 
6438
               ),
6427
6439
        ]
6428
6440
 
6429
6441
    def run(self, file_list,
6433
6445
            switch=None,
6434
6446
            ):
6435
6447
        tree, file_list = WorkingTree.open_containing_paths(file_list,
6436
 
            apply_view=False)
 
6448
                                                            apply_view=False)
6437
6449
        current_view, view_dict = tree.views.get_view_info()
6438
6450
        if name is None:
6439
6451
            name = current_view
6448
6460
                tree.views.set_view_info(None, {})
6449
6461
                self.outf.write(gettext("Deleted all views.\n"))
6450
6462
            elif name is None:
6451
 
                raise errors.BzrCommandError(gettext("No current view to delete"))
 
6463
                raise errors.BzrCommandError(
 
6464
                    gettext("No current view to delete"))
6452
6465
            else:
6453
6466
                tree.views.delete_view(name)
6454
6467
                self.outf.write(gettext("Deleted '%s' view.\n") % name)
6461
6474
                    "Both --switch and --all specified"))
6462
6475
            elif switch == 'off':
6463
6476
                if current_view is None:
6464
 
                    raise errors.BzrCommandError(gettext("No current view to disable"))
 
6477
                    raise errors.BzrCommandError(
 
6478
                        gettext("No current view to disable"))
6465
6479
                tree.views.set_view_info(None, view_dict)
6466
 
                self.outf.write(gettext("Disabled '%s' view.\n") % (current_view))
 
6480
                self.outf.write(gettext("Disabled '%s' view.\n") %
 
6481
                                (current_view))
6467
6482
            else:
6468
6483
                tree.views.set_view_info(switch, view_dict)
6469
6484
                view_str = views.view_display_str(tree.views.lookup_view())
6470
 
                self.outf.write(gettext("Using '{0}' view: {1}\n").format(switch, view_str))
 
6485
                self.outf.write(
 
6486
                    gettext("Using '{0}' view: {1}\n").format(switch, view_str))
6471
6487
        elif all:
6472
6488
            if view_dict:
6473
6489
                self.outf.write(gettext('Views defined:\n'))
6489
6505
                    "Cannot change the 'off' pseudo view"))
6490
6506
            tree.views.set_view(name, sorted(file_list))
6491
6507
            view_str = views.view_display_str(tree.views.lookup_view())
6492
 
            self.outf.write(gettext("Using '{0}' view: {1}\n").format(name, view_str))
 
6508
            self.outf.write(
 
6509
                gettext("Using '{0}' view: {1}\n").format(name, view_str))
6493
6510
        else:
6494
6511
            # list the files
6495
6512
            if name is None:
6497
6514
                self.outf.write(gettext('No current view.\n'))
6498
6515
            else:
6499
6516
                view_str = views.view_display_str(tree.views.lookup_view(name))
6500
 
                self.outf.write(gettext("'{0}' view is: {1}\n").format(name, view_str))
 
6517
                self.outf.write(
 
6518
                    gettext("'{0}' view is: {1}\n").format(name, view_str))
6501
6519
 
6502
6520
 
6503
6521
class cmd_hooks(Command):
6523
6541
class cmd_remove_branch(Command):
6524
6542
    __doc__ = """Remove a branch.
6525
6543
 
6526
 
    This will remove the branch from the specified location but 
 
6544
    This will remove the branch from the specified location but
6527
6545
    will keep any working tree or repository in place.
6528
6546
 
6529
6547
    :Examples:
6537
6555
    takes_args = ["location?"]
6538
6556
 
6539
6557
    takes_options = ['directory',
6540
 
        Option('force', help='Remove branch even if it is the active branch.')]
 
6558
                     Option('force', help='Remove branch even if it is the active branch.')]
6541
6559
 
6542
6560
    aliases = ["rmbranch"]
6543
6561
 
6549
6567
            except errors.NotBranchError:
6550
6568
                active_branch = None
6551
6569
            if (active_branch is not None and
6552
 
                br.control_url == active_branch.control_url):
 
6570
                    br.control_url == active_branch.control_url):
6553
6571
                raise errors.BzrCommandError(
6554
6572
                    gettext("Branch is active. Use --force to remove it."))
6555
6573
        br.controldir.destroy_branch(br.name)
6583
6601
    editor program to decide what the file remaining in the working copy
6584
6602
    should look like.  To do this, add the configuration option
6585
6603
 
6586
 
        change_editor = PROGRAM @new_path @old_path
 
6604
        change_editor = PROGRAM {new_path} {old_path}
6587
6605
 
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 
 
6606
    where {new_path} is replaced with the path of the new version of the
 
6607
    file and {old_path} is replaced with the path of the old version of
 
6608
    the file.  The PROGRAM should save the new file with the desired
6591
6609
    contents of the file in the working tree.
6592
 
        
 
6610
 
6593
6611
    """
6594
6612
 
6595
6613
    takes_args = ['file*']
6618
6636
            writer = breezy.option.diff_writer_registry.get()
6619
6637
        try:
6620
6638
            shelver = Shelver.from_args(writer(self.outf), revision, all,
6621
 
                file_list, message, destroy=destroy, directory=directory)
 
6639
                                        file_list, message, destroy=destroy, directory=directory)
6622
6640
            try:
6623
6641
                shelver.run()
6624
6642
            finally:
6630
6648
        if directory is None:
6631
6649
            directory = u'.'
6632
6650
        tree = WorkingTree.open_containing(directory)[0]
6633
 
        self.add_cleanup(tree.lock_read().unlock)
 
6651
        self.enter_context(tree.lock_read())
6634
6652
        manager = tree.get_shelf_manager()
6635
6653
        shelves = manager.active_shelves()
6636
6654
        if len(shelves) == 0:
6637
6655
            note(gettext('No shelved changes.'))
6638
6656
            return 0
6639
6657
        for shelf_id in reversed(shelves):
6640
 
            message = manager.get_metadata(shelf_id).get('message')
 
6658
            message = manager.get_metadata(shelf_id).get(b'message')
6641
6659
            if message is None:
6642
6660
                message = '<no message>'
6643
6661
            self.outf.write('%3d: %s\n' % (shelf_id, message))
6701
6719
                     Option('dry-run', help='Show files to delete instead of'
6702
6720
                            ' deleting them.'),
6703
6721
                     Option('force', help='Do not prompt before deleting.')]
 
6722
 
6704
6723
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
6705
6724
            force=False, directory=u'.'):
6706
6725
        from .clean_tree import clean_tree
6723
6742
    hidden = True
6724
6743
 
6725
6744
    takes_args = ['path?', 'location?']
 
6745
    takes_options = [
 
6746
        'directory',
 
6747
        Option('force-unversioned',
 
6748
               help='Set reference even if path is not versioned.'),
 
6749
        ]
6726
6750
 
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
 
6751
    def run(self, path=None, directory='.', location=None, force_unversioned=False):
 
6752
        tree, branch, relpath = (
 
6753
            controldir.ControlDir.open_containing_tree_or_branch(directory))
6735
6754
        if tree is None:
6736
6755
            tree = branch.basis_tree()
6737
6756
        if path is None:
6738
 
            info = viewitems(branch._get_all_reference_info())
6739
 
            self._display_reference_info(tree, branch, info)
 
6757
            with tree.lock_read():
 
6758
                info = [
 
6759
                    (path, tree.get_reference_info(path, branch))
 
6760
                    for path in tree.iter_references()]
 
6761
                self._display_reference_info(tree, branch, info)
6740
6762
        else:
6741
 
            file_id = tree.path2id(path)
6742
 
            if file_id is None:
 
6763
            if not tree.is_versioned(path) and not force_unversioned:
6743
6764
                raise errors.NotVersionedError(path)
6744
6765
            if location is None:
6745
 
                info = [(file_id, branch.get_reference_info(file_id))]
 
6766
                info = [(path, tree.get_reference_info(path, branch))]
6746
6767
                self._display_reference_info(tree, branch, info)
6747
6768
            else:
6748
 
                branch.set_reference_info(file_id, path, location)
 
6769
                tree.set_reference_info(path, location)
6749
6770
 
6750
6771
    def _display_reference_info(self, tree, branch, info):
6751
6772
        ref_list = []
6752
 
        for file_id, (path, location) in info:
6753
 
            try:
6754
 
                path = tree.id2path(file_id)
6755
 
            except errors.NoSuchId:
6756
 
                pass
 
6773
        for path, location in info:
6757
6774
            ref_list.append((path, location))
6758
6775
        for path, location in sorted(ref_list):
6759
6776
            self.outf.write('%s %s\n' % (path, location))
6763
6780
    __doc__ = """Export command helps and error messages in po format."""
6764
6781
 
6765
6782
    hidden = True
6766
 
    takes_options = [Option('plugin', 
6767
 
                            help='Export help text from named command '\
 
6783
    takes_options = [Option('plugin',
 
6784
                            help='Export help text from named command '
6768
6785
                                 '(defaults to all built in commands).',
6769
 
                            type=text_type),
 
6786
                            type=str),
6770
6787
                     Option('include-duplicates',
6771
6788
                            help='Output multiple copies of the same msgid '
6772
6789
                                 'string if it appears more than once.'),
6773
 
                            ]
 
6790
                     ]
6774
6791
 
6775
6792
    def run(self, plugin=None, include_duplicates=False):
6776
6793
        from .export_pot import export_pot
6810
6827
        from .transform import link_tree
6811
6828
        target_tree = WorkingTree.open_containing(".")[0]
6812
6829
        source_tree = WorkingTree.open(location)
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()
 
6830
        with target_tree.lock_write(), source_tree.lock_read():
 
6831
            link_tree(target_tree, source_tree)
6822
6832
 
6823
6833
 
6824
6834
class cmd_fetch_ghosts(Command):
6838
6848
        if len(installed) > 0:
6839
6849
            self.outf.write("Installed:\n")
6840
6850
            for rev in installed:
6841
 
                self.outf.write(rev + "\n")
 
6851
                self.outf.write(rev.decode('utf-8') + "\n")
6842
6852
        if len(failed) > 0:
6843
6853
            self.outf.write("Still missing:\n")
6844
6854
            for rev in failed:
6845
 
                self.outf.write(rev + "\n")
 
6855
                self.outf.write(rev.decode('utf-8') + "\n")
6846
6856
        if not no_fix and len(installed) > 0:
6847
6857
            cmd_reconcile().run(".")
6848
6858
 
6849
6859
 
 
6860
class cmd_grep(Command):
 
6861
    """Print lines matching PATTERN for specified files and revisions.
 
6862
 
 
6863
    This command searches the specified files and revisions for a given
 
6864
    pattern.  The pattern is specified as a Python regular expressions[1].
 
6865
 
 
6866
    If the file name is not specified, the revisions starting with the
 
6867
    current directory are searched recursively. If the revision number is
 
6868
    not specified, the working copy is searched. To search the last committed
 
6869
    revision, use the '-r -1' or '-r last:1' option.
 
6870
 
 
6871
    Unversioned files are not searched unless explicitly specified on the
 
6872
    command line. Unversioned directores are not searched.
 
6873
 
 
6874
    When searching a pattern, the output is shown in the 'filepath:string'
 
6875
    format. If a revision is explicitly searched, the output is shown as
 
6876
    'filepath~N:string', where N is the revision number.
 
6877
 
 
6878
    --include and --exclude options can be used to search only (or exclude
 
6879
    from search) files with base name matches the specified Unix style GLOB
 
6880
    pattern.  The GLOB pattern an use *, ?, and [...] as wildcards, and \\
 
6881
    to quote wildcard or backslash character literally. Note that the glob
 
6882
    pattern is not a regular expression.
 
6883
 
 
6884
    [1] http://docs.python.org/library/re.html#regular-expression-syntax
 
6885
    """
 
6886
 
 
6887
    encoding_type = 'replace'
 
6888
    takes_args = ['pattern', 'path*']
 
6889
    takes_options = [
 
6890
        'verbose',
 
6891
        'revision',
 
6892
        Option('color', type=str, argname='when',
 
6893
               help='Show match in color. WHEN is never, always or auto.'),
 
6894
        Option('diff', short_name='p',
 
6895
               help='Grep for pattern in changeset for each revision.'),
 
6896
        ListOption('exclude', type=str, argname='glob', short_name='X',
 
6897
                   help="Skip files whose base name matches GLOB."),
 
6898
        ListOption('include', type=str, argname='glob', short_name='I',
 
6899
                   help="Search only files whose base name matches GLOB."),
 
6900
        Option('files-with-matches', short_name='l',
 
6901
               help='Print only the name of each input file in '
 
6902
               'which PATTERN is found.'),
 
6903
        Option('files-without-match', short_name='L',
 
6904
               help='Print only the name of each input file in '
 
6905
               'which PATTERN is not found.'),
 
6906
        Option('fixed-string', short_name='F',
 
6907
               help='Interpret PATTERN is a single fixed string (not regex).'),
 
6908
        Option('from-root',
 
6909
               help='Search for pattern starting from the root of the branch. '
 
6910
               '(implies --recursive)'),
 
6911
        Option('ignore-case', short_name='i',
 
6912
               help='Ignore case distinctions while matching.'),
 
6913
        Option('levels',
 
6914
               help='Number of levels to display - 0 for all, 1 for collapsed '
 
6915
               '(1 is default).',
 
6916
               argname='N',
 
6917
               type=_parse_levels),
 
6918
        Option('line-number', short_name='n',
 
6919
               help='Show 1-based line number.'),
 
6920
        Option('no-recursive',
 
6921
               help="Don't recurse into subdirectories. (default is --recursive)"),
 
6922
        Option('null', short_name='Z',
 
6923
               help='Write an ASCII NUL (\\0) separator '
 
6924
               'between output lines rather than a newline.'),
 
6925
        ]
 
6926
 
 
6927
    @display_command
 
6928
    def run(self, verbose=False, ignore_case=False, no_recursive=False,
 
6929
            from_root=False, null=False, levels=None, line_number=False,
 
6930
            path_list=None, revision=None, pattern=None, include=None,
 
6931
            exclude=None, fixed_string=False, files_with_matches=False,
 
6932
            files_without_match=False, color=None, diff=False):
 
6933
        from breezy import _termcolor
 
6934
        from . import grep
 
6935
        import re
 
6936
        if path_list is None:
 
6937
            path_list = ['.']
 
6938
        else:
 
6939
            if from_root:
 
6940
                raise errors.BzrCommandError(
 
6941
                    'cannot specify both --from-root and PATH.')
 
6942
 
 
6943
        if files_with_matches and files_without_match:
 
6944
            raise errors.BzrCommandError(
 
6945
                'cannot specify both '
 
6946
                '-l/--files-with-matches and -L/--files-without-matches.')
 
6947
 
 
6948
        global_config = _mod_config.GlobalConfig()
 
6949
 
 
6950
        if color is None:
 
6951
            color = global_config.get_user_option('grep_color')
 
6952
 
 
6953
        if color is None:
 
6954
            color = 'never'
 
6955
 
 
6956
        if color not in ['always', 'never', 'auto']:
 
6957
            raise errors.BzrCommandError('Valid values for --color are '
 
6958
                                         '"always", "never" or "auto".')
 
6959
 
 
6960
        if levels is None:
 
6961
            levels = 1
 
6962
 
 
6963
        print_revno = False
 
6964
        if revision is not None or levels == 0:
 
6965
            # print revision numbers as we may be showing multiple revisions
 
6966
            print_revno = True
 
6967
 
 
6968
        eol_marker = '\n'
 
6969
        if null:
 
6970
            eol_marker = '\0'
 
6971
 
 
6972
        if not ignore_case and grep.is_fixed_string(pattern):
 
6973
            # if the pattern isalnum, implicitly use to -F for faster grep
 
6974
            fixed_string = True
 
6975
        elif ignore_case and fixed_string:
 
6976
            # GZ 2010-06-02: Fall back to regexp rather than lowercasing
 
6977
            #                pattern and text which will cause pain later
 
6978
            fixed_string = False
 
6979
            pattern = re.escape(pattern)
 
6980
 
 
6981
        patternc = None
 
6982
        re_flags = re.MULTILINE
 
6983
        if ignore_case:
 
6984
            re_flags |= re.IGNORECASE
 
6985
 
 
6986
        if not fixed_string:
 
6987
            patternc = grep.compile_pattern(
 
6988
                pattern.encode(grep._user_encoding), re_flags)
 
6989
 
 
6990
        if color == 'always':
 
6991
            show_color = True
 
6992
        elif color == 'never':
 
6993
            show_color = False
 
6994
        elif color == 'auto':
 
6995
            show_color = _termcolor.allow_color()
 
6996
 
 
6997
        opts = grep.GrepOptions()
 
6998
 
 
6999
        opts.verbose = verbose
 
7000
        opts.ignore_case = ignore_case
 
7001
        opts.no_recursive = no_recursive
 
7002
        opts.from_root = from_root
 
7003
        opts.null = null
 
7004
        opts.levels = levels
 
7005
        opts.line_number = line_number
 
7006
        opts.path_list = path_list
 
7007
        opts.revision = revision
 
7008
        opts.pattern = pattern
 
7009
        opts.include = include
 
7010
        opts.exclude = exclude
 
7011
        opts.fixed_string = fixed_string
 
7012
        opts.files_with_matches = files_with_matches
 
7013
        opts.files_without_match = files_without_match
 
7014
        opts.color = color
 
7015
        opts.diff = False
 
7016
 
 
7017
        opts.eol_marker = eol_marker
 
7018
        opts.print_revno = print_revno
 
7019
        opts.patternc = patternc
 
7020
        opts.recursive = not no_recursive
 
7021
        opts.fixed_string = fixed_string
 
7022
        opts.outf = self.outf
 
7023
        opts.show_color = show_color
 
7024
 
 
7025
        if diff:
 
7026
            # options not used:
 
7027
            # files_with_matches, files_without_match
 
7028
            # levels(?), line_number, from_root
 
7029
            # include, exclude
 
7030
            # These are silently ignored.
 
7031
            grep.grep_diff(opts)
 
7032
        elif revision is None:
 
7033
            grep.workingtree_grep(opts)
 
7034
        else:
 
7035
            grep.versioned_grep(opts)
 
7036
 
 
7037
 
 
7038
class cmd_patch(Command):
 
7039
    """Apply a named patch to the current tree.
 
7040
 
 
7041
    """
 
7042
 
 
7043
    takes_args = ['filename?']
 
7044
    takes_options = [Option('strip', type=int, short_name='p',
 
7045
                            help=("Strip the smallest prefix containing num "
 
7046
                                  "leading slashes from filenames.")),
 
7047
                     Option('silent', help='Suppress chatter.')]
 
7048
 
 
7049
    def run(self, filename=None, strip=None, silent=False):
 
7050
        from .patch import patch_tree
 
7051
        wt = WorkingTree.open_containing('.')[0]
 
7052
        if strip is None:
 
7053
            strip = 1
 
7054
        my_file = None
 
7055
        if filename is None:
 
7056
            my_file = getattr(sys.stdin, 'buffer', sys.stdin)
 
7057
        else:
 
7058
            my_file = open(filename, 'rb')
 
7059
        patches = [my_file.read()]
 
7060
        return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
 
7061
 
 
7062
 
 
7063
class cmd_resolve_location(Command):
 
7064
    __doc__ = """Expand a location to a full URL.
 
7065
 
 
7066
    :Examples:
 
7067
        Look up a Launchpad URL.
 
7068
 
 
7069
            brz resolve-location lp:brz
 
7070
    """
 
7071
    takes_args = ['location']
 
7072
    hidden = True
 
7073
 
 
7074
    def run(self, location):
 
7075
        from .location import location_to_url
 
7076
        url = location_to_url(location)
 
7077
        display_url = urlutils.unescape_for_display(url, self.outf.encoding)
 
7078
        self.outf.write('%s\n' % display_url)
 
7079
 
 
7080
 
6850
7081
def _register_lazy_builtins():
6851
7082
    # register lazy builtins from other modules; called at startup and should
6852
7083
    # be only called once.
6853
7084
    for (name, aliases, module_name) in [
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
 
        ]:
 
7085
            ('cmd_bisect', [], 'breezy.bisect'),
 
7086
            ('cmd_bundle_info', [], 'breezy.bzr.bundle.commands'),
 
7087
            ('cmd_config', [], 'breezy.config'),
 
7088
            ('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
 
7089
            ('cmd_file_id', [], 'breezy.bzr.debug_commands'),
 
7090
            ('cmd_file_path', [], 'breezy.bzr.debug_commands'),
 
7091
            ('cmd_version_info', [], 'breezy.cmd_version_info'),
 
7092
            ('cmd_resolve', ['resolved'], 'breezy.conflicts'),
 
7093
            ('cmd_conflicts', [], 'breezy.conflicts'),
 
7094
            ('cmd_ping', [], 'breezy.bzr.smart.ping'),
 
7095
            ('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
 
7096
            ('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
 
7097
            ('cmd_test_script', [], 'breezy.cmd_test_script'),
 
7098
            ]:
6866
7099
        builtin_command_registry.register_lazy(name, aliases, module_name)