~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2010-02-27 01:34:49 UTC
  • mto: This revision was merged to the branch mainline in revision 5064.
  • Revision ID: mbp@canonical.com-20100227013449-zxostilwfoendxfv
Handle "Directory not empty" from ftp as DirectoryNotEmpty.

FtpTransport._translate_ftp_error can handle all ftp errors; there's no clear
distinction between 'temporary' and 'permament'.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
    bundle,
32
32
    btree_index,
33
33
    bzrdir,
 
34
    directory_service,
34
35
    delta,
35
36
    config,
36
37
    errors,
43
44
    reconfigure,
44
45
    rename_map,
45
46
    revision as _mod_revision,
 
47
    static_tuple,
46
48
    symbol_versioning,
 
49
    timestamp,
47
50
    transport,
48
51
    ui,
49
52
    urlutils,
51
54
    )
52
55
from bzrlib.branch import Branch
53
56
from bzrlib.conflicts import ConflictList
 
57
from bzrlib.transport import memory
54
58
from bzrlib.revisionspec import RevisionSpec, RevisionInfo
55
59
from bzrlib.smtp_connection import SMTPConnection
56
60
from bzrlib.workingtree import WorkingTree
120
124
 
121
125
 
122
126
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
 
127
    """Get a revision tree. Not suitable for commands that change the tree.
 
128
    
 
129
    Specifically, the basis tree in dirstate trees is coupled to the dirstate
 
130
    and doing a commit/uncommit/pull will at best fail due to changing the
 
131
    basis revision data.
 
132
 
 
133
    If tree is passed in, it should be already locked, for lifetime management
 
134
    of the trees internal cached state.
 
135
    """
123
136
    if branch is None:
124
137
        branch = tree.branch
125
138
    if revisions is None:
248
261
    unknown
249
262
        Not versioned and not matching an ignore pattern.
250
263
 
 
264
    Additionally for directories, symlinks and files with an executable
 
265
    bit, Bazaar indicates their type using a trailing character: '/', '@'
 
266
    or '*' respectively.
 
267
 
251
268
    To see ignored files use 'bzr ignored'.  For details on the
252
269
    changes to file texts, use 'bzr diff'.
253
270
 
323
340
    # cat-revision is more for frontends so should be exact
324
341
    encoding = 'strict'
325
342
 
 
343
    def print_revision(self, revisions, revid):
 
344
        stream = revisions.get_record_stream([(revid,)], 'unordered', True)
 
345
        record = stream.next()
 
346
        if record.storage_kind == 'absent':
 
347
            raise errors.NoSuchRevision(revisions, revid)
 
348
        revtext = record.get_bytes_as('fulltext')
 
349
        self.outf.write(revtext.decode('utf-8'))
 
350
 
326
351
    @display_command
327
352
    def run(self, revision_id=None, revision=None):
328
353
        if revision_id is not None and revision is not None:
333
358
                                         ' --revision or a revision_id')
334
359
        b = WorkingTree.open_containing(u'.')[0].branch
335
360
 
336
 
        # TODO: jam 20060112 should cat-revision always output utf-8?
337
 
        if revision_id is not None:
338
 
            revision_id = osutils.safe_revision_id(revision_id, warn=False)
339
 
            try:
340
 
                self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
341
 
            except errors.NoSuchRevision:
342
 
                msg = "The repository %s contains no revision %s." % (b.repository.base,
343
 
                    revision_id)
344
 
                raise errors.BzrCommandError(msg)
345
 
        elif revision is not None:
346
 
            for rev in revision:
347
 
                if rev is None:
348
 
                    raise errors.BzrCommandError('You cannot specify a NULL'
349
 
                                                 ' revision.')
350
 
                rev_id = rev.as_revision_id(b)
351
 
                self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
 
361
        revisions = b.repository.revisions
 
362
        if revisions is None:
 
363
            raise errors.BzrCommandError('Repository %r does not support '
 
364
                'access to raw revision texts')
352
365
 
 
366
        b.repository.lock_read()
 
367
        try:
 
368
            # TODO: jam 20060112 should cat-revision always output utf-8?
 
369
            if revision_id is not None:
 
370
                revision_id = osutils.safe_revision_id(revision_id, warn=False)
 
371
                try:
 
372
                    self.print_revision(revisions, revision_id)
 
373
                except errors.NoSuchRevision:
 
374
                    msg = "The repository %s contains no revision %s." % (
 
375
                        b.repository.base, revision_id)
 
376
                    raise errors.BzrCommandError(msg)
 
377
            elif revision is not None:
 
378
                for rev in revision:
 
379
                    if rev is None:
 
380
                        raise errors.BzrCommandError(
 
381
                            'You cannot specify a NULL revision.')
 
382
                    rev_id = rev.as_revision_id(b)
 
383
                    self.print_revision(revisions, rev_id)
 
384
        finally:
 
385
            b.repository.unlock()
 
386
        
353
387
 
354
388
class cmd_dump_btree(Command):
355
389
    """Dump the contents of a btree index file to stdout.
422
456
        for node in bt.iter_all_entries():
423
457
            # Node is made up of:
424
458
            # (index, key, value, [references])
425
 
            self.outf.write('%s\n' % (node[1:],))
 
459
            refs_as_tuples = static_tuple.as_tuples(node[3])
 
460
            as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
 
461
            self.outf.write('%s\n' % (as_tuple,))
426
462
 
427
463
 
428
464
class cmd_remove_tree(Command):
434
470
    To re-create the working tree, use "bzr checkout".
435
471
    """
436
472
    _see_also = ['checkout', 'working-trees']
437
 
    takes_args = ['location?']
 
473
    takes_args = ['location*']
438
474
    takes_options = [
439
475
        Option('force',
440
476
               help='Remove the working tree even if it has '
441
477
                    'uncommitted changes.'),
442
478
        ]
443
479
 
444
 
    def run(self, location='.', force=False):
445
 
        d = bzrdir.BzrDir.open(location)
446
 
 
447
 
        try:
448
 
            working = d.open_workingtree()
449
 
        except errors.NoWorkingTree:
450
 
            raise errors.BzrCommandError("No working tree to remove")
451
 
        except errors.NotLocalUrl:
452
 
            raise errors.BzrCommandError("You cannot remove the working tree"
453
 
                                         " of a remote path")
454
 
        if not force:
455
 
            # XXX: What about pending merges ? -- vila 20090629
456
 
            if working.has_changes(working.basis_tree()):
457
 
                raise errors.UncommittedChanges(working)
458
 
 
459
 
        working_path = working.bzrdir.root_transport.base
460
 
        branch_path = working.branch.bzrdir.root_transport.base
461
 
        if working_path != branch_path:
462
 
            raise errors.BzrCommandError("You cannot remove the working tree"
463
 
                                         " from a lightweight checkout")
464
 
 
465
 
        d.destroy_workingtree()
 
480
    def run(self, location_list, force=False):
 
481
        if not location_list:
 
482
            location_list=['.']
 
483
 
 
484
        for location in location_list:
 
485
            d = bzrdir.BzrDir.open(location)
 
486
            
 
487
            try:
 
488
                working = d.open_workingtree()
 
489
            except errors.NoWorkingTree:
 
490
                raise errors.BzrCommandError("No working tree to remove")
 
491
            except errors.NotLocalUrl:
 
492
                raise errors.BzrCommandError("You cannot remove the working tree"
 
493
                                             " of a remote path")
 
494
            if not force:
 
495
                if (working.has_changes()):
 
496
                    raise errors.UncommittedChanges(working)
 
497
 
 
498
            working_path = working.bzrdir.root_transport.base
 
499
            branch_path = working.branch.bzrdir.root_transport.base
 
500
            if working_path != branch_path:
 
501
                raise errors.BzrCommandError("You cannot remove the working tree"
 
502
                                             " from a lightweight checkout")
 
503
 
 
504
            d.destroy_workingtree()
466
505
 
467
506
 
468
507
class cmd_revno(Command):
485
524
                wt.lock_read()
486
525
            except (errors.NoWorkingTree, errors.NotLocalUrl):
487
526
                raise errors.NoWorkingTree(location)
 
527
            self.add_cleanup(wt.unlock)
 
528
            revid = wt.last_revision()
488
529
            try:
489
 
                revid = wt.last_revision()
490
 
                try:
491
 
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
492
 
                except errors.NoSuchRevision:
493
 
                    revno_t = ('???',)
494
 
                revno = ".".join(str(n) for n in revno_t)
495
 
            finally:
496
 
                wt.unlock()
 
530
                revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
531
            except errors.NoSuchRevision:
 
532
                revno_t = ('???',)
 
533
            revno = ".".join(str(n) for n in revno_t)
497
534
        else:
498
535
            b = Branch.open_containing(location)[0]
499
536
            b.lock_read()
500
 
            try:
501
 
                revno = b.revno()
502
 
            finally:
503
 
                b.unlock()
504
 
 
 
537
            self.add_cleanup(b.unlock)
 
538
            revno = b.revno()
 
539
        self.cleanup_now()
505
540
        self.outf.write(str(revno) + '\n')
506
541
 
507
542
 
529
564
            wt = WorkingTree.open_containing(directory)[0]
530
565
            b = wt.branch
531
566
            wt.lock_read()
 
567
            self.add_cleanup(wt.unlock)
532
568
        except (errors.NoWorkingTree, errors.NotLocalUrl):
533
569
            wt = None
534
570
            b = Branch.open_containing(directory)[0]
535
571
            b.lock_read()
536
 
        try:
537
 
            revision_ids = []
538
 
            if revision is not None:
539
 
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
540
 
            if revision_info_list is not None:
541
 
                for rev_str in revision_info_list:
542
 
                    rev_spec = RevisionSpec.from_string(rev_str)
543
 
                    revision_ids.append(rev_spec.as_revision_id(b))
544
 
            # No arguments supplied, default to the last revision
545
 
            if len(revision_ids) == 0:
546
 
                if tree:
547
 
                    if wt is None:
548
 
                        raise errors.NoWorkingTree(directory)
549
 
                    revision_ids.append(wt.last_revision())
550
 
                else:
551
 
                    revision_ids.append(b.last_revision())
552
 
 
553
 
            revinfos = []
554
 
            maxlen = 0
555
 
            for revision_id in revision_ids:
556
 
                try:
557
 
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
558
 
                    revno = '.'.join(str(i) for i in dotted_revno)
559
 
                except errors.NoSuchRevision:
560
 
                    revno = '???'
561
 
                maxlen = max(maxlen, len(revno))
562
 
                revinfos.append([revno, revision_id])
563
 
        finally:
564
 
            if wt is None:
565
 
                b.unlock()
 
572
            self.add_cleanup(b.unlock)
 
573
        revision_ids = []
 
574
        if revision is not None:
 
575
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
576
        if revision_info_list is not None:
 
577
            for rev_str in revision_info_list:
 
578
                rev_spec = RevisionSpec.from_string(rev_str)
 
579
                revision_ids.append(rev_spec.as_revision_id(b))
 
580
        # No arguments supplied, default to the last revision
 
581
        if len(revision_ids) == 0:
 
582
            if tree:
 
583
                if wt is None:
 
584
                    raise errors.NoWorkingTree(directory)
 
585
                revision_ids.append(wt.last_revision())
566
586
            else:
567
 
                wt.unlock()
568
 
 
 
587
                revision_ids.append(b.last_revision())
 
588
 
 
589
        revinfos = []
 
590
        maxlen = 0
 
591
        for revision_id in revision_ids:
 
592
            try:
 
593
                dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
594
                revno = '.'.join(str(i) for i in dotted_revno)
 
595
            except errors.NoSuchRevision:
 
596
                revno = '???'
 
597
            maxlen = max(maxlen, len(revno))
 
598
            revinfos.append([revno, revision_id])
 
599
 
 
600
        self.cleanup_now()
569
601
        for ri in revinfos:
570
602
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
571
603
 
603
635
    branches that will be merged later (without showing the two different
604
636
    adds as a conflict). It is also useful when merging another project
605
637
    into a subdirectory of this one.
 
638
    
 
639
    Any files matching patterns in the ignore list will not be added
 
640
    unless they are explicitly mentioned.
606
641
    """
607
642
    takes_args = ['file*']
608
643
    takes_options = [
616
651
               help='Lookup file ids from this tree.'),
617
652
        ]
618
653
    encoding_type = 'replace'
619
 
    _see_also = ['remove']
 
654
    _see_also = ['remove', 'ignore']
620
655
 
621
656
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
622
657
            file_ids_from=None):
640
675
 
641
676
        if base_tree:
642
677
            base_tree.lock_read()
643
 
        try:
644
 
            file_list = self._maybe_expand_globs(file_list)
645
 
            tree, file_list = tree_files_for_add(file_list)
646
 
            added, ignored = tree.smart_add(file_list, not
647
 
                no_recurse, action=action, save=not dry_run)
648
 
        finally:
649
 
            if base_tree is not None:
650
 
                base_tree.unlock()
 
678
            self.add_cleanup(base_tree.unlock)
 
679
        tree, file_list = tree_files_for_add(file_list)
 
680
        added, ignored = tree.smart_add(file_list, not
 
681
            no_recurse, action=action, save=not dry_run)
 
682
        self.cleanup_now()
651
683
        if len(ignored) > 0:
652
684
            if verbose:
653
685
                for glob in sorted(ignored.keys()):
654
686
                    for path in ignored[glob]:
655
687
                        self.outf.write("ignored %s matching \"%s\"\n"
656
688
                                        % (path, glob))
657
 
            else:
658
 
                match_len = 0
659
 
                for glob, paths in ignored.items():
660
 
                    match_len += len(paths)
661
 
                self.outf.write("ignored %d file(s).\n" % match_len)
662
 
            self.outf.write("If you wish to add ignored files, "
663
 
                            "please add them explicitly by name. "
664
 
                            "(\"bzr ignored\" gives a list)\n")
665
689
 
666
690
 
667
691
class cmd_mkdir(Command):
725
749
        revision = _get_one_revision('inventory', revision)
726
750
        work_tree, file_list = tree_files(file_list)
727
751
        work_tree.lock_read()
728
 
        try:
729
 
            if revision is not None:
730
 
                tree = revision.as_tree(work_tree.branch)
731
 
 
732
 
                extra_trees = [work_tree]
733
 
                tree.lock_read()
734
 
            else:
735
 
                tree = work_tree
736
 
                extra_trees = []
737
 
 
738
 
            if file_list is not None:
739
 
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
740
 
                                          require_versioned=True)
741
 
                # find_ids_across_trees may include some paths that don't
742
 
                # exist in 'tree'.
743
 
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
744
 
                                 for file_id in file_ids if file_id in tree)
745
 
            else:
746
 
                entries = tree.inventory.entries()
747
 
        finally:
748
 
            tree.unlock()
749
 
            if tree is not work_tree:
750
 
                work_tree.unlock()
751
 
 
 
752
        self.add_cleanup(work_tree.unlock)
 
753
        if revision is not None:
 
754
            tree = revision.as_tree(work_tree.branch)
 
755
 
 
756
            extra_trees = [work_tree]
 
757
            tree.lock_read()
 
758
            self.add_cleanup(tree.unlock)
 
759
        else:
 
760
            tree = work_tree
 
761
            extra_trees = []
 
762
 
 
763
        if file_list is not None:
 
764
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
765
                                      require_versioned=True)
 
766
            # find_ids_across_trees may include some paths that don't
 
767
            # exist in 'tree'.
 
768
            entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
769
                             for file_id in file_ids if file_id in tree)
 
770
        else:
 
771
            entries = tree.inventory.entries()
 
772
 
 
773
        self.cleanup_now()
752
774
        for path, entry in entries:
753
775
            if kind and kind != entry.kind:
754
776
                continue
800
822
            raise errors.BzrCommandError("missing file argument")
801
823
        tree, rel_names = tree_files(names_list, canonicalize=False)
802
824
        tree.lock_tree_write()
803
 
        try:
804
 
            self._run(tree, names_list, rel_names, after)
805
 
        finally:
806
 
            tree.unlock()
 
825
        self.add_cleanup(tree.unlock)
 
826
        self._run(tree, names_list, rel_names, after)
807
827
 
808
828
    def run_auto(self, names_list, after, dry_run):
809
829
        if names_list is not None and len(names_list) > 1:
814
834
                                         ' --auto.')
815
835
        work_tree, file_list = tree_files(names_list, default_branch='.')
816
836
        work_tree.lock_tree_write()
817
 
        try:
818
 
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
819
 
        finally:
820
 
            work_tree.unlock()
 
837
        self.add_cleanup(work_tree.unlock)
 
838
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
821
839
 
822
840
    def _run(self, tree, names_list, rel_names, after):
823
841
        into_existing = osutils.isdir(names_list[-1])
844
862
            # All entries reference existing inventory items, so fix them up
845
863
            # for cicp file-systems.
846
864
            rel_names = tree.get_canonical_inventory_paths(rel_names)
847
 
            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
848
 
                self.outf.write("%s => %s\n" % pair)
 
865
            for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
 
866
                if not is_quiet():
 
867
                    self.outf.write("%s => %s\n" % (src, dest))
849
868
        else:
850
869
            if len(names_list) != 2:
851
870
                raise errors.BzrCommandError('to mv multiple files the'
895
914
            dest = osutils.pathjoin(dest_parent, dest_tail)
896
915
            mutter("attempting to move %s => %s", src, dest)
897
916
            tree.rename_one(src, dest, after=after)
898
 
            self.outf.write("%s => %s\n" % (src, dest))
 
917
            if not is_quiet():
 
918
                self.outf.write("%s => %s\n" % (src, dest))
899
919
 
900
920
 
901
921
class cmd_pull(Command):
902
922
    """Turn this branch into a mirror of another branch.
903
923
 
904
 
    This command only works on branches that have not diverged.  Branches are
905
 
    considered diverged if the destination branch's most recent commit is one
906
 
    that has not been merged (directly or indirectly) into the parent.
 
924
    By default, this command only works on branches that have not diverged.
 
925
    Branches are considered diverged if the destination branch's most recent 
 
926
    commit is one that has not been merged (directly or indirectly) into the 
 
927
    parent.
907
928
 
908
929
    If branches have diverged, you can use 'bzr merge' to integrate the changes
909
930
    from one into the other.  Once one branch has merged, the other should
910
931
    be able to pull it again.
911
932
 
912
 
    If you want to forget your local changes and just update your branch to
913
 
    match the remote one, use pull --overwrite.
 
933
    If you want to replace your local changes and just want your branch to
 
934
    match the remote one, use pull --overwrite. This will work even if the two
 
935
    branches have diverged.
914
936
 
915
937
    If there is no default location set, the first pull will set it.  After
916
938
    that, you can omit the location to use the default.  To change the
997
1019
 
998
1020
        if branch_from is not branch_to:
999
1021
            branch_from.lock_read()
1000
 
        try:
1001
 
            if revision is not None:
1002
 
                revision_id = revision.as_revision_id(branch_from)
1003
 
 
1004
 
            branch_to.lock_write()
1005
 
            try:
1006
 
                if tree_to is not None:
1007
 
                    view_info = _get_view_info_for_change_reporter(tree_to)
1008
 
                    change_reporter = delta._ChangeReporter(
1009
 
                        unversioned_filter=tree_to.is_ignored,
1010
 
                        view_info=view_info)
1011
 
                    result = tree_to.pull(
1012
 
                        branch_from, overwrite, revision_id, change_reporter,
1013
 
                        possible_transports=possible_transports, local=local)
1014
 
                else:
1015
 
                    result = branch_to.pull(
1016
 
                        branch_from, overwrite, revision_id, local=local)
1017
 
 
1018
 
                result.report(self.outf)
1019
 
                if verbose and result.old_revid != result.new_revid:
1020
 
                    log.show_branch_change(
1021
 
                        branch_to, self.outf, result.old_revno,
1022
 
                        result.old_revid)
1023
 
            finally:
1024
 
                branch_to.unlock()
1025
 
        finally:
1026
 
            if branch_from is not branch_to:
1027
 
                branch_from.unlock()
 
1022
            self.add_cleanup(branch_from.unlock)
 
1023
        if revision is not None:
 
1024
            revision_id = revision.as_revision_id(branch_from)
 
1025
 
 
1026
        branch_to.lock_write()
 
1027
        self.add_cleanup(branch_to.unlock)
 
1028
        if tree_to is not None:
 
1029
            view_info = _get_view_info_for_change_reporter(tree_to)
 
1030
            change_reporter = delta._ChangeReporter(
 
1031
                unversioned_filter=tree_to.is_ignored,
 
1032
                view_info=view_info)
 
1033
            result = tree_to.pull(
 
1034
                branch_from, overwrite, revision_id, change_reporter,
 
1035
                possible_transports=possible_transports, local=local)
 
1036
        else:
 
1037
            result = branch_to.pull(
 
1038
                branch_from, overwrite, revision_id, local=local)
 
1039
 
 
1040
        result.report(self.outf)
 
1041
        if verbose and result.old_revid != result.new_revid:
 
1042
            log.show_branch_change(
 
1043
                branch_to, self.outf, result.old_revno,
 
1044
                result.old_revid)
1028
1045
 
1029
1046
 
1030
1047
class cmd_push(Command):
1105
1122
        else:
1106
1123
            revision_id = None
1107
1124
        if strict and tree is not None and revision_id is None:
1108
 
            if (tree.has_changes(tree.basis_tree())
1109
 
                or len(tree.get_parent_ids()) > 1):
 
1125
            if (tree.has_changes()):
1110
1126
                raise errors.UncommittedChanges(
1111
1127
                    tree, more='Use --no-strict to force the push.')
1112
1128
            if tree.last_revision() != tree.branch.last_revision():
1172
1188
        help='Hard-link working tree files where possible.'),
1173
1189
        Option('no-tree',
1174
1190
            help="Create a branch without a working-tree."),
 
1191
        Option('switch',
 
1192
            help="Switch the checkout in the current directory "
 
1193
                 "to the new branch."),
1175
1194
        Option('stacked',
1176
1195
            help='Create a stacked branch referring to the source branch. '
1177
1196
                'The new branch will depend on the availability of the source '
1183
1202
                    ' directory exists, but does not already'
1184
1203
                    ' have a control directory.  This flag will'
1185
1204
                    ' allow branch to proceed.'),
 
1205
        Option('bind',
 
1206
            help="Bind new branch to from location."),
1186
1207
        ]
1187
1208
    aliases = ['get', 'clone']
1188
1209
 
1189
1210
    def run(self, from_location, to_location=None, revision=None,
1190
1211
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1191
 
            use_existing_dir=False):
 
1212
            use_existing_dir=False, switch=False, bind=False):
 
1213
        from bzrlib import switch as _mod_switch
1192
1214
        from bzrlib.tag import _merge_tags_if_possible
1193
 
 
1194
1215
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1195
1216
            from_location)
1196
 
        if (accelerator_tree is not None and
1197
 
            accelerator_tree.supports_content_filtering()):
1198
 
            accelerator_tree = None
1199
1217
        revision = _get_one_revision('branch', revision)
1200
1218
        br_from.lock_read()
 
1219
        self.add_cleanup(br_from.unlock)
 
1220
        if revision is not None:
 
1221
            revision_id = revision.as_revision_id(br_from)
 
1222
        else:
 
1223
            # FIXME - wt.last_revision, fallback to branch, fall back to
 
1224
            # None or perhaps NULL_REVISION to mean copy nothing
 
1225
            # RBC 20060209
 
1226
            revision_id = br_from.last_revision()
 
1227
        if to_location is None:
 
1228
            to_location = urlutils.derive_to_location(from_location)
 
1229
        to_transport = transport.get_transport(to_location)
1201
1230
        try:
1202
 
            if revision is not None:
1203
 
                revision_id = revision.as_revision_id(br_from)
 
1231
            to_transport.mkdir('.')
 
1232
        except errors.FileExists:
 
1233
            if not use_existing_dir:
 
1234
                raise errors.BzrCommandError('Target directory "%s" '
 
1235
                    'already exists.' % to_location)
1204
1236
            else:
1205
 
                # FIXME - wt.last_revision, fallback to branch, fall back to
1206
 
                # None or perhaps NULL_REVISION to mean copy nothing
1207
 
                # RBC 20060209
1208
 
                revision_id = br_from.last_revision()
1209
 
            if to_location is None:
1210
 
                to_location = urlutils.derive_to_location(from_location)
1211
 
            to_transport = transport.get_transport(to_location)
1212
 
            try:
1213
 
                to_transport.mkdir('.')
1214
 
            except errors.FileExists:
1215
 
                if not use_existing_dir:
1216
 
                    raise errors.BzrCommandError('Target directory "%s" '
1217
 
                        'already exists.' % to_location)
 
1237
                try:
 
1238
                    bzrdir.BzrDir.open_from_transport(to_transport)
 
1239
                except errors.NotBranchError:
 
1240
                    pass
1218
1241
                else:
1219
 
                    try:
1220
 
                        bzrdir.BzrDir.open_from_transport(to_transport)
1221
 
                    except errors.NotBranchError:
1222
 
                        pass
1223
 
                    else:
1224
 
                        raise errors.AlreadyBranchError(to_location)
1225
 
            except errors.NoSuchFile:
1226
 
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1227
 
                                             % to_location)
1228
 
            try:
1229
 
                # preserve whatever source format we have.
1230
 
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1231
 
                                            possible_transports=[to_transport],
1232
 
                                            accelerator_tree=accelerator_tree,
1233
 
                                            hardlink=hardlink, stacked=stacked,
1234
 
                                            force_new_repo=standalone,
1235
 
                                            create_tree_if_local=not no_tree,
1236
 
                                            source_branch=br_from)
1237
 
                branch = dir.open_branch()
1238
 
            except errors.NoSuchRevision:
1239
 
                to_transport.delete_tree('.')
1240
 
                msg = "The branch %s has no revision %s." % (from_location,
1241
 
                    revision)
1242
 
                raise errors.BzrCommandError(msg)
1243
 
            _merge_tags_if_possible(br_from, branch)
1244
 
            # If the source branch is stacked, the new branch may
1245
 
            # be stacked whether we asked for that explicitly or not.
1246
 
            # We therefore need a try/except here and not just 'if stacked:'
1247
 
            try:
1248
 
                note('Created new stacked branch referring to %s.' %
1249
 
                    branch.get_stacked_on_url())
1250
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1251
 
                errors.UnstackableRepositoryFormat), e:
1252
 
                note('Branched %d revision(s).' % branch.revno())
1253
 
        finally:
1254
 
            br_from.unlock()
 
1242
                    raise errors.AlreadyBranchError(to_location)
 
1243
        except errors.NoSuchFile:
 
1244
            raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
1245
                                         % to_location)
 
1246
        try:
 
1247
            # preserve whatever source format we have.
 
1248
            dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
 
1249
                                        possible_transports=[to_transport],
 
1250
                                        accelerator_tree=accelerator_tree,
 
1251
                                        hardlink=hardlink, stacked=stacked,
 
1252
                                        force_new_repo=standalone,
 
1253
                                        create_tree_if_local=not no_tree,
 
1254
                                        source_branch=br_from)
 
1255
            branch = dir.open_branch()
 
1256
        except errors.NoSuchRevision:
 
1257
            to_transport.delete_tree('.')
 
1258
            msg = "The branch %s has no revision %s." % (from_location,
 
1259
                revision)
 
1260
            raise errors.BzrCommandError(msg)
 
1261
        _merge_tags_if_possible(br_from, branch)
 
1262
        # If the source branch is stacked, the new branch may
 
1263
        # be stacked whether we asked for that explicitly or not.
 
1264
        # We therefore need a try/except here and not just 'if stacked:'
 
1265
        try:
 
1266
            note('Created new stacked branch referring to %s.' %
 
1267
                branch.get_stacked_on_url())
 
1268
        except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1269
            errors.UnstackableRepositoryFormat), e:
 
1270
            note('Branched %d revision(s).' % branch.revno())
 
1271
        if bind:
 
1272
            # Bind to the parent
 
1273
            parent_branch = Branch.open(from_location)
 
1274
            branch.bind(parent_branch)
 
1275
            note('New branch bound to %s' % from_location)
 
1276
        if switch:
 
1277
            # Switch to the new branch
 
1278
            wt, _ = WorkingTree.open_containing('.')
 
1279
            _mod_switch.switch(wt.bzrdir, branch)
 
1280
            note('Switched to branch: %s',
 
1281
                urlutils.unescape_for_display(branch.base, 'utf-8'))
1255
1282
 
1256
1283
 
1257
1284
class cmd_checkout(Command):
1336
1363
    def run(self, dir=u'.'):
1337
1364
        tree = WorkingTree.open_containing(dir)[0]
1338
1365
        tree.lock_read()
1339
 
        try:
1340
 
            new_inv = tree.inventory
1341
 
            old_tree = tree.basis_tree()
1342
 
            old_tree.lock_read()
1343
 
            try:
1344
 
                old_inv = old_tree.inventory
1345
 
                renames = []
1346
 
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
1347
 
                for f, paths, c, v, p, n, k, e in iterator:
1348
 
                    if paths[0] == paths[1]:
1349
 
                        continue
1350
 
                    if None in (paths):
1351
 
                        continue
1352
 
                    renames.append(paths)
1353
 
                renames.sort()
1354
 
                for old_name, new_name in renames:
1355
 
                    self.outf.write("%s => %s\n" % (old_name, new_name))
1356
 
            finally:
1357
 
                old_tree.unlock()
1358
 
        finally:
1359
 
            tree.unlock()
 
1366
        self.add_cleanup(tree.unlock)
 
1367
        new_inv = tree.inventory
 
1368
        old_tree = tree.basis_tree()
 
1369
        old_tree.lock_read()
 
1370
        self.add_cleanup(old_tree.unlock)
 
1371
        old_inv = old_tree.inventory
 
1372
        renames = []
 
1373
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1374
        for f, paths, c, v, p, n, k, e in iterator:
 
1375
            if paths[0] == paths[1]:
 
1376
                continue
 
1377
            if None in (paths):
 
1378
                continue
 
1379
            renames.append(paths)
 
1380
        renames.sort()
 
1381
        for old_name, new_name in renames:
 
1382
            self.outf.write("%s => %s\n" % (old_name, new_name))
1360
1383
 
1361
1384
 
1362
1385
class cmd_update(Command):
1368
1391
 
1369
1392
    If you want to discard your local changes, you can just do a
1370
1393
    'bzr revert' instead of 'bzr commit' after the update.
 
1394
 
 
1395
    If the tree's branch is bound to a master branch, it will also update
 
1396
    the branch from the master.
1371
1397
    """
1372
1398
 
1373
1399
    _see_also = ['pull', 'working-trees', 'status-flags']
1374
1400
    takes_args = ['dir?']
 
1401
    takes_options = ['revision']
1375
1402
    aliases = ['up']
1376
1403
 
1377
 
    def run(self, dir='.'):
 
1404
    def run(self, dir='.', revision=None):
 
1405
        if revision is not None and len(revision) != 1:
 
1406
            raise errors.BzrCommandError(
 
1407
                        "bzr update --revision takes exactly one revision")
1378
1408
        tree = WorkingTree.open_containing(dir)[0]
 
1409
        branch = tree.branch
1379
1410
        possible_transports = []
1380
 
        master = tree.branch.get_master_branch(
 
1411
        master = branch.get_master_branch(
1381
1412
            possible_transports=possible_transports)
1382
1413
        if master is not None:
1383
1414
            tree.lock_write()
 
1415
            branch_location = master.base
1384
1416
        else:
1385
1417
            tree.lock_tree_write()
 
1418
            branch_location = tree.branch.base
 
1419
        self.add_cleanup(tree.unlock)
 
1420
        # get rid of the final '/' and be ready for display
 
1421
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
 
1422
                                                        self.outf.encoding)
 
1423
        existing_pending_merges = tree.get_parent_ids()[1:]
 
1424
        if master is None:
 
1425
            old_tip = None
 
1426
        else:
 
1427
            # may need to fetch data into a heavyweight checkout
 
1428
            # XXX: this may take some time, maybe we should display a
 
1429
            # message
 
1430
            old_tip = branch.update(possible_transports)
 
1431
        if revision is not None:
 
1432
            revision_id = revision[0].as_revision_id(branch)
 
1433
        else:
 
1434
            revision_id = branch.last_revision()
 
1435
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
 
1436
            revno = branch.revision_id_to_revno(revision_id)
 
1437
            note("Tree is up to date at revision %d of branch %s" %
 
1438
                (revno, branch_location))
 
1439
            return 0
 
1440
        view_info = _get_view_info_for_change_reporter(tree)
 
1441
        change_reporter = delta._ChangeReporter(
 
1442
            unversioned_filter=tree.is_ignored,
 
1443
            view_info=view_info)
1386
1444
        try:
1387
 
            existing_pending_merges = tree.get_parent_ids()[1:]
1388
 
            last_rev = _mod_revision.ensure_null(tree.last_revision())
1389
 
            if last_rev == _mod_revision.ensure_null(
1390
 
                tree.branch.last_revision()):
1391
 
                # may be up to date, check master too.
1392
 
                if master is None or last_rev == _mod_revision.ensure_null(
1393
 
                    master.last_revision()):
1394
 
                    revno = tree.branch.revision_id_to_revno(last_rev)
1395
 
                    note("Tree is up to date at revision %d." % (revno,))
1396
 
                    return 0
1397
 
            view_info = _get_view_info_for_change_reporter(tree)
1398
1445
            conflicts = tree.update(
1399
 
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
1400
 
                view_info=view_info), possible_transports=possible_transports)
1401
 
            revno = tree.branch.revision_id_to_revno(
1402
 
                _mod_revision.ensure_null(tree.last_revision()))
1403
 
            note('Updated to revision %d.' % (revno,))
1404
 
            if tree.get_parent_ids()[1:] != existing_pending_merges:
1405
 
                note('Your local commits will now show as pending merges with '
1406
 
                     "'bzr status', and can be committed with 'bzr commit'.")
1407
 
            if conflicts != 0:
1408
 
                return 1
1409
 
            else:
1410
 
                return 0
1411
 
        finally:
1412
 
            tree.unlock()
 
1446
                change_reporter,
 
1447
                possible_transports=possible_transports,
 
1448
                revision=revision_id,
 
1449
                old_tip=old_tip)
 
1450
        except errors.NoSuchRevision, e:
 
1451
            raise errors.BzrCommandError(
 
1452
                                  "branch has no revision %s\n"
 
1453
                                  "bzr update --revision only works"
 
1454
                                  " for a revision in the branch history"
 
1455
                                  % (e.revision))
 
1456
        revno = tree.branch.revision_id_to_revno(
 
1457
            _mod_revision.ensure_null(tree.last_revision()))
 
1458
        note('Updated to revision %d of branch %s' %
 
1459
             (revno, branch_location))
 
1460
        if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1461
            note('Your local commits will now show as pending merges with '
 
1462
                 "'bzr status', and can be committed with 'bzr commit'.")
 
1463
        if conflicts != 0:
 
1464
            return 1
 
1465
        else:
 
1466
            return 0
1413
1467
 
1414
1468
 
1415
1469
class cmd_info(Command):
1472
1526
            title='Deletion Strategy', value_switches=True, enum_switch=False,
1473
1527
            safe='Only delete files if they can be'
1474
1528
                 ' safely recovered (default).',
1475
 
            keep="Don't delete any files.",
 
1529
            keep='Delete from bzr but leave the working copy.',
1476
1530
            force='Delete all the specified files, even if they can not be '
1477
1531
                'recovered and even if they are non-empty directories.')]
1478
1532
    aliases = ['rm', 'del']
1486
1540
            file_list = [f for f in file_list]
1487
1541
 
1488
1542
        tree.lock_write()
1489
 
        try:
1490
 
            # Heuristics should probably all move into tree.remove_smart or
1491
 
            # some such?
1492
 
            if new:
1493
 
                added = tree.changes_from(tree.basis_tree(),
1494
 
                    specific_files=file_list).added
1495
 
                file_list = sorted([f[0] for f in added], reverse=True)
1496
 
                if len(file_list) == 0:
1497
 
                    raise errors.BzrCommandError('No matching files.')
1498
 
            elif file_list is None:
1499
 
                # missing files show up in iter_changes(basis) as
1500
 
                # versioned-with-no-kind.
1501
 
                missing = []
1502
 
                for change in tree.iter_changes(tree.basis_tree()):
1503
 
                    # Find paths in the working tree that have no kind:
1504
 
                    if change[1][1] is not None and change[6][1] is None:
1505
 
                        missing.append(change[1][1])
1506
 
                file_list = sorted(missing, reverse=True)
1507
 
                file_deletion_strategy = 'keep'
1508
 
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
1509
 
                keep_files=file_deletion_strategy=='keep',
1510
 
                force=file_deletion_strategy=='force')
1511
 
        finally:
1512
 
            tree.unlock()
 
1543
        self.add_cleanup(tree.unlock)
 
1544
        # Heuristics should probably all move into tree.remove_smart or
 
1545
        # some such?
 
1546
        if new:
 
1547
            added = tree.changes_from(tree.basis_tree(),
 
1548
                specific_files=file_list).added
 
1549
            file_list = sorted([f[0] for f in added], reverse=True)
 
1550
            if len(file_list) == 0:
 
1551
                raise errors.BzrCommandError('No matching files.')
 
1552
        elif file_list is None:
 
1553
            # missing files show up in iter_changes(basis) as
 
1554
            # versioned-with-no-kind.
 
1555
            missing = []
 
1556
            for change in tree.iter_changes(tree.basis_tree()):
 
1557
                # Find paths in the working tree that have no kind:
 
1558
                if change[1][1] is not None and change[6][1] is None:
 
1559
                    missing.append(change[1][1])
 
1560
            file_list = sorted(missing, reverse=True)
 
1561
            file_deletion_strategy = 'keep'
 
1562
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1563
            keep_files=file_deletion_strategy=='keep',
 
1564
            force=file_deletion_strategy=='force')
1513
1565
 
1514
1566
 
1515
1567
class cmd_file_id(Command):
1660
1712
                lazy_registry=('bzrlib.bzrdir', 'format_registry'),
1661
1713
                converter=lambda name: bzrdir.format_registry.make_bzrdir(name),
1662
1714
                value_switches=True,
1663
 
                title="Branch Format",
 
1715
                title="Branch format",
1664
1716
                ),
1665
1717
         Option('append-revisions-only',
1666
1718
                help='Never change revnos or the existing log.'
1735
1787
 
1736
1788
 
1737
1789
class cmd_init_repository(Command):
1738
 
    """Create a shared repository to hold branches.
 
1790
    """Create a shared repository for branches to share storage space.
1739
1791
 
1740
1792
    New branches created under the repository directory will store their
1741
 
    revisions in the repository, not in the branch directory.
 
1793
    revisions in the repository, not in the branch directory.  For branches
 
1794
    with shared history, this reduces the amount of storage needed and 
 
1795
    speeds up the creation of new branches.
1742
1796
 
1743
 
    If the --no-trees option is used then the branches in the repository
1744
 
    will not have working trees by default.
 
1797
    If the --no-trees option is given then the branches in the repository
 
1798
    will not have working trees by default.  They will still exist as 
 
1799
    directories on disk, but they will not have separate copies of the 
 
1800
    files at a certain revision.  This can be useful for repositories that
 
1801
    store branches which are interacted with through checkouts or remote
 
1802
    branches, such as on a server.
1745
1803
 
1746
1804
    :Examples:
1747
 
        Create a shared repositories holding just branches::
 
1805
        Create a shared repository holding just branches::
1748
1806
 
1749
1807
            bzr init-repo --no-trees repo
1750
1808
            bzr init repo/trunk
1816
1874
 
1817
1875
            bzr diff -r1
1818
1876
 
1819
 
        Difference between revision 2 and revision 1::
1820
 
 
1821
 
            bzr diff -r1..2
1822
 
 
1823
 
        Difference between revision 2 and revision 1 for branch xxx::
1824
 
 
1825
 
            bzr diff -r1..2 xxx
 
1877
        Difference between revision 3 and revision 1::
 
1878
 
 
1879
            bzr diff -r1..3
 
1880
 
 
1881
        Difference between revision 3 and revision 1 for branch xxx::
 
1882
 
 
1883
            bzr diff -r1..3 xxx
 
1884
 
 
1885
        To see the changes introduced in revision X::
 
1886
        
 
1887
            bzr diff -cX
 
1888
 
 
1889
        Note that in the case of a merge, the -c option shows the changes
 
1890
        compared to the left hand parent. To see the changes against
 
1891
        another parent, use::
 
1892
 
 
1893
            bzr diff -r<chosen_parent>..X
 
1894
 
 
1895
        The changes introduced by revision 2 (equivalent to -r1..2)::
 
1896
 
 
1897
            bzr diff -c2
1826
1898
 
1827
1899
        Show just the differences for file NEWS::
1828
1900
 
1874
1946
    @display_command
1875
1947
    def run(self, revision=None, file_list=None, diff_options=None,
1876
1948
            prefix=None, old=None, new=None, using=None):
1877
 
        from bzrlib.diff import _get_trees_to_diff, show_diff_trees
 
1949
        from bzrlib.diff import get_trees_and_branches_to_diff, show_diff_trees
1878
1950
 
1879
1951
        if (prefix is None) or (prefix == '0'):
1880
1952
            # diff -p0 format
1894
1966
            raise errors.BzrCommandError('bzr diff --revision takes exactly'
1895
1967
                                         ' one or two revision specifiers')
1896
1968
 
1897
 
        old_tree, new_tree, specific_files, extra_trees = \
1898
 
                _get_trees_to_diff(file_list, revision, old, new,
1899
 
                apply_view=True)
 
1969
        (old_tree, new_tree,
 
1970
         old_branch, new_branch,
 
1971
         specific_files, extra_trees) = get_trees_and_branches_to_diff(
 
1972
            file_list, revision, old, new, apply_view=True)
1900
1973
        return show_diff_trees(old_tree, new_tree, sys.stdout,
1901
1974
                               specific_files=specific_files,
1902
1975
                               external_diff_options=diff_options,
1920
1993
    def run(self, show_ids=False):
1921
1994
        tree = WorkingTree.open_containing(u'.')[0]
1922
1995
        tree.lock_read()
1923
 
        try:
1924
 
            old = tree.basis_tree()
1925
 
            old.lock_read()
1926
 
            try:
1927
 
                for path, ie in old.inventory.iter_entries():
1928
 
                    if not tree.has_id(ie.file_id):
1929
 
                        self.outf.write(path)
1930
 
                        if show_ids:
1931
 
                            self.outf.write(' ')
1932
 
                            self.outf.write(ie.file_id)
1933
 
                        self.outf.write('\n')
1934
 
            finally:
1935
 
                old.unlock()
1936
 
        finally:
1937
 
            tree.unlock()
 
1996
        self.add_cleanup(tree.unlock)
 
1997
        old = tree.basis_tree()
 
1998
        old.lock_read()
 
1999
        self.add_cleanup(old.unlock)
 
2000
        for path, ie in old.inventory.iter_entries():
 
2001
            if not tree.has_id(ie.file_id):
 
2002
                self.outf.write(path)
 
2003
                if show_ids:
 
2004
                    self.outf.write(' ')
 
2005
                    self.outf.write(ie.file_id)
 
2006
                self.outf.write('\n')
1938
2007
 
1939
2008
 
1940
2009
class cmd_modified(Command):
1976
2045
    def run(self, null=False):
1977
2046
        wt = WorkingTree.open_containing(u'.')[0]
1978
2047
        wt.lock_read()
1979
 
        try:
1980
 
            basis = wt.basis_tree()
1981
 
            basis.lock_read()
1982
 
            try:
1983
 
                basis_inv = basis.inventory
1984
 
                inv = wt.inventory
1985
 
                for file_id in inv:
1986
 
                    if file_id in basis_inv:
1987
 
                        continue
1988
 
                    if inv.is_root(file_id) and len(basis_inv) == 0:
1989
 
                        continue
1990
 
                    path = inv.id2path(file_id)
1991
 
                    if not os.access(osutils.abspath(path), os.F_OK):
1992
 
                        continue
1993
 
                    if null:
1994
 
                        self.outf.write(path + '\0')
1995
 
                    else:
1996
 
                        self.outf.write(osutils.quotefn(path) + '\n')
1997
 
            finally:
1998
 
                basis.unlock()
1999
 
        finally:
2000
 
            wt.unlock()
 
2048
        self.add_cleanup(wt.unlock)
 
2049
        basis = wt.basis_tree()
 
2050
        basis.lock_read()
 
2051
        self.add_cleanup(basis.unlock)
 
2052
        basis_inv = basis.inventory
 
2053
        inv = wt.inventory
 
2054
        for file_id in inv:
 
2055
            if file_id in basis_inv:
 
2056
                continue
 
2057
            if inv.is_root(file_id) and len(basis_inv) == 0:
 
2058
                continue
 
2059
            path = inv.id2path(file_id)
 
2060
            if not os.access(osutils.abspath(path), os.F_OK):
 
2061
                continue
 
2062
            if null:
 
2063
                self.outf.write(path + '\0')
 
2064
            else:
 
2065
                self.outf.write(osutils.quotefn(path) + '\n')
2001
2066
 
2002
2067
 
2003
2068
class cmd_root(Command):
2148
2213
    :Tips & tricks:
2149
2214
 
2150
2215
      GUI tools and IDEs are often better at exploring history than command
2151
 
      line tools. You may prefer qlog or glog from the QBzr and Bzr-Gtk packages
2152
 
      respectively for example. (TortoiseBzr uses qlog for displaying logs.) See
2153
 
      http://bazaar-vcs.org/BzrPlugins and http://bazaar-vcs.org/IDEIntegration.
2154
 
 
2155
 
      Web interfaces are often better at exploring history than command line
2156
 
      tools, particularly for branches on servers. You may prefer Loggerhead
2157
 
      or one of its alternatives. See http://bazaar-vcs.org/WebInterface.
 
2216
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
 
2217
      bzr-explorer shell, or the Loggerhead web interface.  See the Plugin
 
2218
      Guide <http://doc.bazaar.canonical.com/plugins/en/> and
 
2219
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
2158
2220
 
2159
2221
      You may find it useful to add the aliases below to ``bazaar.conf``::
2160
2222
 
2263
2325
        filter_by_dir = False
2264
2326
        if file_list:
2265
2327
            # find the file ids to log and check for directory filtering
2266
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
2267
 
                file_list)
 
2328
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2329
                revision, file_list)
 
2330
            self.add_cleanup(b.unlock)
2268
2331
            for relpath, file_id, kind in file_info_list:
2269
2332
                if file_id is None:
2270
2333
                    raise errors.BzrCommandError(
2288
2351
                location = '.'
2289
2352
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2290
2353
            b = dir.open_branch()
 
2354
            b.lock_read()
 
2355
            self.add_cleanup(b.unlock)
2291
2356
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2292
2357
 
2293
2358
        # Decide on the type of delta & diff filtering to use
2303
2368
        else:
2304
2369
            diff_type = 'full'
2305
2370
 
2306
 
        b.lock_read()
2307
 
        try:
2308
 
            # Build the log formatter
2309
 
            if log_format is None:
2310
 
                log_format = log.log_formatter_registry.get_default(b)
2311
 
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2312
 
                            show_timezone=timezone,
2313
 
                            delta_format=get_verbosity_level(),
2314
 
                            levels=levels,
2315
 
                            show_advice=levels is None)
2316
 
 
2317
 
            # Choose the algorithm for doing the logging. It's annoying
2318
 
            # having multiple code paths like this but necessary until
2319
 
            # the underlying repository format is faster at generating
2320
 
            # deltas or can provide everything we need from the indices.
2321
 
            # The default algorithm - match-using-deltas - works for
2322
 
            # multiple files and directories and is faster for small
2323
 
            # amounts of history (200 revisions say). However, it's too
2324
 
            # slow for logging a single file in a repository with deep
2325
 
            # history, i.e. > 10K revisions. In the spirit of "do no
2326
 
            # evil when adding features", we continue to use the
2327
 
            # original algorithm - per-file-graph - for the "single
2328
 
            # file that isn't a directory without showing a delta" case.
2329
 
            partial_history = revision and b.repository._format.supports_chks
2330
 
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2331
 
                or delta_type or partial_history)
2332
 
 
2333
 
            # Build the LogRequest and execute it
2334
 
            if len(file_ids) == 0:
2335
 
                file_ids = None
2336
 
            rqst = make_log_request_dict(
2337
 
                direction=direction, specific_fileids=file_ids,
2338
 
                start_revision=rev1, end_revision=rev2, limit=limit,
2339
 
                message_search=message, delta_type=delta_type,
2340
 
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2341
 
            Logger(b, rqst).show(lf)
2342
 
        finally:
2343
 
            b.unlock()
 
2371
        # Build the log formatter
 
2372
        if log_format is None:
 
2373
            log_format = log.log_formatter_registry.get_default(b)
 
2374
        # Make a non-encoding output to include the diffs - bug 328007
 
2375
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2376
        lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2377
                        to_exact_file=unencoded_output,
 
2378
                        show_timezone=timezone,
 
2379
                        delta_format=get_verbosity_level(),
 
2380
                        levels=levels,
 
2381
                        show_advice=levels is None)
 
2382
 
 
2383
        # Choose the algorithm for doing the logging. It's annoying
 
2384
        # having multiple code paths like this but necessary until
 
2385
        # the underlying repository format is faster at generating
 
2386
        # deltas or can provide everything we need from the indices.
 
2387
        # The default algorithm - match-using-deltas - works for
 
2388
        # multiple files and directories and is faster for small
 
2389
        # amounts of history (200 revisions say). However, it's too
 
2390
        # slow for logging a single file in a repository with deep
 
2391
        # history, i.e. > 10K revisions. In the spirit of "do no
 
2392
        # evil when adding features", we continue to use the
 
2393
        # original algorithm - per-file-graph - for the "single
 
2394
        # file that isn't a directory without showing a delta" case.
 
2395
        partial_history = revision and b.repository._format.supports_chks
 
2396
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2397
            or delta_type or partial_history)
 
2398
 
 
2399
        # Build the LogRequest and execute it
 
2400
        if len(file_ids) == 0:
 
2401
            file_ids = None
 
2402
        rqst = make_log_request_dict(
 
2403
            direction=direction, specific_fileids=file_ids,
 
2404
            start_revision=rev1, end_revision=rev2, limit=limit,
 
2405
            message_search=message, delta_type=delta_type,
 
2406
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2407
        Logger(b, rqst).show(lf)
2344
2408
 
2345
2409
 
2346
2410
def _get_revision_range(revisionspec_list, branch, command_name):
2410
2474
    @display_command
2411
2475
    def run(self, filename):
2412
2476
        tree, relpath = WorkingTree.open_containing(filename)
 
2477
        file_id = tree.path2id(relpath)
2413
2478
        b = tree.branch
2414
 
        file_id = tree.path2id(relpath)
2415
 
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
 
2479
        b.lock_read()
 
2480
        self.add_cleanup(b.unlock)
 
2481
        touching_revs = log.find_touching_revisions(b, file_id)
 
2482
        for revno, revision_id, what in touching_revs:
2416
2483
            self.outf.write("%6d %s\n" % (revno, what))
2417
2484
 
2418
2485
 
2471
2538
        if from_root:
2472
2539
            if relpath:
2473
2540
                prefix = relpath + '/'
2474
 
        elif fs_path != '.':
 
2541
        elif fs_path != '.' and not fs_path.endswith('/'):
2475
2542
            prefix = fs_path + '/'
2476
2543
 
2477
2544
        if revision is not None or tree is None:
2486
2553
                note("Ignoring files outside view. View is %s" % view_str)
2487
2554
 
2488
2555
        tree.lock_read()
2489
 
        try:
2490
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2491
 
                from_dir=relpath, recursive=recursive):
2492
 
                # Apply additional masking
2493
 
                if not all and not selection[fc]:
2494
 
                    continue
2495
 
                if kind is not None and fkind != kind:
2496
 
                    continue
2497
 
                if apply_view:
2498
 
                    try:
2499
 
                        if relpath:
2500
 
                            fullpath = osutils.pathjoin(relpath, fp)
2501
 
                        else:
2502
 
                            fullpath = fp
2503
 
                        views.check_path_in_view(tree, fullpath)
2504
 
                    except errors.FileOutsideView:
2505
 
                        continue
 
2556
        self.add_cleanup(tree.unlock)
 
2557
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2558
            from_dir=relpath, recursive=recursive):
 
2559
            # Apply additional masking
 
2560
            if not all and not selection[fc]:
 
2561
                continue
 
2562
            if kind is not None and fkind != kind:
 
2563
                continue
 
2564
            if apply_view:
 
2565
                try:
 
2566
                    if relpath:
 
2567
                        fullpath = osutils.pathjoin(relpath, fp)
 
2568
                    else:
 
2569
                        fullpath = fp
 
2570
                    views.check_path_in_view(tree, fullpath)
 
2571
                except errors.FileOutsideView:
 
2572
                    continue
2506
2573
 
2507
 
                # Output the entry
2508
 
                if prefix:
2509
 
                    fp = osutils.pathjoin(prefix, fp)
2510
 
                kindch = entry.kind_character()
2511
 
                outstring = fp + kindch
2512
 
                ui.ui_factory.clear_term()
2513
 
                if verbose:
2514
 
                    outstring = '%-8s %s' % (fc, outstring)
2515
 
                    if show_ids and fid is not None:
2516
 
                        outstring = "%-50s %s" % (outstring, fid)
 
2574
            # Output the entry
 
2575
            if prefix:
 
2576
                fp = osutils.pathjoin(prefix, fp)
 
2577
            kindch = entry.kind_character()
 
2578
            outstring = fp + kindch
 
2579
            ui.ui_factory.clear_term()
 
2580
            if verbose:
 
2581
                outstring = '%-8s %s' % (fc, outstring)
 
2582
                if show_ids and fid is not None:
 
2583
                    outstring = "%-50s %s" % (outstring, fid)
 
2584
                self.outf.write(outstring + '\n')
 
2585
            elif null:
 
2586
                self.outf.write(fp + '\0')
 
2587
                if show_ids:
 
2588
                    if fid is not None:
 
2589
                        self.outf.write(fid)
 
2590
                    self.outf.write('\0')
 
2591
                self.outf.flush()
 
2592
            else:
 
2593
                if show_ids:
 
2594
                    if fid is not None:
 
2595
                        my_id = fid
 
2596
                    else:
 
2597
                        my_id = ''
 
2598
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2599
                else:
2517
2600
                    self.outf.write(outstring + '\n')
2518
 
                elif null:
2519
 
                    self.outf.write(fp + '\0')
2520
 
                    if show_ids:
2521
 
                        if fid is not None:
2522
 
                            self.outf.write(fid)
2523
 
                        self.outf.write('\0')
2524
 
                    self.outf.flush()
2525
 
                else:
2526
 
                    if show_ids:
2527
 
                        if fid is not None:
2528
 
                            my_id = fid
2529
 
                        else:
2530
 
                            my_id = ''
2531
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2532
 
                    else:
2533
 
                        self.outf.write(outstring + '\n')
2534
 
        finally:
2535
 
            tree.unlock()
2536
2601
 
2537
2602
 
2538
2603
class cmd_unknowns(Command):
2553
2618
 
2554
2619
    See ``bzr help patterns`` for details on the syntax of patterns.
2555
2620
 
 
2621
    If a .bzrignore file does not exist, the ignore command
 
2622
    will create one and add the specified files or patterns to the newly
 
2623
    created file. The ignore command will also automatically add the 
 
2624
    .bzrignore file to be versioned. Creating a .bzrignore file without
 
2625
    the use of the ignore command will require an explicit add command.
 
2626
 
2556
2627
    To remove patterns from the ignore list, edit the .bzrignore file.
2557
2628
    After adding, editing or deleting that file either indirectly by
2558
2629
    using this command or directly by using an editor, be sure to commit
2559
2630
    it.
 
2631
    
 
2632
    Patterns prefixed with '!' are exceptions to ignore patterns and take
 
2633
    precedence over regular ignores.  Such exceptions are used to specify
 
2634
    files that should be versioned which would otherwise be ignored.
 
2635
    
 
2636
    Patterns prefixed with '!!' act as regular ignore patterns, but have
 
2637
    precedence over the '!' exception patterns.
2560
2638
 
2561
2639
    Note: ignore patterns containing shell wildcards must be quoted from
2562
2640
    the shell on Unix.
2566
2644
 
2567
2645
            bzr ignore ./Makefile
2568
2646
 
2569
 
        Ignore class files in all directories::
 
2647
        Ignore .class files in all directories...::
2570
2648
 
2571
2649
            bzr ignore "*.class"
2572
2650
 
 
2651
        ...but do not ignore "special.class"::
 
2652
 
 
2653
            bzr ignore "!special.class"
 
2654
 
2573
2655
        Ignore .o files under the lib directory::
2574
2656
 
2575
2657
            bzr ignore "lib/**/*.o"
2581
2663
        Ignore everything but the "debian" toplevel directory::
2582
2664
 
2583
2665
            bzr ignore "RE:(?!debian/).*"
 
2666
        
 
2667
        Ignore everything except the "local" toplevel directory,
 
2668
        but always ignore "*~" autosave files, even under local/::
 
2669
        
 
2670
            bzr ignore "*"
 
2671
            bzr ignore "!./local"
 
2672
            bzr ignore "!!*~"
2584
2673
    """
2585
2674
 
2586
2675
    _see_also = ['status', 'ignored', 'patterns']
2644
2733
    def run(self):
2645
2734
        tree = WorkingTree.open_containing(u'.')[0]
2646
2735
        tree.lock_read()
2647
 
        try:
2648
 
            for path, file_class, kind, file_id, entry in tree.list_files():
2649
 
                if file_class != 'I':
2650
 
                    continue
2651
 
                ## XXX: Slightly inefficient since this was already calculated
2652
 
                pat = tree.is_ignored(path)
2653
 
                self.outf.write('%-50s %s\n' % (path, pat))
2654
 
        finally:
2655
 
            tree.unlock()
 
2736
        self.add_cleanup(tree.unlock)
 
2737
        for path, file_class, kind, file_id, entry in tree.list_files():
 
2738
            if file_class != 'I':
 
2739
                continue
 
2740
            ## XXX: Slightly inefficient since this was already calculated
 
2741
            pat = tree.is_ignored(path)
 
2742
            self.outf.write('%-50s %s\n' % (path, pat))
2656
2743
 
2657
2744
 
2658
2745
class cmd_lookup_revision(Command):
2761
2848
        tree, branch, relpath = \
2762
2849
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2763
2850
        branch.lock_read()
2764
 
        try:
2765
 
            return self._run(tree, branch, relpath, filename, revision,
2766
 
                             name_from_revision, filters)
2767
 
        finally:
2768
 
            branch.unlock()
 
2851
        self.add_cleanup(branch.unlock)
 
2852
        return self._run(tree, branch, relpath, filename, revision,
 
2853
                         name_from_revision, filters)
2769
2854
 
2770
2855
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2771
2856
        filtered):
2772
2857
        if tree is None:
2773
2858
            tree = b.basis_tree()
2774
2859
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
2860
        rev_tree.lock_read()
 
2861
        self.add_cleanup(rev_tree.unlock)
2775
2862
 
2776
2863
        old_file_id = rev_tree.path2id(relpath)
2777
2864
 
2812
2899
            chunks = content.splitlines(True)
2813
2900
            content = filtered_output_bytes(chunks, filters,
2814
2901
                ContentFilterContext(relpath, rev_tree))
 
2902
            self.cleanup_now()
2815
2903
            self.outf.writelines(content)
2816
2904
        else:
 
2905
            self.cleanup_now()
2817
2906
            self.outf.write(content)
2818
2907
 
2819
2908
 
2926
3015
             Option('strict',
2927
3016
                    help="Refuse to commit if there are unknown "
2928
3017
                    "files in the working tree."),
 
3018
             Option('commit-time', type=str,
 
3019
                    help="Manually set a commit time using commit date "
 
3020
                    "format, e.g. '2009-10-10 08:00:00 +0100'."),
2929
3021
             ListOption('fixes', type=str,
2930
3022
                    help="Mark a bug as being fixed by this revision "
2931
3023
                         "(see \"bzr help bugs\")."),
2938
3030
                         "the master branch until a normal commit "
2939
3031
                         "is performed."
2940
3032
                    ),
2941
 
              Option('show-diff',
2942
 
                     help='When no message is supplied, show the diff along'
2943
 
                     ' with the status summary in the message editor.'),
 
3033
             Option('show-diff',
 
3034
                    help='When no message is supplied, show the diff along'
 
3035
                    ' with the status summary in the message editor.'),
2944
3036
             ]
2945
3037
    aliases = ['ci', 'checkin']
2946
3038
 
2965
3057
 
2966
3058
    def run(self, message=None, file=None, verbose=False, selected_list=None,
2967
3059
            unchanged=False, strict=False, local=False, fixes=None,
2968
 
            author=None, show_diff=False, exclude=None):
 
3060
            author=None, show_diff=False, exclude=None, commit_time=None):
2969
3061
        from bzrlib.errors import (
2970
3062
            PointlessCommit,
2971
3063
            ConflictsInTree,
2977
3069
            make_commit_message_template_encoded
2978
3070
        )
2979
3071
 
 
3072
        commit_stamp = offset = None
 
3073
        if commit_time is not None:
 
3074
            try:
 
3075
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
 
3076
            except ValueError, e:
 
3077
                raise errors.BzrCommandError(
 
3078
                    "Could not parse --commit-time: " + str(e))
 
3079
 
2980
3080
        # TODO: Need a blackbox test for invoking the external editor; may be
2981
3081
        # slightly problematic to run this cross-platform.
2982
3082
 
3002
3102
        if local and not tree.branch.get_bound_location():
3003
3103
            raise errors.LocalRequiresBoundBranch()
3004
3104
 
 
3105
        if message is not None:
 
3106
            try:
 
3107
                file_exists = osutils.lexists(message)
 
3108
            except UnicodeError:
 
3109
                # The commit message contains unicode characters that can't be
 
3110
                # represented in the filesystem encoding, so that can't be a
 
3111
                # file.
 
3112
                file_exists = False
 
3113
            if file_exists:
 
3114
                warning_msg = (
 
3115
                    'The commit message is a file name: "%(f)s".\n'
 
3116
                    '(use --file "%(f)s" to take commit message from that file)'
 
3117
                    % { 'f': message })
 
3118
                ui.ui_factory.show_warning(warning_msg)
 
3119
 
3005
3120
        def get_message(commit_obj):
3006
3121
            """Callback to get commit message"""
3007
3122
            my_message = message
 
3123
            if my_message is not None and '\r' in my_message:
 
3124
                my_message = my_message.replace('\r\n', '\n')
 
3125
                my_message = my_message.replace('\r', '\n')
3008
3126
            if my_message is None and not file:
3009
3127
                t = make_commit_message_template_encoded(tree,
3010
3128
                        selected_list, diff=show_diff,
3025
3143
                raise errors.BzrCommandError("empty commit message specified")
3026
3144
            return my_message
3027
3145
 
 
3146
        # The API permits a commit with a filter of [] to mean 'select nothing'
 
3147
        # but the command line should not do that.
 
3148
        if not selected_list:
 
3149
            selected_list = None
3028
3150
        try:
3029
3151
            tree.commit(message_callback=get_message,
3030
3152
                        specific_files=selected_list,
3031
3153
                        allow_pointless=unchanged, strict=strict, local=local,
3032
3154
                        reporter=None, verbose=verbose, revprops=properties,
3033
 
                        authors=author,
 
3155
                        authors=author, timestamp=commit_stamp,
 
3156
                        timezone=offset,
3034
3157
                        exclude=safe_relpath_files(tree, exclude))
3035
3158
        except PointlessCommit:
3036
3159
            # FIXME: This should really happen before the file is read in;
3323
3446
    Tests that need working space on disk use a common temporary directory,
3324
3447
    typically inside $TMPDIR or /tmp.
3325
3448
 
 
3449
    If you set BZR_TEST_PDB=1 when running selftest, failing tests will drop
 
3450
    into a pdb postmortem session.
 
3451
 
3326
3452
    :Examples:
3327
3453
        Run only tests relating to 'ignore'::
3328
3454
 
3337
3463
    def get_transport_type(typestring):
3338
3464
        """Parse and return a transport specifier."""
3339
3465
        if typestring == "sftp":
3340
 
            from bzrlib.transport.sftp import SFTPAbsoluteServer
3341
 
            return SFTPAbsoluteServer
 
3466
            from bzrlib.tests import stub_sftp
 
3467
            return stub_sftp.SFTPAbsoluteServer
3342
3468
        if typestring == "memory":
3343
 
            from bzrlib.transport.memory import MemoryServer
3344
 
            return MemoryServer
 
3469
            from bzrlib.tests import test_server
 
3470
            return memory.MemoryServer
3345
3471
        if typestring == "fakenfs":
3346
 
            from bzrlib.transport.fakenfs import FakeNFSServer
3347
 
            return FakeNFSServer
 
3472
            from bzrlib.tests import test_server
 
3473
            return test_server.FakeNFSServer
3348
3474
        msg = "No known transport type %s. Supported types are: sftp\n" %\
3349
3475
            (typestring)
3350
3476
        raise errors.BzrCommandError(msg)
3365
3491
                     Option('lsprof-timed',
3366
3492
                            help='Generate lsprof output for benchmarked'
3367
3493
                                 ' sections of code.'),
 
3494
                     Option('lsprof-tests',
 
3495
                            help='Generate lsprof output for each test.'),
3368
3496
                     Option('cache-dir', type=str,
3369
3497
                            help='Cache intermediate benchmark output in this '
3370
3498
                                 'directory.'),
3411
3539
            first=False, list_only=False,
3412
3540
            randomize=None, exclude=None, strict=False,
3413
3541
            load_list=None, debugflag=None, starting_with=None, subunit=False,
3414
 
            parallel=None):
 
3542
            parallel=None, lsprof_tests=False):
3415
3543
        from bzrlib.tests import selftest
3416
3544
        import bzrlib.benchmarks as benchmarks
3417
3545
        from bzrlib.benchmarks import tree_creator
3441
3569
            verbose = not is_quiet()
3442
3570
            # TODO: should possibly lock the history file...
3443
3571
            benchfile = open(".perf_history", "at", buffering=1)
 
3572
            self.add_cleanup(benchfile.close)
3444
3573
        else:
3445
3574
            test_suite_factory = None
3446
3575
            benchfile = None
3447
 
        try:
3448
 
            selftest_kwargs = {"verbose": verbose,
3449
 
                              "pattern": pattern,
3450
 
                              "stop_on_failure": one,
3451
 
                              "transport": transport,
3452
 
                              "test_suite_factory": test_suite_factory,
3453
 
                              "lsprof_timed": lsprof_timed,
3454
 
                              "bench_history": benchfile,
3455
 
                              "matching_tests_first": first,
3456
 
                              "list_only": list_only,
3457
 
                              "random_seed": randomize,
3458
 
                              "exclude_pattern": exclude,
3459
 
                              "strict": strict,
3460
 
                              "load_list": load_list,
3461
 
                              "debug_flags": debugflag,
3462
 
                              "starting_with": starting_with
3463
 
                              }
3464
 
            selftest_kwargs.update(self.additional_selftest_args)
3465
 
            result = selftest(**selftest_kwargs)
3466
 
        finally:
3467
 
            if benchfile is not None:
3468
 
                benchfile.close()
 
3576
        selftest_kwargs = {"verbose": verbose,
 
3577
                          "pattern": pattern,
 
3578
                          "stop_on_failure": one,
 
3579
                          "transport": transport,
 
3580
                          "test_suite_factory": test_suite_factory,
 
3581
                          "lsprof_timed": lsprof_timed,
 
3582
                          "lsprof_tests": lsprof_tests,
 
3583
                          "bench_history": benchfile,
 
3584
                          "matching_tests_first": first,
 
3585
                          "list_only": list_only,
 
3586
                          "random_seed": randomize,
 
3587
                          "exclude_pattern": exclude,
 
3588
                          "strict": strict,
 
3589
                          "load_list": load_list,
 
3590
                          "debug_flags": debugflag,
 
3591
                          "starting_with": starting_with
 
3592
                          }
 
3593
        selftest_kwargs.update(self.additional_selftest_args)
 
3594
        result = selftest(**selftest_kwargs)
3469
3595
        return int(not result)
3470
3596
 
3471
3597
 
3510
3636
        branch1 = Branch.open_containing(branch)[0]
3511
3637
        branch2 = Branch.open_containing(other)[0]
3512
3638
        branch1.lock_read()
3513
 
        try:
3514
 
            branch2.lock_read()
3515
 
            try:
3516
 
                last1 = ensure_null(branch1.last_revision())
3517
 
                last2 = ensure_null(branch2.last_revision())
3518
 
 
3519
 
                graph = branch1.repository.get_graph(branch2.repository)
3520
 
                base_rev_id = graph.find_unique_lca(last1, last2)
3521
 
 
3522
 
                print 'merge base is revision %s' % base_rev_id
3523
 
            finally:
3524
 
                branch2.unlock()
3525
 
        finally:
3526
 
            branch1.unlock()
 
3639
        self.add_cleanup(branch1.unlock)
 
3640
        branch2.lock_read()
 
3641
        self.add_cleanup(branch2.unlock)
 
3642
        last1 = ensure_null(branch1.last_revision())
 
3643
        last2 = ensure_null(branch2.last_revision())
 
3644
 
 
3645
        graph = branch1.repository.get_graph(branch2.repository)
 
3646
        base_rev_id = graph.find_unique_lca(last1, last2)
 
3647
 
 
3648
        print 'merge base is revision %s' % base_rev_id
3527
3649
 
3528
3650
 
3529
3651
class cmd_merge(Command):
3562
3684
    committed to record the result of the merge.
3563
3685
 
3564
3686
    merge refuses to run if there are any uncommitted changes, unless
3565
 
    --force is given.
 
3687
    --force is given. The --force option can also be used to create a
 
3688
    merge revision which has more than two parents.
 
3689
 
 
3690
    If one would like to merge changes from the working tree of the other
 
3691
    branch without merging any committed revisions, the --uncommitted option
 
3692
    can be given.
3566
3693
 
3567
3694
    To select only some changes to merge, use "merge -i", which will prompt
3568
3695
    you to apply each diff hunk and file change, similar to "shelve".
3580
3707
 
3581
3708
            bzr merge -r 81..82 ../bzr.dev
3582
3709
 
3583
 
        To apply a merge directive contained in /tmp/merge:
 
3710
        To apply a merge directive contained in /tmp/merge::
3584
3711
 
3585
3712
            bzr merge /tmp/merge
 
3713
 
 
3714
        To create a merge revision with three parents from two branches
 
3715
        feature1a and feature1b:
 
3716
 
 
3717
            bzr merge ../feature1a
 
3718
            bzr merge ../feature1b --force
 
3719
            bzr commit -m 'revision with three parents'
3586
3720
    """
3587
3721
 
3588
3722
    encoding_type = 'exact'
3633
3767
        verified = 'inapplicable'
3634
3768
        tree = WorkingTree.open_containing(directory)[0]
3635
3769
 
3636
 
        # die as quickly as possible if there are uncommitted changes
3637
3770
        try:
3638
3771
            basis_tree = tree.revision_tree(tree.last_revision())
3639
3772
        except errors.NoSuchRevision:
3640
3773
            basis_tree = tree.basis_tree()
 
3774
 
 
3775
        # die as quickly as possible if there are uncommitted changes
3641
3776
        if not force:
3642
 
            if tree.has_changes(basis_tree):
 
3777
            if tree.has_changes():
3643
3778
                raise errors.UncommittedChanges(tree)
3644
3779
 
3645
3780
        view_info = _get_view_info_for_change_reporter(tree)
3646
3781
        change_reporter = delta._ChangeReporter(
3647
3782
            unversioned_filter=tree.is_ignored, view_info=view_info)
3648
 
        cleanups = []
3649
 
        try:
3650
 
            pb = ui.ui_factory.nested_progress_bar()
3651
 
            cleanups.append(pb.finished)
3652
 
            tree.lock_write()
3653
 
            cleanups.append(tree.unlock)
3654
 
            if location is not None:
3655
 
                try:
3656
 
                    mergeable = bundle.read_mergeable_from_url(location,
3657
 
                        possible_transports=possible_transports)
3658
 
                except errors.NotABundle:
3659
 
                    mergeable = None
3660
 
                else:
3661
 
                    if uncommitted:
3662
 
                        raise errors.BzrCommandError('Cannot use --uncommitted'
3663
 
                            ' with bundles or merge directives.')
3664
 
 
3665
 
                    if revision is not None:
3666
 
                        raise errors.BzrCommandError(
3667
 
                            'Cannot use -r with merge directives or bundles')
3668
 
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
3669
 
                       mergeable, pb)
3670
 
 
3671
 
            if merger is None and uncommitted:
3672
 
                if revision is not None and len(revision) > 0:
3673
 
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3674
 
                        ' --revision at the same time.')
3675
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3676
 
                                                          cleanups)
3677
 
                allow_pending = False
3678
 
 
3679
 
            if merger is None:
3680
 
                merger, allow_pending = self._get_merger_from_branch(tree,
3681
 
                    location, revision, remember, possible_transports, pb)
3682
 
 
3683
 
            merger.merge_type = merge_type
3684
 
            merger.reprocess = reprocess
3685
 
            merger.show_base = show_base
3686
 
            self.sanity_check_merger(merger)
3687
 
            if (merger.base_rev_id == merger.other_rev_id and
3688
 
                merger.other_rev_id is not None):
3689
 
                note('Nothing to do.')
 
3783
        pb = ui.ui_factory.nested_progress_bar()
 
3784
        self.add_cleanup(pb.finished)
 
3785
        tree.lock_write()
 
3786
        self.add_cleanup(tree.unlock)
 
3787
        if location is not None:
 
3788
            try:
 
3789
                mergeable = bundle.read_mergeable_from_url(location,
 
3790
                    possible_transports=possible_transports)
 
3791
            except errors.NotABundle:
 
3792
                mergeable = None
 
3793
            else:
 
3794
                if uncommitted:
 
3795
                    raise errors.BzrCommandError('Cannot use --uncommitted'
 
3796
                        ' with bundles or merge directives.')
 
3797
 
 
3798
                if revision is not None:
 
3799
                    raise errors.BzrCommandError(
 
3800
                        'Cannot use -r with merge directives or bundles')
 
3801
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3802
                   mergeable, None)
 
3803
 
 
3804
        if merger is None and uncommitted:
 
3805
            if revision is not None and len(revision) > 0:
 
3806
                raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3807
                    ' --revision at the same time.')
 
3808
            merger = self.get_merger_from_uncommitted(tree, location, None)
 
3809
            allow_pending = False
 
3810
 
 
3811
        if merger is None:
 
3812
            merger, allow_pending = self._get_merger_from_branch(tree,
 
3813
                location, revision, remember, possible_transports, None)
 
3814
 
 
3815
        merger.merge_type = merge_type
 
3816
        merger.reprocess = reprocess
 
3817
        merger.show_base = show_base
 
3818
        self.sanity_check_merger(merger)
 
3819
        if (merger.base_rev_id == merger.other_rev_id and
 
3820
            merger.other_rev_id is not None):
 
3821
            note('Nothing to do.')
 
3822
            return 0
 
3823
        if pull:
 
3824
            if merger.interesting_files is not None:
 
3825
                raise errors.BzrCommandError('Cannot pull individual files')
 
3826
            if (merger.base_rev_id == tree.last_revision()):
 
3827
                result = tree.pull(merger.other_branch, False,
 
3828
                                   merger.other_rev_id)
 
3829
                result.report(self.outf)
3690
3830
                return 0
3691
 
            if pull:
3692
 
                if merger.interesting_files is not None:
3693
 
                    raise errors.BzrCommandError('Cannot pull individual files')
3694
 
                if (merger.base_rev_id == tree.last_revision()):
3695
 
                    result = tree.pull(merger.other_branch, False,
3696
 
                                       merger.other_rev_id)
3697
 
                    result.report(self.outf)
3698
 
                    return 0
3699
 
            merger.check_basis(False)
3700
 
            if preview:
3701
 
                return self._do_preview(merger, cleanups)
3702
 
            elif interactive:
3703
 
                return self._do_interactive(merger, cleanups)
3704
 
            else:
3705
 
                return self._do_merge(merger, change_reporter, allow_pending,
3706
 
                                      verified)
3707
 
        finally:
3708
 
            for cleanup in reversed(cleanups):
3709
 
                cleanup()
 
3831
        if merger.this_basis is None:
 
3832
            raise errors.BzrCommandError(
 
3833
                "This branch has no commits."
 
3834
                " (perhaps you would prefer 'bzr pull')")
 
3835
        if preview:
 
3836
            return self._do_preview(merger)
 
3837
        elif interactive:
 
3838
            return self._do_interactive(merger)
 
3839
        else:
 
3840
            return self._do_merge(merger, change_reporter, allow_pending,
 
3841
                                  verified)
3710
3842
 
3711
 
    def _get_preview(self, merger, cleanups):
 
3843
    def _get_preview(self, merger):
3712
3844
        tree_merger = merger.make_merger()
3713
3845
        tt = tree_merger.make_preview_transform()
3714
 
        cleanups.append(tt.finalize)
 
3846
        self.add_cleanup(tt.finalize)
3715
3847
        result_tree = tt.get_preview_tree()
3716
3848
        return result_tree
3717
3849
 
3718
 
    def _do_preview(self, merger, cleanups):
 
3850
    def _do_preview(self, merger):
3719
3851
        from bzrlib.diff import show_diff_trees
3720
 
        result_tree = self._get_preview(merger, cleanups)
 
3852
        result_tree = self._get_preview(merger)
3721
3853
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3722
3854
                        old_label='', new_label='')
3723
3855
 
3733
3865
        else:
3734
3866
            return 0
3735
3867
 
3736
 
    def _do_interactive(self, merger, cleanups):
 
3868
    def _do_interactive(self, merger):
3737
3869
        """Perform an interactive merge.
3738
3870
 
3739
3871
        This works by generating a preview tree of the merge, then using
3741
3873
        and the preview tree.
3742
3874
        """
3743
3875
        from bzrlib import shelf_ui
3744
 
        result_tree = self._get_preview(merger, cleanups)
 
3876
        result_tree = self._get_preview(merger)
3745
3877
        writer = bzrlib.option.diff_writer_registry.get()
3746
3878
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3747
3879
                                   reporter=shelf_ui.ApplyReporter(),
3748
3880
                                   diff_writer=writer(sys.stdout))
3749
 
        shelver.run()
 
3881
        try:
 
3882
            shelver.run()
 
3883
        finally:
 
3884
            shelver.finalize()
3750
3885
 
3751
3886
    def sanity_check_merger(self, merger):
3752
3887
        if (merger.show_base and
3812
3947
            allow_pending = True
3813
3948
        return merger, allow_pending
3814
3949
 
3815
 
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3950
    def get_merger_from_uncommitted(self, tree, location, pb):
3816
3951
        """Get a merger for uncommitted changes.
3817
3952
 
3818
3953
        :param tree: The tree the merger should apply to.
3819
3954
        :param location: The location containing uncommitted changes.
3820
3955
        :param pb: The progress bar to use for showing progress.
3821
 
        :param cleanups: A list of operations to perform to clean up the
3822
 
            temporary directories, unfinalized objects, etc.
3823
3956
        """
3824
3957
        location = self._select_branch_location(tree, location)[0]
3825
3958
        other_tree, other_path = WorkingTree.open_containing(location)
3912
4045
            merge_type = _mod_merge.Merge3Merger
3913
4046
        tree, file_list = tree_files(file_list)
3914
4047
        tree.lock_write()
3915
 
        try:
3916
 
            parents = tree.get_parent_ids()
3917
 
            if len(parents) != 2:
3918
 
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
3919
 
                                             " merges.  Not cherrypicking or"
3920
 
                                             " multi-merges.")
3921
 
            repository = tree.branch.repository
3922
 
            interesting_ids = None
3923
 
            new_conflicts = []
3924
 
            conflicts = tree.conflicts()
3925
 
            if file_list is not None:
3926
 
                interesting_ids = set()
3927
 
                for filename in file_list:
3928
 
                    file_id = tree.path2id(filename)
3929
 
                    if file_id is None:
3930
 
                        raise errors.NotVersionedError(filename)
3931
 
                    interesting_ids.add(file_id)
3932
 
                    if tree.kind(file_id) != "directory":
3933
 
                        continue
 
4048
        self.add_cleanup(tree.unlock)
 
4049
        parents = tree.get_parent_ids()
 
4050
        if len(parents) != 2:
 
4051
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
4052
                                         " merges.  Not cherrypicking or"
 
4053
                                         " multi-merges.")
 
4054
        repository = tree.branch.repository
 
4055
        interesting_ids = None
 
4056
        new_conflicts = []
 
4057
        conflicts = tree.conflicts()
 
4058
        if file_list is not None:
 
4059
            interesting_ids = set()
 
4060
            for filename in file_list:
 
4061
                file_id = tree.path2id(filename)
 
4062
                if file_id is None:
 
4063
                    raise errors.NotVersionedError(filename)
 
4064
                interesting_ids.add(file_id)
 
4065
                if tree.kind(file_id) != "directory":
 
4066
                    continue
3934
4067
 
3935
 
                    for name, ie in tree.inventory.iter_entries(file_id):
3936
 
                        interesting_ids.add(ie.file_id)
3937
 
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
3938
 
            else:
3939
 
                # Remerge only supports resolving contents conflicts
3940
 
                allowed_conflicts = ('text conflict', 'contents conflict')
3941
 
                restore_files = [c.path for c in conflicts
3942
 
                                 if c.typestring in allowed_conflicts]
3943
 
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
3944
 
            tree.set_conflicts(ConflictList(new_conflicts))
3945
 
            if file_list is not None:
3946
 
                restore_files = file_list
3947
 
            for filename in restore_files:
3948
 
                try:
3949
 
                    restore(tree.abspath(filename))
3950
 
                except errors.NotConflicted:
3951
 
                    pass
3952
 
            # Disable pending merges, because the file texts we are remerging
3953
 
            # have not had those merges performed.  If we use the wrong parents
3954
 
            # list, we imply that the working tree text has seen and rejected
3955
 
            # all the changes from the other tree, when in fact those changes
3956
 
            # have not yet been seen.
3957
 
            pb = ui.ui_factory.nested_progress_bar()
3958
 
            tree.set_parent_ids(parents[:1])
 
4068
                for name, ie in tree.inventory.iter_entries(file_id):
 
4069
                    interesting_ids.add(ie.file_id)
 
4070
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4071
        else:
 
4072
            # Remerge only supports resolving contents conflicts
 
4073
            allowed_conflicts = ('text conflict', 'contents conflict')
 
4074
            restore_files = [c.path for c in conflicts
 
4075
                             if c.typestring in allowed_conflicts]
 
4076
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4077
        tree.set_conflicts(ConflictList(new_conflicts))
 
4078
        if file_list is not None:
 
4079
            restore_files = file_list
 
4080
        for filename in restore_files:
3959
4081
            try:
3960
 
                merger = _mod_merge.Merger.from_revision_ids(pb,
3961
 
                                                             tree, parents[1])
3962
 
                merger.interesting_ids = interesting_ids
3963
 
                merger.merge_type = merge_type
3964
 
                merger.show_base = show_base
3965
 
                merger.reprocess = reprocess
3966
 
                conflicts = merger.do_merge()
3967
 
            finally:
3968
 
                tree.set_parent_ids(parents)
3969
 
                pb.finished()
 
4082
                restore(tree.abspath(filename))
 
4083
            except errors.NotConflicted:
 
4084
                pass
 
4085
        # Disable pending merges, because the file texts we are remerging
 
4086
        # have not had those merges performed.  If we use the wrong parents
 
4087
        # list, we imply that the working tree text has seen and rejected
 
4088
        # all the changes from the other tree, when in fact those changes
 
4089
        # have not yet been seen.
 
4090
        tree.set_parent_ids(parents[:1])
 
4091
        try:
 
4092
            merger = _mod_merge.Merger.from_revision_ids(None, tree, parents[1])
 
4093
            merger.interesting_ids = interesting_ids
 
4094
            merger.merge_type = merge_type
 
4095
            merger.show_base = show_base
 
4096
            merger.reprocess = reprocess
 
4097
            conflicts = merger.do_merge()
3970
4098
        finally:
3971
 
            tree.unlock()
 
4099
            tree.set_parent_ids(parents)
3972
4100
        if conflicts > 0:
3973
4101
            return 1
3974
4102
        else:
3996
4124
    name.  If you name a directory, all the contents of that directory will be
3997
4125
    reverted.
3998
4126
 
3999
 
    Any files that have been newly added since that revision will be deleted,
4000
 
    with a backup kept if appropriate.  Directories containing unknown files
4001
 
    will not be deleted.
 
4127
    If you have newly added files since the target revision, they will be
 
4128
    removed.  If the files to be removed have been changed, backups will be
 
4129
    created as above.  Directories containing unknown files will not be
 
4130
    deleted.
4002
4131
 
4003
 
    The working tree contains a list of pending merged revisions, which will
4004
 
    be included as parents in the next commit.  Normally, revert clears that
4005
 
    list as well as reverting the files.  If any files are specified, revert
4006
 
    leaves the pending merge list alone and reverts only the files.  Use "bzr
4007
 
    revert ." in the tree root to revert all files but keep the merge record,
4008
 
    and "bzr revert --forget-merges" to clear the pending merge list without
 
4132
    The working tree contains a list of revisions that have been merged but
 
4133
    not yet committed. These revisions will be included as additional parents
 
4134
    of the next commit.  Normally, using revert clears that list as well as
 
4135
    reverting the files.  If any files are specified, revert leaves the list
 
4136
    of uncommitted merges alone and reverts only the files.  Use ``bzr revert
 
4137
    .`` in the tree root to revert all files but keep the recorded merges,
 
4138
    and ``bzr revert --forget-merges`` to clear the pending merge list without
4009
4139
    reverting any files.
 
4140
 
 
4141
    Using "bzr revert --forget-merges", it is possible to apply all of the
 
4142
    changes from a branch in a single revision.  To do this, perform the merge
 
4143
    as desired.  Then doing revert with the "--forget-merges" option will keep
 
4144
    the content of the tree as it was, but it will clear the list of pending
 
4145
    merges.  The next commit will then contain all of the changes that are
 
4146
    present in the other branch, but without any other parent revisions.
 
4147
    Because this technique forgets where these changes originated, it may
 
4148
    cause additional conflicts on later merges involving the same source and
 
4149
    target branches.
4010
4150
    """
4011
4151
 
4012
4152
    _see_also = ['cat', 'export']
4022
4162
            forget_merges=None):
4023
4163
        tree, file_list = tree_files(file_list)
4024
4164
        tree.lock_write()
4025
 
        try:
4026
 
            if forget_merges:
4027
 
                tree.set_parent_ids(tree.get_parent_ids()[:1])
4028
 
            else:
4029
 
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4030
 
        finally:
4031
 
            tree.unlock()
 
4165
        self.add_cleanup(tree.unlock)
 
4166
        if forget_merges:
 
4167
            tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4168
        else:
 
4169
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4032
4170
 
4033
4171
    @staticmethod
4034
4172
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4035
4173
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4036
 
        pb = ui.ui_factory.nested_progress_bar()
4037
 
        try:
4038
 
            tree.revert(file_list, rev_tree, not no_backup, pb,
4039
 
                report_changes=True)
4040
 
        finally:
4041
 
            pb.finished()
 
4174
        tree.revert(file_list, rev_tree, not no_backup, None,
 
4175
            report_changes=True)
4042
4176
 
4043
4177
 
4044
4178
class cmd_assert_fail(Command):
4093
4227
    To filter on a range of revisions, you can use the command -r begin..end
4094
4228
    -r revision requests a specific revision, -r ..end or -r begin.. are
4095
4229
    also valid.
 
4230
            
 
4231
    :Exit values:
 
4232
        1 - some missing revisions
 
4233
        0 - no missing revisions
4096
4234
 
4097
4235
    :Examples:
4098
4236
 
4181
4319
        if remote_branch.base == local_branch.base:
4182
4320
            remote_branch = local_branch
4183
4321
 
 
4322
        local_branch.lock_read()
 
4323
        self.add_cleanup(local_branch.unlock)
4184
4324
        local_revid_range = _revision_range_to_revid_range(
4185
4325
            _get_revision_range(my_revision, local_branch,
4186
4326
                self.name()))
4187
4327
 
 
4328
        remote_branch.lock_read()
 
4329
        self.add_cleanup(remote_branch.unlock)
4188
4330
        remote_revid_range = _revision_range_to_revid_range(
4189
4331
            _get_revision_range(revision,
4190
4332
                remote_branch, self.name()))
4191
4333
 
4192
 
        local_branch.lock_read()
4193
 
        try:
4194
 
            remote_branch.lock_read()
4195
 
            try:
4196
 
                local_extra, remote_extra = find_unmerged(
4197
 
                    local_branch, remote_branch, restrict,
4198
 
                    backward=not reverse,
4199
 
                    include_merges=include_merges,
4200
 
                    local_revid_range=local_revid_range,
4201
 
                    remote_revid_range=remote_revid_range)
4202
 
 
4203
 
                if log_format is None:
4204
 
                    registry = log.log_formatter_registry
4205
 
                    log_format = registry.get_default(local_branch)
4206
 
                lf = log_format(to_file=self.outf,
4207
 
                                show_ids=show_ids,
4208
 
                                show_timezone='original')
4209
 
 
4210
 
                status_code = 0
4211
 
                if local_extra and not theirs_only:
4212
 
                    message("You have %d extra revision(s):\n" %
4213
 
                        len(local_extra))
4214
 
                    for revision in iter_log_revisions(local_extra,
4215
 
                                        local_branch.repository,
4216
 
                                        verbose):
4217
 
                        lf.log_revision(revision)
4218
 
                    printed_local = True
4219
 
                    status_code = 1
4220
 
                else:
4221
 
                    printed_local = False
4222
 
 
4223
 
                if remote_extra and not mine_only:
4224
 
                    if printed_local is True:
4225
 
                        message("\n\n\n")
4226
 
                    message("You are missing %d revision(s):\n" %
4227
 
                        len(remote_extra))
4228
 
                    for revision in iter_log_revisions(remote_extra,
4229
 
                                        remote_branch.repository,
4230
 
                                        verbose):
4231
 
                        lf.log_revision(revision)
4232
 
                    status_code = 1
4233
 
 
4234
 
                if mine_only and not local_extra:
4235
 
                    # We checked local, and found nothing extra
4236
 
                    message('This branch is up to date.\n')
4237
 
                elif theirs_only and not remote_extra:
4238
 
                    # We checked remote, and found nothing extra
4239
 
                    message('Other branch is up to date.\n')
4240
 
                elif not (mine_only or theirs_only or local_extra or
4241
 
                          remote_extra):
4242
 
                    # We checked both branches, and neither one had extra
4243
 
                    # revisions
4244
 
                    message("Branches are up to date.\n")
4245
 
            finally:
4246
 
                remote_branch.unlock()
4247
 
        finally:
4248
 
            local_branch.unlock()
 
4334
        local_extra, remote_extra = find_unmerged(
 
4335
            local_branch, remote_branch, restrict,
 
4336
            backward=not reverse,
 
4337
            include_merges=include_merges,
 
4338
            local_revid_range=local_revid_range,
 
4339
            remote_revid_range=remote_revid_range)
 
4340
 
 
4341
        if log_format is None:
 
4342
            registry = log.log_formatter_registry
 
4343
            log_format = registry.get_default(local_branch)
 
4344
        lf = log_format(to_file=self.outf,
 
4345
                        show_ids=show_ids,
 
4346
                        show_timezone='original')
 
4347
 
 
4348
        status_code = 0
 
4349
        if local_extra and not theirs_only:
 
4350
            message("You have %d extra revision(s):\n" %
 
4351
                len(local_extra))
 
4352
            for revision in iter_log_revisions(local_extra,
 
4353
                                local_branch.repository,
 
4354
                                verbose):
 
4355
                lf.log_revision(revision)
 
4356
            printed_local = True
 
4357
            status_code = 1
 
4358
        else:
 
4359
            printed_local = False
 
4360
 
 
4361
        if remote_extra and not mine_only:
 
4362
            if printed_local is True:
 
4363
                message("\n\n\n")
 
4364
            message("You are missing %d revision(s):\n" %
 
4365
                len(remote_extra))
 
4366
            for revision in iter_log_revisions(remote_extra,
 
4367
                                remote_branch.repository,
 
4368
                                verbose):
 
4369
                lf.log_revision(revision)
 
4370
            status_code = 1
 
4371
 
 
4372
        if mine_only and not local_extra:
 
4373
            # We checked local, and found nothing extra
 
4374
            message('This branch is up to date.\n')
 
4375
        elif theirs_only and not remote_extra:
 
4376
            # We checked remote, and found nothing extra
 
4377
            message('Other branch is up to date.\n')
 
4378
        elif not (mine_only or theirs_only or local_extra or
 
4379
                  remote_extra):
 
4380
            # We checked both branches, and neither one had extra
 
4381
            # revisions
 
4382
            message("Branches are up to date.\n")
 
4383
        self.cleanup_now()
4249
4384
        if not status_code and parent is None and other_branch is not None:
4250
4385
            local_branch.lock_write()
4251
 
            try:
4252
 
                # handle race conditions - a parent might be set while we run.
4253
 
                if local_branch.get_parent() is None:
4254
 
                    local_branch.set_parent(remote_branch.base)
4255
 
            finally:
4256
 
                local_branch.unlock()
 
4386
            self.add_cleanup(local_branch.unlock)
 
4387
            # handle race conditions - a parent might be set while we run.
 
4388
            if local_branch.get_parent() is None:
 
4389
                local_branch.set_parent(remote_branch.base)
4257
4390
        return status_code
4258
4391
 
4259
4392
 
4287
4420
    adding new commands, providing additional network transports and
4288
4421
    customizing log output.
4289
4422
 
4290
 
    See the Bazaar web site, http://bazaar-vcs.org, for further
4291
 
    information on plugins including where to find them and how to
4292
 
    install them. Instructions are also provided there on how to
4293
 
    write new plugins using the Python programming language.
 
4423
    See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
 
4424
    for further information on plugins including where to find them and how to
 
4425
    install them. Instructions are also provided there on how to write new
 
4426
    plugins using the Python programming language.
4294
4427
    """
4295
4428
    takes_options = ['verbose']
4296
4429
 
4338
4471
        else:
4339
4472
            b = Branch.open(branch)
4340
4473
        b.lock_read()
4341
 
        try:
4342
 
            if revision is None:
4343
 
                rev_id = b.last_revision()
4344
 
            else:
4345
 
                rev_id = revision[0].as_revision_id(b)
4346
 
            t = testament_class.from_revision(b.repository, rev_id)
4347
 
            if long:
4348
 
                sys.stdout.writelines(t.as_text_lines())
4349
 
            else:
4350
 
                sys.stdout.write(t.as_short_text())
4351
 
        finally:
4352
 
            b.unlock()
 
4474
        self.add_cleanup(b.unlock)
 
4475
        if revision is None:
 
4476
            rev_id = b.last_revision()
 
4477
        else:
 
4478
            rev_id = revision[0].as_revision_id(b)
 
4479
        t = testament_class.from_revision(b.repository, rev_id)
 
4480
        if long:
 
4481
            sys.stdout.writelines(t.as_text_lines())
 
4482
        else:
 
4483
            sys.stdout.write(t.as_short_text())
4353
4484
 
4354
4485
 
4355
4486
class cmd_annotate(Command):
4381
4512
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4382
4513
        if wt is not None:
4383
4514
            wt.lock_read()
 
4515
            self.add_cleanup(wt.unlock)
4384
4516
        else:
4385
4517
            branch.lock_read()
4386
 
        try:
4387
 
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4388
 
            if wt is not None:
4389
 
                file_id = wt.path2id(relpath)
4390
 
            else:
4391
 
                file_id = tree.path2id(relpath)
4392
 
            if file_id is None:
4393
 
                raise errors.NotVersionedError(filename)
4394
 
            file_version = tree.inventory[file_id].revision
4395
 
            if wt is not None and revision is None:
4396
 
                # If there is a tree and we're not annotating historical
4397
 
                # versions, annotate the working tree's content.
4398
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4399
 
                    show_ids=show_ids)
4400
 
            else:
4401
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4402
 
                              show_ids=show_ids)
4403
 
        finally:
4404
 
            if wt is not None:
4405
 
                wt.unlock()
4406
 
            else:
4407
 
                branch.unlock()
 
4518
            self.add_cleanup(branch.unlock)
 
4519
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4520
        tree.lock_read()
 
4521
        self.add_cleanup(tree.unlock)
 
4522
        if wt is not None:
 
4523
            file_id = wt.path2id(relpath)
 
4524
        else:
 
4525
            file_id = tree.path2id(relpath)
 
4526
        if file_id is None:
 
4527
            raise errors.NotVersionedError(filename)
 
4528
        file_version = tree.inventory[file_id].revision
 
4529
        if wt is not None and revision is None:
 
4530
            # If there is a tree and we're not annotating historical
 
4531
            # versions, annotate the working tree's content.
 
4532
            annotate_file_tree(wt, file_id, self.outf, long, all,
 
4533
                show_ids=show_ids)
 
4534
        else:
 
4535
            annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4536
                          show_ids=show_ids)
4408
4537
 
4409
4538
 
4410
4539
class cmd_re_sign(Command):
4422
4551
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4423
4552
        b = WorkingTree.open_containing(u'.')[0].branch
4424
4553
        b.lock_write()
4425
 
        try:
4426
 
            return self._run(b, revision_id_list, revision)
4427
 
        finally:
4428
 
            b.unlock()
 
4554
        self.add_cleanup(b.unlock)
 
4555
        return self._run(b, revision_id_list, revision)
4429
4556
 
4430
4557
    def _run(self, b, revision_id_list, revision):
4431
4558
        import bzrlib.gpg as gpg
4482
4609
    before they will be applied to the local branch.
4483
4610
 
4484
4611
    Bound branches use the nickname of its master branch unless it is set
4485
 
    locally, in which case binding will update the the local nickname to be
 
4612
    locally, in which case binding will update the local nickname to be
4486
4613
    that of the master.
4487
4614
    """
4488
4615
 
4500
4627
                    'This format does not remember old locations.')
4501
4628
            else:
4502
4629
                if location is None:
4503
 
                    raise errors.BzrCommandError('No location supplied and no '
4504
 
                        'previous location known')
 
4630
                    if b.get_bound_location() is not None:
 
4631
                        raise errors.BzrCommandError('Branch is already bound')
 
4632
                    else:
 
4633
                        raise errors.BzrCommandError('No location supplied '
 
4634
                            'and no previous location known')
4505
4635
        b_other = Branch.open(location)
4506
4636
        try:
4507
4637
            b.bind(b_other)
4577
4707
 
4578
4708
        if tree is not None:
4579
4709
            tree.lock_write()
 
4710
            self.add_cleanup(tree.unlock)
4580
4711
        else:
4581
4712
            b.lock_write()
4582
 
        try:
4583
 
            return self._run(b, tree, dry_run, verbose, revision, force,
4584
 
                             local=local)
4585
 
        finally:
4586
 
            if tree is not None:
4587
 
                tree.unlock()
4588
 
            else:
4589
 
                b.unlock()
 
4713
            self.add_cleanup(b.unlock)
 
4714
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4590
4715
 
4591
4716
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4592
4717
        from bzrlib.log import log_formatter, show_log
4609
4734
                rev_id = b.get_rev_id(revno)
4610
4735
 
4611
4736
        if rev_id is None or _mod_revision.is_null(rev_id):
4612
 
            self.outf.write('No revisions to uncommit.\n')
 
4737
            ui.ui_factory.note('No revisions to uncommit.')
4613
4738
            return 1
4614
4739
 
 
4740
        log_collector = ui.ui_factory.make_output_stream()
4615
4741
        lf = log_formatter('short',
4616
 
                           to_file=self.outf,
 
4742
                           to_file=log_collector,
4617
4743
                           show_timezone='original')
4618
4744
 
4619
4745
        show_log(b,
4624
4750
                 end_revision=last_revno)
4625
4751
 
4626
4752
        if dry_run:
4627
 
            print 'Dry-run, pretending to remove the above revisions.'
4628
 
            if not force:
4629
 
                val = raw_input('Press <enter> to continue')
 
4753
            ui.ui_factory.note('Dry-run, pretending to remove the above revisions.')
4630
4754
        else:
4631
 
            print 'The above revision(s) will be removed.'
4632
 
            if not force:
4633
 
                val = raw_input('Are you sure [y/N]? ')
4634
 
                if val.lower() not in ('y', 'yes'):
4635
 
                    print 'Canceled'
4636
 
                    return 0
 
4755
            ui.ui_factory.note('The above revision(s) will be removed.')
 
4756
 
 
4757
        if not force:
 
4758
            if not ui.ui_factory.get_boolean('Are you sure [y/N]? '):
 
4759
                ui.ui_factory.note('Canceled')
 
4760
                return 0
4637
4761
 
4638
4762
        mutter('Uncommitting from {%s} to {%s}',
4639
4763
               last_rev_id, rev_id)
4640
4764
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
4641
4765
                 revno=revno, local=local)
4642
 
        note('You can restore the old tip by running:\n'
4643
 
             '  bzr pull . -r revid:%s', last_rev_id)
 
4766
        ui.ui_factory.note('You can restore the old tip by running:\n'
 
4767
             '  bzr pull . -r revid:%s' % last_rev_id)
4644
4768
 
4645
4769
 
4646
4770
class cmd_break_lock(Command):
4649
4773
    CAUTION: Locks should only be broken when you are sure that the process
4650
4774
    holding the lock has been stopped.
4651
4775
 
4652
 
    You can get information on what locks are open via the 'bzr info' command.
 
4776
    You can get information on what locks are open via the 'bzr info
 
4777
    [location]' command.
4653
4778
 
4654
4779
    :Examples:
4655
4780
        bzr break-lock
 
4781
        bzr break-lock bzr+ssh://example.com/bzr/foo
4656
4782
    """
4657
4783
    takes_args = ['location?']
4658
4784
 
4688
4814
    takes_options = [
4689
4815
        Option('inet',
4690
4816
               help='Serve on stdin/out for use from inetd or sshd.'),
4691
 
        RegistryOption('protocol', 
4692
 
               help="Protocol to serve.", 
 
4817
        RegistryOption('protocol',
 
4818
               help="Protocol to serve.",
4693
4819
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
4694
4820
               value_switches=True),
4695
4821
        Option('port',
4704
4830
        Option('allow-writes',
4705
4831
               help='By default the server is a readonly server.  Supplying '
4706
4832
                    '--allow-writes enables write access to the contents of '
4707
 
                    'the served directory and below.'
 
4833
                    'the served directory and below.  Note that ``bzr serve`` '
 
4834
                    'does not perform authentication, so unless some form of '
 
4835
                    'external authentication is arranged supplying this '
 
4836
                    'option leads to global uncontrolled write access to your '
 
4837
                    'file system.'
4708
4838
                ),
4709
4839
        ]
4710
4840
 
4926
5056
      directly from the merge directive, without retrieving data from a
4927
5057
      branch.
4928
5058
 
4929
 
    If --no-bundle is specified, then public_branch is needed (and must be
4930
 
    up-to-date), so that the receiver can perform the merge using the
4931
 
    public_branch.  The public_branch is always included if known, so that
4932
 
    people can check it later.
4933
 
 
4934
 
    The submit branch defaults to the parent, but can be overridden.  Both
4935
 
    submit branch and public branch will be remembered if supplied.
4936
 
 
4937
 
    If a public_branch is known for the submit_branch, that public submit
4938
 
    branch is used in the merge instructions.  This means that a local mirror
4939
 
    can be used as your actual submit branch, once you have set public_branch
4940
 
    for that mirror.
 
5059
    `bzr send` creates a compact data set that, when applied using bzr
 
5060
    merge, has the same effect as merging from the source branch.  
 
5061
    
 
5062
    By default the merge directive is self-contained and can be applied to any
 
5063
    branch containing submit_branch in its ancestory without needing access to
 
5064
    the source branch.
 
5065
    
 
5066
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5067
    revisions, but only a structured request to merge from the
 
5068
    public_location.  In that case the public_branch is needed and it must be
 
5069
    up-to-date and accessible to the recipient.  The public_branch is always
 
5070
    included if known, so that people can check it later.
 
5071
 
 
5072
    The submit branch defaults to the parent of the source branch, but can be
 
5073
    overridden.  Both submit branch and public branch will be remembered in
 
5074
    branch.conf the first time they are used for a particular branch.  The
 
5075
    source branch defaults to that containing the working directory, but can
 
5076
    be changed using --from.
 
5077
 
 
5078
    In order to calculate those changes, bzr must analyse the submit branch.
 
5079
    Therefore it is most efficient for the submit branch to be a local mirror.
 
5080
    If a public location is known for the submit_branch, that location is used
 
5081
    in the merge directive.
 
5082
 
 
5083
    The default behaviour is to send the merge directive by mail, unless -o is
 
5084
    given, in which case it is sent to a file.
4941
5085
 
4942
5086
    Mail is sent using your preferred mail program.  This should be transparent
4943
5087
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
4945
5089
 
4946
5090
    To use a specific mail program, set the mail_client configuration option.
4947
5091
    (For Thunderbird 1.5, this works around some bugs.)  Supported values for
4948
 
    specific clients are "claws", "evolution", "kmail", "mutt", and
4949
 
    "thunderbird"; generic options are "default", "editor", "emacsclient",
4950
 
    "mapi", and "xdg-email".  Plugins may also add supported clients.
 
5092
    specific clients are "claws", "evolution", "kmail", "mail.app" (MacOS X's
 
5093
    Mail.app), "mutt", and "thunderbird"; generic options are "default",
 
5094
    "editor", "emacsclient", "mapi", and "xdg-email".  Plugins may also add
 
5095
    supported clients.
4951
5096
 
4952
5097
    If mail is being sent, a to address is required.  This can be supplied
4953
5098
    either on the commandline, by setting the submit_to configuration
4962
5107
 
4963
5108
    The merge directives created by bzr send may be applied using bzr merge or
4964
5109
    bzr pull by specifying a file containing a merge directive as the location.
 
5110
 
 
5111
    bzr send makes extensive use of public locations to map local locations into
 
5112
    URLs that can be used by other people.  See `bzr help configuration` to
 
5113
    set them, and use `bzr info` to display them.
4965
5114
    """
4966
5115
 
4967
5116
    encoding_type = 'exact'
5126
5275
            ):
5127
5276
        branch, relpath = Branch.open_containing(directory)
5128
5277
        branch.lock_write()
5129
 
        try:
5130
 
            if delete:
5131
 
                branch.tags.delete_tag(tag_name)
5132
 
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
5278
        self.add_cleanup(branch.unlock)
 
5279
        if delete:
 
5280
            branch.tags.delete_tag(tag_name)
 
5281
            self.outf.write('Deleted tag %s.\n' % tag_name)
 
5282
        else:
 
5283
            if revision:
 
5284
                if len(revision) != 1:
 
5285
                    raise errors.BzrCommandError(
 
5286
                        "Tags can only be placed on a single revision, "
 
5287
                        "not on a range")
 
5288
                revision_id = revision[0].as_revision_id(branch)
5133
5289
            else:
5134
 
                if revision:
5135
 
                    if len(revision) != 1:
5136
 
                        raise errors.BzrCommandError(
5137
 
                            "Tags can only be placed on a single revision, "
5138
 
                            "not on a range")
5139
 
                    revision_id = revision[0].as_revision_id(branch)
5140
 
                else:
5141
 
                    revision_id = branch.last_revision()
5142
 
                if (not force) and branch.tags.has_tag(tag_name):
5143
 
                    raise errors.TagAlreadyExists(tag_name)
5144
 
                branch.tags.set_tag(tag_name, revision_id)
5145
 
                self.outf.write('Created tag %s.\n' % tag_name)
5146
 
        finally:
5147
 
            branch.unlock()
 
5290
                revision_id = branch.last_revision()
 
5291
            if (not force) and branch.tags.has_tag(tag_name):
 
5292
                raise errors.TagAlreadyExists(tag_name)
 
5293
            branch.tags.set_tag(tag_name, revision_id)
 
5294
            self.outf.write('Created tag %s.\n' % tag_name)
5148
5295
 
5149
5296
 
5150
5297
class cmd_tags(Command):
5183
5330
            return
5184
5331
 
5185
5332
        branch.lock_read()
5186
 
        try:
5187
 
            if revision:
5188
 
                graph = branch.repository.get_graph()
5189
 
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5190
 
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5191
 
                # only show revisions between revid1 and revid2 (inclusive)
5192
 
                tags = [(tag, revid) for tag, revid in tags if
5193
 
                    graph.is_between(revid, revid1, revid2)]
5194
 
            if sort == 'alpha':
5195
 
                tags.sort()
5196
 
            elif sort == 'time':
5197
 
                timestamps = {}
5198
 
                for tag, revid in tags:
5199
 
                    try:
5200
 
                        revobj = branch.repository.get_revision(revid)
5201
 
                    except errors.NoSuchRevision:
5202
 
                        timestamp = sys.maxint # place them at the end
5203
 
                    else:
5204
 
                        timestamp = revobj.timestamp
5205
 
                    timestamps[revid] = timestamp
5206
 
                tags.sort(key=lambda x: timestamps[x[1]])
5207
 
            if not show_ids:
5208
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5209
 
                for index, (tag, revid) in enumerate(tags):
5210
 
                    try:
5211
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5212
 
                        if isinstance(revno, tuple):
5213
 
                            revno = '.'.join(map(str, revno))
5214
 
                    except errors.NoSuchRevision:
5215
 
                        # Bad tag data/merges can lead to tagged revisions
5216
 
                        # which are not in this branch. Fail gracefully ...
5217
 
                        revno = '?'
5218
 
                    tags[index] = (tag, revno)
5219
 
        finally:
5220
 
            branch.unlock()
 
5333
        self.add_cleanup(branch.unlock)
 
5334
        if revision:
 
5335
            graph = branch.repository.get_graph()
 
5336
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5337
            revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5338
            # only show revisions between revid1 and revid2 (inclusive)
 
5339
            tags = [(tag, revid) for tag, revid in tags if
 
5340
                graph.is_between(revid, revid1, revid2)]
 
5341
        if sort == 'alpha':
 
5342
            tags.sort()
 
5343
        elif sort == 'time':
 
5344
            timestamps = {}
 
5345
            for tag, revid in tags:
 
5346
                try:
 
5347
                    revobj = branch.repository.get_revision(revid)
 
5348
                except errors.NoSuchRevision:
 
5349
                    timestamp = sys.maxint # place them at the end
 
5350
                else:
 
5351
                    timestamp = revobj.timestamp
 
5352
                timestamps[revid] = timestamp
 
5353
            tags.sort(key=lambda x: timestamps[x[1]])
 
5354
        if not show_ids:
 
5355
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5356
            for index, (tag, revid) in enumerate(tags):
 
5357
                try:
 
5358
                    revno = branch.revision_id_to_dotted_revno(revid)
 
5359
                    if isinstance(revno, tuple):
 
5360
                        revno = '.'.join(map(str, revno))
 
5361
                except errors.NoSuchRevision:
 
5362
                    # Bad tag data/merges can lead to tagged revisions
 
5363
                    # which are not in this branch. Fail gracefully ...
 
5364
                    revno = '?'
 
5365
                tags[index] = (tag, revno)
 
5366
        self.cleanup_now()
5221
5367
        for tag, revspec in tags:
5222
5368
            self.outf.write('%-20s %s\n' % (tag, revspec))
5223
5369
 
5259
5405
            ),
5260
5406
        Option('bind-to', help='Branch to bind checkout to.', type=str),
5261
5407
        Option('force',
5262
 
               help='Perform reconfiguration even if local changes'
5263
 
               ' will be lost.')
 
5408
            help='Perform reconfiguration even if local changes'
 
5409
            ' will be lost.'),
 
5410
        Option('stacked-on',
 
5411
            help='Reconfigure a branch to be stacked on another branch.',
 
5412
            type=unicode,
 
5413
            ),
 
5414
        Option('unstacked',
 
5415
            help='Reconfigure a branch to be unstacked.  This '
 
5416
                'may require copying substantial data into it.',
 
5417
            ),
5264
5418
        ]
5265
5419
 
5266
 
    def run(self, location=None, target_type=None, bind_to=None, force=False):
 
5420
    def run(self, location=None, target_type=None, bind_to=None, force=False,
 
5421
            stacked_on=None,
 
5422
            unstacked=None):
5267
5423
        directory = bzrdir.BzrDir.open(location)
 
5424
        if stacked_on and unstacked:
 
5425
            raise BzrCommandError("Can't use both --stacked-on and --unstacked")
 
5426
        elif stacked_on is not None:
 
5427
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
5428
        elif unstacked:
 
5429
            reconfigure.ReconfigureUnstacked().apply(directory)
 
5430
        # At the moment you can use --stacked-on and a different
 
5431
        # reconfiguration shape at the same time; there seems no good reason
 
5432
        # to ban it.
5268
5433
        if target_type is None:
5269
 
            raise errors.BzrCommandError('No target configuration specified')
 
5434
            if stacked_on or unstacked:
 
5435
                return
 
5436
            else:
 
5437
                raise errors.BzrCommandError('No target configuration '
 
5438
                    'specified')
5270
5439
        elif target_type == 'branch':
5271
5440
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
5272
5441
        elif target_type == 'tree':
5309
5478
    /path/to/newbranch.
5310
5479
 
5311
5480
    Bound branches use the nickname of its master branch unless it is set
5312
 
    locally, in which case switching will update the the local nickname to be
 
5481
    locally, in which case switching will update the local nickname to be
5313
5482
    that of the master.
5314
5483
    """
5315
5484
 
5316
 
    takes_args = ['to_location']
 
5485
    takes_args = ['to_location?']
5317
5486
    takes_options = [Option('force',
5318
5487
                        help='Switch even if local commits will be lost.'),
 
5488
                     'revision',
5319
5489
                     Option('create-branch', short_name='b',
5320
5490
                        help='Create the target branch from this one before'
5321
5491
                             ' switching to it.'),
5322
 
                     ]
 
5492
                    ]
5323
5493
 
5324
 
    def run(self, to_location, force=False, create_branch=False):
 
5494
    def run(self, to_location=None, force=False, create_branch=False,
 
5495
            revision=None):
5325
5496
        from bzrlib import switch
5326
5497
        tree_location = '.'
 
5498
        revision = _get_one_revision('switch', revision)
5327
5499
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
 
5500
        if to_location is None:
 
5501
            if revision is None:
 
5502
                raise errors.BzrCommandError('You must supply either a'
 
5503
                                             ' revision or a location')
 
5504
            to_location = '.'
5328
5505
        try:
5329
5506
            branch = control_dir.open_branch()
5330
5507
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5335
5512
            if branch is None:
5336
5513
                raise errors.BzrCommandError('cannot create branch without'
5337
5514
                                             ' source branch')
 
5515
            to_location = directory_service.directories.dereference(
 
5516
                              to_location)
5338
5517
            if '/' not in to_location and '\\' not in to_location:
5339
5518
                # This path is meant to be relative to the existing branch
5340
5519
                this_url = self._get_branch_location(control_dir)
5342
5521
            to_branch = branch.bzrdir.sprout(to_location,
5343
5522
                                 possible_transports=[branch.bzrdir.root_transport],
5344
5523
                                 source_branch=branch).open_branch()
5345
 
            # try:
5346
 
            #     from_branch = control_dir.open_branch()
5347
 
            # except errors.NotBranchError:
5348
 
            #     raise BzrCommandError('Cannot create a branch from this'
5349
 
            #         ' location when we cannot open this branch')
5350
 
            # from_branch.bzrdir.sprout(
5351
 
            pass
5352
5524
        else:
5353
5525
            try:
5354
5526
                to_branch = Branch.open(to_location)
5356
5528
                this_url = self._get_branch_location(control_dir)
5357
5529
                to_branch = Branch.open(
5358
5530
                    urlutils.join(this_url, '..', to_location))
5359
 
        switch.switch(control_dir, to_branch, force)
 
5531
        if revision is not None:
 
5532
            revision = revision.as_revision_id(to_branch)
 
5533
        switch.switch(control_dir, to_branch, force, revision_id=revision)
5360
5534
        if had_explicit_nick:
5361
5535
            branch = control_dir.open_branch() #get the new branch!
5362
5536
            branch.nick = to_branch.nick
5604
5778
        if writer is None:
5605
5779
            writer = bzrlib.option.diff_writer_registry.get()
5606
5780
        try:
5607
 
            Shelver.from_args(writer(sys.stdout), revision, all, file_list,
5608
 
                              message, destroy=destroy).run()
 
5781
            shelver = Shelver.from_args(writer(sys.stdout), revision, all,
 
5782
                file_list, message, destroy=destroy)
 
5783
            try:
 
5784
                shelver.run()
 
5785
            finally:
 
5786
                shelver.finalize()
5609
5787
        except errors.UserAbort:
5610
5788
            return 0
5611
5789
 
5612
5790
    def run_for_list(self):
5613
5791
        tree = WorkingTree.open_containing('.')[0]
5614
5792
        tree.lock_read()
5615
 
        try:
5616
 
            manager = tree.get_shelf_manager()
5617
 
            shelves = manager.active_shelves()
5618
 
            if len(shelves) == 0:
5619
 
                note('No shelved changes.')
5620
 
                return 0
5621
 
            for shelf_id in reversed(shelves):
5622
 
                message = manager.get_metadata(shelf_id).get('message')
5623
 
                if message is None:
5624
 
                    message = '<no message>'
5625
 
                self.outf.write('%3d: %s\n' % (shelf_id, message))
5626
 
            return 1
5627
 
        finally:
5628
 
            tree.unlock()
 
5793
        self.add_cleanup(tree.unlock)
 
5794
        manager = tree.get_shelf_manager()
 
5795
        shelves = manager.active_shelves()
 
5796
        if len(shelves) == 0:
 
5797
            note('No shelved changes.')
 
5798
            return 0
 
5799
        for shelf_id in reversed(shelves):
 
5800
            message = manager.get_metadata(shelf_id).get('message')
 
5801
            if message is None:
 
5802
                message = '<no message>'
 
5803
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5804
        return 1
5629
5805
 
5630
5806
 
5631
5807
class cmd_unshelve(Command):
5643
5819
            enum_switch=False, value_switches=True,
5644
5820
            apply="Apply changes and remove from the shelf.",
5645
5821
            dry_run="Show changes, but do not apply or remove them.",
5646
 
            delete_only="Delete changes without applying them."
 
5822
            preview="Instead of unshelving the changes, show the diff that "
 
5823
                    "would result from unshelving.",
 
5824
            delete_only="Delete changes without applying them.",
 
5825
            keep="Apply changes but don't delete them.",
5647
5826
        )
5648
5827
    ]
5649
5828
    _see_also = ['shelve']
5650
5829
 
5651
5830
    def run(self, shelf_id=None, action='apply'):
5652
5831
        from bzrlib.shelf_ui import Unshelver
5653
 
        Unshelver.from_args(shelf_id, action).run()
 
5832
        unshelver = Unshelver.from_args(shelf_id, action)
 
5833
        try:
 
5834
            unshelver.run()
 
5835
        finally:
 
5836
            unshelver.tree.unlock()
5654
5837
 
5655
5838
 
5656
5839
class cmd_clean_tree(Command):
5746
5929
    )
5747
5930
from bzrlib.foreign import cmd_dpush
5748
5931
from bzrlib.sign_my_commits import cmd_sign_my_commits
5749
 
from bzrlib.weave_commands import cmd_versionedfile_list, \
5750
 
        cmd_weave_plan_merge, cmd_weave_merge_text