/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Jelmer Vernooij
  • Date: 2010-03-21 21:39:33 UTC
  • mfrom: (5102 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5143.
  • Revision ID: jelmer@samba.org-20100321213933-fexeh9zcoz8oaju2
merge bzr.dev.

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,
53
54
    )
54
55
from bzrlib.branch import Branch
55
56
from bzrlib.conflicts import ConflictList
 
57
from bzrlib.transport import memory
56
58
from bzrlib.revisionspec import RevisionSpec, RevisionInfo
57
59
from bzrlib.smtp_connection import SMTPConnection
58
60
from bzrlib.workingtree import WorkingTree
338
340
    # cat-revision is more for frontends so should be exact
339
341
    encoding = 'strict'
340
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
 
341
351
    @display_command
342
352
    def run(self, revision_id=None, revision=None):
343
353
        if revision_id is not None and revision is not None:
348
358
                                         ' --revision or a revision_id')
349
359
        b = WorkingTree.open_containing(u'.')[0].branch
350
360
 
351
 
        # TODO: jam 20060112 should cat-revision always output utf-8?
352
 
        if revision_id is not None:
353
 
            revision_id = osutils.safe_revision_id(revision_id, warn=False)
354
 
            try:
355
 
                self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
356
 
            except errors.NoSuchRevision:
357
 
                msg = "The repository %s contains no revision %s." % (b.repository.base,
358
 
                    revision_id)
359
 
                raise errors.BzrCommandError(msg)
360
 
        elif revision is not None:
361
 
            for rev in revision:
362
 
                if rev is None:
363
 
                    raise errors.BzrCommandError('You cannot specify a NULL'
364
 
                                                 ' revision.')
365
 
                rev_id = rev.as_revision_id(b)
366
 
                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')
367
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
        
368
387
 
369
388
class cmd_dump_btree(Command):
370
389
    """Dump the contents of a btree index file to stdout.
437
456
        for node in bt.iter_all_entries():
438
457
            # Node is made up of:
439
458
            # (index, key, value, [references])
440
 
            refs_as_tuples = static_tuple.as_tuples(node[3])
 
459
            try:
 
460
                refs = node[3]
 
461
            except IndexError:
 
462
                refs_as_tuples = None
 
463
            else:
 
464
                refs_as_tuples = static_tuple.as_tuples(refs)
441
465
            as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
442
466
            self.outf.write('%s\n' % (as_tuple,))
443
467
 
451
475
    To re-create the working tree, use "bzr checkout".
452
476
    """
453
477
    _see_also = ['checkout', 'working-trees']
454
 
    takes_args = ['location?']
 
478
    takes_args = ['location*']
455
479
    takes_options = [
456
480
        Option('force',
457
481
               help='Remove the working tree even if it has '
458
482
                    'uncommitted changes.'),
459
483
        ]
460
484
 
461
 
    def run(self, location='.', force=False):
462
 
        d = bzrdir.BzrDir.open(location)
463
 
 
464
 
        try:
465
 
            working = d.open_workingtree()
466
 
        except errors.NoWorkingTree:
467
 
            raise errors.BzrCommandError("No working tree to remove")
468
 
        except errors.NotLocalUrl:
469
 
            raise errors.BzrCommandError("You cannot remove the working tree"
470
 
                                         " of a remote path")
471
 
        if not force:
472
 
            if (working.has_changes()):
473
 
                raise errors.UncommittedChanges(working)
474
 
 
475
 
        working_path = working.bzrdir.root_transport.base
476
 
        branch_path = working.branch.bzrdir.root_transport.base
477
 
        if working_path != branch_path:
478
 
            raise errors.BzrCommandError("You cannot remove the working tree"
479
 
                                         " from a lightweight checkout")
480
 
 
481
 
        d.destroy_workingtree()
 
485
    def run(self, location_list, force=False):
 
486
        if not location_list:
 
487
            location_list=['.']
 
488
 
 
489
        for location in location_list:
 
490
            d = bzrdir.BzrDir.open(location)
 
491
            
 
492
            try:
 
493
                working = d.open_workingtree()
 
494
            except errors.NoWorkingTree:
 
495
                raise errors.BzrCommandError("No working tree to remove")
 
496
            except errors.NotLocalUrl:
 
497
                raise errors.BzrCommandError("You cannot remove the working tree"
 
498
                                             " of a remote path")
 
499
            if not force:
 
500
                if (working.has_changes()):
 
501
                    raise errors.UncommittedChanges(working)
 
502
 
 
503
            working_path = working.bzrdir.root_transport.base
 
504
            branch_path = working.branch.bzrdir.root_transport.base
 
505
            if working_path != branch_path:
 
506
                raise errors.BzrCommandError("You cannot remove the working tree"
 
507
                                             " from a lightweight checkout")
 
508
 
 
509
            d.destroy_workingtree()
482
510
 
483
511
 
484
512
class cmd_revno(Command):
501
529
                wt.lock_read()
502
530
            except (errors.NoWorkingTree, errors.NotLocalUrl):
503
531
                raise errors.NoWorkingTree(location)
 
532
            self.add_cleanup(wt.unlock)
 
533
            revid = wt.last_revision()
504
534
            try:
505
 
                revid = wt.last_revision()
506
 
                try:
507
 
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
508
 
                except errors.NoSuchRevision:
509
 
                    revno_t = ('???',)
510
 
                revno = ".".join(str(n) for n in revno_t)
511
 
            finally:
512
 
                wt.unlock()
 
535
                revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
536
            except errors.NoSuchRevision:
 
537
                revno_t = ('???',)
 
538
            revno = ".".join(str(n) for n in revno_t)
513
539
        else:
514
540
            b = Branch.open_containing(location)[0]
515
541
            b.lock_read()
516
 
            try:
517
 
                revno = b.revno()
518
 
            finally:
519
 
                b.unlock()
520
 
 
 
542
            self.add_cleanup(b.unlock)
 
543
            revno = b.revno()
 
544
        self.cleanup_now()
521
545
        self.outf.write(str(revno) + '\n')
522
546
 
523
547
 
545
569
            wt = WorkingTree.open_containing(directory)[0]
546
570
            b = wt.branch
547
571
            wt.lock_read()
 
572
            self.add_cleanup(wt.unlock)
548
573
        except (errors.NoWorkingTree, errors.NotLocalUrl):
549
574
            wt = None
550
575
            b = Branch.open_containing(directory)[0]
551
576
            b.lock_read()
552
 
        try:
553
 
            revision_ids = []
554
 
            if revision is not None:
555
 
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
556
 
            if revision_info_list is not None:
557
 
                for rev_str in revision_info_list:
558
 
                    rev_spec = RevisionSpec.from_string(rev_str)
559
 
                    revision_ids.append(rev_spec.as_revision_id(b))
560
 
            # No arguments supplied, default to the last revision
561
 
            if len(revision_ids) == 0:
562
 
                if tree:
563
 
                    if wt is None:
564
 
                        raise errors.NoWorkingTree(directory)
565
 
                    revision_ids.append(wt.last_revision())
566
 
                else:
567
 
                    revision_ids.append(b.last_revision())
568
 
 
569
 
            revinfos = []
570
 
            maxlen = 0
571
 
            for revision_id in revision_ids:
572
 
                try:
573
 
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
574
 
                    revno = '.'.join(str(i) for i in dotted_revno)
575
 
                except errors.NoSuchRevision:
576
 
                    revno = '???'
577
 
                maxlen = max(maxlen, len(revno))
578
 
                revinfos.append([revno, revision_id])
579
 
        finally:
580
 
            if wt is None:
581
 
                b.unlock()
 
577
            self.add_cleanup(b.unlock)
 
578
        revision_ids = []
 
579
        if revision is not None:
 
580
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
581
        if revision_info_list is not None:
 
582
            for rev_str in revision_info_list:
 
583
                rev_spec = RevisionSpec.from_string(rev_str)
 
584
                revision_ids.append(rev_spec.as_revision_id(b))
 
585
        # No arguments supplied, default to the last revision
 
586
        if len(revision_ids) == 0:
 
587
            if tree:
 
588
                if wt is None:
 
589
                    raise errors.NoWorkingTree(directory)
 
590
                revision_ids.append(wt.last_revision())
582
591
            else:
583
 
                wt.unlock()
584
 
 
 
592
                revision_ids.append(b.last_revision())
 
593
 
 
594
        revinfos = []
 
595
        maxlen = 0
 
596
        for revision_id in revision_ids:
 
597
            try:
 
598
                dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
599
                revno = '.'.join(str(i) for i in dotted_revno)
 
600
            except errors.NoSuchRevision:
 
601
                revno = '???'
 
602
            maxlen = max(maxlen, len(revno))
 
603
            revinfos.append([revno, revision_id])
 
604
 
 
605
        self.cleanup_now()
585
606
        for ri in revinfos:
586
607
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
587
608
 
659
680
 
660
681
        if base_tree:
661
682
            base_tree.lock_read()
662
 
        try:
663
 
            tree, file_list = tree_files_for_add(file_list)
664
 
            added, ignored = tree.smart_add(file_list, not
665
 
                no_recurse, action=action, save=not dry_run)
666
 
        finally:
667
 
            if base_tree is not None:
668
 
                base_tree.unlock()
 
683
            self.add_cleanup(base_tree.unlock)
 
684
        tree, file_list = tree_files_for_add(file_list)
 
685
        added, ignored = tree.smart_add(file_list, not
 
686
            no_recurse, action=action, save=not dry_run)
 
687
        self.cleanup_now()
669
688
        if len(ignored) > 0:
670
689
            if verbose:
671
690
                for glob in sorted(ignored.keys()):
685
704
 
686
705
    def run(self, dir_list):
687
706
        for d in dir_list:
688
 
            os.mkdir(d)
689
707
            wt, dd = WorkingTree.open_containing(d)
690
 
            wt.add([dd])
691
 
            self.outf.write('added %s\n' % d)
 
708
            base = os.path.dirname(dd)
 
709
            id = wt.path2id(base)
 
710
            if id != None:
 
711
                os.mkdir(d)
 
712
                wt.add([dd])
 
713
                self.outf.write('added %s\n' % d)
 
714
            else:
 
715
                raise errors.NotVersionedError(path=base)
692
716
 
693
717
 
694
718
class cmd_relpath(Command):
735
759
        revision = _get_one_revision('inventory', revision)
736
760
        work_tree, file_list = tree_files(file_list)
737
761
        work_tree.lock_read()
738
 
        try:
739
 
            if revision is not None:
740
 
                tree = revision.as_tree(work_tree.branch)
741
 
 
742
 
                extra_trees = [work_tree]
743
 
                tree.lock_read()
744
 
            else:
745
 
                tree = work_tree
746
 
                extra_trees = []
747
 
 
748
 
            if file_list is not None:
749
 
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
750
 
                                          require_versioned=True)
751
 
                # find_ids_across_trees may include some paths that don't
752
 
                # exist in 'tree'.
753
 
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
754
 
                                 for file_id in file_ids if file_id in tree)
755
 
            else:
756
 
                entries = tree.inventory.entries()
757
 
        finally:
758
 
            tree.unlock()
759
 
            if tree is not work_tree:
760
 
                work_tree.unlock()
761
 
 
 
762
        self.add_cleanup(work_tree.unlock)
 
763
        if revision is not None:
 
764
            tree = revision.as_tree(work_tree.branch)
 
765
 
 
766
            extra_trees = [work_tree]
 
767
            tree.lock_read()
 
768
            self.add_cleanup(tree.unlock)
 
769
        else:
 
770
            tree = work_tree
 
771
            extra_trees = []
 
772
 
 
773
        if file_list is not None:
 
774
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
775
                                      require_versioned=True)
 
776
            # find_ids_across_trees may include some paths that don't
 
777
            # exist in 'tree'.
 
778
            entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
779
                             for file_id in file_ids if file_id in tree)
 
780
        else:
 
781
            entries = tree.inventory.entries()
 
782
 
 
783
        self.cleanup_now()
762
784
        for path, entry in entries:
763
785
            if kind and kind != entry.kind:
764
786
                continue
810
832
            raise errors.BzrCommandError("missing file argument")
811
833
        tree, rel_names = tree_files(names_list, canonicalize=False)
812
834
        tree.lock_tree_write()
813
 
        try:
814
 
            self._run(tree, names_list, rel_names, after)
815
 
        finally:
816
 
            tree.unlock()
 
835
        self.add_cleanup(tree.unlock)
 
836
        self._run(tree, names_list, rel_names, after)
817
837
 
818
838
    def run_auto(self, names_list, after, dry_run):
819
839
        if names_list is not None and len(names_list) > 1:
824
844
                                         ' --auto.')
825
845
        work_tree, file_list = tree_files(names_list, default_branch='.')
826
846
        work_tree.lock_tree_write()
827
 
        try:
828
 
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
829
 
        finally:
830
 
            work_tree.unlock()
 
847
        self.add_cleanup(work_tree.unlock)
 
848
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
831
849
 
832
850
    def _run(self, tree, names_list, rel_names, after):
833
851
        into_existing = osutils.isdir(names_list[-1])
1011
1029
 
1012
1030
        if branch_from is not branch_to:
1013
1031
            branch_from.lock_read()
1014
 
        try:
1015
 
            if revision is not None:
1016
 
                revision_id = revision.as_revision_id(branch_from)
1017
 
 
1018
 
            branch_to.lock_write()
1019
 
            try:
1020
 
                if tree_to is not None:
1021
 
                    view_info = _get_view_info_for_change_reporter(tree_to)
1022
 
                    change_reporter = delta._ChangeReporter(
1023
 
                        unversioned_filter=tree_to.is_ignored,
1024
 
                        view_info=view_info)
1025
 
                    result = tree_to.pull(
1026
 
                        branch_from, overwrite, revision_id, change_reporter,
1027
 
                        possible_transports=possible_transports, local=local)
1028
 
                else:
1029
 
                    result = branch_to.pull(
1030
 
                        branch_from, overwrite, revision_id, local=local)
1031
 
 
1032
 
                result.report(self.outf)
1033
 
                if verbose and result.old_revid != result.new_revid:
1034
 
                    log.show_branch_change(
1035
 
                        branch_to, self.outf, result.old_revno,
1036
 
                        result.old_revid)
1037
 
            finally:
1038
 
                branch_to.unlock()
1039
 
        finally:
1040
 
            if branch_from is not branch_to:
1041
 
                branch_from.unlock()
 
1032
            self.add_cleanup(branch_from.unlock)
 
1033
        if revision is not None:
 
1034
            revision_id = revision.as_revision_id(branch_from)
 
1035
 
 
1036
        branch_to.lock_write()
 
1037
        self.add_cleanup(branch_to.unlock)
 
1038
        if tree_to is not None:
 
1039
            view_info = _get_view_info_for_change_reporter(tree_to)
 
1040
            change_reporter = delta._ChangeReporter(
 
1041
                unversioned_filter=tree_to.is_ignored,
 
1042
                view_info=view_info)
 
1043
            result = tree_to.pull(
 
1044
                branch_from, overwrite, revision_id, change_reporter,
 
1045
                possible_transports=possible_transports, local=local)
 
1046
        else:
 
1047
            result = branch_to.pull(
 
1048
                branch_from, overwrite, revision_id, local=local)
 
1049
 
 
1050
        result.report(self.outf)
 
1051
        if verbose and result.old_revid != result.new_revid:
 
1052
            log.show_branch_change(
 
1053
                branch_to, self.outf, result.old_revno,
 
1054
                result.old_revid)
1042
1055
 
1043
1056
 
1044
1057
class cmd_push(Command):
1199
1212
                    ' directory exists, but does not already'
1200
1213
                    ' have a control directory.  This flag will'
1201
1214
                    ' allow branch to proceed.'),
 
1215
        Option('bind',
 
1216
            help="Bind new branch to from location."),
1202
1217
        ]
1203
1218
    aliases = ['get', 'clone']
1204
1219
 
1205
1220
    def run(self, from_location, to_location=None, revision=None,
1206
1221
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1207
 
            use_existing_dir=False, switch=False):
 
1222
            use_existing_dir=False, switch=False, bind=False):
1208
1223
        from bzrlib import switch as _mod_switch
1209
1224
        from bzrlib.tag import _merge_tags_if_possible
1210
1225
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1211
1226
            from_location)
1212
1227
        revision = _get_one_revision('branch', revision)
1213
1228
        br_from.lock_read()
 
1229
        self.add_cleanup(br_from.unlock)
 
1230
        if revision is not None:
 
1231
            revision_id = revision.as_revision_id(br_from)
 
1232
        else:
 
1233
            # FIXME - wt.last_revision, fallback to branch, fall back to
 
1234
            # None or perhaps NULL_REVISION to mean copy nothing
 
1235
            # RBC 20060209
 
1236
            revision_id = br_from.last_revision()
 
1237
        if to_location is None:
 
1238
            to_location = urlutils.derive_to_location(from_location)
 
1239
        to_transport = transport.get_transport(to_location)
1214
1240
        try:
1215
 
            if revision is not None:
1216
 
                revision_id = revision.as_revision_id(br_from)
 
1241
            to_transport.mkdir('.')
 
1242
        except errors.FileExists:
 
1243
            if not use_existing_dir:
 
1244
                raise errors.BzrCommandError('Target directory "%s" '
 
1245
                    'already exists.' % to_location)
1217
1246
            else:
1218
 
                # FIXME - wt.last_revision, fallback to branch, fall back to
1219
 
                # None or perhaps NULL_REVISION to mean copy nothing
1220
 
                # RBC 20060209
1221
 
                revision_id = br_from.last_revision()
1222
 
            if to_location is None:
1223
 
                to_location = urlutils.derive_to_location(from_location)
1224
 
            to_transport = transport.get_transport(to_location)
1225
 
            try:
1226
 
                to_transport.mkdir('.')
1227
 
            except errors.FileExists:
1228
 
                if not use_existing_dir:
1229
 
                    raise errors.BzrCommandError('Target directory "%s" '
1230
 
                        'already exists.' % to_location)
 
1247
                try:
 
1248
                    bzrdir.BzrDir.open_from_transport(to_transport)
 
1249
                except errors.NotBranchError:
 
1250
                    pass
1231
1251
                else:
1232
 
                    try:
1233
 
                        bzrdir.BzrDir.open_from_transport(to_transport)
1234
 
                    except errors.NotBranchError:
1235
 
                        pass
1236
 
                    else:
1237
 
                        raise errors.AlreadyBranchError(to_location)
1238
 
            except errors.NoSuchFile:
1239
 
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1240
 
                                             % to_location)
1241
 
            try:
1242
 
                # preserve whatever source format we have.
1243
 
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1244
 
                                            possible_transports=[to_transport],
1245
 
                                            accelerator_tree=accelerator_tree,
1246
 
                                            hardlink=hardlink, stacked=stacked,
1247
 
                                            force_new_repo=standalone,
1248
 
                                            create_tree_if_local=not no_tree,
1249
 
                                            source_branch=br_from)
1250
 
                branch = dir.open_branch()
1251
 
            except errors.NoSuchRevision:
1252
 
                to_transport.delete_tree('.')
1253
 
                msg = "The branch %s has no revision %s." % (from_location,
1254
 
                    revision)
1255
 
                raise errors.BzrCommandError(msg)
1256
 
            _merge_tags_if_possible(br_from, branch)
1257
 
            # If the source branch is stacked, the new branch may
1258
 
            # be stacked whether we asked for that explicitly or not.
1259
 
            # We therefore need a try/except here and not just 'if stacked:'
1260
 
            try:
1261
 
                note('Created new stacked branch referring to %s.' %
1262
 
                    branch.get_stacked_on_url())
1263
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1264
 
                errors.UnstackableRepositoryFormat), e:
1265
 
                note('Branched %d revision(s).' % branch.revno())
1266
 
            if switch:
1267
 
                # Switch to the new branch
1268
 
                wt, _ = WorkingTree.open_containing('.')
1269
 
                _mod_switch.switch(wt.bzrdir, branch)
1270
 
                note('Switched to branch: %s',
1271
 
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
1272
 
        finally:
1273
 
            br_from.unlock()
 
1252
                    raise errors.AlreadyBranchError(to_location)
 
1253
        except errors.NoSuchFile:
 
1254
            raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
1255
                                         % to_location)
 
1256
        try:
 
1257
            # preserve whatever source format we have.
 
1258
            dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
 
1259
                                        possible_transports=[to_transport],
 
1260
                                        accelerator_tree=accelerator_tree,
 
1261
                                        hardlink=hardlink, stacked=stacked,
 
1262
                                        force_new_repo=standalone,
 
1263
                                        create_tree_if_local=not no_tree,
 
1264
                                        source_branch=br_from)
 
1265
            branch = dir.open_branch()
 
1266
        except errors.NoSuchRevision:
 
1267
            to_transport.delete_tree('.')
 
1268
            msg = "The branch %s has no revision %s." % (from_location,
 
1269
                revision)
 
1270
            raise errors.BzrCommandError(msg)
 
1271
        _merge_tags_if_possible(br_from, branch)
 
1272
        # If the source branch is stacked, the new branch may
 
1273
        # be stacked whether we asked for that explicitly or not.
 
1274
        # We therefore need a try/except here and not just 'if stacked:'
 
1275
        try:
 
1276
            note('Created new stacked branch referring to %s.' %
 
1277
                branch.get_stacked_on_url())
 
1278
        except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1279
            errors.UnstackableRepositoryFormat), e:
 
1280
            note('Branched %d revision(s).' % branch.revno())
 
1281
        if bind:
 
1282
            # Bind to the parent
 
1283
            parent_branch = Branch.open(from_location)
 
1284
            branch.bind(parent_branch)
 
1285
            note('New branch bound to %s' % from_location)
 
1286
        if switch:
 
1287
            # Switch to the new branch
 
1288
            wt, _ = WorkingTree.open_containing('.')
 
1289
            _mod_switch.switch(wt.bzrdir, branch)
 
1290
            note('Switched to branch: %s',
 
1291
                urlutils.unescape_for_display(branch.base, 'utf-8'))
1274
1292
 
1275
1293
 
1276
1294
class cmd_checkout(Command):
1355
1373
    def run(self, dir=u'.'):
1356
1374
        tree = WorkingTree.open_containing(dir)[0]
1357
1375
        tree.lock_read()
1358
 
        try:
1359
 
            new_inv = tree.inventory
1360
 
            old_tree = tree.basis_tree()
1361
 
            old_tree.lock_read()
1362
 
            try:
1363
 
                old_inv = old_tree.inventory
1364
 
                renames = []
1365
 
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
1366
 
                for f, paths, c, v, p, n, k, e in iterator:
1367
 
                    if paths[0] == paths[1]:
1368
 
                        continue
1369
 
                    if None in (paths):
1370
 
                        continue
1371
 
                    renames.append(paths)
1372
 
                renames.sort()
1373
 
                for old_name, new_name in renames:
1374
 
                    self.outf.write("%s => %s\n" % (old_name, new_name))
1375
 
            finally:
1376
 
                old_tree.unlock()
1377
 
        finally:
1378
 
            tree.unlock()
 
1376
        self.add_cleanup(tree.unlock)
 
1377
        new_inv = tree.inventory
 
1378
        old_tree = tree.basis_tree()
 
1379
        old_tree.lock_read()
 
1380
        self.add_cleanup(old_tree.unlock)
 
1381
        old_inv = old_tree.inventory
 
1382
        renames = []
 
1383
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1384
        for f, paths, c, v, p, n, k, e in iterator:
 
1385
            if paths[0] == paths[1]:
 
1386
                continue
 
1387
            if None in (paths):
 
1388
                continue
 
1389
            renames.append(paths)
 
1390
        renames.sort()
 
1391
        for old_name, new_name in renames:
 
1392
            self.outf.write("%s => %s\n" % (old_name, new_name))
1379
1393
 
1380
1394
 
1381
1395
class cmd_update(Command):
1387
1401
 
1388
1402
    If you want to discard your local changes, you can just do a
1389
1403
    'bzr revert' instead of 'bzr commit' after the update.
 
1404
 
 
1405
    If the tree's branch is bound to a master branch, it will also update
 
1406
    the branch from the master.
1390
1407
    """
1391
1408
 
1392
1409
    _see_also = ['pull', 'working-trees', 'status-flags']
1393
1410
    takes_args = ['dir?']
 
1411
    takes_options = ['revision']
1394
1412
    aliases = ['up']
1395
1413
 
1396
 
    def run(self, dir='.'):
 
1414
    def run(self, dir='.', revision=None):
 
1415
        if revision is not None and len(revision) != 1:
 
1416
            raise errors.BzrCommandError(
 
1417
                        "bzr update --revision takes exactly one revision")
1397
1418
        tree = WorkingTree.open_containing(dir)[0]
 
1419
        branch = tree.branch
1398
1420
        possible_transports = []
1399
 
        master = tree.branch.get_master_branch(
 
1421
        master = branch.get_master_branch(
1400
1422
            possible_transports=possible_transports)
1401
1423
        if master is not None:
1402
1424
            tree.lock_write()
1404
1426
        else:
1405
1427
            tree.lock_tree_write()
1406
1428
            branch_location = tree.branch.base
 
1429
        self.add_cleanup(tree.unlock)
1407
1430
        # get rid of the final '/' and be ready for display
1408
1431
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
1409
1432
                                                        self.outf.encoding)
 
1433
        existing_pending_merges = tree.get_parent_ids()[1:]
 
1434
        if master is None:
 
1435
            old_tip = None
 
1436
        else:
 
1437
            # may need to fetch data into a heavyweight checkout
 
1438
            # XXX: this may take some time, maybe we should display a
 
1439
            # message
 
1440
            old_tip = branch.update(possible_transports)
 
1441
        if revision is not None:
 
1442
            revision_id = revision[0].as_revision_id(branch)
 
1443
        else:
 
1444
            revision_id = branch.last_revision()
 
1445
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
 
1446
            revno = branch.revision_id_to_revno(revision_id)
 
1447
            note("Tree is up to date at revision %d of branch %s" %
 
1448
                (revno, branch_location))
 
1449
            return 0
 
1450
        view_info = _get_view_info_for_change_reporter(tree)
 
1451
        change_reporter = delta._ChangeReporter(
 
1452
            unversioned_filter=tree.is_ignored,
 
1453
            view_info=view_info)
1410
1454
        try:
1411
 
            existing_pending_merges = tree.get_parent_ids()[1:]
1412
 
            last_rev = _mod_revision.ensure_null(tree.last_revision())
1413
 
            if last_rev == _mod_revision.ensure_null(
1414
 
                tree.branch.last_revision()):
1415
 
                # may be up to date, check master too.
1416
 
                if master is None or last_rev == _mod_revision.ensure_null(
1417
 
                    master.last_revision()):
1418
 
                    revno = tree.branch.revision_id_to_revno(last_rev)
1419
 
                    note('Tree is up to date at revision %d of branch %s'
1420
 
                         % (revno, branch_location))
1421
 
                    return 0
1422
 
            view_info = _get_view_info_for_change_reporter(tree)
1423
1455
            conflicts = tree.update(
1424
 
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
1425
 
                view_info=view_info), possible_transports=possible_transports)
1426
 
            revno = tree.branch.revision_id_to_revno(
1427
 
                _mod_revision.ensure_null(tree.last_revision()))
1428
 
            note('Updated to revision %d of branch %s' %
1429
 
                 (revno, branch_location))
1430
 
            if tree.get_parent_ids()[1:] != existing_pending_merges:
1431
 
                note('Your local commits will now show as pending merges with '
1432
 
                     "'bzr status', and can be committed with 'bzr commit'.")
1433
 
            if conflicts != 0:
1434
 
                return 1
1435
 
            else:
1436
 
                return 0
1437
 
        finally:
1438
 
            tree.unlock()
 
1456
                change_reporter,
 
1457
                possible_transports=possible_transports,
 
1458
                revision=revision_id,
 
1459
                old_tip=old_tip)
 
1460
        except errors.NoSuchRevision, e:
 
1461
            raise errors.BzrCommandError(
 
1462
                                  "branch has no revision %s\n"
 
1463
                                  "bzr update --revision only works"
 
1464
                                  " for a revision in the branch history"
 
1465
                                  % (e.revision))
 
1466
        revno = tree.branch.revision_id_to_revno(
 
1467
            _mod_revision.ensure_null(tree.last_revision()))
 
1468
        note('Updated to revision %d of branch %s' %
 
1469
             (revno, branch_location))
 
1470
        if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1471
            note('Your local commits will now show as pending merges with '
 
1472
                 "'bzr status', and can be committed with 'bzr commit'.")
 
1473
        if conflicts != 0:
 
1474
            return 1
 
1475
        else:
 
1476
            return 0
1439
1477
 
1440
1478
 
1441
1479
class cmd_info(Command):
1512
1550
            file_list = [f for f in file_list]
1513
1551
 
1514
1552
        tree.lock_write()
1515
 
        try:
1516
 
            # Heuristics should probably all move into tree.remove_smart or
1517
 
            # some such?
1518
 
            if new:
1519
 
                added = tree.changes_from(tree.basis_tree(),
1520
 
                    specific_files=file_list).added
1521
 
                file_list = sorted([f[0] for f in added], reverse=True)
1522
 
                if len(file_list) == 0:
1523
 
                    raise errors.BzrCommandError('No matching files.')
1524
 
            elif file_list is None:
1525
 
                # missing files show up in iter_changes(basis) as
1526
 
                # versioned-with-no-kind.
1527
 
                missing = []
1528
 
                for change in tree.iter_changes(tree.basis_tree()):
1529
 
                    # Find paths in the working tree that have no kind:
1530
 
                    if change[1][1] is not None and change[6][1] is None:
1531
 
                        missing.append(change[1][1])
1532
 
                file_list = sorted(missing, reverse=True)
1533
 
                file_deletion_strategy = 'keep'
1534
 
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
1535
 
                keep_files=file_deletion_strategy=='keep',
1536
 
                force=file_deletion_strategy=='force')
1537
 
        finally:
1538
 
            tree.unlock()
 
1553
        self.add_cleanup(tree.unlock)
 
1554
        # Heuristics should probably all move into tree.remove_smart or
 
1555
        # some such?
 
1556
        if new:
 
1557
            added = tree.changes_from(tree.basis_tree(),
 
1558
                specific_files=file_list).added
 
1559
            file_list = sorted([f[0] for f in added], reverse=True)
 
1560
            if len(file_list) == 0:
 
1561
                raise errors.BzrCommandError('No matching files.')
 
1562
        elif file_list is None:
 
1563
            # missing files show up in iter_changes(basis) as
 
1564
            # versioned-with-no-kind.
 
1565
            missing = []
 
1566
            for change in tree.iter_changes(tree.basis_tree()):
 
1567
                # Find paths in the working tree that have no kind:
 
1568
                if change[1][1] is not None and change[6][1] is None:
 
1569
                    missing.append(change[1][1])
 
1570
            file_list = sorted(missing, reverse=True)
 
1571
            file_deletion_strategy = 'keep'
 
1572
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1573
            keep_files=file_deletion_strategy=='keep',
 
1574
            force=file_deletion_strategy=='force')
1539
1575
 
1540
1576
 
1541
1577
class cmd_file_id(Command):
1967
2003
    def run(self, show_ids=False):
1968
2004
        tree = WorkingTree.open_containing(u'.')[0]
1969
2005
        tree.lock_read()
1970
 
        try:
1971
 
            old = tree.basis_tree()
1972
 
            old.lock_read()
1973
 
            try:
1974
 
                for path, ie in old.inventory.iter_entries():
1975
 
                    if not tree.has_id(ie.file_id):
1976
 
                        self.outf.write(path)
1977
 
                        if show_ids:
1978
 
                            self.outf.write(' ')
1979
 
                            self.outf.write(ie.file_id)
1980
 
                        self.outf.write('\n')
1981
 
            finally:
1982
 
                old.unlock()
1983
 
        finally:
1984
 
            tree.unlock()
 
2006
        self.add_cleanup(tree.unlock)
 
2007
        old = tree.basis_tree()
 
2008
        old.lock_read()
 
2009
        self.add_cleanup(old.unlock)
 
2010
        for path, ie in old.inventory.iter_entries():
 
2011
            if not tree.has_id(ie.file_id):
 
2012
                self.outf.write(path)
 
2013
                if show_ids:
 
2014
                    self.outf.write(' ')
 
2015
                    self.outf.write(ie.file_id)
 
2016
                self.outf.write('\n')
1985
2017
 
1986
2018
 
1987
2019
class cmd_modified(Command):
2023
2055
    def run(self, null=False):
2024
2056
        wt = WorkingTree.open_containing(u'.')[0]
2025
2057
        wt.lock_read()
2026
 
        try:
2027
 
            basis = wt.basis_tree()
2028
 
            basis.lock_read()
2029
 
            try:
2030
 
                basis_inv = basis.inventory
2031
 
                inv = wt.inventory
2032
 
                for file_id in inv:
2033
 
                    if file_id in basis_inv:
2034
 
                        continue
2035
 
                    if inv.is_root(file_id) and len(basis_inv) == 0:
2036
 
                        continue
2037
 
                    path = inv.id2path(file_id)
2038
 
                    if not os.access(osutils.abspath(path), os.F_OK):
2039
 
                        continue
2040
 
                    if null:
2041
 
                        self.outf.write(path + '\0')
2042
 
                    else:
2043
 
                        self.outf.write(osutils.quotefn(path) + '\n')
2044
 
            finally:
2045
 
                basis.unlock()
2046
 
        finally:
2047
 
            wt.unlock()
 
2058
        self.add_cleanup(wt.unlock)
 
2059
        basis = wt.basis_tree()
 
2060
        basis.lock_read()
 
2061
        self.add_cleanup(basis.unlock)
 
2062
        basis_inv = basis.inventory
 
2063
        inv = wt.inventory
 
2064
        for file_id in inv:
 
2065
            if file_id in basis_inv:
 
2066
                continue
 
2067
            if inv.is_root(file_id) and len(basis_inv) == 0:
 
2068
                continue
 
2069
            path = inv.id2path(file_id)
 
2070
            if not os.access(osutils.abspath(path), os.F_OK):
 
2071
                continue
 
2072
            if null:
 
2073
                self.outf.write(path + '\0')
 
2074
            else:
 
2075
                self.outf.write(osutils.quotefn(path) + '\n')
2048
2076
 
2049
2077
 
2050
2078
class cmd_root(Command):
2195
2223
    :Tips & tricks:
2196
2224
 
2197
2225
      GUI tools and IDEs are often better at exploring history than command
2198
 
      line tools. You may prefer qlog or glog from the QBzr and Bzr-Gtk packages
2199
 
      respectively for example. (TortoiseBzr uses qlog for displaying logs.) See
2200
 
      http://bazaar-vcs.org/BzrPlugins and http://bazaar-vcs.org/IDEIntegration.
2201
 
 
2202
 
      Web interfaces are often better at exploring history than command line
2203
 
      tools, particularly for branches on servers. You may prefer Loggerhead
2204
 
      or one of its alternatives. See http://bazaar-vcs.org/WebInterface.
 
2226
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
 
2227
      bzr-explorer shell, or the Loggerhead web interface.  See the Plugin
 
2228
      Guide <http://doc.bazaar.canonical.com/plugins/en/> and
 
2229
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
2205
2230
 
2206
2231
      You may find it useful to add the aliases below to ``bazaar.conf``::
2207
2232
 
2308
2333
 
2309
2334
        file_ids = []
2310
2335
        filter_by_dir = False
2311
 
        b = None
2312
 
        try:
2313
 
            if file_list:
2314
 
                # find the file ids to log and check for directory filtering
2315
 
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2316
 
                    revision, file_list)
2317
 
                for relpath, file_id, kind in file_info_list:
2318
 
                    if file_id is None:
2319
 
                        raise errors.BzrCommandError(
2320
 
                            "Path unknown at end or start of revision range: %s" %
2321
 
                            relpath)
2322
 
                    # If the relpath is the top of the tree, we log everything
2323
 
                    if relpath == '':
2324
 
                        file_ids = []
2325
 
                        break
2326
 
                    else:
2327
 
                        file_ids.append(file_id)
2328
 
                    filter_by_dir = filter_by_dir or (
2329
 
                        kind in ['directory', 'tree-reference'])
2330
 
            else:
2331
 
                # log everything
2332
 
                # FIXME ? log the current subdir only RBC 20060203
2333
 
                if revision is not None \
2334
 
                        and len(revision) > 0 and revision[0].get_branch():
2335
 
                    location = revision[0].get_branch()
 
2336
        if file_list:
 
2337
            # find the file ids to log and check for directory filtering
 
2338
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2339
                revision, file_list)
 
2340
            self.add_cleanup(b.unlock)
 
2341
            for relpath, file_id, kind in file_info_list:
 
2342
                if file_id is None:
 
2343
                    raise errors.BzrCommandError(
 
2344
                        "Path unknown at end or start of revision range: %s" %
 
2345
                        relpath)
 
2346
                # If the relpath is the top of the tree, we log everything
 
2347
                if relpath == '':
 
2348
                    file_ids = []
 
2349
                    break
2336
2350
                else:
2337
 
                    location = '.'
2338
 
                dir, relpath = bzrdir.BzrDir.open_containing(location)
2339
 
                b = dir.open_branch()
2340
 
                b.lock_read()
2341
 
                rev1, rev2 = _get_revision_range(revision, b, self.name())
2342
 
 
2343
 
            # Decide on the type of delta & diff filtering to use
2344
 
            # TODO: add an --all-files option to make this configurable & consistent
2345
 
            if not verbose:
2346
 
                delta_type = None
2347
 
            else:
2348
 
                delta_type = 'full'
2349
 
            if not show_diff:
2350
 
                diff_type = None
2351
 
            elif file_ids:
2352
 
                diff_type = 'partial'
2353
 
            else:
2354
 
                diff_type = 'full'
2355
 
 
2356
 
            # Build the log formatter
2357
 
            if log_format is None:
2358
 
                log_format = log.log_formatter_registry.get_default(b)
2359
 
            # Make a non-encoding output to include the diffs - bug 328007
2360
 
            unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2361
 
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2362
 
                            to_exact_file=unencoded_output,
2363
 
                            show_timezone=timezone,
2364
 
                            delta_format=get_verbosity_level(),
2365
 
                            levels=levels,
2366
 
                            show_advice=levels is None)
2367
 
 
2368
 
            # Choose the algorithm for doing the logging. It's annoying
2369
 
            # having multiple code paths like this but necessary until
2370
 
            # the underlying repository format is faster at generating
2371
 
            # deltas or can provide everything we need from the indices.
2372
 
            # The default algorithm - match-using-deltas - works for
2373
 
            # multiple files and directories and is faster for small
2374
 
            # amounts of history (200 revisions say). However, it's too
2375
 
            # slow for logging a single file in a repository with deep
2376
 
            # history, i.e. > 10K revisions. In the spirit of "do no
2377
 
            # evil when adding features", we continue to use the
2378
 
            # original algorithm - per-file-graph - for the "single
2379
 
            # file that isn't a directory without showing a delta" case.
2380
 
            partial_history = revision and b.repository._format.supports_chks
2381
 
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2382
 
                or delta_type or partial_history)
2383
 
 
2384
 
            # Build the LogRequest and execute it
2385
 
            if len(file_ids) == 0:
2386
 
                file_ids = None
2387
 
            rqst = make_log_request_dict(
2388
 
                direction=direction, specific_fileids=file_ids,
2389
 
                start_revision=rev1, end_revision=rev2, limit=limit,
2390
 
                message_search=message, delta_type=delta_type,
2391
 
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2392
 
            Logger(b, rqst).show(lf)
2393
 
        finally:
2394
 
            if b is not None:
2395
 
                b.unlock()
 
2351
                    file_ids.append(file_id)
 
2352
                filter_by_dir = filter_by_dir or (
 
2353
                    kind in ['directory', 'tree-reference'])
 
2354
        else:
 
2355
            # log everything
 
2356
            # FIXME ? log the current subdir only RBC 20060203
 
2357
            if revision is not None \
 
2358
                    and len(revision) > 0 and revision[0].get_branch():
 
2359
                location = revision[0].get_branch()
 
2360
            else:
 
2361
                location = '.'
 
2362
            dir, relpath = bzrdir.BzrDir.open_containing(location)
 
2363
            b = dir.open_branch()
 
2364
            b.lock_read()
 
2365
            self.add_cleanup(b.unlock)
 
2366
            rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2367
 
 
2368
        # Decide on the type of delta & diff filtering to use
 
2369
        # TODO: add an --all-files option to make this configurable & consistent
 
2370
        if not verbose:
 
2371
            delta_type = None
 
2372
        else:
 
2373
            delta_type = 'full'
 
2374
        if not show_diff:
 
2375
            diff_type = None
 
2376
        elif file_ids:
 
2377
            diff_type = 'partial'
 
2378
        else:
 
2379
            diff_type = 'full'
 
2380
 
 
2381
        # Build the log formatter
 
2382
        if log_format is None:
 
2383
            log_format = log.log_formatter_registry.get_default(b)
 
2384
        # Make a non-encoding output to include the diffs - bug 328007
 
2385
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2386
        lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2387
                        to_exact_file=unencoded_output,
 
2388
                        show_timezone=timezone,
 
2389
                        delta_format=get_verbosity_level(),
 
2390
                        levels=levels,
 
2391
                        show_advice=levels is None)
 
2392
 
 
2393
        # Choose the algorithm for doing the logging. It's annoying
 
2394
        # having multiple code paths like this but necessary until
 
2395
        # the underlying repository format is faster at generating
 
2396
        # deltas or can provide everything we need from the indices.
 
2397
        # The default algorithm - match-using-deltas - works for
 
2398
        # multiple files and directories and is faster for small
 
2399
        # amounts of history (200 revisions say). However, it's too
 
2400
        # slow for logging a single file in a repository with deep
 
2401
        # history, i.e. > 10K revisions. In the spirit of "do no
 
2402
        # evil when adding features", we continue to use the
 
2403
        # original algorithm - per-file-graph - for the "single
 
2404
        # file that isn't a directory without showing a delta" case.
 
2405
        partial_history = revision and b.repository._format.supports_chks
 
2406
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2407
            or delta_type or partial_history)
 
2408
 
 
2409
        # Build the LogRequest and execute it
 
2410
        if len(file_ids) == 0:
 
2411
            file_ids = None
 
2412
        rqst = make_log_request_dict(
 
2413
            direction=direction, specific_fileids=file_ids,
 
2414
            start_revision=rev1, end_revision=rev2, limit=limit,
 
2415
            message_search=message, delta_type=delta_type,
 
2416
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2417
        Logger(b, rqst).show(lf)
2396
2418
 
2397
2419
 
2398
2420
def _get_revision_range(revisionspec_list, branch, command_name):
2416
2438
            raise errors.BzrCommandError(
2417
2439
                "bzr %s doesn't accept two revisions in different"
2418
2440
                " branches." % command_name)
2419
 
        rev1 = start_spec.in_history(branch)
 
2441
        if start_spec.spec is None:
 
2442
            # Avoid loading all the history.
 
2443
            rev1 = RevisionInfo(branch, None, None)
 
2444
        else:
 
2445
            rev1 = start_spec.in_history(branch)
2420
2446
        # Avoid loading all of history when we know a missing
2421
2447
        # end of range means the last revision ...
2422
2448
        if end_spec.spec is None:
2465
2491
        file_id = tree.path2id(relpath)
2466
2492
        b = tree.branch
2467
2493
        b.lock_read()
2468
 
        try:
2469
 
            touching_revs = log.find_touching_revisions(b, file_id)
2470
 
            for revno, revision_id, what in touching_revs:
2471
 
                self.outf.write("%6d %s\n" % (revno, what))
2472
 
        finally:
2473
 
            b.unlock()
 
2494
        self.add_cleanup(b.unlock)
 
2495
        touching_revs = log.find_touching_revisions(b, file_id)
 
2496
        for revno, revision_id, what in touching_revs:
 
2497
            self.outf.write("%6d %s\n" % (revno, what))
2474
2498
 
2475
2499
 
2476
2500
class cmd_ls(Command):
2543
2567
                note("Ignoring files outside view. View is %s" % view_str)
2544
2568
 
2545
2569
        tree.lock_read()
2546
 
        try:
2547
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2548
 
                from_dir=relpath, recursive=recursive):
2549
 
                # Apply additional masking
2550
 
                if not all and not selection[fc]:
2551
 
                    continue
2552
 
                if kind is not None and fkind != kind:
2553
 
                    continue
2554
 
                if apply_view:
2555
 
                    try:
2556
 
                        if relpath:
2557
 
                            fullpath = osutils.pathjoin(relpath, fp)
2558
 
                        else:
2559
 
                            fullpath = fp
2560
 
                        views.check_path_in_view(tree, fullpath)
2561
 
                    except errors.FileOutsideView:
2562
 
                        continue
 
2570
        self.add_cleanup(tree.unlock)
 
2571
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2572
            from_dir=relpath, recursive=recursive):
 
2573
            # Apply additional masking
 
2574
            if not all and not selection[fc]:
 
2575
                continue
 
2576
            if kind is not None and fkind != kind:
 
2577
                continue
 
2578
            if apply_view:
 
2579
                try:
 
2580
                    if relpath:
 
2581
                        fullpath = osutils.pathjoin(relpath, fp)
 
2582
                    else:
 
2583
                        fullpath = fp
 
2584
                    views.check_path_in_view(tree, fullpath)
 
2585
                except errors.FileOutsideView:
 
2586
                    continue
2563
2587
 
2564
 
                # Output the entry
2565
 
                if prefix:
2566
 
                    fp = osutils.pathjoin(prefix, fp)
2567
 
                kindch = entry.kind_character()
2568
 
                outstring = fp + kindch
2569
 
                ui.ui_factory.clear_term()
2570
 
                if verbose:
2571
 
                    outstring = '%-8s %s' % (fc, outstring)
2572
 
                    if show_ids and fid is not None:
2573
 
                        outstring = "%-50s %s" % (outstring, fid)
 
2588
            # Output the entry
 
2589
            if prefix:
 
2590
                fp = osutils.pathjoin(prefix, fp)
 
2591
            kindch = entry.kind_character()
 
2592
            outstring = fp + kindch
 
2593
            ui.ui_factory.clear_term()
 
2594
            if verbose:
 
2595
                outstring = '%-8s %s' % (fc, outstring)
 
2596
                if show_ids and fid is not None:
 
2597
                    outstring = "%-50s %s" % (outstring, fid)
 
2598
                self.outf.write(outstring + '\n')
 
2599
            elif null:
 
2600
                self.outf.write(fp + '\0')
 
2601
                if show_ids:
 
2602
                    if fid is not None:
 
2603
                        self.outf.write(fid)
 
2604
                    self.outf.write('\0')
 
2605
                self.outf.flush()
 
2606
            else:
 
2607
                if show_ids:
 
2608
                    if fid is not None:
 
2609
                        my_id = fid
 
2610
                    else:
 
2611
                        my_id = ''
 
2612
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2613
                else:
2574
2614
                    self.outf.write(outstring + '\n')
2575
 
                elif null:
2576
 
                    self.outf.write(fp + '\0')
2577
 
                    if show_ids:
2578
 
                        if fid is not None:
2579
 
                            self.outf.write(fid)
2580
 
                        self.outf.write('\0')
2581
 
                    self.outf.flush()
2582
 
                else:
2583
 
                    if show_ids:
2584
 
                        if fid is not None:
2585
 
                            my_id = fid
2586
 
                        else:
2587
 
                            my_id = ''
2588
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2589
 
                    else:
2590
 
                        self.outf.write(outstring + '\n')
2591
 
        finally:
2592
 
            tree.unlock()
2593
2615
 
2594
2616
 
2595
2617
class cmd_unknowns(Command):
2620
2642
    After adding, editing or deleting that file either indirectly by
2621
2643
    using this command or directly by using an editor, be sure to commit
2622
2644
    it.
 
2645
    
 
2646
    Patterns prefixed with '!' are exceptions to ignore patterns and take
 
2647
    precedence over regular ignores.  Such exceptions are used to specify
 
2648
    files that should be versioned which would otherwise be ignored.
 
2649
    
 
2650
    Patterns prefixed with '!!' act as regular ignore patterns, but have
 
2651
    precedence over the '!' exception patterns.
2623
2652
 
2624
2653
    Note: ignore patterns containing shell wildcards must be quoted from
2625
2654
    the shell on Unix.
2629
2658
 
2630
2659
            bzr ignore ./Makefile
2631
2660
 
2632
 
        Ignore class files in all directories::
 
2661
        Ignore .class files in all directories...::
2633
2662
 
2634
2663
            bzr ignore "*.class"
2635
2664
 
 
2665
        ...but do not ignore "special.class"::
 
2666
 
 
2667
            bzr ignore "!special.class"
 
2668
 
2636
2669
        Ignore .o files under the lib directory::
2637
2670
 
2638
2671
            bzr ignore "lib/**/*.o"
2644
2677
        Ignore everything but the "debian" toplevel directory::
2645
2678
 
2646
2679
            bzr ignore "RE:(?!debian/).*"
 
2680
        
 
2681
        Ignore everything except the "local" toplevel directory,
 
2682
        but always ignore "*~" autosave files, even under local/::
 
2683
        
 
2684
            bzr ignore "*"
 
2685
            bzr ignore "!./local"
 
2686
            bzr ignore "!!*~"
2647
2687
    """
2648
2688
 
2649
2689
    _see_also = ['status', 'ignored', 'patterns']
2658
2698
        if old_default_rules is not None:
2659
2699
            # dump the rules and exit
2660
2700
            for pattern in ignores.OLD_DEFAULTS:
2661
 
                print pattern
 
2701
                self.outf.write("%s\n" % pattern)
2662
2702
            return
2663
2703
        if not name_pattern_list:
2664
2704
            raise errors.BzrCommandError("ignore requires at least one "
2680
2720
            if id is not None:
2681
2721
                filename = entry[0]
2682
2722
                if ignored.match(filename):
2683
 
                    matches.append(filename.encode('utf-8'))
 
2723
                    matches.append(filename)
2684
2724
        tree.unlock()
2685
2725
        if len(matches) > 0:
2686
 
            print "Warning: the following files are version controlled and" \
2687
 
                  " match your ignore pattern:\n%s" \
2688
 
                  "\nThese files will continue to be version controlled" \
2689
 
                  " unless you 'bzr remove' them." % ("\n".join(matches),)
 
2726
            self.outf.write("Warning: the following files are version controlled and"
 
2727
                  " match your ignore pattern:\n%s"
 
2728
                  "\nThese files will continue to be version controlled"
 
2729
                  " unless you 'bzr remove' them.\n" % ("\n".join(matches),))
2690
2730
 
2691
2731
 
2692
2732
class cmd_ignored(Command):
2707
2747
    def run(self):
2708
2748
        tree = WorkingTree.open_containing(u'.')[0]
2709
2749
        tree.lock_read()
2710
 
        try:
2711
 
            for path, file_class, kind, file_id, entry in tree.list_files():
2712
 
                if file_class != 'I':
2713
 
                    continue
2714
 
                ## XXX: Slightly inefficient since this was already calculated
2715
 
                pat = tree.is_ignored(path)
2716
 
                self.outf.write('%-50s %s\n' % (path, pat))
2717
 
        finally:
2718
 
            tree.unlock()
 
2750
        self.add_cleanup(tree.unlock)
 
2751
        for path, file_class, kind, file_id, entry in tree.list_files():
 
2752
            if file_class != 'I':
 
2753
                continue
 
2754
            ## XXX: Slightly inefficient since this was already calculated
 
2755
            pat = tree.is_ignored(path)
 
2756
            self.outf.write('%-50s %s\n' % (path, pat))
2719
2757
 
2720
2758
 
2721
2759
class cmd_lookup_revision(Command):
2732
2770
        try:
2733
2771
            revno = int(revno)
2734
2772
        except ValueError:
2735
 
            raise errors.BzrCommandError("not a valid revision-number: %r" % revno)
2736
 
 
2737
 
        print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
2773
            raise errors.BzrCommandError("not a valid revision-number: %r"
 
2774
                                         % revno)
 
2775
        revid = WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
2776
        self.outf.write("%s\n" % revid)
2738
2777
 
2739
2778
 
2740
2779
class cmd_export(Command):
2776
2815
        Option('root',
2777
2816
               type=str,
2778
2817
               help="Name of the root directory inside the exported file."),
 
2818
        Option('per-file-timestamps',
 
2819
               help='Set modification time of files to that of the last '
 
2820
                    'revision in which it was changed.'),
2779
2821
        ]
2780
2822
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
2781
 
        root=None, filters=False):
 
2823
        root=None, filters=False, per_file_timestamps=False):
2782
2824
        from bzrlib.export import export
2783
2825
 
2784
2826
        if branch_or_subdir is None:
2791
2833
 
2792
2834
        rev_tree = _get_one_revision_tree('export', revision, branch=b, tree=tree)
2793
2835
        try:
2794
 
            export(rev_tree, dest, format, root, subdir, filtered=filters)
 
2836
            export(rev_tree, dest, format, root, subdir, filtered=filters,
 
2837
                   per_file_timestamps=per_file_timestamps)
2795
2838
        except errors.NoSuchExportFormat, e:
2796
2839
            raise errors.BzrCommandError('Unsupported export format: %s' % e.format)
2797
2840
 
2824
2867
        tree, branch, relpath = \
2825
2868
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2826
2869
        branch.lock_read()
2827
 
        try:
2828
 
            return self._run(tree, branch, relpath, filename, revision,
2829
 
                             name_from_revision, filters)
2830
 
        finally:
2831
 
            branch.unlock()
 
2870
        self.add_cleanup(branch.unlock)
 
2871
        return self._run(tree, branch, relpath, filename, revision,
 
2872
                         name_from_revision, filters)
2832
2873
 
2833
2874
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2834
2875
        filtered):
2835
2876
        if tree is None:
2836
2877
            tree = b.basis_tree()
2837
2878
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
2879
        rev_tree.lock_read()
 
2880
        self.add_cleanup(rev_tree.unlock)
2838
2881
 
2839
2882
        old_file_id = rev_tree.path2id(relpath)
2840
2883
 
2875
2918
            chunks = content.splitlines(True)
2876
2919
            content = filtered_output_bytes(chunks, filters,
2877
2920
                ContentFilterContext(relpath, rev_tree))
 
2921
            self.cleanup_now()
2878
2922
            self.outf.writelines(content)
2879
2923
        else:
 
2924
            self.cleanup_now()
2880
2925
            self.outf.write(content)
2881
2926
 
2882
2927
 
2885
2930
    hidden = True
2886
2931
    @display_command
2887
2932
    def run(self):
2888
 
        print osutils.local_time_offset()
 
2933
        self.outf.write("%s\n" % osutils.local_time_offset())
2889
2934
 
2890
2935
 
2891
2936
 
3142
3187
            raise errors.BzrCommandError("Commit refused because there are"
3143
3188
                              " unknown files in the working tree.")
3144
3189
        except errors.BoundBranchOutOfDate, e:
3145
 
            raise errors.BzrCommandError(str(e) + "\n"
3146
 
            'To commit to master branch, run update and then commit.\n'
3147
 
            'You can also pass --local to commit to continue working '
3148
 
            'disconnected.')
 
3190
            e.extra_help = ("\n"
 
3191
                'To commit to master branch, run update and then commit.\n'
 
3192
                'You can also pass --local to commit to continue working '
 
3193
                'disconnected.')
 
3194
            raise
3149
3195
 
3150
3196
 
3151
3197
class cmd_check(Command):
3313
3359
 
3314
3360
    @display_command
3315
3361
    def printme(self, branch):
3316
 
        print branch.nick
 
3362
        self.outf.write('%s\n' % branch.nick)
3317
3363
 
3318
3364
 
3319
3365
class cmd_alias(Command):
3437
3483
    def get_transport_type(typestring):
3438
3484
        """Parse and return a transport specifier."""
3439
3485
        if typestring == "sftp":
3440
 
            from bzrlib.transport.sftp import SFTPAbsoluteServer
3441
 
            return SFTPAbsoluteServer
 
3486
            from bzrlib.tests import stub_sftp
 
3487
            return stub_sftp.SFTPAbsoluteServer
3442
3488
        if typestring == "memory":
3443
 
            from bzrlib.transport.memory import MemoryServer
3444
 
            return MemoryServer
 
3489
            from bzrlib.tests import test_server
 
3490
            return memory.MemoryServer
3445
3491
        if typestring == "fakenfs":
3446
 
            from bzrlib.transport.fakenfs import FakeNFSServer
3447
 
            return FakeNFSServer
 
3492
            from bzrlib.tests import test_server
 
3493
            return test_server.FakeNFSServer
3448
3494
        msg = "No known transport type %s. Supported types are: sftp\n" %\
3449
3495
            (typestring)
3450
3496
        raise errors.BzrCommandError(msg)
3543
3589
            verbose = not is_quiet()
3544
3590
            # TODO: should possibly lock the history file...
3545
3591
            benchfile = open(".perf_history", "at", buffering=1)
 
3592
            self.add_cleanup(benchfile.close)
3546
3593
        else:
3547
3594
            test_suite_factory = None
3548
3595
            benchfile = None
3549
 
        try:
3550
 
            selftest_kwargs = {"verbose": verbose,
3551
 
                              "pattern": pattern,
3552
 
                              "stop_on_failure": one,
3553
 
                              "transport": transport,
3554
 
                              "test_suite_factory": test_suite_factory,
3555
 
                              "lsprof_timed": lsprof_timed,
3556
 
                              "lsprof_tests": lsprof_tests,
3557
 
                              "bench_history": benchfile,
3558
 
                              "matching_tests_first": first,
3559
 
                              "list_only": list_only,
3560
 
                              "random_seed": randomize,
3561
 
                              "exclude_pattern": exclude,
3562
 
                              "strict": strict,
3563
 
                              "load_list": load_list,
3564
 
                              "debug_flags": debugflag,
3565
 
                              "starting_with": starting_with
3566
 
                              }
3567
 
            selftest_kwargs.update(self.additional_selftest_args)
3568
 
            result = selftest(**selftest_kwargs)
3569
 
        finally:
3570
 
            if benchfile is not None:
3571
 
                benchfile.close()
 
3596
        selftest_kwargs = {"verbose": verbose,
 
3597
                          "pattern": pattern,
 
3598
                          "stop_on_failure": one,
 
3599
                          "transport": transport,
 
3600
                          "test_suite_factory": test_suite_factory,
 
3601
                          "lsprof_timed": lsprof_timed,
 
3602
                          "lsprof_tests": lsprof_tests,
 
3603
                          "bench_history": benchfile,
 
3604
                          "matching_tests_first": first,
 
3605
                          "list_only": list_only,
 
3606
                          "random_seed": randomize,
 
3607
                          "exclude_pattern": exclude,
 
3608
                          "strict": strict,
 
3609
                          "load_list": load_list,
 
3610
                          "debug_flags": debugflag,
 
3611
                          "starting_with": starting_with
 
3612
                          }
 
3613
        selftest_kwargs.update(self.additional_selftest_args)
 
3614
        result = selftest(**selftest_kwargs)
3572
3615
        return int(not result)
3573
3616
 
3574
3617
 
3596
3639
 
3597
3640
    @display_command
3598
3641
    def run(self):
3599
 
        print "It sure does!"
 
3642
        self.outf.write("It sure does!\n")
3600
3643
 
3601
3644
 
3602
3645
class cmd_find_merge_base(Command):
3613
3656
        branch1 = Branch.open_containing(branch)[0]
3614
3657
        branch2 = Branch.open_containing(other)[0]
3615
3658
        branch1.lock_read()
3616
 
        try:
3617
 
            branch2.lock_read()
3618
 
            try:
3619
 
                last1 = ensure_null(branch1.last_revision())
3620
 
                last2 = ensure_null(branch2.last_revision())
3621
 
 
3622
 
                graph = branch1.repository.get_graph(branch2.repository)
3623
 
                base_rev_id = graph.find_unique_lca(last1, last2)
3624
 
 
3625
 
                print 'merge base is revision %s' % base_rev_id
3626
 
            finally:
3627
 
                branch2.unlock()
3628
 
        finally:
3629
 
            branch1.unlock()
 
3659
        self.add_cleanup(branch1.unlock)
 
3660
        branch2.lock_read()
 
3661
        self.add_cleanup(branch2.unlock)
 
3662
        last1 = ensure_null(branch1.last_revision())
 
3663
        last2 = ensure_null(branch2.last_revision())
 
3664
 
 
3665
        graph = branch1.repository.get_graph(branch2.repository)
 
3666
        base_rev_id = graph.find_unique_lca(last1, last2)
 
3667
 
 
3668
        self.outf.write('merge base is revision %s\n' % base_rev_id)
3630
3669
 
3631
3670
 
3632
3671
class cmd_merge(Command):
3665
3704
    committed to record the result of the merge.
3666
3705
 
3667
3706
    merge refuses to run if there are any uncommitted changes, unless
3668
 
    --force is given.
 
3707
    --force is given. The --force option can also be used to create a
 
3708
    merge revision which has more than two parents.
 
3709
 
 
3710
    If one would like to merge changes from the working tree of the other
 
3711
    branch without merging any committed revisions, the --uncommitted option
 
3712
    can be given.
3669
3713
 
3670
3714
    To select only some changes to merge, use "merge -i", which will prompt
3671
3715
    you to apply each diff hunk and file change, similar to "shelve".
3686
3730
        To apply a merge directive contained in /tmp/merge::
3687
3731
 
3688
3732
            bzr merge /tmp/merge
 
3733
 
 
3734
        To create a merge revision with three parents from two branches
 
3735
        feature1a and feature1b:
 
3736
 
 
3737
            bzr merge ../feature1a
 
3738
            bzr merge ../feature1b --force
 
3739
            bzr commit -m 'revision with three parents'
3689
3740
    """
3690
3741
 
3691
3742
    encoding_type = 'exact'
3749
3800
        view_info = _get_view_info_for_change_reporter(tree)
3750
3801
        change_reporter = delta._ChangeReporter(
3751
3802
            unversioned_filter=tree.is_ignored, view_info=view_info)
3752
 
        cleanups = []
3753
 
        try:
3754
 
            pb = ui.ui_factory.nested_progress_bar()
3755
 
            cleanups.append(pb.finished)
3756
 
            tree.lock_write()
3757
 
            cleanups.append(tree.unlock)
3758
 
            if location is not None:
3759
 
                try:
3760
 
                    mergeable = bundle.read_mergeable_from_url(location,
3761
 
                        possible_transports=possible_transports)
3762
 
                except errors.NotABundle:
3763
 
                    mergeable = None
3764
 
                else:
3765
 
                    if uncommitted:
3766
 
                        raise errors.BzrCommandError('Cannot use --uncommitted'
3767
 
                            ' with bundles or merge directives.')
3768
 
 
3769
 
                    if revision is not None:
3770
 
                        raise errors.BzrCommandError(
3771
 
                            'Cannot use -r with merge directives or bundles')
3772
 
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
3773
 
                       mergeable, pb)
3774
 
 
3775
 
            if merger is None and uncommitted:
3776
 
                if revision is not None and len(revision) > 0:
3777
 
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3778
 
                        ' --revision at the same time.')
3779
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3780
 
                                                          cleanups)
3781
 
                allow_pending = False
3782
 
 
3783
 
            if merger is None:
3784
 
                merger, allow_pending = self._get_merger_from_branch(tree,
3785
 
                    location, revision, remember, possible_transports, pb)
3786
 
 
3787
 
            merger.merge_type = merge_type
3788
 
            merger.reprocess = reprocess
3789
 
            merger.show_base = show_base
3790
 
            self.sanity_check_merger(merger)
3791
 
            if (merger.base_rev_id == merger.other_rev_id and
3792
 
                merger.other_rev_id is not None):
3793
 
                note('Nothing to do.')
 
3803
        pb = ui.ui_factory.nested_progress_bar()
 
3804
        self.add_cleanup(pb.finished)
 
3805
        tree.lock_write()
 
3806
        self.add_cleanup(tree.unlock)
 
3807
        if location is not None:
 
3808
            try:
 
3809
                mergeable = bundle.read_mergeable_from_url(location,
 
3810
                    possible_transports=possible_transports)
 
3811
            except errors.NotABundle:
 
3812
                mergeable = None
 
3813
            else:
 
3814
                if uncommitted:
 
3815
                    raise errors.BzrCommandError('Cannot use --uncommitted'
 
3816
                        ' with bundles or merge directives.')
 
3817
 
 
3818
                if revision is not None:
 
3819
                    raise errors.BzrCommandError(
 
3820
                        'Cannot use -r with merge directives or bundles')
 
3821
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3822
                   mergeable, None)
 
3823
 
 
3824
        if merger is None and uncommitted:
 
3825
            if revision is not None and len(revision) > 0:
 
3826
                raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3827
                    ' --revision at the same time.')
 
3828
            merger = self.get_merger_from_uncommitted(tree, location, None)
 
3829
            allow_pending = False
 
3830
 
 
3831
        if merger is None:
 
3832
            merger, allow_pending = self._get_merger_from_branch(tree,
 
3833
                location, revision, remember, possible_transports, None)
 
3834
 
 
3835
        merger.merge_type = merge_type
 
3836
        merger.reprocess = reprocess
 
3837
        merger.show_base = show_base
 
3838
        self.sanity_check_merger(merger)
 
3839
        if (merger.base_rev_id == merger.other_rev_id and
 
3840
            merger.other_rev_id is not None):
 
3841
            note('Nothing to do.')
 
3842
            return 0
 
3843
        if pull:
 
3844
            if merger.interesting_files is not None:
 
3845
                raise errors.BzrCommandError('Cannot pull individual files')
 
3846
            if (merger.base_rev_id == tree.last_revision()):
 
3847
                result = tree.pull(merger.other_branch, False,
 
3848
                                   merger.other_rev_id)
 
3849
                result.report(self.outf)
3794
3850
                return 0
3795
 
            if pull:
3796
 
                if merger.interesting_files is not None:
3797
 
                    raise errors.BzrCommandError('Cannot pull individual files')
3798
 
                if (merger.base_rev_id == tree.last_revision()):
3799
 
                    result = tree.pull(merger.other_branch, False,
3800
 
                                       merger.other_rev_id)
3801
 
                    result.report(self.outf)
3802
 
                    return 0
3803
 
            if merger.this_basis is None:
3804
 
                raise errors.BzrCommandError(
3805
 
                    "This branch has no commits."
3806
 
                    " (perhaps you would prefer 'bzr pull')")
3807
 
            if preview:
3808
 
                return self._do_preview(merger, cleanups)
3809
 
            elif interactive:
3810
 
                return self._do_interactive(merger, cleanups)
3811
 
            else:
3812
 
                return self._do_merge(merger, change_reporter, allow_pending,
3813
 
                                      verified)
3814
 
        finally:
3815
 
            for cleanup in reversed(cleanups):
3816
 
                cleanup()
 
3851
        if merger.this_basis is None:
 
3852
            raise errors.BzrCommandError(
 
3853
                "This branch has no commits."
 
3854
                " (perhaps you would prefer 'bzr pull')")
 
3855
        if preview:
 
3856
            return self._do_preview(merger)
 
3857
        elif interactive:
 
3858
            return self._do_interactive(merger)
 
3859
        else:
 
3860
            return self._do_merge(merger, change_reporter, allow_pending,
 
3861
                                  verified)
3817
3862
 
3818
 
    def _get_preview(self, merger, cleanups):
 
3863
    def _get_preview(self, merger):
3819
3864
        tree_merger = merger.make_merger()
3820
3865
        tt = tree_merger.make_preview_transform()
3821
 
        cleanups.append(tt.finalize)
 
3866
        self.add_cleanup(tt.finalize)
3822
3867
        result_tree = tt.get_preview_tree()
3823
3868
        return result_tree
3824
3869
 
3825
 
    def _do_preview(self, merger, cleanups):
 
3870
    def _do_preview(self, merger):
3826
3871
        from bzrlib.diff import show_diff_trees
3827
 
        result_tree = self._get_preview(merger, cleanups)
 
3872
        result_tree = self._get_preview(merger)
3828
3873
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3829
3874
                        old_label='', new_label='')
3830
3875
 
3840
3885
        else:
3841
3886
            return 0
3842
3887
 
3843
 
    def _do_interactive(self, merger, cleanups):
 
3888
    def _do_interactive(self, merger):
3844
3889
        """Perform an interactive merge.
3845
3890
 
3846
3891
        This works by generating a preview tree of the merge, then using
3848
3893
        and the preview tree.
3849
3894
        """
3850
3895
        from bzrlib import shelf_ui
3851
 
        result_tree = self._get_preview(merger, cleanups)
 
3896
        result_tree = self._get_preview(merger)
3852
3897
        writer = bzrlib.option.diff_writer_registry.get()
3853
3898
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3854
3899
                                   reporter=shelf_ui.ApplyReporter(),
3922
3967
            allow_pending = True
3923
3968
        return merger, allow_pending
3924
3969
 
3925
 
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3970
    def get_merger_from_uncommitted(self, tree, location, pb):
3926
3971
        """Get a merger for uncommitted changes.
3927
3972
 
3928
3973
        :param tree: The tree the merger should apply to.
3929
3974
        :param location: The location containing uncommitted changes.
3930
3975
        :param pb: The progress bar to use for showing progress.
3931
 
        :param cleanups: A list of operations to perform to clean up the
3932
 
            temporary directories, unfinalized objects, etc.
3933
3976
        """
3934
3977
        location = self._select_branch_location(tree, location)[0]
3935
3978
        other_tree, other_path = WorkingTree.open_containing(location)
4022
4065
            merge_type = _mod_merge.Merge3Merger
4023
4066
        tree, file_list = tree_files(file_list)
4024
4067
        tree.lock_write()
4025
 
        try:
4026
 
            parents = tree.get_parent_ids()
4027
 
            if len(parents) != 2:
4028
 
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
4029
 
                                             " merges.  Not cherrypicking or"
4030
 
                                             " multi-merges.")
4031
 
            repository = tree.branch.repository
4032
 
            interesting_ids = None
4033
 
            new_conflicts = []
4034
 
            conflicts = tree.conflicts()
4035
 
            if file_list is not None:
4036
 
                interesting_ids = set()
4037
 
                for filename in file_list:
4038
 
                    file_id = tree.path2id(filename)
4039
 
                    if file_id is None:
4040
 
                        raise errors.NotVersionedError(filename)
4041
 
                    interesting_ids.add(file_id)
4042
 
                    if tree.kind(file_id) != "directory":
4043
 
                        continue
 
4068
        self.add_cleanup(tree.unlock)
 
4069
        parents = tree.get_parent_ids()
 
4070
        if len(parents) != 2:
 
4071
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
4072
                                         " merges.  Not cherrypicking or"
 
4073
                                         " multi-merges.")
 
4074
        repository = tree.branch.repository
 
4075
        interesting_ids = None
 
4076
        new_conflicts = []
 
4077
        conflicts = tree.conflicts()
 
4078
        if file_list is not None:
 
4079
            interesting_ids = set()
 
4080
            for filename in file_list:
 
4081
                file_id = tree.path2id(filename)
 
4082
                if file_id is None:
 
4083
                    raise errors.NotVersionedError(filename)
 
4084
                interesting_ids.add(file_id)
 
4085
                if tree.kind(file_id) != "directory":
 
4086
                    continue
4044
4087
 
4045
 
                    for name, ie in tree.inventory.iter_entries(file_id):
4046
 
                        interesting_ids.add(ie.file_id)
4047
 
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4048
 
            else:
4049
 
                # Remerge only supports resolving contents conflicts
4050
 
                allowed_conflicts = ('text conflict', 'contents conflict')
4051
 
                restore_files = [c.path for c in conflicts
4052
 
                                 if c.typestring in allowed_conflicts]
4053
 
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4054
 
            tree.set_conflicts(ConflictList(new_conflicts))
4055
 
            if file_list is not None:
4056
 
                restore_files = file_list
4057
 
            for filename in restore_files:
4058
 
                try:
4059
 
                    restore(tree.abspath(filename))
4060
 
                except errors.NotConflicted:
4061
 
                    pass
4062
 
            # Disable pending merges, because the file texts we are remerging
4063
 
            # have not had those merges performed.  If we use the wrong parents
4064
 
            # list, we imply that the working tree text has seen and rejected
4065
 
            # all the changes from the other tree, when in fact those changes
4066
 
            # have not yet been seen.
4067
 
            pb = ui.ui_factory.nested_progress_bar()
4068
 
            tree.set_parent_ids(parents[:1])
 
4088
                for name, ie in tree.inventory.iter_entries(file_id):
 
4089
                    interesting_ids.add(ie.file_id)
 
4090
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4091
        else:
 
4092
            # Remerge only supports resolving contents conflicts
 
4093
            allowed_conflicts = ('text conflict', 'contents conflict')
 
4094
            restore_files = [c.path for c in conflicts
 
4095
                             if c.typestring in allowed_conflicts]
 
4096
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4097
        tree.set_conflicts(ConflictList(new_conflicts))
 
4098
        if file_list is not None:
 
4099
            restore_files = file_list
 
4100
        for filename in restore_files:
4069
4101
            try:
4070
 
                merger = _mod_merge.Merger.from_revision_ids(pb,
4071
 
                                                             tree, parents[1])
4072
 
                merger.interesting_ids = interesting_ids
4073
 
                merger.merge_type = merge_type
4074
 
                merger.show_base = show_base
4075
 
                merger.reprocess = reprocess
4076
 
                conflicts = merger.do_merge()
4077
 
            finally:
4078
 
                tree.set_parent_ids(parents)
4079
 
                pb.finished()
 
4102
                restore(tree.abspath(filename))
 
4103
            except errors.NotConflicted:
 
4104
                pass
 
4105
        # Disable pending merges, because the file texts we are remerging
 
4106
        # have not had those merges performed.  If we use the wrong parents
 
4107
        # list, we imply that the working tree text has seen and rejected
 
4108
        # all the changes from the other tree, when in fact those changes
 
4109
        # have not yet been seen.
 
4110
        tree.set_parent_ids(parents[:1])
 
4111
        try:
 
4112
            merger = _mod_merge.Merger.from_revision_ids(None, tree, parents[1])
 
4113
            merger.interesting_ids = interesting_ids
 
4114
            merger.merge_type = merge_type
 
4115
            merger.show_base = show_base
 
4116
            merger.reprocess = reprocess
 
4117
            conflicts = merger.do_merge()
4080
4118
        finally:
4081
 
            tree.unlock()
 
4119
            tree.set_parent_ids(parents)
4082
4120
        if conflicts > 0:
4083
4121
            return 1
4084
4122
        else:
4111
4149
    created as above.  Directories containing unknown files will not be
4112
4150
    deleted.
4113
4151
 
4114
 
    The working tree contains a list of pending merged revisions, which will
4115
 
    be included as parents in the next commit.  Normally, revert clears that
4116
 
    list as well as reverting the files.  If any files are specified, revert
4117
 
    leaves the pending merge list alone and reverts only the files.  Use "bzr
4118
 
    revert ." in the tree root to revert all files but keep the merge record,
4119
 
    and "bzr revert --forget-merges" to clear the pending merge list without
 
4152
    The working tree contains a list of revisions that have been merged but
 
4153
    not yet committed. These revisions will be included as additional parents
 
4154
    of the next commit.  Normally, using revert clears that list as well as
 
4155
    reverting the files.  If any files are specified, revert leaves the list
 
4156
    of uncommitted merges alone and reverts only the files.  Use ``bzr revert
 
4157
    .`` in the tree root to revert all files but keep the recorded merges,
 
4158
    and ``bzr revert --forget-merges`` to clear the pending merge list without
4120
4159
    reverting any files.
4121
4160
 
4122
 
    Using "bzr revert --forget-merges", it is possible to apply the changes
4123
 
    from an arbitrary merge as a single revision.  To do this, perform the
4124
 
    merge as desired.  Then doing revert with the "--forget-merges" option will
4125
 
    keep the content of the tree as it was, but it will clear the list of
4126
 
    pending merges.  The next commit will then contain all of the changes that
4127
 
    would have been in the merge, but without any mention of the other parent
4128
 
    revisions.  Because this technique forgets where these changes originated,
4129
 
    it may cause additional conflicts on later merges involving the source and
 
4161
    Using "bzr revert --forget-merges", it is possible to apply all of the
 
4162
    changes from a branch in a single revision.  To do this, perform the merge
 
4163
    as desired.  Then doing revert with the "--forget-merges" option will keep
 
4164
    the content of the tree as it was, but it will clear the list of pending
 
4165
    merges.  The next commit will then contain all of the changes that are
 
4166
    present in the other branch, but without any other parent revisions.
 
4167
    Because this technique forgets where these changes originated, it may
 
4168
    cause additional conflicts on later merges involving the same source and
4130
4169
    target branches.
4131
4170
    """
4132
4171
 
4143
4182
            forget_merges=None):
4144
4183
        tree, file_list = tree_files(file_list)
4145
4184
        tree.lock_write()
4146
 
        try:
4147
 
            if forget_merges:
4148
 
                tree.set_parent_ids(tree.get_parent_ids()[:1])
4149
 
            else:
4150
 
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4151
 
        finally:
4152
 
            tree.unlock()
 
4185
        self.add_cleanup(tree.unlock)
 
4186
        if forget_merges:
 
4187
            tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4188
        else:
 
4189
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4153
4190
 
4154
4191
    @staticmethod
4155
4192
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4156
4193
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4157
 
        pb = ui.ui_factory.nested_progress_bar()
4158
 
        try:
4159
 
            tree.revert(file_list, rev_tree, not no_backup, pb,
4160
 
                report_changes=True)
4161
 
        finally:
4162
 
            pb.finished()
 
4194
        tree.revert(file_list, rev_tree, not no_backup, None,
 
4195
            report_changes=True)
4163
4196
 
4164
4197
 
4165
4198
class cmd_assert_fail(Command):
4306
4339
        if remote_branch.base == local_branch.base:
4307
4340
            remote_branch = local_branch
4308
4341
 
 
4342
        local_branch.lock_read()
 
4343
        self.add_cleanup(local_branch.unlock)
4309
4344
        local_revid_range = _revision_range_to_revid_range(
4310
4345
            _get_revision_range(my_revision, local_branch,
4311
4346
                self.name()))
4312
4347
 
 
4348
        remote_branch.lock_read()
 
4349
        self.add_cleanup(remote_branch.unlock)
4313
4350
        remote_revid_range = _revision_range_to_revid_range(
4314
4351
            _get_revision_range(revision,
4315
4352
                remote_branch, self.name()))
4316
4353
 
4317
 
        local_branch.lock_read()
4318
 
        try:
4319
 
            remote_branch.lock_read()
4320
 
            try:
4321
 
                local_extra, remote_extra = find_unmerged(
4322
 
                    local_branch, remote_branch, restrict,
4323
 
                    backward=not reverse,
4324
 
                    include_merges=include_merges,
4325
 
                    local_revid_range=local_revid_range,
4326
 
                    remote_revid_range=remote_revid_range)
4327
 
 
4328
 
                if log_format is None:
4329
 
                    registry = log.log_formatter_registry
4330
 
                    log_format = registry.get_default(local_branch)
4331
 
                lf = log_format(to_file=self.outf,
4332
 
                                show_ids=show_ids,
4333
 
                                show_timezone='original')
4334
 
 
4335
 
                status_code = 0
4336
 
                if local_extra and not theirs_only:
4337
 
                    message("You have %d extra revision(s):\n" %
4338
 
                        len(local_extra))
4339
 
                    for revision in iter_log_revisions(local_extra,
4340
 
                                        local_branch.repository,
4341
 
                                        verbose):
4342
 
                        lf.log_revision(revision)
4343
 
                    printed_local = True
4344
 
                    status_code = 1
4345
 
                else:
4346
 
                    printed_local = False
4347
 
 
4348
 
                if remote_extra and not mine_only:
4349
 
                    if printed_local is True:
4350
 
                        message("\n\n\n")
4351
 
                    message("You are missing %d revision(s):\n" %
4352
 
                        len(remote_extra))
4353
 
                    for revision in iter_log_revisions(remote_extra,
4354
 
                                        remote_branch.repository,
4355
 
                                        verbose):
4356
 
                        lf.log_revision(revision)
4357
 
                    status_code = 1
4358
 
 
4359
 
                if mine_only and not local_extra:
4360
 
                    # We checked local, and found nothing extra
4361
 
                    message('This branch is up to date.\n')
4362
 
                elif theirs_only and not remote_extra:
4363
 
                    # We checked remote, and found nothing extra
4364
 
                    message('Other branch is up to date.\n')
4365
 
                elif not (mine_only or theirs_only or local_extra or
4366
 
                          remote_extra):
4367
 
                    # We checked both branches, and neither one had extra
4368
 
                    # revisions
4369
 
                    message("Branches are up to date.\n")
4370
 
            finally:
4371
 
                remote_branch.unlock()
4372
 
        finally:
4373
 
            local_branch.unlock()
 
4354
        local_extra, remote_extra = find_unmerged(
 
4355
            local_branch, remote_branch, restrict,
 
4356
            backward=not reverse,
 
4357
            include_merges=include_merges,
 
4358
            local_revid_range=local_revid_range,
 
4359
            remote_revid_range=remote_revid_range)
 
4360
 
 
4361
        if log_format is None:
 
4362
            registry = log.log_formatter_registry
 
4363
            log_format = registry.get_default(local_branch)
 
4364
        lf = log_format(to_file=self.outf,
 
4365
                        show_ids=show_ids,
 
4366
                        show_timezone='original')
 
4367
 
 
4368
        status_code = 0
 
4369
        if local_extra and not theirs_only:
 
4370
            message("You have %d extra revision(s):\n" %
 
4371
                len(local_extra))
 
4372
            for revision in iter_log_revisions(local_extra,
 
4373
                                local_branch.repository,
 
4374
                                verbose):
 
4375
                lf.log_revision(revision)
 
4376
            printed_local = True
 
4377
            status_code = 1
 
4378
        else:
 
4379
            printed_local = False
 
4380
 
 
4381
        if remote_extra and not mine_only:
 
4382
            if printed_local is True:
 
4383
                message("\n\n\n")
 
4384
            message("You are missing %d revision(s):\n" %
 
4385
                len(remote_extra))
 
4386
            for revision in iter_log_revisions(remote_extra,
 
4387
                                remote_branch.repository,
 
4388
                                verbose):
 
4389
                lf.log_revision(revision)
 
4390
            status_code = 1
 
4391
 
 
4392
        if mine_only and not local_extra:
 
4393
            # We checked local, and found nothing extra
 
4394
            message('This branch is up to date.\n')
 
4395
        elif theirs_only and not remote_extra:
 
4396
            # We checked remote, and found nothing extra
 
4397
            message('Other branch is up to date.\n')
 
4398
        elif not (mine_only or theirs_only or local_extra or
 
4399
                  remote_extra):
 
4400
            # We checked both branches, and neither one had extra
 
4401
            # revisions
 
4402
            message("Branches are up to date.\n")
 
4403
        self.cleanup_now()
4374
4404
        if not status_code and parent is None and other_branch is not None:
4375
4405
            local_branch.lock_write()
4376
 
            try:
4377
 
                # handle race conditions - a parent might be set while we run.
4378
 
                if local_branch.get_parent() is None:
4379
 
                    local_branch.set_parent(remote_branch.base)
4380
 
            finally:
4381
 
                local_branch.unlock()
 
4406
            self.add_cleanup(local_branch.unlock)
 
4407
            # handle race conditions - a parent might be set while we run.
 
4408
            if local_branch.get_parent() is None:
 
4409
                local_branch.set_parent(remote_branch.base)
4382
4410
        return status_code
4383
4411
 
4384
4412
 
4412
4440
    adding new commands, providing additional network transports and
4413
4441
    customizing log output.
4414
4442
 
4415
 
    See the Bazaar web site, http://bazaar-vcs.org, for further
4416
 
    information on plugins including where to find them and how to
4417
 
    install them. Instructions are also provided there on how to
4418
 
    write new plugins using the Python programming language.
 
4443
    See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
 
4444
    for further information on plugins including where to find them and how to
 
4445
    install them. Instructions are also provided there on how to write new
 
4446
    plugins using the Python programming language.
4419
4447
    """
4420
4448
    takes_options = ['verbose']
4421
4449
 
4436
4464
                doc = '(no description)'
4437
4465
            result.append((name_ver, doc, plugin.path()))
4438
4466
        for name_ver, doc, path in sorted(result):
4439
 
            print name_ver
4440
 
            print '   ', doc
 
4467
            self.outf.write("%s\n" % name_ver)
 
4468
            self.outf.write("   %s\n" % doc)
4441
4469
            if verbose:
4442
 
                print '   ', path
4443
 
            print
 
4470
                self.outf.write("   %s\n" % path)
 
4471
            self.outf.write("\n")
4444
4472
 
4445
4473
 
4446
4474
class cmd_testament(Command):
4463
4491
        else:
4464
4492
            b = Branch.open(branch)
4465
4493
        b.lock_read()
4466
 
        try:
4467
 
            if revision is None:
4468
 
                rev_id = b.last_revision()
4469
 
            else:
4470
 
                rev_id = revision[0].as_revision_id(b)
4471
 
            t = testament_class.from_revision(b.repository, rev_id)
4472
 
            if long:
4473
 
                sys.stdout.writelines(t.as_text_lines())
4474
 
            else:
4475
 
                sys.stdout.write(t.as_short_text())
4476
 
        finally:
4477
 
            b.unlock()
 
4494
        self.add_cleanup(b.unlock)
 
4495
        if revision is None:
 
4496
            rev_id = b.last_revision()
 
4497
        else:
 
4498
            rev_id = revision[0].as_revision_id(b)
 
4499
        t = testament_class.from_revision(b.repository, rev_id)
 
4500
        if long:
 
4501
            sys.stdout.writelines(t.as_text_lines())
 
4502
        else:
 
4503
            sys.stdout.write(t.as_short_text())
4478
4504
 
4479
4505
 
4480
4506
class cmd_annotate(Command):
4506
4532
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4507
4533
        if wt is not None:
4508
4534
            wt.lock_read()
 
4535
            self.add_cleanup(wt.unlock)
4509
4536
        else:
4510
4537
            branch.lock_read()
4511
 
        try:
4512
 
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4513
 
            if wt is not None:
4514
 
                file_id = wt.path2id(relpath)
4515
 
            else:
4516
 
                file_id = tree.path2id(relpath)
4517
 
            if file_id is None:
4518
 
                raise errors.NotVersionedError(filename)
4519
 
            file_version = tree.inventory[file_id].revision
4520
 
            if wt is not None and revision is None:
4521
 
                # If there is a tree and we're not annotating historical
4522
 
                # versions, annotate the working tree's content.
4523
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4524
 
                    show_ids=show_ids)
4525
 
            else:
4526
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4527
 
                              show_ids=show_ids)
4528
 
        finally:
4529
 
            if wt is not None:
4530
 
                wt.unlock()
4531
 
            else:
4532
 
                branch.unlock()
 
4538
            self.add_cleanup(branch.unlock)
 
4539
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4540
        tree.lock_read()
 
4541
        self.add_cleanup(tree.unlock)
 
4542
        if wt is not None:
 
4543
            file_id = wt.path2id(relpath)
 
4544
        else:
 
4545
            file_id = tree.path2id(relpath)
 
4546
        if file_id is None:
 
4547
            raise errors.NotVersionedError(filename)
 
4548
        file_version = tree.inventory[file_id].revision
 
4549
        if wt is not None and revision is None:
 
4550
            # If there is a tree and we're not annotating historical
 
4551
            # versions, annotate the working tree's content.
 
4552
            annotate_file_tree(wt, file_id, self.outf, long, all,
 
4553
                show_ids=show_ids)
 
4554
        else:
 
4555
            annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4556
                          show_ids=show_ids)
4533
4557
 
4534
4558
 
4535
4559
class cmd_re_sign(Command):
4547
4571
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4548
4572
        b = WorkingTree.open_containing(u'.')[0].branch
4549
4573
        b.lock_write()
4550
 
        try:
4551
 
            return self._run(b, revision_id_list, revision)
4552
 
        finally:
4553
 
            b.unlock()
 
4574
        self.add_cleanup(b.unlock)
 
4575
        return self._run(b, revision_id_list, revision)
4554
4576
 
4555
4577
    def _run(self, b, revision_id_list, revision):
4556
4578
        import bzrlib.gpg as gpg
4625
4647
                    'This format does not remember old locations.')
4626
4648
            else:
4627
4649
                if location is None:
4628
 
                    raise errors.BzrCommandError('No location supplied and no '
4629
 
                        'previous location known')
 
4650
                    if b.get_bound_location() is not None:
 
4651
                        raise errors.BzrCommandError('Branch is already bound')
 
4652
                    else:
 
4653
                        raise errors.BzrCommandError('No location supplied '
 
4654
                            'and no previous location known')
4630
4655
        b_other = Branch.open(location)
4631
4656
        try:
4632
4657
            b.bind(b_other)
4702
4727
 
4703
4728
        if tree is not None:
4704
4729
            tree.lock_write()
 
4730
            self.add_cleanup(tree.unlock)
4705
4731
        else:
4706
4732
            b.lock_write()
4707
 
        try:
4708
 
            return self._run(b, tree, dry_run, verbose, revision, force,
4709
 
                             local=local)
4710
 
        finally:
4711
 
            if tree is not None:
4712
 
                tree.unlock()
4713
 
            else:
4714
 
                b.unlock()
 
4733
            self.add_cleanup(b.unlock)
 
4734
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4715
4735
 
4716
4736
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4717
4737
        from bzrlib.log import log_formatter, show_log
4749
4769
                 end_revision=last_revno)
4750
4770
 
4751
4771
        if dry_run:
4752
 
            print 'Dry-run, pretending to remove the above revisions.'
4753
 
            if not force:
4754
 
                val = raw_input('Press <enter> to continue')
 
4772
            self.outf.write('Dry-run, pretending to remove'
 
4773
                            ' the above revisions.\n')
4755
4774
        else:
4756
 
            print 'The above revision(s) will be removed.'
4757
 
            if not force:
4758
 
                val = raw_input('Are you sure [y/N]? ')
4759
 
                if val.lower() not in ('y', 'yes'):
4760
 
                    print 'Canceled'
4761
 
                    return 0
 
4775
            self.outf.write('The above revision(s) will be removed.\n')
 
4776
 
 
4777
        if not force:
 
4778
            if not ui.ui_factory.get_boolean('Are you sure'):
 
4779
                self.outf.write('Canceled')
 
4780
                return 0
4762
4781
 
4763
4782
        mutter('Uncommitting from {%s} to {%s}',
4764
4783
               last_rev_id, rev_id)
4765
4784
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
4766
4785
                 revno=revno, local=local)
4767
 
        note('You can restore the old tip by running:\n'
4768
 
             '  bzr pull . -r revid:%s', last_rev_id)
 
4786
        self.outf.write('You can restore the old tip by running:\n'
 
4787
             '  bzr pull . -r revid:%s\n' % last_rev_id)
4769
4788
 
4770
4789
 
4771
4790
class cmd_break_lock(Command):
4774
4793
    CAUTION: Locks should only be broken when you are sure that the process
4775
4794
    holding the lock has been stopped.
4776
4795
 
4777
 
    You can get information on what locks are open via the 'bzr info' command.
 
4796
    You can get information on what locks are open via the 'bzr info
 
4797
    [location]' command.
4778
4798
 
4779
4799
    :Examples:
4780
4800
        bzr break-lock
 
4801
        bzr break-lock bzr+ssh://example.com/bzr/foo
4781
4802
    """
4782
4803
    takes_args = ['location?']
4783
4804
 
5055
5076
      directly from the merge directive, without retrieving data from a
5056
5077
      branch.
5057
5078
 
5058
 
    If --no-bundle is specified, then public_branch is needed (and must be
5059
 
    up-to-date), so that the receiver can perform the merge using the
5060
 
    public_branch.  The public_branch is always included if known, so that
5061
 
    people can check it later.
5062
 
 
5063
 
    The submit branch defaults to the parent, but can be overridden.  Both
5064
 
    submit branch and public branch will be remembered if supplied.
5065
 
 
5066
 
    If a public_branch is known for the submit_branch, that public submit
5067
 
    branch is used in the merge instructions.  This means that a local mirror
5068
 
    can be used as your actual submit branch, once you have set public_branch
5069
 
    for that mirror.
 
5079
    `bzr send` creates a compact data set that, when applied using bzr
 
5080
    merge, has the same effect as merging from the source branch.  
 
5081
    
 
5082
    By default the merge directive is self-contained and can be applied to any
 
5083
    branch containing submit_branch in its ancestory without needing access to
 
5084
    the source branch.
 
5085
    
 
5086
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5087
    revisions, but only a structured request to merge from the
 
5088
    public_location.  In that case the public_branch is needed and it must be
 
5089
    up-to-date and accessible to the recipient.  The public_branch is always
 
5090
    included if known, so that people can check it later.
 
5091
 
 
5092
    The submit branch defaults to the parent of the source branch, but can be
 
5093
    overridden.  Both submit branch and public branch will be remembered in
 
5094
    branch.conf the first time they are used for a particular branch.  The
 
5095
    source branch defaults to that containing the working directory, but can
 
5096
    be changed using --from.
 
5097
 
 
5098
    In order to calculate those changes, bzr must analyse the submit branch.
 
5099
    Therefore it is most efficient for the submit branch to be a local mirror.
 
5100
    If a public location is known for the submit_branch, that location is used
 
5101
    in the merge directive.
 
5102
 
 
5103
    The default behaviour is to send the merge directive by mail, unless -o is
 
5104
    given, in which case it is sent to a file.
5070
5105
 
5071
5106
    Mail is sent using your preferred mail program.  This should be transparent
5072
5107
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
5092
5127
 
5093
5128
    The merge directives created by bzr send may be applied using bzr merge or
5094
5129
    bzr pull by specifying a file containing a merge directive as the location.
 
5130
 
 
5131
    bzr send makes extensive use of public locations to map local locations into
 
5132
    URLs that can be used by other people.  See `bzr help configuration` to
 
5133
    set them, and use `bzr info` to display them.
5095
5134
    """
5096
5135
 
5097
5136
    encoding_type = 'exact'
5113
5152
               short_name='f',
5114
5153
               type=unicode),
5115
5154
        Option('output', short_name='o',
5116
 
               help='Write merge directive to this file; '
 
5155
               help='Write merge directive to this file or directory; '
5117
5156
                    'use - for stdout.',
5118
5157
               type=unicode),
5119
5158
        Option('strict',
5256
5295
            ):
5257
5296
        branch, relpath = Branch.open_containing(directory)
5258
5297
        branch.lock_write()
5259
 
        try:
5260
 
            if delete:
5261
 
                branch.tags.delete_tag(tag_name)
5262
 
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
5298
        self.add_cleanup(branch.unlock)
 
5299
        if delete:
 
5300
            branch.tags.delete_tag(tag_name)
 
5301
            self.outf.write('Deleted tag %s.\n' % tag_name)
 
5302
        else:
 
5303
            if revision:
 
5304
                if len(revision) != 1:
 
5305
                    raise errors.BzrCommandError(
 
5306
                        "Tags can only be placed on a single revision, "
 
5307
                        "not on a range")
 
5308
                revision_id = revision[0].as_revision_id(branch)
5263
5309
            else:
5264
 
                if revision:
5265
 
                    if len(revision) != 1:
5266
 
                        raise errors.BzrCommandError(
5267
 
                            "Tags can only be placed on a single revision, "
5268
 
                            "not on a range")
5269
 
                    revision_id = revision[0].as_revision_id(branch)
5270
 
                else:
5271
 
                    revision_id = branch.last_revision()
5272
 
                if (not force) and branch.tags.has_tag(tag_name):
5273
 
                    raise errors.TagAlreadyExists(tag_name)
5274
 
                branch.tags.set_tag(tag_name, revision_id)
5275
 
                self.outf.write('Created tag %s.\n' % tag_name)
5276
 
        finally:
5277
 
            branch.unlock()
 
5310
                revision_id = branch.last_revision()
 
5311
            if (not force) and branch.tags.has_tag(tag_name):
 
5312
                raise errors.TagAlreadyExists(tag_name)
 
5313
            branch.tags.set_tag(tag_name, revision_id)
 
5314
            self.outf.write('Created tag %s.\n' % tag_name)
5278
5315
 
5279
5316
 
5280
5317
class cmd_tags(Command):
5313
5350
            return
5314
5351
 
5315
5352
        branch.lock_read()
5316
 
        try:
5317
 
            if revision:
5318
 
                graph = branch.repository.get_graph()
5319
 
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5320
 
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5321
 
                # only show revisions between revid1 and revid2 (inclusive)
5322
 
                tags = [(tag, revid) for tag, revid in tags if
5323
 
                    graph.is_between(revid, revid1, revid2)]
5324
 
            if sort == 'alpha':
5325
 
                tags.sort()
5326
 
            elif sort == 'time':
5327
 
                timestamps = {}
5328
 
                for tag, revid in tags:
5329
 
                    try:
5330
 
                        revobj = branch.repository.get_revision(revid)
5331
 
                    except errors.NoSuchRevision:
5332
 
                        timestamp = sys.maxint # place them at the end
5333
 
                    else:
5334
 
                        timestamp = revobj.timestamp
5335
 
                    timestamps[revid] = timestamp
5336
 
                tags.sort(key=lambda x: timestamps[x[1]])
5337
 
            if not show_ids:
5338
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5339
 
                for index, (tag, revid) in enumerate(tags):
5340
 
                    try:
5341
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5342
 
                        if isinstance(revno, tuple):
5343
 
                            revno = '.'.join(map(str, revno))
5344
 
                    except errors.NoSuchRevision:
5345
 
                        # Bad tag data/merges can lead to tagged revisions
5346
 
                        # which are not in this branch. Fail gracefully ...
5347
 
                        revno = '?'
5348
 
                    tags[index] = (tag, revno)
5349
 
        finally:
5350
 
            branch.unlock()
 
5353
        self.add_cleanup(branch.unlock)
 
5354
        if revision:
 
5355
            graph = branch.repository.get_graph()
 
5356
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5357
            revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5358
            # only show revisions between revid1 and revid2 (inclusive)
 
5359
            tags = [(tag, revid) for tag, revid in tags if
 
5360
                graph.is_between(revid, revid1, revid2)]
 
5361
        if sort == 'alpha':
 
5362
            tags.sort()
 
5363
        elif sort == 'time':
 
5364
            timestamps = {}
 
5365
            for tag, revid in tags:
 
5366
                try:
 
5367
                    revobj = branch.repository.get_revision(revid)
 
5368
                except errors.NoSuchRevision:
 
5369
                    timestamp = sys.maxint # place them at the end
 
5370
                else:
 
5371
                    timestamp = revobj.timestamp
 
5372
                timestamps[revid] = timestamp
 
5373
            tags.sort(key=lambda x: timestamps[x[1]])
 
5374
        if not show_ids:
 
5375
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5376
            for index, (tag, revid) in enumerate(tags):
 
5377
                try:
 
5378
                    revno = branch.revision_id_to_dotted_revno(revid)
 
5379
                    if isinstance(revno, tuple):
 
5380
                        revno = '.'.join(map(str, revno))
 
5381
                except errors.NoSuchRevision:
 
5382
                    # Bad tag data/merges can lead to tagged revisions
 
5383
                    # which are not in this branch. Fail gracefully ...
 
5384
                    revno = '?'
 
5385
                tags[index] = (tag, revno)
 
5386
        self.cleanup_now()
5351
5387
        for tag, revspec in tags:
5352
5388
            self.outf.write('%-20s %s\n' % (tag, revspec))
5353
5389
 
5466
5502
    that of the master.
5467
5503
    """
5468
5504
 
5469
 
    takes_args = ['to_location']
 
5505
    takes_args = ['to_location?']
5470
5506
    takes_options = [Option('force',
5471
5507
                        help='Switch even if local commits will be lost.'),
 
5508
                     'revision',
5472
5509
                     Option('create-branch', short_name='b',
5473
5510
                        help='Create the target branch from this one before'
5474
5511
                             ' switching to it.'),
5475
 
                     ]
 
5512
                    ]
5476
5513
 
5477
 
    def run(self, to_location, force=False, create_branch=False):
 
5514
    def run(self, to_location=None, force=False, create_branch=False,
 
5515
            revision=None):
5478
5516
        from bzrlib import switch
5479
5517
        tree_location = '.'
 
5518
        revision = _get_one_revision('switch', revision)
5480
5519
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
 
5520
        if to_location is None:
 
5521
            if revision is None:
 
5522
                raise errors.BzrCommandError('You must supply either a'
 
5523
                                             ' revision or a location')
 
5524
            to_location = '.'
5481
5525
        try:
5482
5526
            branch = control_dir.open_branch()
5483
5527
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5488
5532
            if branch is None:
5489
5533
                raise errors.BzrCommandError('cannot create branch without'
5490
5534
                                             ' source branch')
 
5535
            to_location = directory_service.directories.dereference(
 
5536
                              to_location)
5491
5537
            if '/' not in to_location and '\\' not in to_location:
5492
5538
                # This path is meant to be relative to the existing branch
5493
5539
                this_url = self._get_branch_location(control_dir)
5495
5541
            to_branch = branch.bzrdir.sprout(to_location,
5496
5542
                                 possible_transports=[branch.bzrdir.root_transport],
5497
5543
                                 source_branch=branch).open_branch()
5498
 
            # try:
5499
 
            #     from_branch = control_dir.open_branch()
5500
 
            # except errors.NotBranchError:
5501
 
            #     raise BzrCommandError('Cannot create a branch from this'
5502
 
            #         ' location when we cannot open this branch')
5503
 
            # from_branch.bzrdir.sprout(
5504
 
            pass
5505
5544
        else:
5506
5545
            try:
5507
5546
                to_branch = Branch.open(to_location)
5509
5548
                this_url = self._get_branch_location(control_dir)
5510
5549
                to_branch = Branch.open(
5511
5550
                    urlutils.join(this_url, '..', to_location))
5512
 
        switch.switch(control_dir, to_branch, force)
 
5551
        if revision is not None:
 
5552
            revision = revision.as_revision_id(to_branch)
 
5553
        switch.switch(control_dir, to_branch, force, revision_id=revision)
5513
5554
        if had_explicit_nick:
5514
5555
            branch = control_dir.open_branch() #get the new branch!
5515
5556
            branch.nick = to_branch.nick
5769
5810
    def run_for_list(self):
5770
5811
        tree = WorkingTree.open_containing('.')[0]
5771
5812
        tree.lock_read()
5772
 
        try:
5773
 
            manager = tree.get_shelf_manager()
5774
 
            shelves = manager.active_shelves()
5775
 
            if len(shelves) == 0:
5776
 
                note('No shelved changes.')
5777
 
                return 0
5778
 
            for shelf_id in reversed(shelves):
5779
 
                message = manager.get_metadata(shelf_id).get('message')
5780
 
                if message is None:
5781
 
                    message = '<no message>'
5782
 
                self.outf.write('%3d: %s\n' % (shelf_id, message))
5783
 
            return 1
5784
 
        finally:
5785
 
            tree.unlock()
 
5813
        self.add_cleanup(tree.unlock)
 
5814
        manager = tree.get_shelf_manager()
 
5815
        shelves = manager.active_shelves()
 
5816
        if len(shelves) == 0:
 
5817
            note('No shelved changes.')
 
5818
            return 0
 
5819
        for shelf_id in reversed(shelves):
 
5820
            message = manager.get_metadata(shelf_id).get('message')
 
5821
            if message is None:
 
5822
                message = '<no message>'
 
5823
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5824
        return 1
5786
5825
 
5787
5826
 
5788
5827
class cmd_unshelve(Command):
5800
5839
            enum_switch=False, value_switches=True,
5801
5840
            apply="Apply changes and remove from the shelf.",
5802
5841
            dry_run="Show changes, but do not apply or remove them.",
 
5842
            preview="Instead of unshelving the changes, show the diff that "
 
5843
                    "would result from unshelving.",
5803
5844
            delete_only="Delete changes without applying them.",
5804
5845
            keep="Apply changes but don't delete them.",
5805
5846
        )
5908
5949
    )
5909
5950
from bzrlib.foreign import cmd_dpush
5910
5951
from bzrlib.sign_my_commits import cmd_sign_my_commits
5911
 
from bzrlib.weave_commands import cmd_versionedfile_list, \
5912
 
        cmd_weave_plan_merge, cmd_weave_merge_text