/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-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

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):
1492
1434
    parameter, as in "branch foo/bar -r 5".
1493
1435
    """
1494
1436
 
 
1437
    aliase = ['sprout']
1495
1438
    _see_also = ['checkout']
1496
1439
    takes_args = ['from_location', 'to_location?']
1497
1440
    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
 
        ]
 
1441
                     Option(
 
1442
                         'hardlink', help='Hard-link working tree files where possible.'),
 
1443
                     Option('files-from', type=str,
 
1444
                            help="Get file contents from this tree."),
 
1445
                     Option('no-tree',
 
1446
                            help="Create a branch without a working-tree."),
 
1447
                     Option('switch',
 
1448
                            help="Switch the checkout in the current directory "
 
1449
                            "to the new branch."),
 
1450
                     Option('stacked',
 
1451
                            help='Create a stacked branch referring to the source branch. '
 
1452
                            'The new branch will depend on the availability of the source '
 
1453
                            'branch for all operations.'),
 
1454
                     Option('standalone',
 
1455
                            help='Do not use a shared repository, even if available.'),
 
1456
                     Option('use-existing-dir',
 
1457
                            help='By default branch will fail if the target'
 
1458
                            ' directory exists, but does not already'
 
1459
                            ' have a control directory.  This flag will'
 
1460
                            ' allow branch to proceed.'),
 
1461
                     Option('bind',
 
1462
                            help="Bind new branch to from location."),
 
1463
                     Option('no-recurse-nested',
 
1464
                            help='Do not recursively check out nested trees.'),
 
1465
                     Option('colocated-branch', short_name='b',
 
1466
                            type=str, help='Name of colocated branch to sprout.'),
 
1467
                     ]
1520
1468
 
1521
1469
    def run(self, from_location, to_location=None, revision=None,
1522
1470
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1523
1471
            use_existing_dir=False, switch=False, bind=False,
1524
 
            files_from=None):
 
1472
            files_from=None, no_recurse_nested=False, colocated_branch=None):
1525
1473
        from breezy import switch as _mod_switch
1526
1474
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1527
 
            from_location)
 
1475
            from_location, name=colocated_branch)
 
1476
        if no_recurse_nested:
 
1477
            recurse = 'none'
 
1478
        else:
 
1479
            recurse = 'down'
1528
1480
        if not (hardlink or files_from):
1529
1481
            # accelerator_tree is usually slower because you have to read N
1530
1482
            # files (no readahead, lots of seeks, etc), but allow the user to
1533
1485
        if files_from is not None and files_from != from_location:
1534
1486
            accelerator_tree = WorkingTree.open(files_from)
1535
1487
        revision = _get_one_revision('branch', revision)
1536
 
        self.add_cleanup(br_from.lock_read().unlock)
 
1488
        self.enter_context(br_from.lock_read())
1537
1489
        if revision is not None:
1538
1490
            revision_id = revision.as_revision_id(br_from)
1539
1491
        else:
1542
1494
            # RBC 20060209
1543
1495
            revision_id = br_from.last_revision()
1544
1496
        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)
 
1497
            to_location = urlutils.derive_to_location(from_location)
 
1498
        to_transport = transport.get_transport(to_location, purpose='write')
1549
1499
        try:
1550
1500
            to_transport.mkdir('.')
1551
1501
        except errors.FileExists:
1555
1505
            except errors.NotBranchError:
1556
1506
                if not use_existing_dir:
1557
1507
                    raise errors.BzrCommandError(gettext('Target directory "%s" '
1558
 
                        'already exists.') % to_location)
 
1508
                                                         'already exists.') % to_location)
1559
1509
                else:
1560
1510
                    to_dir = None
1561
1511
            else:
1578
1528
                    possible_transports=[to_transport],
1579
1529
                    accelerator_tree=accelerator_tree, hardlink=hardlink,
1580
1530
                    stacked=stacked, force_new_repo=standalone,
1581
 
                    create_tree_if_local=not no_tree, source_branch=br_from)
 
1531
                    create_tree_if_local=not no_tree, source_branch=br_from,
 
1532
                    recurse=recurse)
1582
1533
                branch = to_dir.open_branch(
1583
1534
                    possible_transports=[
1584
1535
                        br_from.controldir.root_transport, to_transport])
1593
1544
            except errors.NoRepositoryPresent:
1594
1545
                to_repo = to_dir.create_repository()
1595
1546
            to_repo.fetch(br_from.repository, revision_id=revision_id)
1596
 
            branch = br_from.sprout(to_dir, revision_id=revision_id)
 
1547
            branch = br_from.sprout(
 
1548
                to_dir, revision_id=revision_id)
1597
1549
        br_from.tags.merge_to(branch.tags)
1598
1550
 
1599
1551
        # If the source branch is stacked, the new branch may
1601
1553
        # We therefore need a try/except here and not just 'if stacked:'
1602
1554
        try:
1603
1555
            note(gettext('Created new stacked branch referring to %s.') %
1604
 
                branch.get_stacked_on_url())
 
1556
                 branch.get_stacked_on_url())
1605
1557
        except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
1606
 
            errors.UnstackableRepositoryFormat) as e:
1607
 
            note(ngettext('Branched %d revision.', 'Branched %d revisions.', branch.revno()) % branch.revno())
 
1558
                errors.UnstackableRepositoryFormat) as e:
 
1559
            revno = branch.revno()
 
1560
            if revno is not None:
 
1561
                note(ngettext('Branched %d revision.',
 
1562
                              'Branched %d revisions.',
 
1563
                              branch.revno()) % revno)
 
1564
            else:
 
1565
                note(gettext('Created new branch.'))
1608
1566
        if bind:
1609
1567
            # Bind to the parent
1610
1568
            parent_branch = Branch.open(from_location)
1615
1573
            wt, _ = WorkingTree.open_containing('.')
1616
1574
            _mod_switch.switch(wt.controldir, branch)
1617
1575
            note(gettext('Switched to branch: %s'),
1618
 
                urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1576
                 urlutils.unescape_for_display(branch.base, 'utf-8'))
1619
1577
 
1620
1578
 
1621
1579
class cmd_branches(Command):
1627
1585
 
1628
1586
    takes_args = ['location?']
1629
1587
    takes_options = [
1630
 
                  Option('recursive', short_name='R',
1631
 
                         help='Recursively scan for branches rather than '
1632
 
                              'just looking in the specified location.')]
 
1588
        Option('recursive', short_name='R',
 
1589
               help='Recursively scan for branches rather than '
 
1590
               'just looking in the specified location.')]
1633
1591
 
1634
1592
    def run(self, location=".", recursive=False):
1635
1593
        if recursive:
1636
 
            t = transport.get_transport(location)
 
1594
            t = transport.get_transport(location, purpose='read')
1637
1595
            if not t.listable():
1638
1596
                raise errors.BzrCommandError(
1639
1597
                    "Can't scan this type of location.")
1652
1610
                if name == "":
1653
1611
                    continue
1654
1612
                active = (active_branch is not None and
1655
 
                          active_branch.base == branch.base)
 
1613
                          active_branch.user_url == branch.user_url)
1656
1614
                names[name] = active
1657
1615
            # Only mention the current branch explicitly if it's not
1658
1616
            # one of the colocated branches
1659
 
            if not any(viewvalues(names)) and active_branch is not None:
 
1617
            if not any(names.values()) and active_branch is not None:
1660
1618
                self.outf.write("* %s\n" % gettext("(default)"))
1661
1619
            for name in sorted(names):
1662
1620
                active = names[name]
1664
1622
                    prefix = "*"
1665
1623
                else:
1666
1624
                    prefix = " "
1667
 
                self.outf.write("%s %s\n" % (
1668
 
                    prefix, name.encode(self.outf.encoding)))
 
1625
                self.outf.write("%s %s\n" % (prefix, name))
1669
1626
 
1670
1627
 
1671
1628
class cmd_checkout(Command):
1672
1629
    __doc__ = """Create a new checkout of an existing branch.
1673
1630
 
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.
 
1631
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree
 
1632
    for the branch found in '.'. This is useful if you have removed the working
 
1633
    tree or if it was never created - i.e. if you pushed the branch to its
 
1634
    current location using SFTP.
1678
1635
 
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.
 
1636
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION
 
1637
    will be used.  In other words, "checkout ../foo/bar" will attempt to create
 
1638
    ./bar.  If the BRANCH_LOCATION has no / or path separator embedded, the
 
1639
    TO_LOCATION is derived from the BRANCH_LOCATION by stripping a leading
 
1640
    scheme or drive identifier, if any. For example, "checkout lp:foo-bar" will
 
1641
    attempt to create ./foo-bar.
1685
1642
 
1686
1643
    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.)
 
1644
    parameter, as in "checkout foo/bar -r 5". Note that this will be
 
1645
    immediately out of date [so you cannot commit] but it may be useful (i.e.
 
1646
    to examine old code.)
1690
1647
    """
1691
1648
 
1692
1649
    _see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
1699
1656
                                 "common operations like diff and status without "
1700
1657
                                 "such access, and also support local commits."
1701
1658
                            ),
1702
 
                     Option('files-from', type=text_type,
 
1659
                     Option('files-from', type=str,
1703
1660
                            help="Get file contents from this tree."),
1704
1661
                     Option('hardlink',
1705
1662
                            help='Hard-link working tree files where possible.'
1731
1688
        # if the source and to_location are the same,
1732
1689
        # and there is no working tree,
1733
1690
        # then reconstitute a branch
1734
 
        if (osutils.abspath(to_location) ==
1735
 
            osutils.abspath(branch_location)):
 
1691
        if osutils.abspath(to_location) == osutils.abspath(branch_location):
1736
1692
            try:
1737
1693
                source.controldir.open_workingtree()
1738
1694
            except errors.NoWorkingTree:
1742
1698
                               accelerator_tree, hardlink)
1743
1699
 
1744
1700
 
 
1701
class cmd_clone(Command):
 
1702
    __doc__ = """Clone a control directory.
 
1703
    """
 
1704
 
 
1705
    takes_args = ['from_location', 'to_location?']
 
1706
    takes_options = ['revision',
 
1707
                     Option('no-recurse-nested',
 
1708
                            help='Do not recursively check out nested trees.'),
 
1709
                     ]
 
1710
 
 
1711
    def run(self, from_location, to_location=None, revision=None, no_recurse_nested=False):
 
1712
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
 
1713
            from_location)
 
1714
        if no_recurse_nested:
 
1715
            recurse = 'none'
 
1716
        else:
 
1717
            recurse = 'down'
 
1718
        revision = _get_one_revision('branch', revision)
 
1719
        self.enter_context(br_from.lock_read())
 
1720
        if revision is not None:
 
1721
            revision_id = revision.as_revision_id(br_from)
 
1722
        else:
 
1723
            # FIXME - wt.last_revision, fallback to branch, fall back to
 
1724
            # None or perhaps NULL_REVISION to mean copy nothing
 
1725
            # RBC 20060209
 
1726
            revision_id = br_from.last_revision()
 
1727
        if to_location is None:
 
1728
            to_location = urlutils.derive_to_location(from_location)
 
1729
        target_controldir = br_from.controldir.clone(to_location, revision_id=revision_id)
 
1730
        note(gettext('Created new control directory.'))
 
1731
 
 
1732
 
1745
1733
class cmd_renames(Command):
1746
1734
    __doc__ = """Show list of renamed files.
1747
1735
    """
1754
1742
    @display_command
1755
1743
    def run(self, dir=u'.'):
1756
1744
        tree = WorkingTree.open_containing(dir)[0]
1757
 
        self.add_cleanup(tree.lock_read().unlock)
 
1745
        self.enter_context(tree.lock_read())
1758
1746
        old_tree = tree.basis_tree()
1759
 
        self.add_cleanup(old_tree.lock_read().unlock)
 
1747
        self.enter_context(old_tree.lock_read())
1760
1748
        renames = []
1761
1749
        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)
 
1750
        for change in iterator:
 
1751
            if change.path[0] == change.path[1]:
 
1752
                continue
 
1753
            if None in change.path:
 
1754
                continue
 
1755
            renames.append(change.path)
1768
1756
        renames.sort()
1769
1757
        for old_name, new_name in renames:
1770
1758
            self.outf.write("%s => %s\n" % (old_name, new_name))
1775
1763
 
1776
1764
    This will perform a merge of the destination revision (the tip of the
1777
1765
    branch, or the specified revision) into the working tree, and then make
1778
 
    that revision the basis revision for the working tree.  
 
1766
    that revision the basis revision for the working tree.
1779
1767
 
1780
1768
    You can use this to visit an older revision, or to update a working tree
1781
1769
    that is out of date from its branch.
1782
 
    
 
1770
 
1783
1771
    If there are any uncommitted changes in the tree, they will be carried
1784
1772
    across and remain as uncommitted changes after the update.  To discard
1785
1773
    these changes, use 'brz revert'.  The uncommitted changes may conflict
1788
1776
    If the tree's branch is bound to a master branch, brz will also update
1789
1777
    the branch from the master.
1790
1778
 
1791
 
    You cannot update just a single file or directory, because each Bazaar
 
1779
    You cannot update just a single file or directory, because each Breezy
1792
1780
    working tree has just a single basis revision.  If you want to restore a
1793
1781
    file that has been removed locally, use 'brz revert' instead of 'brz
1794
1782
    update'.  If you want to restore a file to its state in a previous
1796
1784
    out the old content of that file to a new location.
1797
1785
 
1798
1786
    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 
 
1787
    working tree to update.  By default, the working tree that contains the
1800
1788
    current working directory is used.
1801
1789
    """
1802
1790
 
1827
1815
            possible_transports=possible_transports)
1828
1816
        if master is not None:
1829
1817
            branch_location = master.base
1830
 
            tree.lock_write()
 
1818
            self.enter_context(tree.lock_write())
1831
1819
        else:
1832
1820
            branch_location = tree.branch.base
1833
 
            tree.lock_tree_write()
1834
 
        self.add_cleanup(tree.unlock)
 
1821
            self.enter_context(tree.lock_tree_write())
1835
1822
        # get rid of the final '/' and be ready for display
1836
1823
        branch_location = urlutils.unescape_for_display(
1837
1824
            branch_location.rstrip('/'),
1851
1838
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1852
1839
            revno = branch.revision_id_to_dotted_revno(revision_id)
1853
1840
            note(gettext("Tree is up to date at revision {0} of branch {1}"
1854
 
                        ).format('.'.join(map(str, revno)), branch_location))
 
1841
                         ).format('.'.join(map(str, revno)), branch_location))
1855
1842
            return 0
1856
1843
        view_info = _get_view_info_for_change_reporter(tree)
1857
1844
        change_reporter = delta._ChangeReporter(
1866
1853
                show_base=show_base)
1867
1854
        except errors.NoSuchRevision as e:
1868
1855
            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))
 
1856
                "branch has no revision %s\n"
 
1857
                "brz update --revision only works"
 
1858
                " for a revision in the branch history")
 
1859
                % (e.revision))
1873
1860
        revno = tree.branch.revision_id_to_dotted_revno(
1874
1861
            _mod_revision.ensure_null(tree.last_revision()))
1875
1862
        note(gettext('Updated to revision {0} of branch {1}').format(
1877
1864
        parent_ids = tree.get_parent_ids()
1878
1865
        if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
1879
1866
            note(gettext('Your local commits will now show as pending merges with '
1880
 
                 "'brz status', and can be committed with 'brz commit'."))
 
1867
                         "'brz status', and can be committed with 'brz commit'."))
1881
1868
        if conflicts != 0:
1882
1869
            return 1
1883
1870
        else:
1931
1918
class cmd_remove(Command):
1932
1919
    __doc__ = """Remove files or directories.
1933
1920
 
1934
 
    This makes Bazaar stop tracking changes to the specified files. Bazaar will
 
1921
    This makes Breezy stop tracking changes to the specified files. Breezy will
1935
1922
    delete them if they can easily be recovered using revert otherwise they
1936
1923
    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.
 
1924
    parameters are given Breezy will scan for files that are being tracked by
 
1925
    Breezy but missing in your tree and stop tracking them for you.
1939
1926
    """
1940
1927
    takes_args = ['file*']
1941
1928
    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
 
        ]
 
1929
                     Option(
 
1930
                         'new', help='Only remove files that have never been committed.'),
 
1931
                     RegistryOption.from_kwargs('file-deletion-strategy',
 
1932
                                                'The file deletion mode to be used.',
 
1933
                                                title='Deletion Strategy', value_switches=True, enum_switch=False,
 
1934
                                                safe='Backup changed files (default).',
 
1935
                                                keep='Delete from brz but leave the working copy.',
 
1936
                                                no_backup='Don\'t backup changed files.'),
 
1937
                     ]
1950
1938
    aliases = ['rm', 'del']
1951
1939
    encoding_type = 'replace'
1952
1940
 
1953
1941
    def run(self, file_list, verbose=False, new=False,
1954
 
        file_deletion_strategy='safe'):
 
1942
            file_deletion_strategy='safe'):
1955
1943
 
1956
1944
        tree, file_list = WorkingTree.open_containing_paths(file_list)
1957
1945
 
1958
1946
        if file_list is not None:
1959
1947
            file_list = [f for f in file_list]
1960
1948
 
1961
 
        self.add_cleanup(tree.lock_write().unlock)
 
1949
        self.enter_context(tree.lock_write())
1962
1950
        # Heuristics should probably all move into tree.remove_smart or
1963
1951
        # some such?
1964
1952
        if new:
1965
1953
            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)
 
1954
                                      specific_files=file_list).added
 
1955
            file_list = sorted([f.path[1] for f in added], reverse=True)
1968
1956
            if len(file_list) == 0:
1969
1957
                raise errors.BzrCommandError(gettext('No matching files.'))
1970
1958
        elif file_list is None:
1973
1961
            missing = []
1974
1962
            for change in tree.iter_changes(tree.basis_tree()):
1975
1963
                # 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])
 
1964
                if change.path[1] is not None and change.kind[1] is None:
 
1965
                    missing.append(change.path[1])
1978
1966
            file_list = sorted(missing, reverse=True)
1979
1967
            file_deletion_strategy = 'keep'
1980
1968
        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))
 
1969
                    keep_files=file_deletion_strategy == 'keep',
 
1970
                    force=(file_deletion_strategy == 'no-backup'))
2027
1971
 
2028
1972
 
2029
1973
class cmd_reconcile(Command):
2071
2015
    @display_command
2072
2016
    def run(self, location="."):
2073
2017
        branch = Branch.open_containing(location)[0]
2074
 
        self.add_cleanup(branch.lock_read().unlock)
 
2018
        self.enter_context(branch.lock_read())
2075
2019
        graph = branch.repository.get_graph()
2076
2020
        history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
2077
 
            [_mod_revision.NULL_REVISION]))
 
2021
                                                    [_mod_revision.NULL_REVISION]))
2078
2022
        for revid in reversed(history):
2079
2023
            self.outf.write(revid)
2080
2024
            self.outf.write('\n')
2099
2043
            b = wt.branch
2100
2044
            last_revision = wt.last_revision()
2101
2045
 
2102
 
        self.add_cleanup(b.repository.lock_read().unlock)
 
2046
        self.enter_context(b.repository.lock_read())
2103
2047
        graph = b.repository.get_graph()
2104
2048
        revisions = [revid for revid, parents in
2105
 
            graph.iter_ancestry([last_revision])]
 
2049
                     graph.iter_ancestry([last_revision])]
2106
2050
        for revision_id in reversed(revisions):
2107
2051
            if _mod_revision.is_null(revision_id):
2108
2052
                continue
2109
 
            self.outf.write(revision_id + '\n')
 
2053
            self.outf.write(revision_id.decode('utf-8') + '\n')
2110
2054
 
2111
2055
 
2112
2056
class cmd_init(Command):
2132
2076
        brz commit -m "imported project"
2133
2077
    """
2134
2078
 
2135
 
    _see_also = ['init-repository', 'branch', 'checkout']
 
2079
    _see_also = ['init-shared-repository', 'branch', 'checkout']
2136
2080
    takes_args = ['location?']
2137
2081
    takes_options = [
2138
2082
        Option('create-prefix',
2139
2083
               help='Create the path leading up to the branch '
2140
2084
                    '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
 
         ]
 
2085
        RegistryOption('format',
 
2086
                       help='Specify a format for this branch. '
 
2087
                       'See "help formats" for a full list.',
 
2088
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
2089
                       converter=lambda name: controldir.format_registry.make_controldir(
 
2090
                            name),
 
2091
                       value_switches=True,
 
2092
                       title="Branch format",
 
2093
                       ),
 
2094
        Option('append-revisions-only',
 
2095
               help='Never change revnos or the existing log.'
 
2096
               '  Append revisions to it only.'),
 
2097
        Option('no-tree',
 
2098
               'Create a branch without a working tree.')
 
2099
        ]
 
2100
 
2155
2101
    def run(self, location=None, format=None, append_revisions_only=False,
2156
2102
            create_prefix=False, no_tree=False):
2157
2103
        if format is None:
2159
2105
        if location is None:
2160
2106
            location = u'.'
2161
2107
 
2162
 
        to_transport = transport.get_transport(location)
 
2108
        to_transport = transport.get_transport(location, purpose='write')
2163
2109
 
2164
2110
        # The path has to exist to initialize a
2165
2111
        # branch inside of it.
2171
2117
        except errors.NoSuchFile:
2172
2118
            if not create_prefix:
2173
2119
                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)
 
2120
                                                     " does not exist."
 
2121
                                                     "\nYou may supply --create-prefix to create all"
 
2122
                                                     " leading parent directories.")
 
2123
                                             % location)
2178
2124
            to_transport.create_prefix()
2179
2125
 
2180
2126
        try:
2181
 
            a_controldir = controldir.ControlDir.open_from_transport(to_transport)
 
2127
            a_controldir = controldir.ControlDir.open_from_transport(
 
2128
                to_transport)
2182
2129
        except errors.NotBranchError:
2183
2130
            # really a NotBzrDir error...
2184
2131
            create_branch = controldir.ControlDir.create_branch_convenience
2194
2141
            from .transport.local import LocalTransport
2195
2142
            if a_controldir.has_branch():
2196
2143
                if (isinstance(to_transport, LocalTransport)
2197
 
                    and not a_controldir.has_workingtree()):
2198
 
                        raise errors.BranchExistsWithoutWorkingTree(location)
 
2144
                        and not a_controldir.has_workingtree()):
 
2145
                    raise errors.BranchExistsWithoutWorkingTree(location)
2199
2146
                raise errors.AlreadyBranchError(location)
2200
2147
            branch = a_controldir.create_branch()
2201
2148
            if not no_tree and not a_controldir.has_workingtree():
2205
2152
                branch.set_append_revisions_only(True)
2206
2153
            except errors.UpgradeRequired:
2207
2154
                raise errors.BzrCommandError(gettext('This branch format cannot be set'
2208
 
                    ' to append-revisions-only.  Try --default.'))
 
2155
                                                     ' to append-revisions-only.  Try --default.'))
2209
2156
        if not is_quiet():
2210
2157
            from .info import describe_layout, describe_format
2211
2158
            try:
2216
2163
            layout = describe_layout(repository, branch, tree).lower()
2217
2164
            format = describe_format(a_controldir, repository, branch, tree)
2218
2165
            self.outf.write(gettext("Created a {0} (format: {1})\n").format(
2219
 
                  layout, format))
 
2166
                layout, format))
2220
2167
            if repository.is_shared():
2221
 
                #XXX: maybe this can be refactored into transport.path_or_url()
 
2168
                # XXX: maybe this can be refactored into transport.path_or_url()
2222
2169
                url = repository.controldir.root_transport.external_url()
2223
2170
                try:
2224
2171
                    url = urlutils.local_path_from_url(url)
2227
2174
                self.outf.write(gettext("Using shared repository: %s\n") % url)
2228
2175
 
2229
2176
 
2230
 
class cmd_init_repository(Command):
 
2177
class cmd_init_shared_repository(Command):
2231
2178
    __doc__ = """Create a shared repository for branches to share storage space.
2232
2179
 
2233
2180
    New branches created under the repository directory will store their
2234
2181
    revisions in the repository, not in the branch directory.  For branches
2235
 
    with shared history, this reduces the amount of storage needed and 
 
2182
    with shared history, this reduces the amount of storage needed and
2236
2183
    speeds up the creation of new branches.
2237
2184
 
2238
2185
    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 
 
2186
    will not have working trees by default.  They will still exist as
 
2187
    directories on disk, but they will not have separate copies of the
2241
2188
    files at a certain revision.  This can be useful for repositories that
2242
2189
    store branches which are interacted with through checkouts or remote
2243
2190
    branches, such as on a server.
2245
2192
    :Examples:
2246
2193
        Create a shared repository holding just branches::
2247
2194
 
2248
 
            brz init-repo --no-trees repo
 
2195
            brz init-shared-repo --no-trees repo
2249
2196
            brz init repo/trunk
2250
2197
 
2251
2198
        Make a lightweight checkout elsewhere::
2258
2205
    _see_also = ['init', 'branch', 'checkout', 'repositories']
2259
2206
    takes_args = ["location"]
2260
2207
    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'),
 
2208
                                    help='Specify a format for this repository. See'
 
2209
                                    ' "brz help formats" for details.',
 
2210
                                    lazy_registry=(
 
2211
                                        'breezy.controldir', 'format_registry'),
 
2212
                                    converter=lambda name: controldir.format_registry.make_controldir(
 
2213
                                        name),
 
2214
                                    value_switches=True, title='Repository format'),
2266
2215
                     Option('no-trees',
2267
 
                             help='Branches in the repository will default to'
2268
 
                                  ' not having a working tree.'),
2269
 
                    ]
2270
 
    aliases = ["init-repo"]
 
2216
                            help='Branches in the repository will default to'
 
2217
                            ' not having a working tree.'),
 
2218
                     ]
 
2219
    aliases = ["init-shared-repo", "init-repo"]
2271
2220
 
2272
2221
    def run(self, location, format=None, no_trees=False):
2273
2222
        if format is None:
2276
2225
        if location is None:
2277
2226
            location = '.'
2278
2227
 
2279
 
        to_transport = transport.get_transport(location)
 
2228
        to_transport = transport.get_transport(location, purpose='write')
 
2229
 
 
2230
        if format.fixed_components:
 
2231
            repo_format_name = None
 
2232
        else:
 
2233
            repo_format_name = format.repository_format.get_format_string()
2280
2234
 
2281
2235
        (repo, newdir, require_stacking, repository_policy) = (
2282
2236
            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()))
 
2237
                                              create_prefix=True, make_working_trees=not no_trees,
 
2238
                                              shared_repo=True, force_new_repo=True,
 
2239
                                              use_existing_dir=True,
 
2240
                                              repo_format_name=repo_format_name))
2287
2241
        if not is_quiet():
2288
2242
            from .info import show_bzrdir_info
2289
2243
            show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
2304
2258
 
2305
2259
    Note that when using the -r argument with a range of revisions, the
2306
2260
    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 
 
2261
    is, the command does not show the changes introduced by the first
 
2262
    revision in the range.  This differs from the interpretation of
2309
2263
    revision ranges used by "brz log" which includes the first revision
2310
2264
    in the range.
2311
2265
 
2337
2291
            brz diff -c2
2338
2292
 
2339
2293
        To see the changes introduced by revision X::
2340
 
        
 
2294
 
2341
2295
            brz diff -cX
2342
2296
 
2343
2297
        Note that in the case of a merge, the -c option shows the changes
2378
2332
    _see_also = ['status']
2379
2333
    takes_args = ['file*']
2380
2334
    takes_options = [
2381
 
        Option('diff-options', type=text_type,
 
2335
        Option('diff-options', type=str,
2382
2336
               help='Pass these options to the external diff program.'),
2383
 
        Option('prefix', type=text_type,
 
2337
        Option('prefix', type=str,
2384
2338
               short_name='p',
2385
2339
               help='Set prefixes added to old and new filenames, as '
2386
2340
                    'two values separated by a colon. (eg "old/:new/").'),
2387
2341
        Option('old',
2388
 
            help='Branch/tree to compare from.',
2389
 
            type=text_type,
2390
 
            ),
 
2342
               help='Branch/tree to compare from.',
 
2343
               type=str,
 
2344
               ),
2391
2345
        Option('new',
2392
 
            help='Branch/tree to compare to.',
2393
 
            type=text_type,
2394
 
            ),
 
2346
               help='Branch/tree to compare to.',
 
2347
               type=str,
 
2348
               ),
2395
2349
        'revision',
2396
2350
        'change',
2397
2351
        Option('using',
2398
 
            help='Use this command to compare files.',
2399
 
            type=text_type,
2400
 
            ),
 
2352
               help='Use this command to compare files.',
 
2353
               type=str,
 
2354
               ),
2401
2355
        RegistryOption('format',
2402
 
            short_name='F',
2403
 
            help='Diff format to use.',
2404
 
            lazy_registry=('breezy.diff', 'format_registry'),
2405
 
            title='Diff format'),
 
2356
                       short_name='F',
 
2357
                       help='Diff format to use.',
 
2358
                       lazy_registry=('breezy.diff', 'format_registry'),
 
2359
                       title='Diff format'),
2406
2360
        Option('context',
2407
 
            help='How many lines of context to show.',
2408
 
            type=int,
2409
 
            ),
 
2361
               help='How many lines of context to show.',
 
2362
               type=int,
 
2363
               ),
 
2364
        RegistryOption.from_kwargs(
 
2365
            'color',
 
2366
            help='Color mode to use.',
 
2367
            title='Color Mode', value_switches=False, enum_switch=True,
 
2368
            never='Never colorize output.',
 
2369
            auto='Only colorize output if terminal supports it and STDOUT is a'
 
2370
            ' TTY.',
 
2371
            always='Always colorize output (default).'),
 
2372
        Option(
 
2373
            'check-style',
 
2374
            help=('Warn if trailing whitespace or spurious changes have been'
 
2375
                  '  added.'))
2410
2376
        ]
 
2377
 
2411
2378
    aliases = ['di', 'dif']
2412
2379
    encoding_type = 'exact'
2413
2380
 
2414
2381
    @display_command
2415
2382
    def run(self, revision=None, file_list=None, diff_options=None,
2416
2383
            prefix=None, old=None, new=None, using=None, format=None,
2417
 
            context=None):
 
2384
            context=None, color='never'):
2418
2385
        from .diff import (get_trees_and_branches_to_diff_locked,
2419
 
            show_diff_trees)
 
2386
                           show_diff_trees)
2420
2387
 
2421
2388
        if prefix == u'0':
2422
2389
            # diff -p0 format
2434
2401
 
2435
2402
        if revision and len(revision) > 2:
2436
2403
            raise errors.BzrCommandError(gettext('brz diff --revision takes exactly'
2437
 
                                         ' one or two revision specifiers'))
 
2404
                                                 ' one or two revision specifiers'))
2438
2405
 
2439
2406
        if using is not None and format is not None:
2440
2407
            raise errors.BzrCommandError(gettext(
2444
2411
        (old_tree, new_tree,
2445
2412
         old_branch, new_branch,
2446
2413
         specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
2447
 
            file_list, revision, old, new, self.add_cleanup, apply_view=True)
 
2414
            file_list, revision, old, new, self._exit_stack, apply_view=True)
2448
2415
        # GNU diff on Windows uses ANSI encoding for filenames
2449
2416
        path_encoding = osutils.get_diff_header_encoding()
2450
 
        return show_diff_trees(old_tree, new_tree, self.outf,
 
2417
        outf = self.outf
 
2418
        if color == 'auto':
 
2419
            from .terminal import has_ansi_colors
 
2420
            if has_ansi_colors():
 
2421
                color = 'always'
 
2422
            else:
 
2423
                color = 'never'
 
2424
        if 'always' == color:
 
2425
            from .colordiff import DiffWriter
 
2426
            outf = DiffWriter(outf)
 
2427
        return show_diff_trees(old_tree, new_tree, outf,
2451
2428
                               specific_files=specific_files,
2452
2429
                               external_diff_options=diff_options,
2453
2430
                               old_label=old_label, new_label=new_label,
2472
2449
    @display_command
2473
2450
    def run(self, show_ids=False, directory=u'.'):
2474
2451
        tree = WorkingTree.open_containing(directory)[0]
2475
 
        self.add_cleanup(tree.lock_read().unlock)
 
2452
        self.enter_context(tree.lock_read())
2476
2453
        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')
 
2454
        self.enter_context(old.lock_read())
 
2455
        delta = tree.changes_from(old)
 
2456
        for change in delta.removed:
 
2457
            self.outf.write(change.path[0])
 
2458
            if show_ids:
 
2459
                self.outf.write(' ')
 
2460
                self.outf.write(change.file_id)
 
2461
            self.outf.write('\n')
2485
2462
 
2486
2463
 
2487
2464
class cmd_modified(Command):
2495
2472
    @display_command
2496
2473
    def run(self, null=False, directory=u'.'):
2497
2474
        tree = WorkingTree.open_containing(directory)[0]
2498
 
        self.add_cleanup(tree.lock_read().unlock)
 
2475
        self.enter_context(tree.lock_read())
2499
2476
        td = tree.changes_from(tree.basis_tree())
2500
2477
        self.cleanup_now()
2501
 
        for path, id, kind, text_modified, meta_modified in td.modified:
 
2478
        for change in td.modified:
2502
2479
            if null:
2503
 
                self.outf.write(path + '\0')
 
2480
                self.outf.write(change.path[1] + '\0')
2504
2481
            else:
2505
 
                self.outf.write(osutils.quotefn(path) + '\n')
 
2482
                self.outf.write(osutils.quotefn(change.path[1]) + '\n')
2506
2483
 
2507
2484
 
2508
2485
class cmd_added(Command):
2516
2493
    @display_command
2517
2494
    def run(self, null=False, directory=u'.'):
2518
2495
        wt = WorkingTree.open_containing(directory)[0]
2519
 
        self.add_cleanup(wt.lock_read().unlock)
 
2496
        self.enter_context(wt.lock_read())
2520
2497
        basis = wt.basis_tree()
2521
 
        self.add_cleanup(basis.lock_read().unlock)
2522
 
        root_id = wt.get_root_id()
 
2498
        self.enter_context(basis.lock_read())
2523
2499
        for path in wt.all_versioned_paths():
2524
2500
            if basis.has_filename(path):
2525
2501
                continue
2536
2512
class cmd_root(Command):
2537
2513
    __doc__ = """Show the tree root directory.
2538
2514
 
2539
 
    The root is the nearest enclosing directory with a .bzr control
 
2515
    The root is the nearest enclosing directory with a control
2540
2516
    directory."""
2541
2517
 
2542
2518
    takes_args = ['filename?']
 
2519
 
2543
2520
    @display_command
2544
2521
    def run(self, filename=None):
2545
2522
        """Print the branch root."""
2687
2664
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2688
2665
      bzr-explorer shell, or the Loggerhead web interface.  See the Bazaar
2689
2666
      Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2690
 
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
 
2667
      <http://wiki.bazaar.canonical.com/IDEIntegration>.
2691
2668
 
2692
2669
      You may find it useful to add the aliases below to ``breezy.conf``::
2693
2670
 
2721
2698
    takes_args = ['file*']
2722
2699
    _see_also = ['log-formats', 'revisionspec']
2723
2700
    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',
 
2701
        Option('forward',
 
2702
               help='Show from oldest to newest.'),
 
2703
        'timezone',
 
2704
        custom_help('verbose',
 
2705
                    help='Show files changed in each revision.'),
 
2706
        'show-ids',
 
2707
        'revision',
 
2708
        Option('change',
 
2709
               type=breezy.option._parse_revision_str,
 
2710
               short_name='c',
 
2711
               help='Show just the specified revision.'
 
2712
               ' See also "help revisionspec".'),
 
2713
        'log-format',
 
2714
        RegistryOption('authors',
 
2715
                       'What names to list as authors - first, all or committer.',
 
2716
                       title='Authors',
 
2717
                       lazy_registry=(
 
2718
                           'breezy.log', 'author_list_registry'),
 
2719
                       ),
 
2720
        Option('levels',
 
2721
               short_name='n',
 
2722
               help='Number of levels to display - 0 for all, 1 for flat.',
 
2723
               argname='N',
 
2724
               type=_parse_levels),
 
2725
        Option('message',
 
2726
               help='Show revisions whose message matches this '
 
2727
               'regular expression.',
 
2728
               type=str,
 
2729
               hidden=True),
 
2730
        Option('limit',
 
2731
               short_name='l',
 
2732
               help='Limit the output to the first N revisions.',
 
2733
               argname='N',
 
2734
               type=_parse_limit),
 
2735
        Option('show-diff',
 
2736
               short_name='p',
 
2737
               help='Show changes made in each revision as a patch.'),
 
2738
        Option('include-merged',
 
2739
               help='Show merged revisions like --levels 0 does.'),
 
2740
        Option('include-merges', hidden=True,
 
2741
               help='Historical alias for --include-merged.'),
 
2742
        Option('omit-merges',
 
2743
               help='Do not report commits with more than one parent.'),
 
2744
        Option('exclude-common-ancestry',
 
2745
               help='Display only the revisions that are not part'
 
2746
               ' of both ancestries (require -rX..Y).'
 
2747
               ),
 
2748
        Option('signatures',
 
2749
               help='Show digital signature validity.'),
 
2750
        ListOption('match',
 
2751
                   short_name='m',
 
2752
                   help='Show revisions whose properties match this '
 
2753
                   'expression.',
 
2754
                   type=str),
 
2755
        ListOption('match-message',
 
2756
                   help='Show revisions whose message matches this '
 
2757
                   'expression.',
 
2758
                   type=str),
 
2759
        ListOption('match-committer',
2782
2760
                   help='Show revisions whose committer matches this '
2783
2761
                   'expression.',
2784
 
                type=text_type),
2785
 
            ListOption('match-author',
 
2762
                   type=str),
 
2763
        ListOption('match-author',
2786
2764
                   help='Show revisions whose authors match this '
2787
2765
                   'expression.',
2788
 
                type=text_type),
2789
 
            ListOption('match-bugs',
 
2766
                   type=str),
 
2767
        ListOption('match-bugs',
2790
2768
                   help='Show revisions whose bugs match this '
2791
2769
                   'expression.',
2792
 
                type=text_type)
2793
 
            ]
 
2770
                   type=str)
 
2771
        ]
2794
2772
    encoding_type = 'replace'
2795
2773
 
2796
2774
    @display_command
2825
2803
        if include_merged is None:
2826
2804
            include_merged = False
2827
2805
        if (exclude_common_ancestry
2828
 
            and (revision is None or len(revision) != 2)):
 
2806
                and (revision is None or len(revision) != 2)):
2829
2807
            raise errors.BzrCommandError(gettext(
2830
2808
                '--exclude-common-ancestry requires -r with two revisions'))
2831
2809
        if include_merged:
2851
2829
        if file_list:
2852
2830
            # find the file ids to log and check for directory filtering
2853
2831
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2854
 
                revision, file_list, self.add_cleanup)
 
2832
                revision, file_list, self._exit_stack)
2855
2833
            for relpath, file_id, kind in file_info_list:
2856
2834
                if file_id is None:
2857
2835
                    raise errors.BzrCommandError(gettext(
2875
2853
                location = '.'
2876
2854
            dir, relpath = controldir.ControlDir.open_containing(location)
2877
2855
            b = dir.open_branch()
2878
 
            self.add_cleanup(b.lock_read().unlock)
 
2856
            self.enter_context(b.lock_read())
2879
2857
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2880
2858
 
2881
2859
        if b.get_config_stack().get('validate_signatures_in_log'):
2902
2880
        if log_format is None:
2903
2881
            log_format = log.log_formatter_registry.get_default(b)
2904
2882
        # Make a non-encoding output to include the diffs - bug 328007
2905
 
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2883
        unencoded_output = ui.ui_factory.make_output_stream(
 
2884
            encoding_type='exact')
2906
2885
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2907
2886
                        to_exact_file=unencoded_output,
2908
2887
                        show_timezone=timezone,
2925
2904
        # file that isn't a directory without showing a delta" case.
2926
2905
        partial_history = revision and b.repository._format.supports_chks
2927
2906
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2928
 
            or delta_type or partial_history)
 
2907
                              or delta_type or partial_history)
2929
2908
 
2930
2909
        match_dict = {}
2931
2910
        if match:
3001
2980
        rev_id2 = revision_range[1].rev_id
3002
2981
    return rev_id1, rev_id2
3003
2982
 
 
2983
 
3004
2984
def get_log_format(long=False, short=False, line=False, default='long'):
3005
2985
    log_format = default
3006
2986
    if long:
3024
3004
    @display_command
3025
3005
    def run(self, filename):
3026
3006
        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))
 
3007
        with tree.lock_read():
 
3008
            touching_revs = log.find_touching_revisions(
 
3009
                tree.branch.repository, tree.branch.last_revision(), tree, relpath)
 
3010
            for revno, revision_id, what in reversed(list(touching_revs)):
 
3011
                self.outf.write("%6d %s\n" % (revno, what))
3033
3012
 
3034
3013
 
3035
3014
class cmd_ls(Command):
3039
3018
    _see_also = ['status', 'cat']
3040
3019
    takes_args = ['path?']
3041
3020
    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
 
            ]
 
3021
        'verbose',
 
3022
        'revision',
 
3023
        Option('recursive', short_name='R',
 
3024
               help='Recurse into subdirectories.'),
 
3025
        Option('from-root',
 
3026
               help='Print paths relative to the root of the branch.'),
 
3027
        Option('unknown', short_name='u',
 
3028
               help='Print unknown files.'),
 
3029
        Option('versioned', help='Print versioned files.',
 
3030
               short_name='V'),
 
3031
        Option('ignored', short_name='i',
 
3032
               help='Print ignored files.'),
 
3033
        Option('kind', short_name='k',
 
3034
               help=('List entries of a particular kind: file, '
 
3035
                     'directory, symlink, tree-reference.'),
 
3036
               type=str),
 
3037
        'null',
 
3038
        'show-ids',
 
3039
        'directory',
 
3040
        ]
 
3041
 
3061
3042
    @display_command
3062
3043
    def run(self, revision=None, verbose=False,
3063
3044
            recursive=False, from_root=False,
3064
3045
            unknown=False, versioned=False, ignored=False,
3065
3046
            null=False, kind=None, show_ids=False, path=None, directory=None):
3066
3047
 
3067
 
        if kind and kind not in ('file', 'directory', 'symlink'):
 
3048
        if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
3068
3049
            raise errors.BzrCommandError(gettext('invalid kind specified'))
3069
3050
 
3070
3051
        if verbose and null:
3071
 
            raise errors.BzrCommandError(gettext('Cannot set both --verbose and --null'))
 
3052
            raise errors.BzrCommandError(
 
3053
                gettext('Cannot set both --verbose and --null'))
3072
3054
        all = not (unknown or versioned or ignored)
3073
3055
 
3074
 
        selection = {'I':ignored, '?':unknown, 'V':versioned}
 
3056
        selection = {'I': ignored, '?': unknown, 'V': versioned}
3075
3057
 
3076
3058
        if path is None:
3077
3059
            fs_path = '.'
3078
3060
        else:
3079
3061
            if from_root:
3080
3062
                raise errors.BzrCommandError(gettext('cannot specify both --from-root'
3081
 
                                             ' and PATH'))
 
3063
                                                     ' and PATH'))
3082
3064
            fs_path = path
3083
3065
        tree, branch, relpath = \
3084
3066
            _open_directory_or_containing_tree_or_branch(fs_path, directory)
3102
3084
                view_str = views.view_display_str(view_files)
3103
3085
                note(gettext("Ignoring files outside view. View is %s") % view_str)
3104
3086
 
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):
 
3087
        self.enter_context(tree.lock_read())
 
3088
        for fp, fc, fkind, entry in tree.list_files(
 
3089
                include_root=False, from_dir=relpath, recursive=recursive):
3108
3090
            # Apply additional masking
3109
3091
            if not all and not selection[fc]:
3110
3092
                continue
3128
3110
            ui.ui_factory.clear_term()
3129
3111
            if verbose:
3130
3112
                outstring = '%-8s %s' % (fc, outstring)
3131
 
                if show_ids and fid is not None:
3132
 
                    outstring = "%-50s %s" % (outstring, fid)
 
3113
                if show_ids and getattr(entry, 'file_id', None) is not None:
 
3114
                    outstring = "%-50s %s" % (outstring, entry.file_id.decode('utf-8'))
3133
3115
                self.outf.write(outstring + '\n')
3134
3116
            elif null:
3135
3117
                self.outf.write(fp + '\0')
3136
3118
                if show_ids:
3137
 
                    if fid is not None:
3138
 
                        self.outf.write(fid)
 
3119
                    if getattr(entry, 'file_id', None) is not None:
 
3120
                        self.outf.write(entry.file_id.decode('utf-8'))
3139
3121
                    self.outf.write('\0')
3140
3122
                self.outf.flush()
3141
3123
            else:
3142
3124
                if show_ids:
3143
 
                    if fid is not None:
3144
 
                        my_id = fid
 
3125
                    if getattr(entry, 'file_id', None) is not None:
 
3126
                        my_id = entry.file_id.decode('utf-8')
3145
3127
                    else:
3146
3128
                        my_id = ''
3147
3129
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
3170
3152
 
3171
3153
    If a .bzrignore file does not exist, the ignore command
3172
3154
    will create one and add the specified files or patterns to the newly
3173
 
    created file. The ignore command will also automatically add the 
 
3155
    created file. The ignore command will also automatically add the
3174
3156
    .bzrignore file to be versioned. Creating a .bzrignore file without
3175
3157
    the use of the ignore command will require an explicit add command.
3176
3158
 
3178
3160
    After adding, editing or deleting that file either indirectly by
3179
3161
    using this command or directly by using an editor, be sure to commit
3180
3162
    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.
 
3163
 
 
3164
    Breezy also supports a global ignore file ~/.config/breezy/ignore. On
 
3165
    Windows the global ignore file can be found in the application data
 
3166
    directory as
 
3167
    C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
3185
3168
    Global ignores are not touched by this command. The global ignore file
3186
3169
    can be edited directly using an editor.
3187
3170
 
3188
3171
    Patterns prefixed with '!' are exceptions to ignore patterns and take
3189
3172
    precedence over regular ignores.  Such exceptions are used to specify
3190
3173
    files that should be versioned which would otherwise be ignored.
3191
 
    
 
3174
 
3192
3175
    Patterns prefixed with '!!' act as regular ignore patterns, but have
3193
3176
    precedence over the '!' exception patterns.
3194
3177
 
3195
 
    :Notes: 
3196
 
        
 
3178
    :Notes:
 
3179
 
3197
3180
    * Ignore patterns containing shell wildcards must be quoted from
3198
3181
      the shell on Unix.
3199
3182
 
3228
3211
        Ignore everything but the "debian" toplevel directory::
3229
3212
 
3230
3213
            brz ignore "RE:(?!debian/).*"
3231
 
        
 
3214
 
3232
3215
        Ignore everything except the "local" toplevel directory,
3233
3216
        but always ignore autosave files ending in ~, even under local/::
3234
 
        
 
3217
 
3235
3218
            brz ignore "*"
3236
3219
            brz ignore "!./local"
3237
3220
            brz ignore "!!*~"
3240
3223
    _see_also = ['status', 'ignored', 'patterns']
3241
3224
    takes_args = ['name_pattern*']
3242
3225
    takes_options = ['directory',
3243
 
        Option('default-rules',
3244
 
               help='Display the default ignore rules that brz uses.')
3245
 
        ]
 
3226
                     Option('default-rules',
 
3227
                            help='Display the default ignore rules that brz uses.')
 
3228
                     ]
3246
3229
 
3247
3230
    def run(self, name_pattern_list=None, default_rules=None,
3248
3231
            directory=u'.'):
3254
3237
            return
3255
3238
        if not name_pattern_list:
3256
3239
            raise errors.BzrCommandError(gettext("ignore requires at least one "
3257
 
                "NAME_PATTERN or --default-rules."))
 
3240
                                                 "NAME_PATTERN or --default-rules."))
3258
3241
        name_pattern_list = [globbing.normalize_pattern(p)
3259
3242
                             for p in name_pattern_list]
3260
3243
        bad_patterns = ''
3264
3247
                bad_patterns_count += 1
3265
3248
                bad_patterns += ('\n  %s' % p)
3266
3249
        if bad_patterns:
3267
 
            msg = (ngettext('Invalid ignore pattern found. %s', 
 
3250
            msg = (ngettext('Invalid ignore pattern found. %s',
3268
3251
                            'Invalid ignore patterns found. %s',
3269
3252
                            bad_patterns_count) % bad_patterns)
3270
3253
            ui.ui_factory.show_error(msg)
3271
3254
            raise lazy_regex.InvalidPattern('')
3272
3255
        for name_pattern in name_pattern_list:
3273
3256
            if (name_pattern[0] == '/' or
3274
 
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
 
3257
                    (len(name_pattern) > 1 and name_pattern[1] == ':')):
3275
3258
                raise errors.BzrCommandError(gettext(
3276
3259
                    "NAME_PATTERN should not be an absolute path"))
3277
3260
        tree, relpath = WorkingTree.open_containing(directory)
3278
3261
        ignores.tree_ignores_add_patterns(tree, name_pattern_list)
3279
3262
        ignored = globbing.Globster(name_pattern_list)
3280
3263
        matches = []
3281
 
        self.add_cleanup(tree.lock_read().unlock)
3282
 
        for entry in tree.list_files():
3283
 
            id = entry[3]
 
3264
        self.enter_context(tree.lock_read())
 
3265
        for filename, fc, fkind, entry in tree.list_files():
 
3266
            id = getattr(entry, 'file_id', None)
3284
3267
            if id is not None:
3285
 
                filename = entry[0]
3286
3268
                if ignored.match(filename):
3287
3269
                    matches.append(filename)
3288
3270
        if len(matches) > 0:
3289
3271
            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),))
 
3272
                                    "controlled and match your ignore pattern:\n%s"
 
3273
                                    "\nThese files will continue to be version controlled"
 
3274
                                    " unless you 'brz remove' them.\n") % ("\n".join(matches),))
3293
3275
 
3294
3276
 
3295
3277
class cmd_ignored(Command):
3310
3292
    @display_command
3311
3293
    def run(self, directory=u'.'):
3312
3294
        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():
 
3295
        self.enter_context(tree.lock_read())
 
3296
        for path, file_class, kind, entry in tree.list_files():
3315
3297
            if file_class != 'I':
3316
3298
                continue
3317
 
            ## XXX: Slightly inefficient since this was already calculated
 
3299
            # XXX: Slightly inefficient since this was already calculated
3318
3300
            pat = tree.is_ignored(path)
3319
3301
            self.outf.write('%-50s %s\n' % (path, pat))
3320
3302
 
3336
3318
        except ValueError:
3337
3319
            raise errors.BzrCommandError(gettext("not a valid revision-number: %r")
3338
3320
                                         % revno)
3339
 
        revid = WorkingTree.open_containing(directory)[0].branch.get_rev_id(revno)
3340
 
        self.outf.write("%s\n" % revid)
 
3321
        revid = WorkingTree.open_containing(
 
3322
            directory)[0].branch.get_rev_id(revno)
 
3323
        self.outf.write("%s\n" % revid.decode('utf-8'))
3341
3324
 
3342
3325
 
3343
3326
class cmd_export(Command):
3369
3352
      =================       =========================
3370
3353
    """
3371
3354
    encoding = 'exact'
 
3355
    encoding_type = 'exact'
3372
3356
    takes_args = ['dest', 'branch_or_subdir?']
3373
3357
    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
 
        ]
 
3358
                     Option('format',
 
3359
                            help="Type of file to export to.",
 
3360
                            type=str),
 
3361
                     'revision',
 
3362
                     Option('filters', help='Apply content filters to export the '
 
3363
                            'convenient form.'),
 
3364
                     Option('root',
 
3365
                            type=str,
 
3366
                            help="Name of the root directory inside the exported file."),
 
3367
                     Option('per-file-timestamps',
 
3368
                            help='Set modification time of files to that of the last '
 
3369
                            'revision in which it was changed.'),
 
3370
                     Option('uncommitted',
 
3371
                            help='Export the working tree contents rather than that of the '
 
3372
                            'last revision.'),
 
3373
                     ]
 
3374
 
3390
3375
    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
 
3376
            root=None, filters=False, per_file_timestamps=False, uncommitted=False,
 
3377
            directory=u'.'):
 
3378
        from .export import export, guess_format, get_root_name
3394
3379
 
3395
3380
        if branch_or_subdir is None:
3396
3381
            branch_or_subdir = directory
3398
3383
        (tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
3399
3384
            branch_or_subdir)
3400
3385
        if tree is not None:
3401
 
            self.add_cleanup(tree.lock_read().unlock)
 
3386
            self.enter_context(tree.lock_read())
3402
3387
 
3403
3388
        if uncommitted:
3404
3389
            if tree is None:
3406
3391
                    gettext("--uncommitted requires a working tree"))
3407
3392
            export_tree = tree
3408
3393
        else:
3409
 
            export_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
 
3394
            export_tree = _get_one_revision_tree(
 
3395
                'export', revision, branch=b,
 
3396
                tree=tree)
 
3397
 
 
3398
        if format is None:
 
3399
            format = guess_format(dest)
 
3400
 
 
3401
        if root is None:
 
3402
            root = get_root_name(dest)
 
3403
 
 
3404
        if not per_file_timestamps:
 
3405
            force_mtime = time.time()
 
3406
        else:
 
3407
            force_mtime = None
 
3408
 
 
3409
        if filters:
 
3410
            from breezy.filter_tree import ContentFilterTree
 
3411
            export_tree = ContentFilterTree(
 
3412
                export_tree, export_tree._content_filter_stack)
 
3413
 
3410
3414
        try:
3411
 
            export(export_tree, dest, format, root, subdir, filtered=filters,
 
3415
            export(export_tree, dest, format, root, subdir,
3412
3416
                   per_file_timestamps=per_file_timestamps)
3413
3417
        except errors.NoSuchExportFormat as e:
3414
3418
            raise errors.BzrCommandError(
3426
3430
 
3427
3431
    _see_also = ['ls']
3428
3432
    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
 
        ]
 
3433
                     Option('name-from-revision',
 
3434
                            help='The path name in the old tree.'),
 
3435
                     Option('filters', help='Apply content filters to display the '
 
3436
                            'convenience form.'),
 
3437
                     'revision',
 
3438
                     ]
3434
3439
    takes_args = ['filename']
3435
3440
    encoding_type = 'exact'
3436
3441
 
3439
3444
            filters=False, directory=None):
3440
3445
        if revision is not None and len(revision) != 1:
3441
3446
            raise errors.BzrCommandError(gettext("brz cat --revision takes exactly"
3442
 
                                         " one revision specifier"))
 
3447
                                                 " one revision specifier"))
3443
3448
        tree, branch, relpath = \
3444
3449
            _open_directory_or_containing_tree_or_branch(filename, directory)
3445
 
        self.add_cleanup(branch.lock_read().unlock)
 
3450
        self.enter_context(branch.lock_read())
3446
3451
        return self._run(tree, branch, relpath, filename, revision,
3447
3452
                         name_from_revision, filters)
3448
3453
 
3449
3454
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
3450
 
        filtered):
 
3455
             filtered):
 
3456
        import shutil
3451
3457
        if tree is None:
3452
3458
            tree = b.basis_tree()
3453
3459
        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.
 
3460
        self.enter_context(rev_tree.lock_read())
 
3461
 
3462
3462
        if name_from_revision:
3463
3463
            # Try in revision if requested
3464
 
            if old_file_id is None:
 
3464
            if not rev_tree.is_versioned(relpath):
3465
3465
                raise errors.BzrCommandError(gettext(
3466
3466
                    "{0!r} is not present in revision {1}").format(
3467
3467
                        filename, rev_tree.get_revision_id()))
3468
 
            else:
3469
 
                actual_file_id = old_file_id
 
3468
            rev_tree_path = relpath
3470
3469
        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()))
 
3470
            try:
 
3471
                rev_tree_path = _mod_tree.find_previous_path(
 
3472
                    tree, rev_tree, relpath)
 
3473
            except errors.NoSuchFile:
 
3474
                rev_tree_path = None
 
3475
 
 
3476
            if rev_tree_path is None:
 
3477
                # Path didn't exist in working tree
 
3478
                if not rev_tree.is_versioned(relpath):
 
3479
                    raise errors.BzrCommandError(gettext(
 
3480
                        "{0!r} is not present in revision {1}").format(
 
3481
                            filename, rev_tree.get_revision_id()))
 
3482
                else:
 
3483
                    # Fall back to the same path in the basis tree, if present.
 
3484
                    rev_tree_path = relpath
 
3485
 
3480
3486
        if filtered:
3481
3487
            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)
 
3488
            filter_tree = ContentFilterTree(
 
3489
                rev_tree, rev_tree._content_filter_stack)
 
3490
            fileobj = filter_tree.get_file(rev_tree_path)
3485
3491
        else:
3486
 
            content = rev_tree.get_file_text(relpath, actual_file_id)
 
3492
            fileobj = rev_tree.get_file(rev_tree_path)
 
3493
        shutil.copyfileobj(fileobj, self.outf)
3487
3494
        self.cleanup_now()
3488
 
        self.outf.write(content)
3489
3495
 
3490
3496
 
3491
3497
class cmd_local_time_offset(Command):
3492
3498
    __doc__ = """Show the offset in seconds from GMT to local time."""
3493
3499
    hidden = True
 
3500
 
3494
3501
    @display_command
3495
3502
    def run(self):
3496
3503
        self.outf.write("%s\n" % osutils.local_time_offset())
3497
3504
 
3498
3505
 
3499
 
 
3500
3506
class cmd_commit(Command):
3501
3507
    __doc__ = """Commit changes into a new revision.
3502
3508
 
3516
3522
      If selected files are specified, only changes to those files are
3517
3523
      committed.  If a directory is specified then the directory and
3518
3524
      everything within it is committed.
3519
 
  
 
3525
 
3520
3526
      When excludes are given, they take precedence over selected files.
3521
3527
      For example, to commit only changes within foo, but not changes
3522
3528
      within foo/bar::
3523
 
  
 
3529
 
3524
3530
        brz commit foo -x foo/bar
3525
 
  
 
3531
 
3526
3532
      A selective commit after a merge is not yet supported.
3527
3533
 
3528
3534
    :Custom authors:
3533
3539
      "John Doe <jdoe@example.com>". If there is more than one author of
3534
3540
      the change you can specify the option multiple times, once for each
3535
3541
      author.
3536
 
  
 
3542
 
3537
3543
    :Checks:
3538
3544
 
3539
3545
      A common mistake is to forget to add a new file or directory before
3544
3550
 
3545
3551
    :Things to note:
3546
3552
 
3547
 
      If you accidentially commit the wrong changes or make a spelling
 
3553
      If you accidentally commit the wrong changes or make a spelling
3548
3554
      mistake in the commit message say, you can use the uncommit command
3549
3555
      to undo it. See ``brz help uncommit`` for details.
3550
3556
 
3557
3563
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
3558
3564
    takes_args = ['selected*']
3559
3565
    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
 
             ]
 
3566
        ListOption(
 
3567
            'exclude', type=str, short_name='x',
 
3568
            help="Do not consider changes made to a given path."),
 
3569
        Option('message', type=str,
 
3570
               short_name='m',
 
3571
               help="Description of the new revision."),
 
3572
        'verbose',
 
3573
        Option('unchanged',
 
3574
               help='Commit even if nothing has changed.'),
 
3575
        Option('file', type=str,
 
3576
               short_name='F',
 
3577
               argname='msgfile',
 
3578
               help='Take commit message from this file.'),
 
3579
        Option('strict',
 
3580
               help="Refuse to commit if there are unknown "
 
3581
               "files in the working tree."),
 
3582
        Option('commit-time', type=str,
 
3583
               help="Manually set a commit time using commit date "
 
3584
               "format, e.g. '2009-10-10 08:00:00 +0100'."),
 
3585
        ListOption(
 
3586
            'bugs', type=str,
 
3587
            help="Link to a related bug. (see \"brz help bugs\")."),
 
3588
        ListOption(
 
3589
            'fixes', type=str,
 
3590
            help="Mark a bug as being fixed by this revision "
 
3591
                 "(see \"brz help bugs\")."),
 
3592
        ListOption(
 
3593
            'author', type=str,
 
3594
            help="Set the author's name, if it's different "
 
3595
                 "from the committer."),
 
3596
        Option('local',
 
3597
               help="Perform a local commit in a bound "
 
3598
                    "branch.  Local commits are not pushed to "
 
3599
                    "the master branch until a normal commit "
 
3600
                    "is performed."
 
3601
               ),
 
3602
        Option('show-diff', short_name='p',
 
3603
               help='When no message is supplied, show the diff along'
 
3604
               ' with the status summary in the message editor.'),
 
3605
        Option('lossy',
 
3606
               help='When committing to a foreign version control '
 
3607
               'system do not push data that can not be natively '
 
3608
               'represented.'), ]
3598
3609
    aliases = ['ci', 'checkin']
3599
3610
 
3600
 
    def _iter_bug_fix_urls(self, fixes, branch):
3601
 
        default_bugtracker  = None
 
3611
    def _iter_bug_urls(self, bugs, branch, status):
 
3612
        default_bugtracker = None
3602
3613
        # Configure the properties for bug fixing attributes.
3603
 
        for fixed_bug in fixes:
3604
 
            tokens = fixed_bug.split(':')
 
3614
        for bug in bugs:
 
3615
            tokens = bug.split(':')
3605
3616
            if len(tokens) == 1:
3606
3617
                if default_bugtracker is None:
3607
3618
                    branch_config = branch.get_config_stack()
3613
3624
                        "'tracker:id' or specify a default bug tracker "
3614
3625
                        "using the `bugtracker` option.\nSee "
3615
3626
                        "\"brz help bugs\" for more information on this "
3616
 
                        "feature. Commit refused.") % fixed_bug)
 
3627
                        "feature. Commit refused.") % bug)
3617
3628
                tag = default_bugtracker
3618
3629
                bug_id = tokens[0]
3619
3630
            elif len(tokens) != 2:
3620
3631
                raise errors.BzrCommandError(gettext(
3621
3632
                    "Invalid bug %s. Must be in the form of 'tracker:id'. "
3622
3633
                    "See \"brz help bugs\" for more information on this "
3623
 
                    "feature.\nCommit refused.") % fixed_bug)
 
3634
                    "feature.\nCommit refused.") % bug)
3624
3635
            else:
3625
3636
                tag, bug_id = tokens
3626
3637
            try:
3627
 
                yield bugtracker.get_bug_url(tag, branch, bug_id)
 
3638
                yield bugtracker.get_bug_url(tag, branch, bug_id), status
3628
3639
            except bugtracker.UnknownBugTrackerAbbreviation:
3629
3640
                raise errors.BzrCommandError(gettext(
3630
 
                    'Unrecognized bug %s. Commit refused.') % fixed_bug)
 
3641
                    'Unrecognized bug %s. Commit refused.') % bug)
3631
3642
            except bugtracker.MalformedBugIdentifier as e:
3632
3643
                raise errors.BzrCommandError(gettext(
3633
3644
                    u"%s\nCommit refused.") % (e,))
3634
3645
 
3635
3646
    def run(self, message=None, file=None, verbose=False, selected_list=None,
3636
 
            unchanged=False, strict=False, local=False, fixes=None,
 
3647
            unchanged=False, strict=False, local=False, fixes=None, bugs=None,
3637
3648
            author=None, show_diff=False, exclude=None, commit_time=None,
3638
3649
            lossy=False):
 
3650
        import itertools
3639
3651
        from .commit import (
3640
3652
            PointlessCommit,
3641
3653
            )
3669
3681
 
3670
3682
        if fixes is None:
3671
3683
            fixes = []
 
3684
        if bugs is None:
 
3685
            bugs = []
3672
3686
        bug_property = bugtracker.encode_fixes_bug_urls(
3673
 
            self._iter_bug_fix_urls(fixes, tree.branch))
 
3687
            itertools.chain(
 
3688
                self._iter_bug_urls(bugs, tree.branch, bugtracker.RELATED),
 
3689
                self._iter_bug_urls(fixes, tree.branch, bugtracker.FIXED)))
3674
3690
        if bug_property:
3675
 
            properties['bugs'] = bug_property
 
3691
            properties[u'bugs'] = bug_property
3676
3692
 
3677
3693
        if local and not tree.branch.get_bound_location():
3678
3694
            raise errors.LocalRequiresBoundBranch()
3689
3705
                warning_msg = (
3690
3706
                    'The commit message is a file name: "%(f)s".\n'
3691
3707
                    '(use --file "%(f)s" to take commit message from that file)'
3692
 
                    % { 'f': message })
 
3708
                    % {'f': message})
3693
3709
                ui.ui_factory.show_warning(warning_msg)
3694
3710
            if '\r' in message:
3695
3711
                message = message.replace('\r\n', '\n')
3701
3717
        def get_message(commit_obj):
3702
3718
            """Callback to get commit message"""
3703
3719
            if file:
3704
 
                f = open(file)
3705
 
                try:
 
3720
                with open(file, 'rb') as f:
3706
3721
                    my_message = f.read().decode(osutils.get_user_encoding())
3707
 
                finally:
3708
 
                    f.close()
3709
3722
            elif message is not None:
3710
3723
                my_message = message
3711
3724
            else:
3712
3725
                # No message supplied: make one up.
3713
3726
                # text is the status of the tree
3714
3727
                text = make_commit_message_template_encoded(tree,
3715
 
                        selected_list, diff=show_diff,
3716
 
                        output_encoding=osutils.get_user_encoding())
 
3728
                                                            selected_list, diff=show_diff,
 
3729
                                                            output_encoding=osutils.get_user_encoding())
3717
3730
                # start_message is the template generated from hooks
3718
3731
                # XXX: Warning - looks like hooks return unicode,
3719
3732
                # make_commit_message_template_encoded returns user encoding.
3721
3734
                # avoid this.
3722
3735
                my_message = set_commit_message(commit_obj)
3723
3736
                if my_message is None:
3724
 
                    start_message = generate_commit_message_template(commit_obj)
 
3737
                    start_message = generate_commit_message_template(
 
3738
                        commit_obj)
 
3739
                    if start_message is not None:
 
3740
                        start_message = start_message.encode(
 
3741
                            osutils.get_user_encoding())
3725
3742
                    my_message = edit_commit_message_encoded(text,
3726
 
                        start_message=start_message)
 
3743
                                                             start_message=start_message)
3727
3744
                if my_message is None:
3728
3745
                    raise errors.BzrCommandError(gettext("please specify a commit"
3729
 
                        " message with either --message or --file"))
 
3746
                                                         " message with either --message or --file"))
3730
3747
                if my_message == "":
3731
3748
                    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 \"\"."))
 
3749
                                                         " Please specify a commit message with either"
 
3750
                                                         " --message or --file or leave a blank message"
 
3751
                                                         " with --message \"\"."))
3735
3752
            return my_message
3736
3753
 
3737
3754
        # The API permits a commit with a filter of [] to mean 'select nothing'
3749
3766
                        lossy=lossy)
3750
3767
        except PointlessCommit:
3751
3768
            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."))
 
3769
                                                 " Please 'brz add' the files you want to commit, or use"
 
3770
                                                 " --unchanged to force an empty commit."))
3754
3771
        except ConflictsInTree:
3755
3772
            raise errors.BzrCommandError(gettext('Conflicts detected in working '
3756
 
                'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
3757
 
                ' resolve.'))
 
3773
                                                 'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
 
3774
                                                 ' resolve.'))
3758
3775
        except StrictCommitFailed:
3759
3776
            raise errors.BzrCommandError(gettext("Commit refused because there are"
3760
 
                              " unknown files in the working tree."))
 
3777
                                                 " unknown files in the working tree."))
3761
3778
        except errors.BoundBranchOutOfDate as e:
3762
3779
            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.'))
 
3780
                                    'To commit to master branch, run update and then commit.\n'
 
3781
                                    'You can also pass --local to commit to continue working '
 
3782
                                    'disconnected.'))
3766
3783
            raise
3767
3784
 
3768
3785
 
3786
3803
    unreferenced ancestors
3787
3804
        Texts that are ancestors of other texts, but
3788
3805
        are not properly referenced by the revision ancestry.  This is a
3789
 
        subtle problem that Bazaar can work around.
 
3806
        subtle problem that Breezy can work around.
3790
3807
 
3791
3808
    unique file texts
3792
3809
        This is the total number of unique file contents
3798
3815
        entries are modified, but the file contents are not.  It does not
3799
3816
        indicate a problem.
3800
3817
 
3801
 
    If no restrictions are specified, all Bazaar data that is found at the given
 
3818
    If no restrictions are specified, all data that is found at the given
3802
3819
    location will be checked.
3803
3820
 
3804
3821
    :Examples:
3840
3857
    __doc__ = """Upgrade a repository, branch or working tree to a newer format.
3841
3858
 
3842
3859
    When the default format has changed after a major new release of
3843
 
    Bazaar, you may be informed during certain operations that you
 
3860
    Bazaar/Breezy, you may be informed during certain operations that you
3844
3861
    should upgrade. Upgrading to a newer format may improve performance
3845
3862
    or make new features available. It may however limit interoperability
3846
 
    with older repositories or with older versions of Bazaar.
 
3863
    with older repositories or with older versions of Bazaar or Breezy.
3847
3864
 
3848
3865
    If you wish to upgrade to a particular format rather than the
3849
3866
    current default, that can be specified using the --format option.
3865
3882
    If the conversion of a branch fails, remaining branches are still
3866
3883
    tried.
3867
3884
 
3868
 
    For more information on upgrades, see the Bazaar Upgrade Guide,
3869
 
    http://doc.bazaar.canonical.com/latest/en/upgrade-guide/.
 
3885
    For more information on upgrades, see the Breezy Upgrade Guide,
 
3886
    https://www.breezy-vcs.org/doc/en/upgrade-guide/.
3870
3887
    """
3871
3888
 
3872
3889
    _see_also = ['check', 'reconcile', 'formats']
3873
3890
    takes_args = ['url?']
3874
3891
    takes_options = [
3875
3892
        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'),
 
3893
                       help='Upgrade to a specific format.  See "brz help'
 
3894
                       ' formats" for details.',
 
3895
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
3896
                       converter=lambda name: controldir.format_registry.make_controldir(
 
3897
                           name),
 
3898
                       value_switches=True, title='Branch format'),
3881
3899
        Option('clean',
3882
 
            help='Remove the backup.bzr directory if successful.'),
 
3900
               help='Remove the backup.bzr directory if successful.'),
3883
3901
        Option('dry-run',
3884
 
            help="Show what would be done, but don't actually do anything."),
 
3902
               help="Show what would be done, but don't actually do anything."),
3885
3903
    ]
3886
3904
 
3887
3905
    def run(self, url='.', format=None, clean=False, dry_run=False):
3907
3925
 
3908
3926
            brz whoami "Frank Chu <fchu@example.com>"
3909
3927
    """
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
 
                    ]
 
3928
    takes_options = ['directory',
 
3929
                     Option('email',
 
3930
                            help='Display email address only.'),
 
3931
                     Option('branch',
 
3932
                            help='Set identity for the current branch instead of '
 
3933
                            'globally.'),
 
3934
                     ]
3917
3935
    takes_args = ['name?']
3918
3936
    encoding_type = 'replace'
3919
3937
 
3938
3956
 
3939
3957
        if email:
3940
3958
            raise errors.BzrCommandError(gettext("--email can only be used to display existing "
3941
 
                                         "identity"))
 
3959
                                                 "identity"))
3942
3960
 
3943
3961
        # display a warning if an email address isn't included in the given name.
3944
3962
        try:
3945
3963
            _mod_config.extract_email_address(name)
3946
 
        except _mod_config.NoEmailInUsername as e:
 
3964
        except _mod_config.NoEmailInUsername:
3947
3965
            warning('"%s" does not seem to contain an email address.  '
3948
3966
                    'This is allowed, but not recommended.', name)
3949
3967
 
3953
3971
                c = Branch.open_containing(u'.')[0].get_config_stack()
3954
3972
            else:
3955
3973
                b = Branch.open(directory)
3956
 
                self.add_cleanup(b.lock_write().unlock)
 
3974
                self.enter_context(b.lock_write())
3957
3975
                c = b.get_config_stack()
3958
3976
        else:
3959
3977
            c = _mod_config.GlobalStack()
3974
3992
    _see_also = ['info']
3975
3993
    takes_args = ['nickname?']
3976
3994
    takes_options = ['directory']
 
3995
 
3977
3996
    def run(self, nickname=None, directory=u'.'):
3978
3997
        branch = Branch.open_containing(directory)[0]
3979
3998
        if nickname is None:
4022
4041
            if equal_pos == -1:
4023
4042
                self.print_alias(name)
4024
4043
            else:
4025
 
                self.set_alias(name[:equal_pos], name[equal_pos+1:])
 
4044
                self.set_alias(name[:equal_pos], name[equal_pos + 1:])
4026
4045
 
4027
4046
    def remove_alias(self, alias_name):
4028
4047
        if alias_name is None:
4037
4056
    def print_aliases(self):
4038
4057
        """Print out the defined aliases in a similar format to bash."""
4039
4058
        aliases = _mod_config.GlobalConfig().get_aliases()
4040
 
        for key, value in sorted(viewitems(aliases)):
 
4059
        for key, value in sorted(aliases.items()):
4041
4060
            self.outf.write('brz alias %s="%s"\n' % (key, value))
4042
4061
 
4043
4062
    @display_command
4107
4126
    """
4108
4127
    # NB: this is used from the class without creating an instance, which is
4109
4128
    # why it does not have a self parameter.
 
4129
 
4110
4130
    def get_transport_type(typestring):
4111
4131
        """Parse and return a transport specifier."""
4112
4132
        if typestring == "sftp":
4126
4146
    takes_args = ['testspecs*']
4127
4147
    takes_options = ['verbose',
4128
4148
                     Option('one',
4129
 
                             help='Stop when one test fails.',
4130
 
                             short_name='1',
4131
 
                             ),
 
4149
                            help='Stop when one test fails.',
 
4150
                            short_name='1',
 
4151
                            ),
4132
4152
                     Option('transport',
4133
4153
                            help='Use a different transport by default '
4134
4154
                                 'throughout the test suite.',
4148
4168
                     Option('list-only',
4149
4169
                            help='List the tests instead of running them.'),
4150
4170
                     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",
 
4171
                                    help="Run the test suite in parallel.",
 
4172
                                    lazy_registry=(
 
4173
                                        'breezy.tests', 'parallel_registry'),
 
4174
                                    value_switches=False,
 
4175
                                    ),
 
4176
                     Option('randomize', type=str, argname="SEED",
4156
4177
                            help='Randomize the order of tests using the given'
4157
4178
                                 ' seed or "now" for the current time.'),
4158
 
                     ListOption('exclude', type=text_type, argname="PATTERN",
 
4179
                     ListOption('exclude', type=str, argname="PATTERN",
4159
4180
                                short_name='x',
4160
4181
                                help='Exclude tests that match this regular'
4161
4182
                                ' expression.'),
4165
4186
                            help='Output test progress via subunit v2.'),
4166
4187
                     Option('strict', help='Fail on missing dependencies or '
4167
4188
                            'known failures.'),
4168
 
                     Option('load-list', type=text_type, argname='TESTLISTFILE',
 
4189
                     Option('load-list', type=str, argname='TESTLISTFILE',
4169
4190
                            help='Load a test id list from a text file.'),
4170
 
                     ListOption('debugflag', type=text_type, short_name='E',
 
4191
                     ListOption('debugflag', type=str, short_name='E',
4171
4192
                                help='Turn on a selftest debug flag.'),
4172
 
                     ListOption('starting-with', type=text_type, argname='TESTID',
 
4193
                     ListOption('starting-with', type=str, argname='TESTID',
4173
4194
                                param_name='starting_with', short_name='s',
4174
 
                                help=
4175
 
                                'Load only the tests starting with TESTID.'),
 
4195
                                help='Load only the tests starting with TESTID.'),
4176
4196
                     Option('sync',
4177
4197
                            help="By default we disable fsync and fdatasync"
4178
4198
                                 " while running the test suite.")
4197
4217
        # too heavily. The call should be as early as possible, as
4198
4218
        # error reporting for past duplicate imports won't have useful
4199
4219
        # backtraces.
4200
 
        lazy_import.disallow_proxying()
 
4220
        if sys.version_info[0] < 3:
 
4221
            # TODO(pad.lv/1696545): Allow proxying on Python 3, since
 
4222
            # disallowing it currently leads to failures in many places.
 
4223
            lazy_import.disallow_proxying()
4201
4224
 
4202
 
        from . import tests
 
4225
        try:
 
4226
            from . import tests
 
4227
        except ImportError:
 
4228
            raise errors.BzrCommandError("tests not available. Install the "
 
4229
                                         "breezy tests to run the breezy testsuite.")
4203
4230
 
4204
4231
        if testspecs_list is not None:
4205
4232
            pattern = '|'.join(testspecs_list)
4214
4241
                    "to use --subunit."))
4215
4242
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
4216
4243
            # On Windows, disable automatic conversion of '\n' to '\r\n' in
4217
 
            # stdout, which would corrupt the subunit stream. 
 
4244
            # stdout, which would corrupt the subunit stream.
4218
4245
            # FIXME: This has been fixed in subunit trunk (>0.0.5) so the
4219
4246
            # following code can be deleted when it's sufficiently deployed
4220
4247
            # -- vila/mgz 20100514
4221
4248
            if (sys.platform == "win32"
4222
 
                and getattr(sys.stdout, 'fileno', None) is not None):
 
4249
                    and getattr(sys.stdout, 'fileno', None) is not None):
4223
4250
                import msvcrt
4224
4251
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4225
4252
        if subunit2:
4246
4273
        if not sync:
4247
4274
            self._disable_fsync()
4248
4275
        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
 
                          }
 
4276
                           "pattern": pattern,
 
4277
                           "stop_on_failure": one,
 
4278
                           "transport": transport,
 
4279
                           "test_suite_factory": test_suite_factory,
 
4280
                           "lsprof_timed": lsprof_timed,
 
4281
                           "lsprof_tests": lsprof_tests,
 
4282
                           "matching_tests_first": first,
 
4283
                           "list_only": list_only,
 
4284
                           "random_seed": randomize,
 
4285
                           "exclude_pattern": exclude_pattern,
 
4286
                           "strict": strict,
 
4287
                           "load_list": load_list,
 
4288
                           "debug_flags": debugflag,
 
4289
                           "starting_with": starting_with
 
4290
                           }
4264
4291
        selftest_kwargs.update(self.additional_selftest_args)
4265
4292
 
4266
4293
        # Make deprecation warnings visible, unless -Werror is set
4322
4349
 
4323
4350
        branch1 = Branch.open_containing(branch)[0]
4324
4351
        branch2 = Branch.open_containing(other)[0]
4325
 
        self.add_cleanup(branch1.lock_read().unlock)
4326
 
        self.add_cleanup(branch2.lock_read().unlock)
 
4352
        self.enter_context(branch1.lock_read())
 
4353
        self.enter_context(branch2.lock_read())
4327
4354
        last1 = ensure_null(branch1.last_revision())
4328
4355
        last2 = ensure_null(branch2.last_revision())
4329
4356
 
4330
4357
        graph = branch1.repository.get_graph(branch2.repository)
4331
4358
        base_rev_id = graph.find_unique_lca(last1, last2)
4332
4359
 
4333
 
        self.outf.write(gettext('merge base is revision %s\n') % base_rev_id)
 
4360
        self.outf.write(gettext('merge base is revision %s\n') %
 
4361
                        base_rev_id.decode('utf-8'))
4334
4362
 
4335
4363
 
4336
4364
class cmd_merge(Command):
4355
4383
    through OTHER, excluding BASE but including OTHER, will be merged.  If this
4356
4384
    causes some revisions to be skipped, i.e. if the destination branch does
4357
4385
    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
 
4386
    a "cherrypick". Unlike a normal merge, Breezy does not currently track
4359
4387
    cherrypicks. The changes look like a normal commit, and the history of the
4360
4388
    changes from the other branch is not stored in the commit.
4361
4389
 
4378
4406
    committed to record the result of the merge.
4379
4407
 
4380
4408
    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 
 
4409
    --force is given.  If --force is given, then the changes from the source
4382
4410
    will be merged with the current working tree, including any uncommitted
4383
4411
    changes in the tree.  The --force option can also be used to create a
4384
4412
    merge revision which has more than two parents.
4431
4459
        Option('uncommitted', help='Apply uncommitted changes'
4432
4460
               ' from a working copy, instead of branch changes.'),
4433
4461
        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.'),
 
4462
               ' completely merged into the source, pull from the'
 
4463
               ' source rather than merging.  When this happens,'
 
4464
               ' you do not need to commit the result.'),
4437
4465
        custom_help('directory',
4438
 
               help='Branch to merge into, '
 
4466
                    help='Branch to merge into, '
4439
4467
                    'rather than the one containing the working directory.'),
4440
4468
        Option('preview', help='Instead of merging, show a diff of the'
4441
4469
               ' merge.'),
4442
4470
        Option('interactive', help='Select changes interactively.',
4443
 
            short_name='i')
 
4471
               short_name='i')
4444
4472
    ]
4445
4473
 
4446
4474
    def run(self, location=None, revision=None, force=False,
4453
4481
        if merge_type is None:
4454
4482
            merge_type = _mod_merge.Merge3Merger
4455
4483
 
4456
 
        if directory is None: directory = u'.'
 
4484
        if directory is None:
 
4485
            directory = u'.'
4457
4486
        possible_transports = []
4458
4487
        merger = None
4459
4488
        allow_pending = True
4460
4489
        verified = 'inapplicable'
4461
4490
 
4462
4491
        tree = WorkingTree.open_containing(directory)[0]
4463
 
        if tree.branch.revno() == 0:
 
4492
        if tree.branch.last_revision() == _mod_revision.NULL_REVISION:
4464
4493
            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()
 
4494
                                                 'https://bugs.launchpad.net/bzr/+bug/308562'))
4471
4495
 
4472
4496
        # die as quickly as possible if there are uncommitted changes
4473
4497
        if not force:
4478
4502
        change_reporter = delta._ChangeReporter(
4479
4503
            unversioned_filter=tree.is_ignored, view_info=view_info)
4480
4504
        pb = ui.ui_factory.nested_progress_bar()
4481
 
        self.add_cleanup(pb.finished)
4482
 
        self.add_cleanup(tree.lock_write().unlock)
 
4505
        self.enter_context(pb)
 
4506
        self.enter_context(tree.lock_write())
4483
4507
        if location is not None:
4484
4508
            try:
4485
 
                mergeable = bundle.read_mergeable_from_url(location,
4486
 
                    possible_transports=possible_transports)
 
4509
                mergeable = _mod_mergeable.read_mergeable_from_url(
 
4510
                    location, possible_transports=possible_transports)
4487
4511
            except errors.NotABundle:
4488
4512
                mergeable = None
4489
4513
            else:
4490
4514
                if uncommitted:
4491
4515
                    raise errors.BzrCommandError(gettext('Cannot use --uncommitted'
4492
 
                        ' with bundles or merge directives.'))
 
4516
                                                         ' with bundles or merge directives.'))
4493
4517
 
4494
4518
                if revision is not None:
4495
4519
                    raise errors.BzrCommandError(gettext(
4496
4520
                        'Cannot use -r with merge directives or bundles'))
4497
4521
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
4498
 
                   mergeable)
 
4522
                                                                    mergeable)
4499
4523
 
4500
4524
        if merger is None and uncommitted:
4501
4525
            if revision is not None and len(revision) > 0:
4502
4526
                raise errors.BzrCommandError(gettext('Cannot use --uncommitted and'
4503
 
                    ' --revision at the same time.'))
 
4527
                                                     ' --revision at the same time.'))
4504
4528
            merger = self.get_merger_from_uncommitted(tree, location, None)
4505
4529
            allow_pending = False
4506
4530
 
4507
4531
        if merger is None:
4508
4532
            merger, allow_pending = self._get_merger_from_branch(tree,
4509
 
                location, revision, remember, possible_transports, None)
 
4533
                                                                 location, revision, remember, possible_transports, None)
4510
4534
 
4511
4535
        merger.merge_type = merge_type
4512
4536
        merger.reprocess = reprocess
4513
4537
        merger.show_base = show_base
4514
4538
        self.sanity_check_merger(merger)
4515
4539
        if (merger.base_rev_id == merger.other_rev_id and
4516
 
            merger.other_rev_id is not None):
 
4540
                merger.other_rev_id is not None):
4517
4541
            # check if location is a nonexistent file (and not a branch) to
4518
4542
            # disambiguate the 'Nothing to do'
4519
4543
            if merger.interesting_files:
4520
4544
                if not merger.other_tree.has_filename(
4521
 
                    merger.interesting_files[0]):
 
4545
                        merger.interesting_files[0]):
4522
4546
                    note(gettext("merger: ") + str(merger))
4523
4547
                    raise errors.PathsDoNotExist([location])
4524
4548
            note(gettext('Nothing to do.'))
4525
4549
            return 0
4526
4550
        if pull and not preview:
4527
4551
            if merger.interesting_files is not None:
4528
 
                raise errors.BzrCommandError(gettext('Cannot pull individual files'))
 
4552
                raise errors.BzrCommandError(
 
4553
                    gettext('Cannot pull individual files'))
4529
4554
            if (merger.base_rev_id == tree.last_revision()):
4530
4555
                result = tree.pull(merger.other_branch, False,
4531
4556
                                   merger.other_rev_id)
4546
4571
    def _get_preview(self, merger):
4547
4572
        tree_merger = merger.make_merger()
4548
4573
        tt = tree_merger.make_preview_transform()
4549
 
        self.add_cleanup(tt.finalize)
 
4574
        self.enter_context(tt)
4550
4575
        result_tree = tt.get_preview_tree()
4551
4576
        return result_tree
4552
4577
 
4590
4615
 
4591
4616
    def sanity_check_merger(self, merger):
4592
4617
        if (merger.show_base and
4593
 
            not merger.merge_type is _mod_merge.Merge3Merger):
 
4618
                merger.merge_type is not _mod_merge.Merge3Merger):
4594
4619
            raise errors.BzrCommandError(gettext("Show-base is not supported for this"
4595
 
                                         " merge type. %s") % merger.merge_type)
 
4620
                                                 " merge type. %s") % merger.merge_type)
4596
4621
        if merger.reprocess is None:
4597
4622
            if merger.show_base:
4598
4623
                merger.reprocess = False
4601
4626
                merger.reprocess = merger.merge_type.supports_reprocess
4602
4627
        if merger.reprocess and not merger.merge_type.supports_reprocess:
4603
4628
            raise errors.BzrCommandError(gettext("Conflict reduction is not supported"
4604
 
                                         " for merge type %s.") %
 
4629
                                                 " for merge type %s.") %
4605
4630
                                         merger.merge_type)
4606
4631
        if merger.reprocess and merger.show_base:
4607
4632
            raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
4608
 
                                         " show base."))
 
4633
                                                 " show base."))
 
4634
 
 
4635
        if (merger.merge_type.requires_file_merge_plan and
 
4636
            (not getattr(merger.this_tree, 'plan_file_merge', None) or
 
4637
             not getattr(merger.other_tree, 'plan_file_merge', None) or
 
4638
             (merger.base_tree is not None and
 
4639
                 not getattr(merger.base_tree, 'plan_file_merge', None)))):
 
4640
            raise errors.BzrCommandError(
 
4641
                gettext('Plan file merge unsupported: '
 
4642
                        'Merge type incompatible with tree formats.'))
4609
4643
 
4610
4644
    def _get_merger_from_branch(self, tree, location, revision, remember,
4611
4645
                                possible_transports, pb):
4612
4646
        """Produce a merger from a location, assuming it refers to a branch."""
4613
4647
        # find the branch locations
4614
4648
        other_loc, user_location = self._select_branch_location(tree, location,
4615
 
            revision, -1)
 
4649
                                                                revision, -1)
4616
4650
        if revision is not None and len(revision) == 2:
4617
4651
            base_loc, _unused = self._select_branch_location(tree,
4618
 
                location, revision, 0)
 
4652
                                                             location, revision, 0)
4619
4653
        else:
4620
4654
            base_loc = other_loc
4621
4655
        # Open the branches
4622
4656
        other_branch, other_path = Branch.open_containing(other_loc,
4623
 
            possible_transports)
 
4657
                                                          possible_transports)
4624
4658
        if base_loc == other_loc:
4625
4659
            base_branch = other_branch
4626
4660
        else:
4627
4661
            base_branch, base_path = Branch.open_containing(base_loc,
4628
 
                possible_transports)
 
4662
                                                            possible_transports)
4629
4663
        # Find the revision ids
4630
4664
        other_revision_id = None
4631
4665
        base_revision_id = None
4643
4677
        # - user ask to remember or there is no previous location set to merge
4644
4678
        #   from and user didn't ask to *not* remember
4645
4679
        if (user_location is not None
4646
 
            and ((remember
4647
 
                  or (remember is None
4648
 
                      and tree.branch.get_submit_branch() is None)))):
 
4680
            and ((remember or
 
4681
                 (remember is None and
 
4682
                  tree.branch.get_submit_branch() is None)))):
4649
4683
            tree.branch.set_submit_branch(other_branch.base)
4650
4684
        # Merge tags (but don't set them in the master branch yet, the user
4651
4685
        # might revert this merge).  Commit will propagate them.
4652
4686
        other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
4653
4687
        merger = _mod_merge.Merger.from_revision_ids(tree,
4654
 
            other_revision_id, base_revision_id, other_branch, base_branch)
 
4688
                                                     other_revision_id, base_revision_id, other_branch, base_branch)
4655
4689
        if other_path != '':
4656
4690
            allow_pending = False
4657
4691
            merger.interesting_files = [other_path]
4692
4726
            will be the user-entered location.
4693
4727
        """
4694
4728
        if (revision is not None and index is not None
4695
 
            and revision[index] is not None):
 
4729
                and revision[index] is not None):
4696
4730
            branch = revision[index].get_branch()
4697
4731
            if branch is not None:
4698
4732
                return branch, branch
4714
4748
            stored_location_type = "parent"
4715
4749
        mutter("%s", stored_location)
4716
4750
        if stored_location is None:
4717
 
            raise errors.BzrCommandError(gettext("No location specified or remembered"))
 
4751
            raise errors.BzrCommandError(
 
4752
                gettext("No location specified or remembered"))
4718
4753
        display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
4719
4754
        note(gettext("{0} remembered {1} location {2}").format(verb_string,
4720
 
                stored_location_type, display_url))
 
4755
                                                               stored_location_type, display_url))
4721
4756
        return stored_location
4722
4757
 
4723
4758
 
4745
4780
    """
4746
4781
    takes_args = ['file*']
4747
4782
    takes_options = [
4748
 
            'merge-type',
4749
 
            'reprocess',
4750
 
            Option('show-base',
4751
 
                   help="Show base revision text in conflicts."),
4752
 
            ]
 
4783
        'merge-type',
 
4784
        'reprocess',
 
4785
        Option('show-base',
 
4786
               help="Show base revision text in conflicts."),
 
4787
        ]
4753
4788
 
4754
4789
    def run(self, file_list=None, merge_type=None, show_base=False,
4755
4790
            reprocess=False):
4757
4792
        if merge_type is None:
4758
4793
            merge_type = _mod_merge.Merge3Merger
4759
4794
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4760
 
        self.add_cleanup(tree.lock_write().unlock)
 
4795
        self.enter_context(tree.lock_write())
4761
4796
        parents = tree.get_parent_ids()
4762
4797
        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
 
4798
            raise errors.BzrCommandError(
 
4799
                gettext("Sorry, remerge only works after normal"
 
4800
                        " merges.  Not cherrypicking or multi-merges."))
 
4801
        interesting_files = None
4768
4802
        new_conflicts = []
4769
4803
        conflicts = tree.conflicts()
4770
4804
        if file_list is not None:
4771
 
            interesting_ids = set()
 
4805
            interesting_files = set()
4772
4806
            for filename in file_list:
4773
 
                file_id = tree.path2id(filename)
4774
 
                if file_id is None:
 
4807
                if not tree.is_versioned(filename):
4775
4808
                    raise errors.NotVersionedError(filename)
4776
 
                interesting_ids.add(file_id)
4777
 
                if tree.kind(filename, file_id) != "directory":
 
4809
                interesting_files.add(filename)
 
4810
                if tree.kind(filename) != "directory":
4778
4811
                    continue
4779
4812
 
4780
 
                # FIXME: Support nested trees
4781
 
                for name, ie in tree.root_inventory.iter_entries(file_id):
4782
 
                    interesting_ids.add(ie.file_id)
 
4813
                for path, ie in tree.iter_entries_by_dir(
 
4814
                        specific_files=[filename]):
 
4815
                    interesting_files.add(path)
4783
4816
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4784
4817
        else:
4785
4818
            # Remerge only supports resolving contents conflicts
4786
4819
            allowed_conflicts = ('text conflict', 'contents conflict')
4787
4820
            restore_files = [c.path for c in conflicts
4788
4821
                             if c.typestring in allowed_conflicts]
4789
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4822
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
4790
4823
        tree.set_conflicts(ConflictList(new_conflicts))
4791
4824
        if file_list is not None:
4792
4825
            restore_files = file_list
4803
4836
        tree.set_parent_ids(parents[:1])
4804
4837
        try:
4805
4838
            merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
4806
 
            merger.interesting_ids = interesting_ids
 
4839
            merger.interesting_files = interesting_files
4807
4840
            merger.merge_type = merge_type
4808
4841
            merger.show_base = show_base
4809
4842
            merger.reprocess = reprocess
4836
4869
    update command.
4837
4870
 
4838
4871
    Uncommitted changes to files that are reverted will be discarded.
4839
 
    Howver, by default, any files that have been manually changed will be
 
4872
    However, by default, any files that have been manually changed will be
4840
4873
    backed up first.  (Files changed only by merge are not backed up.)  Backup
4841
4874
    files have '.~#~' appended to their name, where # is a number.
4842
4875
 
4882
4915
    def run(self, revision=None, no_backup=False, file_list=None,
4883
4916
            forget_merges=None):
4884
4917
        tree, file_list = WorkingTree.open_containing_paths(file_list)
4885
 
        self.add_cleanup(tree.lock_tree_write().unlock)
 
4918
        self.enter_context(tree.lock_tree_write())
4886
4919
        if forget_merges:
4887
4920
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4888
4921
        else:
4892
4925
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4893
4926
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4894
4927
        tree.revert(file_list, rev_tree, not no_backup, None,
4895
 
            report_changes=True)
 
4928
                    report_changes=True)
4896
4929
 
4897
4930
 
4898
4931
class cmd_assert_fail(Command):
4911
4944
 
4912
4945
    _see_also = ['topics']
4913
4946
    takes_options = [
4914
 
            Option('long', 'Show help on all commands.'),
4915
 
            ]
 
4947
        Option('long', 'Show help on all commands.'),
 
4948
        ]
4916
4949
    takes_args = ['topic?']
4917
4950
    aliases = ['?', '--help', '-?', '-h']
4918
4951
 
4947
4980
    To filter on a range of revisions, you can use the command -r begin..end
4948
4981
    -r revision requests a specific revision, -r ..end or -r begin.. are
4949
4982
    also valid.
4950
 
            
 
4983
 
4951
4984
    :Exit values:
4952
4985
        1 - some missing revisions
4953
4986
        0 - no missing revisions
4989
5022
        'show-ids',
4990
5023
        'verbose',
4991
5024
        custom_help('revision',
4992
 
             help='Filter on other branch revisions (inclusive). '
4993
 
                'See "help revisionspec" for details.'),
 
5025
                    help='Filter on other branch revisions (inclusive). '
 
5026
                    'See "help revisionspec" for details.'),
4994
5027
        Option('my-revision',
4995
 
            type=_parse_revision_str,
4996
 
            help='Filter on local branch revisions (inclusive). '
4997
 
                'See "help revisionspec" for details.'),
 
5028
               type=_parse_revision_str,
 
5029
               help='Filter on local branch revisions (inclusive). '
 
5030
               'See "help revisionspec" for details.'),
4998
5031
        Option('include-merged',
4999
5032
               'Show all revisions in addition to the mainline ones.'),
5000
5033
        Option('include-merges', hidden=True,
5010
5043
            include_merged=None, revision=None, my_revision=None,
5011
5044
            directory=u'.'):
5012
5045
        from breezy.missing import find_unmerged, iter_log_revisions
 
5046
 
5013
5047
        def message(s):
5014
5048
            if not is_quiet():
5015
5049
                self.outf.write(s)
5030
5064
            restrict = 'remote'
5031
5065
 
5032
5066
        local_branch = Branch.open_containing(directory)[0]
5033
 
        self.add_cleanup(local_branch.lock_read().unlock)
 
5067
        self.enter_context(local_branch.lock_read())
5034
5068
 
5035
5069
        parent = local_branch.get_parent()
5036
5070
        if other_branch is None:
5037
5071
            other_branch = parent
5038
5072
            if other_branch is None:
5039
5073
                raise errors.BzrCommandError(gettext("No peer location known"
5040
 
                                             " or specified."))
 
5074
                                                     " or specified."))
5041
5075
            display_url = urlutils.unescape_for_display(parent,
5042
5076
                                                        self.outf.encoding)
5043
5077
            message(gettext("Using saved parent location: {0}\n").format(
5047
5081
        if remote_branch.base == local_branch.base:
5048
5082
            remote_branch = local_branch
5049
5083
        else:
5050
 
            self.add_cleanup(remote_branch.lock_read().unlock)
 
5084
            self.enter_context(remote_branch.lock_read())
5051
5085
 
5052
5086
        local_revid_range = _revision_range_to_revid_range(
5053
5087
            _get_revision_range(my_revision, local_branch,
5054
 
                self.name()))
 
5088
                                self.name()))
5055
5089
 
5056
5090
        remote_revid_range = _revision_range_to_revid_range(
5057
5091
            _get_revision_range(revision,
5058
 
                remote_branch, self.name()))
 
5092
                                remote_branch, self.name()))
5059
5093
 
5060
5094
        local_extra, remote_extra = find_unmerged(
5061
5095
            local_branch, remote_branch, restrict,
5074
5108
        status_code = 0
5075
5109
        if local_extra and not theirs_only:
5076
5110
            message(ngettext("You have %d extra revision:\n",
5077
 
                             "You have %d extra revisions:\n", 
 
5111
                             "You have %d extra revisions:\n",
5078
5112
                             len(local_extra)) %
5079
 
                len(local_extra))
 
5113
                    len(local_extra))
5080
5114
            rev_tag_dict = {}
5081
5115
            if local_branch.supports_tags():
5082
5116
                rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5083
5117
            for revision in iter_log_revisions(local_extra,
5084
 
                                local_branch.repository,
5085
 
                                verbose,
5086
 
                                rev_tag_dict):
 
5118
                                               local_branch.repository,
 
5119
                                               verbose,
 
5120
                                               rev_tag_dict):
5087
5121
                lf.log_revision(revision)
5088
5122
            printed_local = True
5089
5123
            status_code = 1
5096
5130
            message(ngettext("You are missing %d revision:\n",
5097
5131
                             "You are missing %d revisions:\n",
5098
5132
                             len(remote_extra)) %
5099
 
                len(remote_extra))
 
5133
                    len(remote_extra))
5100
5134
            if remote_branch.supports_tags():
5101
5135
                rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5102
5136
            for revision in iter_log_revisions(remote_extra,
5103
 
                                remote_branch.repository,
5104
 
                                verbose,
5105
 
                                rev_tag_dict):
 
5137
                                               remote_branch.repository,
 
5138
                                               verbose,
 
5139
                                               rev_tag_dict):
5106
5140
                lf.log_revision(revision)
5107
5141
            status_code = 1
5108
5142
 
5119
5153
            message(gettext("Branches are up to date.\n"))
5120
5154
        self.cleanup_now()
5121
5155
        if not status_code and parent is None and other_branch is not None:
5122
 
            self.add_cleanup(local_branch.lock_write().unlock)
 
5156
            self.enter_context(local_branch.lock_write())
5123
5157
            # handle race conditions - a parent might be set while we run.
5124
5158
            if local_branch.get_parent() is None:
5125
5159
                local_branch.set_parent(remote_branch.base)
5148
5182
    _see_also = ['repositories']
5149
5183
    takes_args = ['branch_or_repo?']
5150
5184
    takes_options = [
5151
 
        Option('clean-obsolete-packs', 'Delete obsolete packs to save disk space.'),
 
5185
        Option('clean-obsolete-packs',
 
5186
               'Delete obsolete packs to save disk space.'),
5152
5187
        ]
5153
5188
 
5154
5189
    def run(self, branch_or_repo='.', clean_obsolete_packs=False):
5169
5204
 
5170
5205
    --verbose shows the path where each plugin is located.
5171
5206
 
5172
 
    A plugin is an external component for Bazaar that extends the
5173
 
    revision control system, by adding or replacing code in Bazaar.
 
5207
    A plugin is an external component for Breezy that extends the
 
5208
    revision control system, by adding or replacing code in Breezy.
5174
5209
    Plugins can do a variety of things, including overriding commands,
5175
5210
    adding new commands, providing additional network transports and
5176
5211
    customizing log output.
5193
5228
class cmd_testament(Command):
5194
5229
    __doc__ = """Show testament (signing-form) of a revision."""
5195
5230
    takes_options = [
5196
 
            'revision',
5197
 
            Option('long', help='Produce long-format testament.'),
5198
 
            Option('strict',
5199
 
                   help='Produce a strict-format testament.')]
 
5231
        'revision',
 
5232
        Option('long', help='Produce long-format testament.'),
 
5233
        Option('strict',
 
5234
               help='Produce a strict-format testament.')]
5200
5235
    takes_args = ['branch?']
5201
5236
    encoding_type = 'exact'
 
5237
 
5202
5238
    @display_command
5203
5239
    def run(self, branch=u'.', revision=None, long=False, strict=False):
5204
 
        from .testament import Testament, StrictTestament
 
5240
        from .bzr.testament import Testament, StrictTestament
5205
5241
        if strict is True:
5206
5242
            testament_class = StrictTestament
5207
5243
        else:
5210
5246
            b = Branch.open_containing(branch)[0]
5211
5247
        else:
5212
5248
            b = Branch.open(branch)
5213
 
        self.add_cleanup(b.lock_read().unlock)
 
5249
        self.enter_context(b.lock_read())
5214
5250
        if revision is None:
5215
5251
            rev_id = b.last_revision()
5216
5252
        else:
5253
5289
        wt, branch, relpath = \
5254
5290
            _open_directory_or_containing_tree_or_branch(filename, directory)
5255
5291
        if wt is not None:
5256
 
            self.add_cleanup(wt.lock_read().unlock)
 
5292
            self.enter_context(wt.lock_read())
5257
5293
        else:
5258
 
            self.add_cleanup(branch.lock_read().unlock)
 
5294
            self.enter_context(branch.lock_read())
5259
5295
        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:
 
5296
        self.enter_context(tree.lock_read())
 
5297
        if wt is not None and revision is None:
 
5298
            if not wt.is_versioned(relpath):
 
5299
                raise errors.NotVersionedError(relpath)
5268
5300
            # If there is a tree and we're not annotating historical
5269
5301
            # versions, annotate the working tree's content.
5270
5302
            annotate_file_tree(wt, relpath, self.outf, long, all,
5271
 
                show_ids=show_ids, file_id=file_id)
 
5303
                               show_ids=show_ids)
5272
5304
        else:
 
5305
            if not tree.is_versioned(relpath):
 
5306
                raise errors.NotVersionedError(relpath)
5273
5307
            annotate_file_tree(tree, relpath, self.outf, long, all,
5274
 
                show_ids=show_ids, branch=branch, file_id=file_id)
 
5308
                               show_ids=show_ids, branch=branch)
5275
5309
 
5276
5310
 
5277
5311
class cmd_re_sign(Command):
5278
5312
    __doc__ = """Create a digital signature for an existing revision."""
5279
5313
    # TODO be able to replace existing ones.
5280
5314
 
5281
 
    hidden = True # is this right ?
 
5315
    hidden = True  # is this right ?
5282
5316
    takes_args = ['revision_id*']
5283
5317
    takes_options = ['directory', 'revision']
5284
5318
 
5285
5319
    def run(self, revision_id_list=None, revision=None, directory=u'.'):
5286
5320
        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'))
 
5321
            raise errors.BzrCommandError(
 
5322
                gettext('You can only supply one of revision_id or --revision'))
5288
5323
        if revision_id_list is None and revision is None:
5289
 
            raise errors.BzrCommandError(gettext('You must supply either --revision or a revision_id'))
 
5324
            raise errors.BzrCommandError(
 
5325
                gettext('You must supply either --revision or a revision_id'))
5290
5326
        b = WorkingTree.open_containing(directory)[0].branch
5291
 
        self.add_cleanup(b.lock_write().unlock)
 
5327
        self.enter_context(b.lock_write())
5292
5328
        return self._run(b, revision_id_list, revision)
5293
5329
 
5294
5330
    def _run(self, b, revision_id_list, revision):
5295
 
        from . import gpg
 
5331
        from .repository import WriteGroup
5296
5332
        gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
5297
5333
        if revision_id_list is not None:
5298
 
            b.repository.start_write_group()
5299
 
            try:
 
5334
            with WriteGroup(b.repository):
5300
5335
                for revision_id in revision_id_list:
5301
5336
                    revision_id = cache_utf8.encode(revision_id)
5302
5337
                    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
5338
        elif revision is not None:
5309
5339
            if len(revision) == 1:
5310
5340
                revno, rev_id = revision[0].in_history(b)
5311
 
                b.repository.start_write_group()
5312
 
                try:
 
5341
                with WriteGroup(b.repository):
5313
5342
                    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
5343
            elif len(revision) == 2:
5320
5344
                # are they both on rh- if so we can walk between them
5321
5345
                # might be nice to have a range helper for arbitrary
5325
5349
                if to_revid is None:
5326
5350
                    to_revno = b.revno()
5327
5351
                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:
 
5352
                    raise errors.BzrCommandError(
 
5353
                        gettext('Cannot sign a range of non-revision-history revisions'))
 
5354
                with WriteGroup(b.repository):
5331
5355
                    for revno in range(from_revno, to_revno + 1):
5332
5356
                        b.repository.sign_revision(b.get_rev_id(revno),
5333
5357
                                                   gpg_strategy)
5334
 
                except:
5335
 
                    b.repository.abort_write_group()
5336
 
                    raise
5337
 
                else:
5338
 
                    b.repository.commit_write_group()
5339
5358
            else:
5340
 
                raise errors.BzrCommandError(gettext('Please supply either one revision, or a range.'))
 
5359
                raise errors.BzrCommandError(
 
5360
                    gettext('Please supply either one revision, or a range.'))
5341
5361
 
5342
5362
 
5343
5363
class cmd_bind(Command):
5362
5382
            try:
5363
5383
                location = b.get_old_bound_location()
5364
5384
            except errors.UpgradeRequired:
5365
 
                raise errors.BzrCommandError(gettext('No location supplied.  '
5366
 
                    'This format does not remember old locations.'))
 
5385
                raise errors.BzrCommandError(
 
5386
                    gettext('No location supplied.  '
 
5387
                            'This format does not remember old locations.'))
5367
5388
            else:
5368
5389
                if location is None:
5369
5390
                    if b.get_bound_location() is not None:
5377
5398
        try:
5378
5399
            b.bind(b_other)
5379
5400
        except errors.DivergedBranches:
5380
 
            raise errors.BzrCommandError(gettext('These branches have diverged.'
5381
 
                                         ' Try merging, and then bind again.'))
 
5401
            raise errors.BzrCommandError(
 
5402
                gettext('These branches have diverged.'
 
5403
                        ' Try merging, and then bind again.'))
5382
5404
        if b.get_config().has_explicit_nickname():
5383
5405
            b.nick = b_other.nick
5384
5406
 
5422
5444
    # information in shared branches as well.
5423
5445
    _see_also = ['commit']
5424
5446
    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
 
                    ]
 
5447
                     Option('dry-run', help='Don\'t actually make changes.'),
 
5448
                     Option('force', help='Say yes to all questions.'),
 
5449
                     Option('keep-tags',
 
5450
                            help='Keep tags that point to removed revisions.'),
 
5451
                     Option('local',
 
5452
                            help="Only remove the commits from the local "
 
5453
                            "branch when in a checkout."
 
5454
                            ),
 
5455
                     ]
5434
5456
    takes_args = ['location?']
5435
5457
    aliases = []
5436
5458
    encoding_type = 'replace'
5448
5470
            b = control.open_branch()
5449
5471
 
5450
5472
        if tree is not None:
5451
 
            self.add_cleanup(tree.lock_write().unlock)
 
5473
            self.enter_context(tree.lock_write())
5452
5474
        else:
5453
 
            self.add_cleanup(b.lock_write().unlock)
 
5475
            self.enter_context(b.lock_write())
5454
5476
        return self._run(b, tree, dry_run, verbose, revision, force,
5455
 
                         local, keep_tags)
 
5477
                         local, keep_tags, location)
5456
5478
 
5457
5479
    def _run(self, b, tree, dry_run, verbose, revision, force, local,
5458
 
             keep_tags):
 
5480
             keep_tags, location):
5459
5481
        from .log import log_formatter, show_log
5460
5482
        from .uncommit import uncommit
5461
5483
 
5492
5514
 
5493
5515
        if dry_run:
5494
5516
            self.outf.write(gettext('Dry-run, pretending to remove'
5495
 
                            ' the above revisions.\n'))
 
5517
                                    ' the above revisions.\n'))
5496
5518
        else:
5497
 
            self.outf.write(gettext('The above revision(s) will be removed.\n'))
 
5519
            self.outf.write(
 
5520
                gettext('The above revision(s) will be removed.\n'))
5498
5521
 
5499
5522
        if not force:
5500
5523
            if not ui.ui_factory.confirm_action(
5508
5531
               last_rev_id, rev_id)
5509
5532
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
5510
5533
                 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)
 
5534
        if location != '.':
 
5535
            self.outf.write(
 
5536
                gettext('You can restore the old tip by running:\n'
 
5537
                        '  brz pull -d %s %s -r revid:%s\n')
 
5538
                % (location, location, last_rev_id.decode('utf-8')))
 
5539
        else:
 
5540
            self.outf.write(
 
5541
                gettext('You can restore the old tip by running:\n'
 
5542
                        '  brz pull . -r revid:%s\n')
 
5543
                % last_rev_id.decode('utf-8'))
5513
5544
 
5514
5545
 
5515
5546
class cmd_break_lock(Command):
5527
5558
    :Examples:
5528
5559
        brz break-lock
5529
5560
        brz break-lock brz+ssh://example.com/brz/foo
5530
 
        brz break-lock --conf ~/.bazaar
 
5561
        brz break-lock --conf ~/.config/breezy
5531
5562
    """
5532
5563
 
5533
5564
    takes_args = ['location?']
5535
5566
        Option('config',
5536
5567
               help='LOCATION is the directory where the config lock is.'),
5537
5568
        Option('force',
5538
 
            help='Do not ask for confirmation before breaking the lock.'),
 
5569
               help='Do not ask for confirmation before breaking the lock.'),
5539
5570
        ]
5540
5571
 
5541
5572
    def run(self, location=None, config=False, force=False):
5543
5574
            location = u'.'
5544
5575
        if force:
5545
5576
            ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
5546
 
                None,
5547
 
                {'breezy.lockdir.break': True})
 
5577
                                                               None,
 
5578
                                                               {'breezy.lockdir.break': True})
5548
5579
        if config:
5549
5580
            conf = _mod_config.LockableConfig(file_name=location)
5550
5581
            conf.break_lock()
5579
5610
        Option('inet',
5580
5611
               help='Serve on stdin/out for use from inetd or sshd.'),
5581
5612
        RegistryOption('protocol',
5582
 
               help="Protocol to serve.",
5583
 
               lazy_registry=('breezy.transport', 'transport_server_registry'),
5584
 
               value_switches=True),
 
5613
                       help="Protocol to serve.",
 
5614
                       lazy_registry=('breezy.transport',
 
5615
                                      'transport_server_registry'),
 
5616
                       value_switches=True),
5585
5617
        Option('listen',
5586
 
               help='Listen for connections on nominated address.', type=text_type),
 
5618
               help='Listen for connections on nominated address.',
 
5619
               type=str),
5587
5620
        Option('port',
5588
5621
               help='Listen for connections on nominated port.  Passing 0 as '
5589
5622
                    'the port number will result in a dynamically allocated '
5590
5623
                    'port.  The default port depends on the protocol.',
5591
5624
               type=int),
5592
5625
        custom_help('directory',
5593
 
               help='Serve contents of this directory.'),
 
5626
                    help='Serve contents of this directory.'),
5594
5627
        Option('allow-writes',
5595
5628
               help='By default the server is a readonly server.  Supplying '
5596
5629
                    '--allow-writes enables write access to the contents of '
5599
5632
                    'external authentication is arranged supplying this '
5600
5633
                    'option leads to global uncontrolled write access to your '
5601
5634
                    'file system.'
5602
 
                ),
 
5635
               ),
5603
5636
        Option('client-timeout', type=float,
5604
5637
               help='Override the default idle client timeout (5min).'),
5605
5638
        ]
5606
5639
 
5607
5640
    def run(self, listen=None, port=None, inet=False, directory=None,
5608
5641
            allow_writes=False, protocol=None, client_timeout=None):
5609
 
        from . import transport
 
5642
        from . import location, transport
5610
5643
        if directory is None:
5611
5644
            directory = osutils.getcwd()
5612
5645
        if protocol is None:
5613
5646
            protocol = transport.transport_server_registry.get()
5614
 
        url = transport.location_to_url(directory)
 
5647
        url = location.location_to_url(directory)
5615
5648
        if not allow_writes:
5616
5649
            url = 'readonly+' + url
5617
5650
        t = transport.get_transport_from_url(url)
5635
5668
    _see_also = ['split']
5636
5669
    takes_args = ['tree']
5637
5670
    takes_options = [
5638
 
            Option('reference', help='Join by reference.', hidden=True),
5639
 
            ]
 
5671
        Option('reference', help='Join by reference.', hidden=True),
 
5672
        ]
5640
5673
 
5641
5674
    def run(self, tree, reference=False):
5642
5675
        from breezy.mutabletree import BadReferenceTarget
5656
5689
                # XXX: Would be better to just raise a nicely printable
5657
5690
                # exception from the real origin.  Also below.  mbp 20070306
5658
5691
                raise errors.BzrCommandError(
5659
 
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5692
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5660
5693
        else:
5661
5694
            try:
5662
5695
                containing_tree.subsume(sub_tree)
5663
5696
            except errors.BadSubsumeSource as e:
5664
5697
                raise errors.BzrCommandError(
5665
 
                       gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5698
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
5666
5699
 
5667
5700
 
5668
5701
class cmd_split(Command):
5682
5715
 
5683
5716
    def run(self, tree):
5684
5717
        containing_tree, subdir = WorkingTree.open_containing(tree)
5685
 
        sub_id = containing_tree.path2id(subdir)
5686
 
        if sub_id is None:
 
5718
        if not containing_tree.is_versioned(subdir):
5687
5719
            raise errors.NotVersionedError(subdir)
5688
5720
        try:
5689
 
            containing_tree.extract(subdir, sub_id)
 
5721
            containing_tree.extract(subdir)
5690
5722
        except errors.RootNotRich:
5691
5723
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
5692
5724
 
5716
5748
 
5717
5749
    takes_options = [
5718
5750
        'directory',
5719
 
        RegistryOption.from_kwargs('patch-type',
 
5751
        RegistryOption.from_kwargs(
 
5752
            'patch-type',
5720
5753
            'The type of patch to include in the directive.',
5721
5754
            title='Patch type',
5722
5755
            value_switches=True,
5725
5758
            diff='Normal unified diff.',
5726
5759
            plain='No patch, just directive.'),
5727
5760
        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.')
 
5761
        Option('mail-to', type=str,
 
5762
               help='Instead of printing the directive, email to this '
 
5763
               'address.'),
 
5764
        Option('message', type=str, short_name='m',
 
5765
               help='Message to use when committing this merge.')
5732
5766
        ]
5733
5767
 
5734
5768
    encoding_type = 'exact'
5752
5786
        if submit_branch is None:
5753
5787
            submit_branch = branch.get_parent()
5754
5788
        if submit_branch is None:
5755
 
            raise errors.BzrCommandError(gettext('No submit branch specified or known'))
 
5789
            raise errors.BzrCommandError(
 
5790
                gettext('No submit branch specified or known'))
5756
5791
 
5757
5792
        stored_public_branch = branch.get_public_branch()
5758
5793
        if public_branch is None:
5761
5796
            # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5762
5797
            branch.set_public_branch(public_branch)
5763
5798
        if not include_bundle and public_branch is None:
5764
 
            raise errors.BzrCommandError(gettext('No public branch specified or'
5765
 
                                         ' known'))
 
5799
            raise errors.BzrCommandError(
 
5800
                gettext('No public branch specified or known'))
5766
5801
        base_revision_id = None
5767
5802
        if revision is not None:
5768
5803
            if len(revision) > 2:
5769
 
                raise errors.BzrCommandError(gettext('brz merge-directive takes '
5770
 
                    'at most two one revision identifiers'))
 
5804
                raise errors.BzrCommandError(
 
5805
                    gettext('brz merge-directive takes '
 
5806
                            'at most two one revision identifiers'))
5771
5807
            revision_id = revision[-1].as_revision_id(branch)
5772
5808
            if len(revision) == 2:
5773
5809
                base_revision_id = revision[0].as_revision_id(branch)
5807
5843
      branch.
5808
5844
 
5809
5845
    `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
 
    
 
5846
    merge, has the same effect as merging from the source branch.
 
5847
 
5812
5848
    By default the merge directive is self-contained and can be applied to any
5813
5849
    branch containing submit_branch in its ancestory without needing access to
5814
5850
    the source branch.
5815
 
    
5816
 
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5851
 
 
5852
    If --no-bundle is specified, then Breezy doesn't send the contents of the
5817
5853
    revisions, but only a structured request to merge from the
5818
5854
    public_location.  In that case the public_branch is needed and it must be
5819
5855
    up-to-date and accessible to the recipient.  The public_branch is always
5844
5880
    If the preferred client can't be found (or used), your editor will be used.
5845
5881
 
5846
5882
    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.
 
5883
    Supported values for specific clients are "claws", "evolution", "kmail",
 
5884
    "mail.app" (MacOS X's Mail.app), "mutt", and "thunderbird"; generic options
 
5885
    are "default", "editor", "emacsclient", "mapi", and "xdg-email".  Plugins
 
5886
    may also add supported clients.
5852
5887
 
5853
5888
    If mail is being sent, a to address is required.  This can be supplied
5854
5889
    either on the commandline, by setting the submit_to configuration
5855
5890
    option in the branch itself or the child_submit_to configuration option
5856
5891
    in the submit branch.
5857
5892
 
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
5893
    The merge directives created by brz send may be applied using brz merge or
5865
5894
    brz pull by specifying a file containing a merge directive as the location.
5866
5895
 
5886
5915
               help='Branch to generate the submission from, '
5887
5916
               'rather than the one containing the working directory.',
5888
5917
               short_name='f',
5889
 
               type=text_type),
 
5918
               type=str),
5890
5919
        Option('output', short_name='o',
5891
5920
               help='Write merge directive to this file or directory; '
5892
5921
                    'use - for stdout.',
5893
 
               type=text_type),
 
5922
               type=str),
5894
5923
        Option('strict',
5895
5924
               help='Refuse to send if there are uncommitted changes in'
5896
5925
               ' the working tree, --no-strict disables the check.'),
5897
5926
        Option('mail-to', help='Mail the request to this address.',
5898
 
               type=text_type),
 
5927
               type=str),
5899
5928
        'revision',
5900
5929
        'message',
5901
 
        Option('body', help='Body for the email.', type=text_type),
 
5930
        Option('body', help='Body for the email.', type=str),
5902
5931
        RegistryOption('format',
5903
5932
                       help='Use the specified output format.',
5904
5933
                       lazy_registry=('breezy.send', 'format_registry')),
5941
5970
    branch is used in the merge instructions.  This means that a local mirror
5942
5971
    can be used as your actual submit branch, once you have set public_branch
5943
5972
    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
5973
    """
5951
5974
 
5952
5975
    takes_options = [
5960
5983
               help='Branch to generate the submission from, '
5961
5984
               'rather than the one containing the working directory.',
5962
5985
               short_name='f',
5963
 
               type=text_type),
 
5986
               type=str),
5964
5987
        Option('output', short_name='o', help='Write directive to this file.',
5965
 
               type=text_type),
 
5988
               type=str),
5966
5989
        Option('strict',
5967
5990
               help='Refuse to bundle revisions if there are uncommitted'
5968
5991
               ' changes in the working tree, --no-strict disables the check.'),
5984
6007
            output = '-'
5985
6008
        from .send import send
5986
6009
        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)
 
6010
                    format, no_bundle, no_patch, output,
 
6011
                    kwargs.get('from', '.'), None, None, None,
 
6012
                    self.outf, strict=strict)
5990
6013
 
5991
6014
 
5992
6015
class cmd_tag(Command):
6005
6028
    To rename a tag (change the name but keep it on the same revsion), run ``brz
6006
6029
    tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
6007
6030
 
6008
 
    If no tag name is specified it will be determined through the 
 
6031
    If no tag name is specified it will be determined through the
6009
6032
    'automatic_tag_name' hook. This can e.g. be used to automatically tag
6010
6033
    upstream releases by reading configure.ac. See ``brz help hooks`` for
6011
6034
    details.
6015
6038
    takes_args = ['tag_name?']
6016
6039
    takes_options = [
6017
6040
        Option('delete',
6018
 
            help='Delete this tag rather than placing it.',
6019
 
            ),
 
6041
               help='Delete this tag rather than placing it.',
 
6042
               ),
6020
6043
        custom_help('directory',
6021
 
            help='Branch in which to place the tag.'),
 
6044
                    help='Branch in which to place the tag.'),
6022
6045
        Option('force',
6023
 
            help='Replace existing tags.',
6024
 
            ),
 
6046
               help='Replace existing tags.',
 
6047
               ),
6025
6048
        'revision',
6026
6049
        ]
6027
6050
 
6032
6055
            revision=None,
6033
6056
            ):
6034
6057
        branch, relpath = Branch.open_containing(directory)
6035
 
        self.add_cleanup(branch.lock_write().unlock)
 
6058
        self.enter_context(branch.lock_write())
6036
6059
        if delete:
6037
6060
            if tag_name is None:
6038
 
                raise errors.BzrCommandError(gettext("No tag specified to delete."))
 
6061
                raise errors.BzrCommandError(
 
6062
                    gettext("No tag specified to delete."))
6039
6063
            branch.tags.delete_tag(tag_name)
6040
6064
            note(gettext('Deleted tag %s.') % tag_name)
6041
6065
        else:
6077
6101
    _see_also = ['tag']
6078
6102
    takes_options = [
6079
6103
        custom_help('directory',
6080
 
            help='Branch whose tags should be displayed.'),
 
6104
                    help='Branch whose tags should be displayed.'),
6081
6105
        RegistryOption('sort',
6082
 
            'Sort tags by different criteria.', title='Sorting',
6083
 
            lazy_registry=('breezy.tag', 'tag_sort_methods')
6084
 
            ),
 
6106
                       'Sort tags by different criteria.', title='Sorting',
 
6107
                       lazy_registry=('breezy.tag', 'tag_sort_methods')
 
6108
                       ),
6085
6109
        'show-ids',
6086
6110
        'revision',
6087
6111
    ]
6091
6115
        from .tag import tag_sort_methods
6092
6116
        branch, relpath = Branch.open_containing(directory)
6093
6117
 
6094
 
        tags = list(viewitems(branch.tags.get_tag_dict()))
 
6118
        tags = list(branch.tags.get_tag_dict().items())
6095
6119
        if not tags:
6096
6120
            return
6097
6121
 
6098
 
        self.add_cleanup(branch.lock_read().unlock)
 
6122
        self.enter_context(branch.lock_read())
6099
6123
        if revision:
6100
6124
            # Restrict to the specified range
6101
6125
            tags = self._tags_for_range(branch, revision)
6116
6140
                    # which are not in this branch. Fail gracefully ...
6117
6141
                    revno = '?'
6118
6142
                tags[index] = (tag, revno)
 
6143
        else:
 
6144
            tags = [(tag, revid.decode('utf-8')) for (tag, revid) in tags]
6119
6145
        self.cleanup_now()
6120
6146
        for tag, revspec in tags:
6121
6147
            self.outf.write('%-20s %s\n' % (tag, revspec))
6122
6148
 
6123
6149
    def _tags_for_range(self, branch, revision):
6124
 
        range_valid = True
6125
6150
        rev1, rev2 = _get_revision_range(revision, branch, self.name())
6126
6151
        revid1, revid2 = rev1.rev_id, rev2.rev_id
6127
6152
        # _get_revision_range will always set revid2 if it's not specified.
6139
6164
        tagged_revids = branch.tags.get_reverse_tag_dict()
6140
6165
        found = []
6141
6166
        for r in branch.iter_merge_sorted_revisions(
6142
 
            start_revision_id=revid2, stop_revision_id=revid1,
6143
 
            stop_rule='include'):
 
6167
                start_revision_id=revid2, stop_revision_id=revid1,
 
6168
                stop_rule='include'):
6144
6169
            revid_tags = tagged_revids.get(r[0], None)
6145
6170
            if revid_tags:
6146
6171
                found.extend([(tag, r[0]) for tag in revid_tags])
6173
6198
            tree='Reconfigure to be an unbound branch with a working tree.',
6174
6199
            checkout='Reconfigure to be a bound branch with a working tree.',
6175
6200
            lightweight_checkout='Reconfigure to be a lightweight'
6176
 
                ' checkout (with no local history).',
 
6201
            ' checkout (with no local history).',
6177
6202
            ),
6178
6203
        RegistryOption.from_kwargs(
6179
6204
            'repository_type',
6181
6206
            help='Location fo the repository.',
6182
6207
            value_switches=True, enum_switch=False,
6183
6208
            standalone='Reconfigure to be a standalone branch '
6184
 
                '(i.e. stop using shared repository).',
 
6209
            '(i.e. stop using shared repository).',
6185
6210
            use_shared='Reconfigure to use a shared repository.',
6186
6211
            ),
6187
6212
        RegistryOption.from_kwargs(
6190
6215
            help='Whether new branches in the repository have trees.',
6191
6216
            value_switches=True, enum_switch=False,
6192
6217
            with_trees='Reconfigure repository to create '
6193
 
                'working trees on branches by default.',
 
6218
            'working trees on branches by default.',
6194
6219
            with_no_trees='Reconfigure repository to not create '
6195
 
                'working trees on branches by default.'
 
6220
            'working trees on branches by default.'
6196
6221
            ),
6197
 
        Option('bind-to', help='Branch to bind checkout to.', type=text_type),
 
6222
        Option('bind-to', help='Branch to bind checkout to.', type=str),
6198
6223
        Option('force',
6199
 
            help='Perform reconfiguration even if local changes'
6200
 
            ' will be lost.'),
 
6224
               help='Perform reconfiguration even if local changes'
 
6225
               ' will be lost.'),
6201
6226
        Option('stacked-on',
6202
 
            help='Reconfigure a branch to be stacked on another branch.',
6203
 
            type=text_type,
6204
 
            ),
 
6227
               help='Reconfigure a branch to be stacked on another branch.',
 
6228
               type=str,
 
6229
               ),
6205
6230
        Option('unstacked',
6206
 
            help='Reconfigure a branch to be unstacked.  This '
6207
 
                'may require copying substantial data into it.',
6208
 
            ),
 
6231
               help='Reconfigure a branch to be unstacked.  This '
 
6232
               'may require copying substantial data into it.',
 
6233
               ),
6209
6234
        ]
6210
6235
 
6211
6236
    def run(self, location=None, bind_to=None, force=False,
6213
6238
            stacked_on=None, unstacked=None):
6214
6239
        directory = controldir.ControlDir.open(location)
6215
6240
        if stacked_on and unstacked:
6216
 
            raise errors.BzrCommandError(gettext("Can't use both --stacked-on and --unstacked"))
 
6241
            raise errors.BzrCommandError(
 
6242
                gettext("Can't use both --stacked-on and --unstacked"))
6217
6243
        elif stacked_on is not None:
6218
6244
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
6219
6245
        elif unstacked:
6223
6249
        # to ban it.
6224
6250
        if (tree_type is None and
6225
6251
            repository_type is None and
6226
 
            repository_trees is None):
 
6252
                repository_trees is None):
6227
6253
            if stacked_on or unstacked:
6228
6254
                return
6229
6255
            else:
6230
6256
                raise errors.BzrCommandError(gettext('No target configuration '
6231
 
                    'specified'))
 
6257
                                                     'specified'))
6232
6258
        reconfiguration = None
6233
6259
        if tree_type == 'branch':
6234
6260
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
6287
6313
    takes_args = ['to_location?']
6288
6314
    takes_options = ['directory',
6289
6315
                     Option('force',
6290
 
                        help='Switch even if local commits will be lost.'),
 
6316
                            help='Switch even if local commits will be lost.'),
6291
6317
                     'revision',
6292
6318
                     Option('create-branch', short_name='b',
6293
 
                        help='Create the target branch from this one before'
6294
 
                             ' switching to it.'),
 
6319
                            help='Create the target branch from this one before'
 
6320
                            ' switching to it.'),
6295
6321
                     Option('store',
6296
 
                        help='Store and restore uncommitted changes in the'
6297
 
                             ' branch.'),
6298
 
                    ]
 
6322
                            help='Store and restore uncommitted changes in the'
 
6323
                            ' branch.'),
 
6324
                     ]
6299
6325
 
6300
6326
    def run(self, to_location=None, force=False, create_branch=False,
6301
6327
            revision=None, directory=u'.', store=False):
6302
6328
        from . import switch
6303
6329
        tree_location = directory
6304
6330
        revision = _get_one_revision('switch', revision)
6305
 
        possible_transports = []
6306
 
        control_dir = controldir.ControlDir.open_containing(tree_location,
6307
 
            possible_transports=possible_transports)[0]
 
6331
        control_dir = controldir.ControlDir.open_containing(tree_location)[0]
 
6332
        possible_transports = [control_dir.root_transport]
6308
6333
        if to_location is None:
6309
6334
            if revision is None:
6310
6335
                raise errors.BzrCommandError(gettext('You must supply either a'
6311
 
                                             ' revision or a location'))
 
6336
                                                     ' revision or a location'))
6312
6337
            to_location = tree_location
6313
6338
        try:
6314
6339
            branch = control_dir.open_branch(
6317
6342
        except errors.NotBranchError:
6318
6343
            branch = None
6319
6344
            had_explicit_nick = False
 
6345
        else:
 
6346
            possible_transports.append(branch.user_transport)
6320
6347
        if create_branch:
6321
6348
            if branch is None:
6322
6349
                raise errors.BzrCommandError(
6323
6350
                    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()
 
6351
            to_location = lookup_new_sibling_branch(
 
6352
                control_dir, to_location,
 
6353
                possible_transports=possible_transports)
 
6354
            if revision is not None:
 
6355
                revision = revision.as_revision_id(branch)
 
6356
            to_branch = branch.controldir.sprout(
 
6357
                to_location,
 
6358
                possible_transports=possible_transports,
 
6359
                revision_id=revision,
 
6360
                source_branch=branch).open_branch()
6329
6361
        else:
6330
6362
            try:
6331
 
                to_branch = Branch.open(to_location,
6332
 
                    possible_transports=possible_transports)
 
6363
                to_branch = Branch.open(
 
6364
                    to_location, possible_transports=possible_transports)
6333
6365
            except errors.NotBranchError:
6334
 
                to_branch = open_sibling_branch(control_dir, to_location,
 
6366
                to_branch = open_sibling_branch(
 
6367
                    control_dir, to_location,
6335
6368
                    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)
 
6369
            if revision is not None:
 
6370
                revision = revision.as_revision_id(to_branch)
 
6371
        possible_transports.append(to_branch.user_transport)
 
6372
        try:
 
6373
            switch.switch(control_dir, to_branch, force, revision_id=revision,
 
6374
                          store_uncommitted=store,
 
6375
                          possible_transports=possible_transports)
 
6376
        except controldir.BranchReferenceLoop:
 
6377
            raise errors.BzrCommandError(
 
6378
                gettext('switching would create a branch reference loop. '
 
6379
                        'Use the "bzr up" command to switch to a '
 
6380
                        'different revision.'))
6340
6381
        if had_explicit_nick:
6341
 
            branch = control_dir.open_branch() #get the new branch!
 
6382
            branch = control_dir.open_branch()  # get the new branch!
6342
6383
            branch.nick = to_branch.nick
6343
 
        note(gettext('Switched to branch: %s'),
6344
 
            urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6345
 
 
 
6384
        if to_branch.name:
 
6385
            if to_branch.controldir.control_url != control_dir.control_url:
 
6386
                note(gettext('Switched to branch %s at %s'),
 
6387
                     to_branch.name, urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6388
            else:
 
6389
                note(gettext('Switched to branch %s'), to_branch.name)
 
6390
        else:
 
6391
            note(gettext('Switched to branch at %s'),
 
6392
                 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6346
6393
 
6347
6394
 
6348
6395
class cmd_view(Command):
6411
6458
    takes_args = ['file*']
6412
6459
    takes_options = [
6413
6460
        Option('all',
6414
 
            help='Apply list or delete action to all views.',
6415
 
            ),
 
6461
               help='Apply list or delete action to all views.',
 
6462
               ),
6416
6463
        Option('delete',
6417
 
            help='Delete the view.',
6418
 
            ),
 
6464
               help='Delete the view.',
 
6465
               ),
6419
6466
        Option('name',
6420
 
            help='Name of the view to define, list or delete.',
6421
 
            type=text_type,
6422
 
            ),
 
6467
               help='Name of the view to define, list or delete.',
 
6468
               type=str,
 
6469
               ),
6423
6470
        Option('switch',
6424
 
            help='Name of the view to switch to.',
6425
 
            type=text_type,
6426
 
            ),
 
6471
               help='Name of the view to switch to.',
 
6472
               type=str,
 
6473
               ),
6427
6474
        ]
6428
6475
 
6429
6476
    def run(self, file_list,
6433
6480
            switch=None,
6434
6481
            ):
6435
6482
        tree, file_list = WorkingTree.open_containing_paths(file_list,
6436
 
            apply_view=False)
 
6483
                                                            apply_view=False)
6437
6484
        current_view, view_dict = tree.views.get_view_info()
6438
6485
        if name is None:
6439
6486
            name = current_view
6448
6495
                tree.views.set_view_info(None, {})
6449
6496
                self.outf.write(gettext("Deleted all views.\n"))
6450
6497
            elif name is None:
6451
 
                raise errors.BzrCommandError(gettext("No current view to delete"))
 
6498
                raise errors.BzrCommandError(
 
6499
                    gettext("No current view to delete"))
6452
6500
            else:
6453
6501
                tree.views.delete_view(name)
6454
6502
                self.outf.write(gettext("Deleted '%s' view.\n") % name)
6461
6509
                    "Both --switch and --all specified"))
6462
6510
            elif switch == 'off':
6463
6511
                if current_view is None:
6464
 
                    raise errors.BzrCommandError(gettext("No current view to disable"))
 
6512
                    raise errors.BzrCommandError(
 
6513
                        gettext("No current view to disable"))
6465
6514
                tree.views.set_view_info(None, view_dict)
6466
 
                self.outf.write(gettext("Disabled '%s' view.\n") % (current_view))
 
6515
                self.outf.write(gettext("Disabled '%s' view.\n") %
 
6516
                                (current_view))
6467
6517
            else:
6468
6518
                tree.views.set_view_info(switch, view_dict)
6469
6519
                view_str = views.view_display_str(tree.views.lookup_view())
6470
 
                self.outf.write(gettext("Using '{0}' view: {1}\n").format(switch, view_str))
 
6520
                self.outf.write(
 
6521
                    gettext("Using '{0}' view: {1}\n").format(switch, view_str))
6471
6522
        elif all:
6472
6523
            if view_dict:
6473
6524
                self.outf.write(gettext('Views defined:\n'))
6489
6540
                    "Cannot change the 'off' pseudo view"))
6490
6541
            tree.views.set_view(name, sorted(file_list))
6491
6542
            view_str = views.view_display_str(tree.views.lookup_view())
6492
 
            self.outf.write(gettext("Using '{0}' view: {1}\n").format(name, view_str))
 
6543
            self.outf.write(
 
6544
                gettext("Using '{0}' view: {1}\n").format(name, view_str))
6493
6545
        else:
6494
6546
            # list the files
6495
6547
            if name is None:
6497
6549
                self.outf.write(gettext('No current view.\n'))
6498
6550
            else:
6499
6551
                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))
 
6552
                self.outf.write(
 
6553
                    gettext("'{0}' view is: {1}\n").format(name, view_str))
6501
6554
 
6502
6555
 
6503
6556
class cmd_hooks(Command):
6523
6576
class cmd_remove_branch(Command):
6524
6577
    __doc__ = """Remove a branch.
6525
6578
 
6526
 
    This will remove the branch from the specified location but 
 
6579
    This will remove the branch from the specified location but
6527
6580
    will keep any working tree or repository in place.
6528
6581
 
6529
6582
    :Examples:
6537
6590
    takes_args = ["location?"]
6538
6591
 
6539
6592
    takes_options = ['directory',
6540
 
        Option('force', help='Remove branch even if it is the active branch.')]
 
6593
                     Option('force', help='Remove branch even if it is the active branch.')]
6541
6594
 
6542
6595
    aliases = ["rmbranch"]
6543
6596
 
6549
6602
            except errors.NotBranchError:
6550
6603
                active_branch = None
6551
6604
            if (active_branch is not None and
6552
 
                br.control_url == active_branch.control_url):
 
6605
                    br.control_url == active_branch.control_url):
6553
6606
                raise errors.BzrCommandError(
6554
6607
                    gettext("Branch is active. Use --force to remove it."))
6555
6608
        br.controldir.destroy_branch(br.name)
6583
6636
    editor program to decide what the file remaining in the working copy
6584
6637
    should look like.  To do this, add the configuration option
6585
6638
 
6586
 
        change_editor = PROGRAM @new_path @old_path
 
6639
        change_editor = PROGRAM {new_path} {old_path}
6587
6640
 
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 
 
6641
    where {new_path} is replaced with the path of the new version of the
 
6642
    file and {old_path} is replaced with the path of the old version of
 
6643
    the file.  The PROGRAM should save the new file with the desired
6591
6644
    contents of the file in the working tree.
6592
 
        
 
6645
 
6593
6646
    """
6594
6647
 
6595
6648
    takes_args = ['file*']
6618
6671
            writer = breezy.option.diff_writer_registry.get()
6619
6672
        try:
6620
6673
            shelver = Shelver.from_args(writer(self.outf), revision, all,
6621
 
                file_list, message, destroy=destroy, directory=directory)
 
6674
                                        file_list, message, destroy=destroy, directory=directory)
6622
6675
            try:
6623
6676
                shelver.run()
6624
6677
            finally:
6630
6683
        if directory is None:
6631
6684
            directory = u'.'
6632
6685
        tree = WorkingTree.open_containing(directory)[0]
6633
 
        self.add_cleanup(tree.lock_read().unlock)
 
6686
        self.enter_context(tree.lock_read())
6634
6687
        manager = tree.get_shelf_manager()
6635
6688
        shelves = manager.active_shelves()
6636
6689
        if len(shelves) == 0:
6637
6690
            note(gettext('No shelved changes.'))
6638
6691
            return 0
6639
6692
        for shelf_id in reversed(shelves):
6640
 
            message = manager.get_metadata(shelf_id).get('message')
 
6693
            message = manager.get_metadata(shelf_id).get(b'message')
6641
6694
            if message is None:
6642
6695
                message = '<no message>'
6643
6696
            self.outf.write('%3d: %s\n' % (shelf_id, message))
6701
6754
                     Option('dry-run', help='Show files to delete instead of'
6702
6755
                            ' deleting them.'),
6703
6756
                     Option('force', help='Do not prompt before deleting.')]
 
6757
 
6704
6758
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
6705
6759
            force=False, directory=u'.'):
6706
6760
        from .clean_tree import clean_tree
6723
6777
    hidden = True
6724
6778
 
6725
6779
    takes_args = ['path?', 'location?']
 
6780
    takes_options = [
 
6781
        'directory',
 
6782
        Option('force-unversioned',
 
6783
               help='Set reference even if path is not versioned.'),
 
6784
        ]
6726
6785
 
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
 
6786
    def run(self, path=None, directory='.', location=None, force_unversioned=False):
 
6787
        tree, branch, relpath = (
 
6788
            controldir.ControlDir.open_containing_tree_or_branch(directory))
6735
6789
        if tree is None:
6736
6790
            tree = branch.basis_tree()
6737
6791
        if path is None:
6738
 
            info = viewitems(branch._get_all_reference_info())
6739
 
            self._display_reference_info(tree, branch, info)
 
6792
            with tree.lock_read():
 
6793
                info = [
 
6794
                    (path, tree.get_reference_info(path, branch))
 
6795
                    for path in tree.iter_references()]
 
6796
                self._display_reference_info(tree, branch, info)
6740
6797
        else:
6741
 
            file_id = tree.path2id(path)
6742
 
            if file_id is None:
 
6798
            if not tree.is_versioned(path) and not force_unversioned:
6743
6799
                raise errors.NotVersionedError(path)
6744
6800
            if location is None:
6745
 
                info = [(file_id, branch.get_reference_info(file_id))]
 
6801
                info = [(path, tree.get_reference_info(path, branch))]
6746
6802
                self._display_reference_info(tree, branch, info)
6747
6803
            else:
6748
 
                branch.set_reference_info(file_id, path, location)
 
6804
                tree.set_reference_info(path, location)
6749
6805
 
6750
6806
    def _display_reference_info(self, tree, branch, info):
6751
6807
        ref_list = []
6752
 
        for file_id, (path, location) in info:
6753
 
            try:
6754
 
                path = tree.id2path(file_id)
6755
 
            except errors.NoSuchId:
6756
 
                pass
 
6808
        for path, location in info:
6757
6809
            ref_list.append((path, location))
6758
6810
        for path, location in sorted(ref_list):
6759
6811
            self.outf.write('%s %s\n' % (path, location))
6763
6815
    __doc__ = """Export command helps and error messages in po format."""
6764
6816
 
6765
6817
    hidden = True
6766
 
    takes_options = [Option('plugin', 
6767
 
                            help='Export help text from named command '\
 
6818
    takes_options = [Option('plugin',
 
6819
                            help='Export help text from named command '
6768
6820
                                 '(defaults to all built in commands).',
6769
 
                            type=text_type),
 
6821
                            type=str),
6770
6822
                     Option('include-duplicates',
6771
6823
                            help='Output multiple copies of the same msgid '
6772
6824
                                 'string if it appears more than once.'),
6773
 
                            ]
 
6825
                     ]
6774
6826
 
6775
6827
    def run(self, plugin=None, include_duplicates=False):
6776
6828
        from .export_pot import export_pot
6810
6862
        from .transform import link_tree
6811
6863
        target_tree = WorkingTree.open_containing(".")[0]
6812
6864
        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()
 
6865
        with target_tree.lock_write(), source_tree.lock_read():
 
6866
            link_tree(target_tree, source_tree)
6822
6867
 
6823
6868
 
6824
6869
class cmd_fetch_ghosts(Command):
6838
6883
        if len(installed) > 0:
6839
6884
            self.outf.write("Installed:\n")
6840
6885
            for rev in installed:
6841
 
                self.outf.write(rev + "\n")
 
6886
                self.outf.write(rev.decode('utf-8') + "\n")
6842
6887
        if len(failed) > 0:
6843
6888
            self.outf.write("Still missing:\n")
6844
6889
            for rev in failed:
6845
 
                self.outf.write(rev + "\n")
 
6890
                self.outf.write(rev.decode('utf-8') + "\n")
6846
6891
        if not no_fix and len(installed) > 0:
6847
6892
            cmd_reconcile().run(".")
6848
6893
 
6849
6894
 
 
6895
class cmd_grep(Command):
 
6896
    """Print lines matching PATTERN for specified files and revisions.
 
6897
 
 
6898
    This command searches the specified files and revisions for a given
 
6899
    pattern.  The pattern is specified as a Python regular expressions[1].
 
6900
 
 
6901
    If the file name is not specified, the revisions starting with the
 
6902
    current directory are searched recursively. If the revision number is
 
6903
    not specified, the working copy is searched. To search the last committed
 
6904
    revision, use the '-r -1' or '-r last:1' option.
 
6905
 
 
6906
    Unversioned files are not searched unless explicitly specified on the
 
6907
    command line. Unversioned directores are not searched.
 
6908
 
 
6909
    When searching a pattern, the output is shown in the 'filepath:string'
 
6910
    format. If a revision is explicitly searched, the output is shown as
 
6911
    'filepath~N:string', where N is the revision number.
 
6912
 
 
6913
    --include and --exclude options can be used to search only (or exclude
 
6914
    from search) files with base name matches the specified Unix style GLOB
 
6915
    pattern.  The GLOB pattern an use *, ?, and [...] as wildcards, and \\
 
6916
    to quote wildcard or backslash character literally. Note that the glob
 
6917
    pattern is not a regular expression.
 
6918
 
 
6919
    [1] http://docs.python.org/library/re.html#regular-expression-syntax
 
6920
    """
 
6921
 
 
6922
    encoding_type = 'replace'
 
6923
    takes_args = ['pattern', 'path*']
 
6924
    takes_options = [
 
6925
        'verbose',
 
6926
        'revision',
 
6927
        Option('color', type=str, argname='when',
 
6928
               help='Show match in color. WHEN is never, always or auto.'),
 
6929
        Option('diff', short_name='p',
 
6930
               help='Grep for pattern in changeset for each revision.'),
 
6931
        ListOption('exclude', type=str, argname='glob', short_name='X',
 
6932
                   help="Skip files whose base name matches GLOB."),
 
6933
        ListOption('include', type=str, argname='glob', short_name='I',
 
6934
                   help="Search only files whose base name matches GLOB."),
 
6935
        Option('files-with-matches', short_name='l',
 
6936
               help='Print only the name of each input file in '
 
6937
               'which PATTERN is found.'),
 
6938
        Option('files-without-match', short_name='L',
 
6939
               help='Print only the name of each input file in '
 
6940
               'which PATTERN is not found.'),
 
6941
        Option('fixed-string', short_name='F',
 
6942
               help='Interpret PATTERN is a single fixed string (not regex).'),
 
6943
        Option('from-root',
 
6944
               help='Search for pattern starting from the root of the branch. '
 
6945
               '(implies --recursive)'),
 
6946
        Option('ignore-case', short_name='i',
 
6947
               help='Ignore case distinctions while matching.'),
 
6948
        Option('levels',
 
6949
               help='Number of levels to display - 0 for all, 1 for collapsed '
 
6950
               '(1 is default).',
 
6951
               argname='N',
 
6952
               type=_parse_levels),
 
6953
        Option('line-number', short_name='n',
 
6954
               help='Show 1-based line number.'),
 
6955
        Option('no-recursive',
 
6956
               help="Don't recurse into subdirectories. (default is --recursive)"),
 
6957
        Option('null', short_name='Z',
 
6958
               help='Write an ASCII NUL (\\0) separator '
 
6959
               'between output lines rather than a newline.'),
 
6960
        ]
 
6961
 
 
6962
    @display_command
 
6963
    def run(self, verbose=False, ignore_case=False, no_recursive=False,
 
6964
            from_root=False, null=False, levels=None, line_number=False,
 
6965
            path_list=None, revision=None, pattern=None, include=None,
 
6966
            exclude=None, fixed_string=False, files_with_matches=False,
 
6967
            files_without_match=False, color=None, diff=False):
 
6968
        from breezy import _termcolor
 
6969
        from . import grep
 
6970
        import re
 
6971
        if path_list is None:
 
6972
            path_list = ['.']
 
6973
        else:
 
6974
            if from_root:
 
6975
                raise errors.BzrCommandError(
 
6976
                    'cannot specify both --from-root and PATH.')
 
6977
 
 
6978
        if files_with_matches and files_without_match:
 
6979
            raise errors.BzrCommandError(
 
6980
                'cannot specify both '
 
6981
                '-l/--files-with-matches and -L/--files-without-matches.')
 
6982
 
 
6983
        global_config = _mod_config.GlobalConfig()
 
6984
 
 
6985
        if color is None:
 
6986
            color = global_config.get_user_option('grep_color')
 
6987
 
 
6988
        if color is None:
 
6989
            color = 'never'
 
6990
 
 
6991
        if color not in ['always', 'never', 'auto']:
 
6992
            raise errors.BzrCommandError('Valid values for --color are '
 
6993
                                         '"always", "never" or "auto".')
 
6994
 
 
6995
        if levels is None:
 
6996
            levels = 1
 
6997
 
 
6998
        print_revno = False
 
6999
        if revision is not None or levels == 0:
 
7000
            # print revision numbers as we may be showing multiple revisions
 
7001
            print_revno = True
 
7002
 
 
7003
        eol_marker = '\n'
 
7004
        if null:
 
7005
            eol_marker = '\0'
 
7006
 
 
7007
        if not ignore_case and grep.is_fixed_string(pattern):
 
7008
            # if the pattern isalnum, implicitly use to -F for faster grep
 
7009
            fixed_string = True
 
7010
        elif ignore_case and fixed_string:
 
7011
            # GZ 2010-06-02: Fall back to regexp rather than lowercasing
 
7012
            #                pattern and text which will cause pain later
 
7013
            fixed_string = False
 
7014
            pattern = re.escape(pattern)
 
7015
 
 
7016
        patternc = None
 
7017
        re_flags = re.MULTILINE
 
7018
        if ignore_case:
 
7019
            re_flags |= re.IGNORECASE
 
7020
 
 
7021
        if not fixed_string:
 
7022
            patternc = grep.compile_pattern(
 
7023
                pattern.encode(grep._user_encoding), re_flags)
 
7024
 
 
7025
        if color == 'always':
 
7026
            show_color = True
 
7027
        elif color == 'never':
 
7028
            show_color = False
 
7029
        elif color == 'auto':
 
7030
            show_color = _termcolor.allow_color()
 
7031
 
 
7032
        opts = grep.GrepOptions()
 
7033
 
 
7034
        opts.verbose = verbose
 
7035
        opts.ignore_case = ignore_case
 
7036
        opts.no_recursive = no_recursive
 
7037
        opts.from_root = from_root
 
7038
        opts.null = null
 
7039
        opts.levels = levels
 
7040
        opts.line_number = line_number
 
7041
        opts.path_list = path_list
 
7042
        opts.revision = revision
 
7043
        opts.pattern = pattern
 
7044
        opts.include = include
 
7045
        opts.exclude = exclude
 
7046
        opts.fixed_string = fixed_string
 
7047
        opts.files_with_matches = files_with_matches
 
7048
        opts.files_without_match = files_without_match
 
7049
        opts.color = color
 
7050
        opts.diff = False
 
7051
 
 
7052
        opts.eol_marker = eol_marker
 
7053
        opts.print_revno = print_revno
 
7054
        opts.patternc = patternc
 
7055
        opts.recursive = not no_recursive
 
7056
        opts.fixed_string = fixed_string
 
7057
        opts.outf = self.outf
 
7058
        opts.show_color = show_color
 
7059
 
 
7060
        if diff:
 
7061
            # options not used:
 
7062
            # files_with_matches, files_without_match
 
7063
            # levels(?), line_number, from_root
 
7064
            # include, exclude
 
7065
            # These are silently ignored.
 
7066
            grep.grep_diff(opts)
 
7067
        elif revision is None:
 
7068
            grep.workingtree_grep(opts)
 
7069
        else:
 
7070
            grep.versioned_grep(opts)
 
7071
 
 
7072
 
 
7073
class cmd_patch(Command):
 
7074
    """Apply a named patch to the current tree.
 
7075
 
 
7076
    """
 
7077
 
 
7078
    takes_args = ['filename?']
 
7079
    takes_options = [Option('strip', type=int, short_name='p',
 
7080
                            help=("Strip the smallest prefix containing num "
 
7081
                                  "leading slashes from filenames.")),
 
7082
                     Option('silent', help='Suppress chatter.')]
 
7083
 
 
7084
    def run(self, filename=None, strip=None, silent=False):
 
7085
        from .patch import patch_tree
 
7086
        wt = WorkingTree.open_containing('.')[0]
 
7087
        if strip is None:
 
7088
            strip = 1
 
7089
        my_file = None
 
7090
        if filename is None:
 
7091
            my_file = getattr(sys.stdin, 'buffer', sys.stdin)
 
7092
        else:
 
7093
            my_file = open(filename, 'rb')
 
7094
        patches = [my_file.read()]
 
7095
        return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
 
7096
 
 
7097
 
 
7098
class cmd_resolve_location(Command):
 
7099
    __doc__ = """Expand a location to a full URL.
 
7100
 
 
7101
    :Examples:
 
7102
        Look up a Launchpad URL.
 
7103
 
 
7104
            brz resolve-location lp:brz
 
7105
    """
 
7106
    takes_args = ['location']
 
7107
    hidden = True
 
7108
 
 
7109
    def run(self, location):
 
7110
        from .location import location_to_url
 
7111
        url = location_to_url(location)
 
7112
        display_url = urlutils.unescape_for_display(url, self.outf.encoding)
 
7113
        self.outf.write('%s\n' % display_url)
 
7114
 
 
7115
 
6850
7116
def _register_lazy_builtins():
6851
7117
    # register lazy builtins from other modules; called at startup and should
6852
7118
    # be only called once.
6853
7119
    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
 
        ]:
 
7120
            ('cmd_bisect', [], 'breezy.bisect'),
 
7121
            ('cmd_bundle_info', [], 'breezy.bzr.bundle.commands'),
 
7122
            ('cmd_config', [], 'breezy.config'),
 
7123
            ('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
 
7124
            ('cmd_file_id', [], 'breezy.bzr.debug_commands'),
 
7125
            ('cmd_file_path', [], 'breezy.bzr.debug_commands'),
 
7126
            ('cmd_version_info', [], 'breezy.cmd_version_info'),
 
7127
            ('cmd_resolve', ['resolved'], 'breezy.conflicts'),
 
7128
            ('cmd_conflicts', [], 'breezy.conflicts'),
 
7129
            ('cmd_ping', [], 'breezy.bzr.smart.ping'),
 
7130
            ('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
 
7131
            ('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
 
7132
            ('cmd_test_script', [], 'breezy.cmd_test_script'),
 
7133
            ]:
6866
7134
        builtin_command_registry.register_lazy(name, aliases, module_name)