~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Jelmer Vernooij
  • Date: 2009-10-27 21:54:26 UTC
  • mfrom: (4771 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4833.
  • Revision ID: jelmer@samba.org-20091027215426-72164bkd4mq9dsd4
merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004-2010 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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,
35
34
    delta,
36
35
    config,
37
36
    errors,
44
43
    reconfigure,
45
44
    rename_map,
46
45
    revision as _mod_revision,
47
 
    static_tuple,
48
46
    symbol_versioning,
49
 
    timestamp,
50
47
    transport,
51
48
    ui,
52
49
    urlutils,
260
257
    unknown
261
258
        Not versioned and not matching an ignore pattern.
262
259
 
263
 
    Additionally for directories, symlinks and files with an executable
264
 
    bit, Bazaar indicates their type using a trailing character: '/', '@'
265
 
    or '*' respectively.
266
 
 
267
260
    To see ignored files use 'bzr ignored'.  For details on the
268
261
    changes to file texts, use 'bzr diff'.
269
262
 
438
431
        for node in bt.iter_all_entries():
439
432
            # Node is made up of:
440
433
            # (index, key, value, [references])
441
 
            refs_as_tuples = static_tuple.as_tuples(node[3])
 
434
            refs_as_tuples = tuple([tuple([tuple(ref) for ref in ref_list])
 
435
                                   for ref_list in node[3]])
442
436
            as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
443
437
            self.outf.write('%s\n' % (as_tuple,))
444
438
 
502
496
                wt.lock_read()
503
497
            except (errors.NoWorkingTree, errors.NotLocalUrl):
504
498
                raise errors.NoWorkingTree(location)
505
 
            self.add_cleanup(wt.unlock)
506
 
            revid = wt.last_revision()
507
499
            try:
508
 
                revno_t = wt.branch.revision_id_to_dotted_revno(revid)
509
 
            except errors.NoSuchRevision:
510
 
                revno_t = ('???',)
511
 
            revno = ".".join(str(n) for n in revno_t)
 
500
                revid = wt.last_revision()
 
501
                try:
 
502
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
503
                except errors.NoSuchRevision:
 
504
                    revno_t = ('???',)
 
505
                revno = ".".join(str(n) for n in revno_t)
 
506
            finally:
 
507
                wt.unlock()
512
508
        else:
513
509
            b = Branch.open_containing(location)[0]
514
510
            b.lock_read()
515
 
            self.add_cleanup(b.unlock)
516
 
            revno = b.revno()
517
 
        self.cleanup_now()
 
511
            try:
 
512
                revno = b.revno()
 
513
            finally:
 
514
                b.unlock()
 
515
 
518
516
        self.outf.write(str(revno) + '\n')
519
517
 
520
518
 
542
540
            wt = WorkingTree.open_containing(directory)[0]
543
541
            b = wt.branch
544
542
            wt.lock_read()
545
 
            self.add_cleanup(wt.unlock)
546
543
        except (errors.NoWorkingTree, errors.NotLocalUrl):
547
544
            wt = None
548
545
            b = Branch.open_containing(directory)[0]
549
546
            b.lock_read()
550
 
            self.add_cleanup(b.unlock)
551
 
        revision_ids = []
552
 
        if revision is not None:
553
 
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
554
 
        if revision_info_list is not None:
555
 
            for rev_str in revision_info_list:
556
 
                rev_spec = RevisionSpec.from_string(rev_str)
557
 
                revision_ids.append(rev_spec.as_revision_id(b))
558
 
        # No arguments supplied, default to the last revision
559
 
        if len(revision_ids) == 0:
560
 
            if tree:
561
 
                if wt is None:
562
 
                    raise errors.NoWorkingTree(directory)
563
 
                revision_ids.append(wt.last_revision())
 
547
        try:
 
548
            revision_ids = []
 
549
            if revision is not None:
 
550
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
551
            if revision_info_list is not None:
 
552
                for rev_str in revision_info_list:
 
553
                    rev_spec = RevisionSpec.from_string(rev_str)
 
554
                    revision_ids.append(rev_spec.as_revision_id(b))
 
555
            # No arguments supplied, default to the last revision
 
556
            if len(revision_ids) == 0:
 
557
                if tree:
 
558
                    if wt is None:
 
559
                        raise errors.NoWorkingTree(directory)
 
560
                    revision_ids.append(wt.last_revision())
 
561
                else:
 
562
                    revision_ids.append(b.last_revision())
 
563
 
 
564
            revinfos = []
 
565
            maxlen = 0
 
566
            for revision_id in revision_ids:
 
567
                try:
 
568
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
569
                    revno = '.'.join(str(i) for i in dotted_revno)
 
570
                except errors.NoSuchRevision:
 
571
                    revno = '???'
 
572
                maxlen = max(maxlen, len(revno))
 
573
                revinfos.append([revno, revision_id])
 
574
        finally:
 
575
            if wt is None:
 
576
                b.unlock()
564
577
            else:
565
 
                revision_ids.append(b.last_revision())
566
 
 
567
 
        revinfos = []
568
 
        maxlen = 0
569
 
        for revision_id in revision_ids:
570
 
            try:
571
 
                dotted_revno = b.revision_id_to_dotted_revno(revision_id)
572
 
                revno = '.'.join(str(i) for i in dotted_revno)
573
 
            except errors.NoSuchRevision:
574
 
                revno = '???'
575
 
            maxlen = max(maxlen, len(revno))
576
 
            revinfos.append([revno, revision_id])
577
 
 
578
 
        self.cleanup_now()
 
578
                wt.unlock()
 
579
 
579
580
        for ri in revinfos:
580
581
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
581
582
 
653
654
 
654
655
        if base_tree:
655
656
            base_tree.lock_read()
656
 
            self.add_cleanup(base_tree.unlock)
657
 
        tree, file_list = tree_files_for_add(file_list)
658
 
        added, ignored = tree.smart_add(file_list, not
659
 
            no_recurse, action=action, save=not dry_run)
660
 
        self.cleanup_now()
 
657
        try:
 
658
            file_list = self._maybe_expand_globs(file_list)
 
659
            tree, file_list = tree_files_for_add(file_list)
 
660
            added, ignored = tree.smart_add(file_list, not
 
661
                no_recurse, action=action, save=not dry_run)
 
662
        finally:
 
663
            if base_tree is not None:
 
664
                base_tree.unlock()
661
665
        if len(ignored) > 0:
662
666
            if verbose:
663
667
                for glob in sorted(ignored.keys()):
727
731
        revision = _get_one_revision('inventory', revision)
728
732
        work_tree, file_list = tree_files(file_list)
729
733
        work_tree.lock_read()
730
 
        self.add_cleanup(work_tree.unlock)
731
 
        if revision is not None:
732
 
            tree = revision.as_tree(work_tree.branch)
733
 
 
734
 
            extra_trees = [work_tree]
735
 
            tree.lock_read()
736
 
            self.add_cleanup(tree.unlock)
737
 
        else:
738
 
            tree = work_tree
739
 
            extra_trees = []
740
 
 
741
 
        if file_list is not None:
742
 
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
743
 
                                      require_versioned=True)
744
 
            # find_ids_across_trees may include some paths that don't
745
 
            # exist in 'tree'.
746
 
            entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
747
 
                             for file_id in file_ids if file_id in tree)
748
 
        else:
749
 
            entries = tree.inventory.entries()
750
 
 
751
 
        self.cleanup_now()
 
734
        try:
 
735
            if revision is not None:
 
736
                tree = revision.as_tree(work_tree.branch)
 
737
 
 
738
                extra_trees = [work_tree]
 
739
                tree.lock_read()
 
740
            else:
 
741
                tree = work_tree
 
742
                extra_trees = []
 
743
 
 
744
            if file_list is not None:
 
745
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
746
                                          require_versioned=True)
 
747
                # find_ids_across_trees may include some paths that don't
 
748
                # exist in 'tree'.
 
749
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
750
                                 for file_id in file_ids if file_id in tree)
 
751
            else:
 
752
                entries = tree.inventory.entries()
 
753
        finally:
 
754
            tree.unlock()
 
755
            if tree is not work_tree:
 
756
                work_tree.unlock()
 
757
 
752
758
        for path, entry in entries:
753
759
            if kind and kind != entry.kind:
754
760
                continue
800
806
            raise errors.BzrCommandError("missing file argument")
801
807
        tree, rel_names = tree_files(names_list, canonicalize=False)
802
808
        tree.lock_tree_write()
803
 
        self.add_cleanup(tree.unlock)
804
 
        self._run(tree, names_list, rel_names, after)
 
809
        try:
 
810
            self._run(tree, names_list, rel_names, after)
 
811
        finally:
 
812
            tree.unlock()
805
813
 
806
814
    def run_auto(self, names_list, after, dry_run):
807
815
        if names_list is not None and len(names_list) > 1:
812
820
                                         ' --auto.')
813
821
        work_tree, file_list = tree_files(names_list, default_branch='.')
814
822
        work_tree.lock_tree_write()
815
 
        self.add_cleanup(work_tree.unlock)
816
 
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
823
        try:
 
824
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
825
        finally:
 
826
            work_tree.unlock()
817
827
 
818
828
    def _run(self, tree, names_list, rel_names, after):
819
829
        into_existing = osutils.isdir(names_list[-1])
840
850
            # All entries reference existing inventory items, so fix them up
841
851
            # for cicp file-systems.
842
852
            rel_names = tree.get_canonical_inventory_paths(rel_names)
843
 
            for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
844
 
                if not is_quiet():
845
 
                    self.outf.write("%s => %s\n" % (src, dest))
 
853
            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
 
854
                self.outf.write("%s => %s\n" % pair)
846
855
        else:
847
856
            if len(names_list) != 2:
848
857
                raise errors.BzrCommandError('to mv multiple files the'
892
901
            dest = osutils.pathjoin(dest_parent, dest_tail)
893
902
            mutter("attempting to move %s => %s", src, dest)
894
903
            tree.rename_one(src, dest, after=after)
895
 
            if not is_quiet():
896
 
                self.outf.write("%s => %s\n" % (src, dest))
 
904
            self.outf.write("%s => %s\n" % (src, dest))
897
905
 
898
906
 
899
907
class cmd_pull(Command):
900
908
    """Turn this branch into a mirror of another branch.
901
909
 
902
 
    By default, this command only works on branches that have not diverged.
903
 
    Branches are considered diverged if the destination branch's most recent 
904
 
    commit is one that has not been merged (directly or indirectly) into the 
905
 
    parent.
 
910
    This command only works on branches that have not diverged.  Branches are
 
911
    considered diverged if the destination branch's most recent commit is one
 
912
    that has not been merged (directly or indirectly) into the parent.
906
913
 
907
914
    If branches have diverged, you can use 'bzr merge' to integrate the changes
908
915
    from one into the other.  Once one branch has merged, the other should
909
916
    be able to pull it again.
910
917
 
911
 
    If you want to replace your local changes and just want your branch to
912
 
    match the remote one, use pull --overwrite. This will work even if the two
913
 
    branches have diverged.
 
918
    If you want to forget your local changes and just update your branch to
 
919
    match the remote one, use pull --overwrite.
914
920
 
915
921
    If there is no default location set, the first pull will set it.  After
916
922
    that, you can omit the location to use the default.  To change the
997
1003
 
998
1004
        if branch_from is not branch_to:
999
1005
            branch_from.lock_read()
1000
 
            self.add_cleanup(branch_from.unlock)
1001
 
        if revision is not None:
1002
 
            revision_id = revision.as_revision_id(branch_from)
1003
 
 
1004
 
        branch_to.lock_write()
1005
 
        self.add_cleanup(branch_to.unlock)
1006
 
        if tree_to is not None:
1007
 
            view_info = _get_view_info_for_change_reporter(tree_to)
1008
 
            change_reporter = delta._ChangeReporter(
1009
 
                unversioned_filter=tree_to.is_ignored,
1010
 
                view_info=view_info)
1011
 
            result = tree_to.pull(
1012
 
                branch_from, overwrite, revision_id, change_reporter,
1013
 
                possible_transports=possible_transports, local=local)
1014
 
        else:
1015
 
            result = branch_to.pull(
1016
 
                branch_from, overwrite, revision_id, local=local)
1017
 
 
1018
 
        result.report(self.outf)
1019
 
        if verbose and result.old_revid != result.new_revid:
1020
 
            log.show_branch_change(
1021
 
                branch_to, self.outf, result.old_revno,
1022
 
                result.old_revid)
 
1006
        try:
 
1007
            if revision is not None:
 
1008
                revision_id = revision.as_revision_id(branch_from)
 
1009
 
 
1010
            branch_to.lock_write()
 
1011
            try:
 
1012
                if tree_to is not None:
 
1013
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1014
                    change_reporter = delta._ChangeReporter(
 
1015
                        unversioned_filter=tree_to.is_ignored,
 
1016
                        view_info=view_info)
 
1017
                    result = tree_to.pull(
 
1018
                        branch_from, overwrite, revision_id, change_reporter,
 
1019
                        possible_transports=possible_transports, local=local)
 
1020
                else:
 
1021
                    result = branch_to.pull(
 
1022
                        branch_from, overwrite, revision_id, local=local)
 
1023
 
 
1024
                result.report(self.outf)
 
1025
                if verbose and result.old_revid != result.new_revid:
 
1026
                    log.show_branch_change(
 
1027
                        branch_to, self.outf, result.old_revno,
 
1028
                        result.old_revid)
 
1029
            finally:
 
1030
                branch_to.unlock()
 
1031
        finally:
 
1032
            if branch_from is not branch_to:
 
1033
                branch_from.unlock()
1023
1034
 
1024
1035
 
1025
1036
class cmd_push(Command):
1180
1191
                    ' directory exists, but does not already'
1181
1192
                    ' have a control directory.  This flag will'
1182
1193
                    ' allow branch to proceed.'),
1183
 
        Option('bind',
1184
 
            help="Bind new branch to from location."),
1185
1194
        ]
1186
1195
    aliases = ['get', 'clone']
1187
1196
 
1188
1197
    def run(self, from_location, to_location=None, revision=None,
1189
1198
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1190
 
            use_existing_dir=False, switch=False, bind=False):
 
1199
            use_existing_dir=False, switch=False):
1191
1200
        from bzrlib import switch as _mod_switch
1192
1201
        from bzrlib.tag import _merge_tags_if_possible
1193
1202
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1194
1203
            from_location)
 
1204
        if (accelerator_tree is not None and
 
1205
            accelerator_tree.supports_content_filtering()):
 
1206
            accelerator_tree = None
1195
1207
        revision = _get_one_revision('branch', revision)
1196
1208
        br_from.lock_read()
1197
 
        self.add_cleanup(br_from.unlock)
1198
 
        if revision is not None:
1199
 
            revision_id = revision.as_revision_id(br_from)
1200
 
        else:
1201
 
            # FIXME - wt.last_revision, fallback to branch, fall back to
1202
 
            # None or perhaps NULL_REVISION to mean copy nothing
1203
 
            # RBC 20060209
1204
 
            revision_id = br_from.last_revision()
1205
 
        if to_location is None:
1206
 
            to_location = urlutils.derive_to_location(from_location)
1207
 
        to_transport = transport.get_transport(to_location)
1208
1209
        try:
1209
 
            to_transport.mkdir('.')
1210
 
        except errors.FileExists:
1211
 
            if not use_existing_dir:
1212
 
                raise errors.BzrCommandError('Target directory "%s" '
1213
 
                    'already exists.' % to_location)
 
1210
            if revision is not None:
 
1211
                revision_id = revision.as_revision_id(br_from)
1214
1212
            else:
1215
 
                try:
1216
 
                    bzrdir.BzrDir.open_from_transport(to_transport)
1217
 
                except errors.NotBranchError:
1218
 
                    pass
 
1213
                # FIXME - wt.last_revision, fallback to branch, fall back to
 
1214
                # None or perhaps NULL_REVISION to mean copy nothing
 
1215
                # RBC 20060209
 
1216
                revision_id = br_from.last_revision()
 
1217
            if to_location is None:
 
1218
                to_location = urlutils.derive_to_location(from_location)
 
1219
            to_transport = transport.get_transport(to_location)
 
1220
            try:
 
1221
                to_transport.mkdir('.')
 
1222
            except errors.FileExists:
 
1223
                if not use_existing_dir:
 
1224
                    raise errors.BzrCommandError('Target directory "%s" '
 
1225
                        'already exists.' % to_location)
1219
1226
                else:
1220
 
                    raise errors.AlreadyBranchError(to_location)
1221
 
        except errors.NoSuchFile:
1222
 
            raise errors.BzrCommandError('Parent of "%s" does not exist.'
1223
 
                                         % to_location)
1224
 
        try:
1225
 
            # preserve whatever source format we have.
1226
 
            dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1227
 
                                        possible_transports=[to_transport],
1228
 
                                        accelerator_tree=accelerator_tree,
1229
 
                                        hardlink=hardlink, stacked=stacked,
1230
 
                                        force_new_repo=standalone,
1231
 
                                        create_tree_if_local=not no_tree,
1232
 
                                        source_branch=br_from)
1233
 
            branch = dir.open_branch()
1234
 
        except errors.NoSuchRevision:
1235
 
            to_transport.delete_tree('.')
1236
 
            msg = "The branch %s has no revision %s." % (from_location,
1237
 
                revision)
1238
 
            raise errors.BzrCommandError(msg)
1239
 
        _merge_tags_if_possible(br_from, branch)
1240
 
        # If the source branch is stacked, the new branch may
1241
 
        # be stacked whether we asked for that explicitly or not.
1242
 
        # We therefore need a try/except here and not just 'if stacked:'
1243
 
        try:
1244
 
            note('Created new stacked branch referring to %s.' %
1245
 
                branch.get_stacked_on_url())
1246
 
        except (errors.NotStacked, errors.UnstackableBranchFormat,
1247
 
            errors.UnstackableRepositoryFormat), e:
1248
 
            note('Branched %d revision(s).' % branch.revno())
1249
 
        if bind:
1250
 
            # Bind to the parent
1251
 
            parent_branch = Branch.open(from_location)
1252
 
            branch.bind(parent_branch)
1253
 
            note('New branch bound to %s' % from_location)
1254
 
        if switch:
1255
 
            # Switch to the new branch
1256
 
            wt, _ = WorkingTree.open_containing('.')
1257
 
            _mod_switch.switch(wt.bzrdir, branch)
1258
 
            note('Switched to branch: %s',
1259
 
                urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1227
                    try:
 
1228
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1229
                    except errors.NotBranchError:
 
1230
                        pass
 
1231
                    else:
 
1232
                        raise errors.AlreadyBranchError(to_location)
 
1233
            except errors.NoSuchFile:
 
1234
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
1235
                                             % to_location)
 
1236
            try:
 
1237
                # preserve whatever source format we have.
 
1238
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
 
1239
                                            possible_transports=[to_transport],
 
1240
                                            accelerator_tree=accelerator_tree,
 
1241
                                            hardlink=hardlink, stacked=stacked,
 
1242
                                            force_new_repo=standalone,
 
1243
                                            create_tree_if_local=not no_tree,
 
1244
                                            source_branch=br_from)
 
1245
                branch = dir.open_branch()
 
1246
            except errors.NoSuchRevision:
 
1247
                to_transport.delete_tree('.')
 
1248
                msg = "The branch %s has no revision %s." % (from_location,
 
1249
                    revision)
 
1250
                raise errors.BzrCommandError(msg)
 
1251
            _merge_tags_if_possible(br_from, branch)
 
1252
            # If the source branch is stacked, the new branch may
 
1253
            # be stacked whether we asked for that explicitly or not.
 
1254
            # We therefore need a try/except here and not just 'if stacked:'
 
1255
            try:
 
1256
                note('Created new stacked branch referring to %s.' %
 
1257
                    branch.get_stacked_on_url())
 
1258
            except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1259
                errors.UnstackableRepositoryFormat), e:
 
1260
                note('Branched %d revision(s).' % branch.revno())
 
1261
            if switch:
 
1262
                # Switch to the new branch
 
1263
                wt, _ = WorkingTree.open_containing('.')
 
1264
                _mod_switch.switch(wt.bzrdir, branch)
 
1265
                note('Switched to branch: %s',
 
1266
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1267
        finally:
 
1268
            br_from.unlock()
1260
1269
 
1261
1270
 
1262
1271
class cmd_checkout(Command):
1341
1350
    def run(self, dir=u'.'):
1342
1351
        tree = WorkingTree.open_containing(dir)[0]
1343
1352
        tree.lock_read()
1344
 
        self.add_cleanup(tree.unlock)
1345
 
        new_inv = tree.inventory
1346
 
        old_tree = tree.basis_tree()
1347
 
        old_tree.lock_read()
1348
 
        self.add_cleanup(old_tree.unlock)
1349
 
        old_inv = old_tree.inventory
1350
 
        renames = []
1351
 
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
1352
 
        for f, paths, c, v, p, n, k, e in iterator:
1353
 
            if paths[0] == paths[1]:
1354
 
                continue
1355
 
            if None in (paths):
1356
 
                continue
1357
 
            renames.append(paths)
1358
 
        renames.sort()
1359
 
        for old_name, new_name in renames:
1360
 
            self.outf.write("%s => %s\n" % (old_name, new_name))
 
1353
        try:
 
1354
            new_inv = tree.inventory
 
1355
            old_tree = tree.basis_tree()
 
1356
            old_tree.lock_read()
 
1357
            try:
 
1358
                old_inv = old_tree.inventory
 
1359
                renames = []
 
1360
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1361
                for f, paths, c, v, p, n, k, e in iterator:
 
1362
                    if paths[0] == paths[1]:
 
1363
                        continue
 
1364
                    if None in (paths):
 
1365
                        continue
 
1366
                    renames.append(paths)
 
1367
                renames.sort()
 
1368
                for old_name, new_name in renames:
 
1369
                    self.outf.write("%s => %s\n" % (old_name, new_name))
 
1370
            finally:
 
1371
                old_tree.unlock()
 
1372
        finally:
 
1373
            tree.unlock()
1361
1374
 
1362
1375
 
1363
1376
class cmd_update(Command):
1369
1382
 
1370
1383
    If you want to discard your local changes, you can just do a
1371
1384
    'bzr revert' instead of 'bzr commit' after the update.
1372
 
 
1373
 
    If the tree's branch is bound to a master branch, it will also update
1374
 
    the branch from the master.
1375
1385
    """
1376
1386
 
1377
1387
    _see_also = ['pull', 'working-trees', 'status-flags']
1378
1388
    takes_args = ['dir?']
1379
 
    takes_options = ['revision']
1380
1389
    aliases = ['up']
1381
1390
 
1382
 
    def run(self, dir='.', revision=None):
1383
 
        if revision is not None and len(revision) != 1:
1384
 
            raise errors.BzrCommandError(
1385
 
                        "bzr update --revision takes exactly one revision")
 
1391
    def run(self, dir='.'):
1386
1392
        tree = WorkingTree.open_containing(dir)[0]
1387
 
        branch = tree.branch
1388
1393
        possible_transports = []
1389
 
        master = branch.get_master_branch(
 
1394
        master = tree.branch.get_master_branch(
1390
1395
            possible_transports=possible_transports)
1391
1396
        if master is not None:
1392
1397
            tree.lock_write()
1393
 
            branch_location = master.base
1394
1398
        else:
1395
1399
            tree.lock_tree_write()
1396
 
            branch_location = tree.branch.base
1397
 
        self.add_cleanup(tree.unlock)
1398
 
        # get rid of the final '/' and be ready for display
1399
 
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
1400
 
                                                        self.outf.encoding)
1401
 
        existing_pending_merges = tree.get_parent_ids()[1:]
1402
 
        if master is None:
1403
 
            old_tip = None
1404
 
        else:
1405
 
            # may need to fetch data into a heavyweight checkout
1406
 
            # XXX: this may take some time, maybe we should display a
1407
 
            # message
1408
 
            old_tip = branch.update(possible_transports)
1409
 
        if revision is not None:
1410
 
            revision_id = revision[0].as_revision_id(branch)
1411
 
        else:
1412
 
            revision_id = branch.last_revision()
1413
 
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1414
 
            revno = branch.revision_id_to_revno(revision_id)
1415
 
            note("Tree is up to date at revision %d of branch %s" %
1416
 
                (revno, branch_location))
1417
 
            return 0
1418
 
        view_info = _get_view_info_for_change_reporter(tree)
1419
 
        change_reporter = delta._ChangeReporter(
1420
 
            unversioned_filter=tree.is_ignored,
1421
 
            view_info=view_info)
1422
1400
        try:
 
1401
            existing_pending_merges = tree.get_parent_ids()[1:]
 
1402
            last_rev = _mod_revision.ensure_null(tree.last_revision())
 
1403
            if last_rev == _mod_revision.ensure_null(
 
1404
                tree.branch.last_revision()):
 
1405
                # may be up to date, check master too.
 
1406
                if master is None or last_rev == _mod_revision.ensure_null(
 
1407
                    master.last_revision()):
 
1408
                    revno = tree.branch.revision_id_to_revno(last_rev)
 
1409
                    note("Tree is up to date at revision %d." % (revno,))
 
1410
                    return 0
 
1411
            view_info = _get_view_info_for_change_reporter(tree)
1423
1412
            conflicts = tree.update(
1424
 
                change_reporter,
1425
 
                possible_transports=possible_transports,
1426
 
                revision=revision_id,
1427
 
                old_tip=old_tip)
1428
 
        except errors.NoSuchRevision, e:
1429
 
            raise errors.BzrCommandError(
1430
 
                                  "branch has no revision %s\n"
1431
 
                                  "bzr update --revision only works"
1432
 
                                  " for a revision in the branch history"
1433
 
                                  % (e.revision))
1434
 
        revno = tree.branch.revision_id_to_revno(
1435
 
            _mod_revision.ensure_null(tree.last_revision()))
1436
 
        note('Updated to revision %d of branch %s' %
1437
 
             (revno, branch_location))
1438
 
        if tree.get_parent_ids()[1:] != existing_pending_merges:
1439
 
            note('Your local commits will now show as pending merges with '
1440
 
                 "'bzr status', and can be committed with 'bzr commit'.")
1441
 
        if conflicts != 0:
1442
 
            return 1
1443
 
        else:
1444
 
            return 0
 
1413
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
 
1414
                view_info=view_info), possible_transports=possible_transports)
 
1415
            revno = tree.branch.revision_id_to_revno(
 
1416
                _mod_revision.ensure_null(tree.last_revision()))
 
1417
            note('Updated to revision %d.' % (revno,))
 
1418
            if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1419
                note('Your local commits will now show as pending merges with '
 
1420
                     "'bzr status', and can be committed with 'bzr commit'.")
 
1421
            if conflicts != 0:
 
1422
                return 1
 
1423
            else:
 
1424
                return 0
 
1425
        finally:
 
1426
            tree.unlock()
1445
1427
 
1446
1428
 
1447
1429
class cmd_info(Command):
1518
1500
            file_list = [f for f in file_list]
1519
1501
 
1520
1502
        tree.lock_write()
1521
 
        self.add_cleanup(tree.unlock)
1522
 
        # Heuristics should probably all move into tree.remove_smart or
1523
 
        # some such?
1524
 
        if new:
1525
 
            added = tree.changes_from(tree.basis_tree(),
1526
 
                specific_files=file_list).added
1527
 
            file_list = sorted([f[0] for f in added], reverse=True)
1528
 
            if len(file_list) == 0:
1529
 
                raise errors.BzrCommandError('No matching files.')
1530
 
        elif file_list is None:
1531
 
            # missing files show up in iter_changes(basis) as
1532
 
            # versioned-with-no-kind.
1533
 
            missing = []
1534
 
            for change in tree.iter_changes(tree.basis_tree()):
1535
 
                # Find paths in the working tree that have no kind:
1536
 
                if change[1][1] is not None and change[6][1] is None:
1537
 
                    missing.append(change[1][1])
1538
 
            file_list = sorted(missing, reverse=True)
1539
 
            file_deletion_strategy = 'keep'
1540
 
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
1541
 
            keep_files=file_deletion_strategy=='keep',
1542
 
            force=file_deletion_strategy=='force')
 
1503
        try:
 
1504
            # Heuristics should probably all move into tree.remove_smart or
 
1505
            # some such?
 
1506
            if new:
 
1507
                added = tree.changes_from(tree.basis_tree(),
 
1508
                    specific_files=file_list).added
 
1509
                file_list = sorted([f[0] for f in added], reverse=True)
 
1510
                if len(file_list) == 0:
 
1511
                    raise errors.BzrCommandError('No matching files.')
 
1512
            elif file_list is None:
 
1513
                # missing files show up in iter_changes(basis) as
 
1514
                # versioned-with-no-kind.
 
1515
                missing = []
 
1516
                for change in tree.iter_changes(tree.basis_tree()):
 
1517
                    # Find paths in the working tree that have no kind:
 
1518
                    if change[1][1] is not None and change[6][1] is None:
 
1519
                        missing.append(change[1][1])
 
1520
                file_list = sorted(missing, reverse=True)
 
1521
                file_deletion_strategy = 'keep'
 
1522
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1523
                keep_files=file_deletion_strategy=='keep',
 
1524
                force=file_deletion_strategy=='force')
 
1525
        finally:
 
1526
            tree.unlock()
1543
1527
 
1544
1528
 
1545
1529
class cmd_file_id(Command):
1765
1749
 
1766
1750
 
1767
1751
class cmd_init_repository(Command):
1768
 
    """Create a shared repository for branches to share storage space.
 
1752
    """Create a shared repository to hold branches.
1769
1753
 
1770
1754
    New branches created under the repository directory will store their
1771
 
    revisions in the repository, not in the branch directory.  For branches
1772
 
    with shared history, this reduces the amount of storage needed and 
1773
 
    speeds up the creation of new branches.
 
1755
    revisions in the repository, not in the branch directory.
1774
1756
 
1775
 
    If the --no-trees option is given then the branches in the repository
1776
 
    will not have working trees by default.  They will still exist as 
1777
 
    directories on disk, but they will not have separate copies of the 
1778
 
    files at a certain revision.  This can be useful for repositories that
1779
 
    store branches which are interacted with through checkouts or remote
1780
 
    branches, such as on a server.
 
1757
    If the --no-trees option is used then the branches in the repository
 
1758
    will not have working trees by default.
1781
1759
 
1782
1760
    :Examples:
1783
 
        Create a shared repository holding just branches::
 
1761
        Create a shared repositories holding just branches::
1784
1762
 
1785
1763
            bzr init-repo --no-trees repo
1786
1764
            bzr init repo/trunk
1852
1830
 
1853
1831
            bzr diff -r1
1854
1832
 
1855
 
        Difference between revision 3 and revision 1::
1856
 
 
1857
 
            bzr diff -r1..3
1858
 
 
1859
 
        Difference between revision 3 and revision 1 for branch xxx::
1860
 
 
1861
 
            bzr diff -r1..3 xxx
1862
 
 
1863
 
        To see the changes introduced in revision X::
1864
 
        
1865
 
            bzr diff -cX
1866
 
 
1867
 
        Note that in the case of a merge, the -c option shows the changes
1868
 
        compared to the left hand parent. To see the changes against
1869
 
        another parent, use::
1870
 
 
1871
 
            bzr diff -r<chosen_parent>..X
1872
 
 
1873
 
        The changes introduced by revision 2 (equivalent to -r1..2)::
1874
 
 
1875
 
            bzr diff -c2
 
1833
        Difference between revision 2 and revision 1::
 
1834
 
 
1835
            bzr diff -r1..2
 
1836
 
 
1837
        Difference between revision 2 and revision 1 for branch xxx::
 
1838
 
 
1839
            bzr diff -r1..2 xxx
1876
1840
 
1877
1841
        Show just the differences for file NEWS::
1878
1842
 
1971
1935
    def run(self, show_ids=False):
1972
1936
        tree = WorkingTree.open_containing(u'.')[0]
1973
1937
        tree.lock_read()
1974
 
        self.add_cleanup(tree.unlock)
1975
 
        old = tree.basis_tree()
1976
 
        old.lock_read()
1977
 
        self.add_cleanup(old.unlock)
1978
 
        for path, ie in old.inventory.iter_entries():
1979
 
            if not tree.has_id(ie.file_id):
1980
 
                self.outf.write(path)
1981
 
                if show_ids:
1982
 
                    self.outf.write(' ')
1983
 
                    self.outf.write(ie.file_id)
1984
 
                self.outf.write('\n')
 
1938
        try:
 
1939
            old = tree.basis_tree()
 
1940
            old.lock_read()
 
1941
            try:
 
1942
                for path, ie in old.inventory.iter_entries():
 
1943
                    if not tree.has_id(ie.file_id):
 
1944
                        self.outf.write(path)
 
1945
                        if show_ids:
 
1946
                            self.outf.write(' ')
 
1947
                            self.outf.write(ie.file_id)
 
1948
                        self.outf.write('\n')
 
1949
            finally:
 
1950
                old.unlock()
 
1951
        finally:
 
1952
            tree.unlock()
1985
1953
 
1986
1954
 
1987
1955
class cmd_modified(Command):
2023
1991
    def run(self, null=False):
2024
1992
        wt = WorkingTree.open_containing(u'.')[0]
2025
1993
        wt.lock_read()
2026
 
        self.add_cleanup(wt.unlock)
2027
 
        basis = wt.basis_tree()
2028
 
        basis.lock_read()
2029
 
        self.add_cleanup(basis.unlock)
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')
 
1994
        try:
 
1995
            basis = wt.basis_tree()
 
1996
            basis.lock_read()
 
1997
            try:
 
1998
                basis_inv = basis.inventory
 
1999
                inv = wt.inventory
 
2000
                for file_id in inv:
 
2001
                    if file_id in basis_inv:
 
2002
                        continue
 
2003
                    if inv.is_root(file_id) and len(basis_inv) == 0:
 
2004
                        continue
 
2005
                    path = inv.id2path(file_id)
 
2006
                    if not os.access(osutils.abspath(path), os.F_OK):
 
2007
                        continue
 
2008
                    if null:
 
2009
                        self.outf.write(path + '\0')
 
2010
                    else:
 
2011
                        self.outf.write(osutils.quotefn(path) + '\n')
 
2012
            finally:
 
2013
                basis.unlock()
 
2014
        finally:
 
2015
            wt.unlock()
2044
2016
 
2045
2017
 
2046
2018
class cmd_root(Command):
2306
2278
        filter_by_dir = False
2307
2279
        if file_list:
2308
2280
            # find the file ids to log and check for directory filtering
2309
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2310
 
                revision, file_list)
2311
 
            self.add_cleanup(b.unlock)
 
2281
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
 
2282
                file_list)
2312
2283
            for relpath, file_id, kind in file_info_list:
2313
2284
                if file_id is None:
2314
2285
                    raise errors.BzrCommandError(
2332
2303
                location = '.'
2333
2304
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2334
2305
            b = dir.open_branch()
2335
 
            b.lock_read()
2336
 
            self.add_cleanup(b.unlock)
2337
2306
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2338
2307
 
2339
2308
        # Decide on the type of delta & diff filtering to use
2349
2318
        else:
2350
2319
            diff_type = 'full'
2351
2320
 
2352
 
        # Build the log formatter
2353
 
        if log_format is None:
2354
 
            log_format = log.log_formatter_registry.get_default(b)
2355
 
        # Make a non-encoding output to include the diffs - bug 328007
2356
 
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2357
 
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2358
 
                        to_exact_file=unencoded_output,
2359
 
                        show_timezone=timezone,
2360
 
                        delta_format=get_verbosity_level(),
2361
 
                        levels=levels,
2362
 
                        show_advice=levels is None)
2363
 
 
2364
 
        # Choose the algorithm for doing the logging. It's annoying
2365
 
        # having multiple code paths like this but necessary until
2366
 
        # the underlying repository format is faster at generating
2367
 
        # deltas or can provide everything we need from the indices.
2368
 
        # The default algorithm - match-using-deltas - works for
2369
 
        # multiple files and directories and is faster for small
2370
 
        # amounts of history (200 revisions say). However, it's too
2371
 
        # slow for logging a single file in a repository with deep
2372
 
        # history, i.e. > 10K revisions. In the spirit of "do no
2373
 
        # evil when adding features", we continue to use the
2374
 
        # original algorithm - per-file-graph - for the "single
2375
 
        # file that isn't a directory without showing a delta" case.
2376
 
        partial_history = revision and b.repository._format.supports_chks
2377
 
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2378
 
            or delta_type or partial_history)
2379
 
 
2380
 
        # Build the LogRequest and execute it
2381
 
        if len(file_ids) == 0:
2382
 
            file_ids = None
2383
 
        rqst = make_log_request_dict(
2384
 
            direction=direction, specific_fileids=file_ids,
2385
 
            start_revision=rev1, end_revision=rev2, limit=limit,
2386
 
            message_search=message, delta_type=delta_type,
2387
 
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
2388
 
        Logger(b, rqst).show(lf)
 
2321
        b.lock_read()
 
2322
        try:
 
2323
            # Build the log formatter
 
2324
            if log_format is None:
 
2325
                log_format = log.log_formatter_registry.get_default(b)
 
2326
            lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2327
                            show_timezone=timezone,
 
2328
                            delta_format=get_verbosity_level(),
 
2329
                            levels=levels,
 
2330
                            show_advice=levels is None)
 
2331
 
 
2332
            # Choose the algorithm for doing the logging. It's annoying
 
2333
            # having multiple code paths like this but necessary until
 
2334
            # the underlying repository format is faster at generating
 
2335
            # deltas or can provide everything we need from the indices.
 
2336
            # The default algorithm - match-using-deltas - works for
 
2337
            # multiple files and directories and is faster for small
 
2338
            # amounts of history (200 revisions say). However, it's too
 
2339
            # slow for logging a single file in a repository with deep
 
2340
            # history, i.e. > 10K revisions. In the spirit of "do no
 
2341
            # evil when adding features", we continue to use the
 
2342
            # original algorithm - per-file-graph - for the "single
 
2343
            # file that isn't a directory without showing a delta" case.
 
2344
            partial_history = revision and b.repository._format.supports_chks
 
2345
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2346
                or delta_type or partial_history)
 
2347
 
 
2348
            # Build the LogRequest and execute it
 
2349
            if len(file_ids) == 0:
 
2350
                file_ids = None
 
2351
            rqst = make_log_request_dict(
 
2352
                direction=direction, specific_fileids=file_ids,
 
2353
                start_revision=rev1, end_revision=rev2, limit=limit,
 
2354
                message_search=message, delta_type=delta_type,
 
2355
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2356
            Logger(b, rqst).show(lf)
 
2357
        finally:
 
2358
            b.unlock()
2389
2359
 
2390
2360
 
2391
2361
def _get_revision_range(revisionspec_list, branch, command_name):
2455
2425
    @display_command
2456
2426
    def run(self, filename):
2457
2427
        tree, relpath = WorkingTree.open_containing(filename)
 
2428
        b = tree.branch
2458
2429
        file_id = tree.path2id(relpath)
2459
 
        b = tree.branch
2460
 
        b.lock_read()
2461
 
        self.add_cleanup(b.unlock)
2462
 
        touching_revs = log.find_touching_revisions(b, file_id)
2463
 
        for revno, revision_id, what in touching_revs:
 
2430
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
2464
2431
            self.outf.write("%6d %s\n" % (revno, what))
2465
2432
 
2466
2433
 
2519
2486
        if from_root:
2520
2487
            if relpath:
2521
2488
                prefix = relpath + '/'
2522
 
        elif fs_path != '.' and not fs_path.endswith('/'):
 
2489
        elif fs_path != '.':
2523
2490
            prefix = fs_path + '/'
2524
2491
 
2525
2492
        if revision is not None or tree is None:
2534
2501
                note("Ignoring files outside view. View is %s" % view_str)
2535
2502
 
2536
2503
        tree.lock_read()
2537
 
        self.add_cleanup(tree.unlock)
2538
 
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2539
 
            from_dir=relpath, recursive=recursive):
2540
 
            # Apply additional masking
2541
 
            if not all and not selection[fc]:
2542
 
                continue
2543
 
            if kind is not None and fkind != kind:
2544
 
                continue
2545
 
            if apply_view:
2546
 
                try:
2547
 
                    if relpath:
2548
 
                        fullpath = osutils.pathjoin(relpath, fp)
2549
 
                    else:
2550
 
                        fullpath = fp
2551
 
                    views.check_path_in_view(tree, fullpath)
2552
 
                except errors.FileOutsideView:
2553
 
                    continue
 
2504
        try:
 
2505
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2506
                from_dir=relpath, recursive=recursive):
 
2507
                # Apply additional masking
 
2508
                if not all and not selection[fc]:
 
2509
                    continue
 
2510
                if kind is not None and fkind != kind:
 
2511
                    continue
 
2512
                if apply_view:
 
2513
                    try:
 
2514
                        if relpath:
 
2515
                            fullpath = osutils.pathjoin(relpath, fp)
 
2516
                        else:
 
2517
                            fullpath = fp
 
2518
                        views.check_path_in_view(tree, fullpath)
 
2519
                    except errors.FileOutsideView:
 
2520
                        continue
2554
2521
 
2555
 
            # Output the entry
2556
 
            if prefix:
2557
 
                fp = osutils.pathjoin(prefix, fp)
2558
 
            kindch = entry.kind_character()
2559
 
            outstring = fp + kindch
2560
 
            ui.ui_factory.clear_term()
2561
 
            if verbose:
2562
 
                outstring = '%-8s %s' % (fc, outstring)
2563
 
                if show_ids and fid is not None:
2564
 
                    outstring = "%-50s %s" % (outstring, fid)
2565
 
                self.outf.write(outstring + '\n')
2566
 
            elif null:
2567
 
                self.outf.write(fp + '\0')
2568
 
                if show_ids:
2569
 
                    if fid is not None:
2570
 
                        self.outf.write(fid)
2571
 
                    self.outf.write('\0')
2572
 
                self.outf.flush()
2573
 
            else:
2574
 
                if show_ids:
2575
 
                    if fid is not None:
2576
 
                        my_id = fid
2577
 
                    else:
2578
 
                        my_id = ''
2579
 
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
2580
 
                else:
 
2522
                # Output the entry
 
2523
                if prefix:
 
2524
                    fp = osutils.pathjoin(prefix, fp)
 
2525
                kindch = entry.kind_character()
 
2526
                outstring = fp + kindch
 
2527
                ui.ui_factory.clear_term()
 
2528
                if verbose:
 
2529
                    outstring = '%-8s %s' % (fc, outstring)
 
2530
                    if show_ids and fid is not None:
 
2531
                        outstring = "%-50s %s" % (outstring, fid)
2581
2532
                    self.outf.write(outstring + '\n')
 
2533
                elif null:
 
2534
                    self.outf.write(fp + '\0')
 
2535
                    if show_ids:
 
2536
                        if fid is not None:
 
2537
                            self.outf.write(fid)
 
2538
                        self.outf.write('\0')
 
2539
                    self.outf.flush()
 
2540
                else:
 
2541
                    if show_ids:
 
2542
                        if fid is not None:
 
2543
                            my_id = fid
 
2544
                        else:
 
2545
                            my_id = ''
 
2546
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2547
                    else:
 
2548
                        self.outf.write(outstring + '\n')
 
2549
        finally:
 
2550
            tree.unlock()
2582
2551
 
2583
2552
 
2584
2553
class cmd_unknowns(Command):
2599
2568
 
2600
2569
    See ``bzr help patterns`` for details on the syntax of patterns.
2601
2570
 
2602
 
    If a .bzrignore file does not exist, the ignore command
2603
 
    will create one and add the specified files or patterns to the newly
2604
 
    created file. The ignore command will also automatically add the 
2605
 
    .bzrignore file to be versioned. Creating a .bzrignore file without
2606
 
    the use of the ignore command will require an explicit add command.
2607
 
 
2608
2571
    To remove patterns from the ignore list, edit the .bzrignore file.
2609
2572
    After adding, editing or deleting that file either indirectly by
2610
2573
    using this command or directly by using an editor, be sure to commit
2696
2659
    def run(self):
2697
2660
        tree = WorkingTree.open_containing(u'.')[0]
2698
2661
        tree.lock_read()
2699
 
        self.add_cleanup(tree.unlock)
2700
 
        for path, file_class, kind, file_id, entry in tree.list_files():
2701
 
            if file_class != 'I':
2702
 
                continue
2703
 
            ## XXX: Slightly inefficient since this was already calculated
2704
 
            pat = tree.is_ignored(path)
2705
 
            self.outf.write('%-50s %s\n' % (path, pat))
 
2662
        try:
 
2663
            for path, file_class, kind, file_id, entry in tree.list_files():
 
2664
                if file_class != 'I':
 
2665
                    continue
 
2666
                ## XXX: Slightly inefficient since this was already calculated
 
2667
                pat = tree.is_ignored(path)
 
2668
                self.outf.write('%-50s %s\n' % (path, pat))
 
2669
        finally:
 
2670
            tree.unlock()
2706
2671
 
2707
2672
 
2708
2673
class cmd_lookup_revision(Command):
2811
2776
        tree, branch, relpath = \
2812
2777
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2813
2778
        branch.lock_read()
2814
 
        self.add_cleanup(branch.unlock)
2815
 
        return self._run(tree, branch, relpath, filename, revision,
2816
 
                         name_from_revision, filters)
 
2779
        try:
 
2780
            return self._run(tree, branch, relpath, filename, revision,
 
2781
                             name_from_revision, filters)
 
2782
        finally:
 
2783
            branch.unlock()
2817
2784
 
2818
2785
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2819
2786
        filtered):
2820
2787
        if tree is None:
2821
2788
            tree = b.basis_tree()
2822
2789
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2823
 
        rev_tree.lock_read()
2824
 
        self.add_cleanup(rev_tree.unlock)
2825
2790
 
2826
2791
        old_file_id = rev_tree.path2id(relpath)
2827
2792
 
2862
2827
            chunks = content.splitlines(True)
2863
2828
            content = filtered_output_bytes(chunks, filters,
2864
2829
                ContentFilterContext(relpath, rev_tree))
2865
 
            self.cleanup_now()
2866
2830
            self.outf.writelines(content)
2867
2831
        else:
2868
 
            self.cleanup_now()
2869
2832
            self.outf.write(content)
2870
2833
 
2871
2834
 
2978
2941
             Option('strict',
2979
2942
                    help="Refuse to commit if there are unknown "
2980
2943
                    "files in the working tree."),
2981
 
             Option('commit-time', type=str,
2982
 
                    help="Manually set a commit time using commit date "
2983
 
                    "format, e.g. '2009-10-10 08:00:00 +0100'."),
2984
2944
             ListOption('fixes', type=str,
2985
2945
                    help="Mark a bug as being fixed by this revision "
2986
2946
                         "(see \"bzr help bugs\")."),
2993
2953
                         "the master branch until a normal commit "
2994
2954
                         "is performed."
2995
2955
                    ),
2996
 
             Option('show-diff',
2997
 
                    help='When no message is supplied, show the diff along'
2998
 
                    ' with the status summary in the message editor.'),
 
2956
              Option('show-diff',
 
2957
                     help='When no message is supplied, show the diff along'
 
2958
                     ' with the status summary in the message editor.'),
2999
2959
             ]
3000
2960
    aliases = ['ci', 'checkin']
3001
2961
 
3020
2980
 
3021
2981
    def run(self, message=None, file=None, verbose=False, selected_list=None,
3022
2982
            unchanged=False, strict=False, local=False, fixes=None,
3023
 
            author=None, show_diff=False, exclude=None, commit_time=None):
 
2983
            author=None, show_diff=False, exclude=None):
3024
2984
        from bzrlib.errors import (
3025
2985
            PointlessCommit,
3026
2986
            ConflictsInTree,
3032
2992
            make_commit_message_template_encoded
3033
2993
        )
3034
2994
 
3035
 
        commit_stamp = offset = None
3036
 
        if commit_time is not None:
3037
 
            try:
3038
 
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
3039
 
            except ValueError, e:
3040
 
                raise errors.BzrCommandError(
3041
 
                    "Could not parse --commit-time: " + str(e))
3042
 
 
3043
2995
        # TODO: Need a blackbox test for invoking the external editor; may be
3044
2996
        # slightly problematic to run this cross-platform.
3045
2997
 
3065
3017
        if local and not tree.branch.get_bound_location():
3066
3018
            raise errors.LocalRequiresBoundBranch()
3067
3019
 
3068
 
        if message is not None:
3069
 
            try:
3070
 
                file_exists = osutils.lexists(message)
3071
 
            except UnicodeError:
3072
 
                # The commit message contains unicode characters that can't be
3073
 
                # represented in the filesystem encoding, so that can't be a
3074
 
                # file.
3075
 
                file_exists = False
3076
 
            if file_exists:
3077
 
                warning_msg = (
3078
 
                    'The commit message is a file name: "%(f)s".\n'
3079
 
                    '(use --file "%(f)s" to take commit message from that file)'
3080
 
                    % { 'f': message })
3081
 
                ui.ui_factory.show_warning(warning_msg)
3082
 
 
3083
3020
        def get_message(commit_obj):
3084
3021
            """Callback to get commit message"""
3085
3022
            my_message = message
3086
 
            if my_message is not None and '\r' in my_message:
3087
 
                my_message = my_message.replace('\r\n', '\n')
3088
 
                my_message = my_message.replace('\r', '\n')
3089
3023
            if my_message is None and not file:
3090
3024
                t = make_commit_message_template_encoded(tree,
3091
3025
                        selected_list, diff=show_diff,
3115
3049
                        specific_files=selected_list,
3116
3050
                        allow_pointless=unchanged, strict=strict, local=local,
3117
3051
                        reporter=None, verbose=verbose, revprops=properties,
3118
 
                        authors=author, timestamp=commit_stamp,
3119
 
                        timezone=offset,
 
3052
                        authors=author,
3120
3053
                        exclude=safe_relpath_files(tree, exclude))
3121
3054
        except PointlessCommit:
3122
3055
            # FIXME: This should really happen before the file is read in;
3532
3465
            verbose = not is_quiet()
3533
3466
            # TODO: should possibly lock the history file...
3534
3467
            benchfile = open(".perf_history", "at", buffering=1)
3535
 
            self.add_cleanup(benchfile.close)
3536
3468
        else:
3537
3469
            test_suite_factory = None
3538
3470
            benchfile = None
3539
 
        selftest_kwargs = {"verbose": verbose,
3540
 
                          "pattern": pattern,
3541
 
                          "stop_on_failure": one,
3542
 
                          "transport": transport,
3543
 
                          "test_suite_factory": test_suite_factory,
3544
 
                          "lsprof_timed": lsprof_timed,
3545
 
                          "lsprof_tests": lsprof_tests,
3546
 
                          "bench_history": benchfile,
3547
 
                          "matching_tests_first": first,
3548
 
                          "list_only": list_only,
3549
 
                          "random_seed": randomize,
3550
 
                          "exclude_pattern": exclude,
3551
 
                          "strict": strict,
3552
 
                          "load_list": load_list,
3553
 
                          "debug_flags": debugflag,
3554
 
                          "starting_with": starting_with
3555
 
                          }
3556
 
        selftest_kwargs.update(self.additional_selftest_args)
3557
 
        result = selftest(**selftest_kwargs)
 
3471
        try:
 
3472
            selftest_kwargs = {"verbose": verbose,
 
3473
                              "pattern": pattern,
 
3474
                              "stop_on_failure": one,
 
3475
                              "transport": transport,
 
3476
                              "test_suite_factory": test_suite_factory,
 
3477
                              "lsprof_timed": lsprof_timed,
 
3478
                              "lsprof_tests": lsprof_tests,
 
3479
                              "bench_history": benchfile,
 
3480
                              "matching_tests_first": first,
 
3481
                              "list_only": list_only,
 
3482
                              "random_seed": randomize,
 
3483
                              "exclude_pattern": exclude,
 
3484
                              "strict": strict,
 
3485
                              "load_list": load_list,
 
3486
                              "debug_flags": debugflag,
 
3487
                              "starting_with": starting_with
 
3488
                              }
 
3489
            selftest_kwargs.update(self.additional_selftest_args)
 
3490
            result = selftest(**selftest_kwargs)
 
3491
        finally:
 
3492
            if benchfile is not None:
 
3493
                benchfile.close()
3558
3494
        return int(not result)
3559
3495
 
3560
3496
 
3599
3535
        branch1 = Branch.open_containing(branch)[0]
3600
3536
        branch2 = Branch.open_containing(other)[0]
3601
3537
        branch1.lock_read()
3602
 
        self.add_cleanup(branch1.unlock)
3603
 
        branch2.lock_read()
3604
 
        self.add_cleanup(branch2.unlock)
3605
 
        last1 = ensure_null(branch1.last_revision())
3606
 
        last2 = ensure_null(branch2.last_revision())
3607
 
 
3608
 
        graph = branch1.repository.get_graph(branch2.repository)
3609
 
        base_rev_id = graph.find_unique_lca(last1, last2)
3610
 
 
3611
 
        print 'merge base is revision %s' % base_rev_id
 
3538
        try:
 
3539
            branch2.lock_read()
 
3540
            try:
 
3541
                last1 = ensure_null(branch1.last_revision())
 
3542
                last2 = ensure_null(branch2.last_revision())
 
3543
 
 
3544
                graph = branch1.repository.get_graph(branch2.repository)
 
3545
                base_rev_id = graph.find_unique_lca(last1, last2)
 
3546
 
 
3547
                print 'merge base is revision %s' % base_rev_id
 
3548
            finally:
 
3549
                branch2.unlock()
 
3550
        finally:
 
3551
            branch1.unlock()
3612
3552
 
3613
3553
 
3614
3554
class cmd_merge(Command):
3665
3605
 
3666
3606
            bzr merge -r 81..82 ../bzr.dev
3667
3607
 
3668
 
        To apply a merge directive contained in /tmp/merge::
 
3608
        To apply a merge directive contained in /tmp/merge:
3669
3609
 
3670
3610
            bzr merge /tmp/merge
3671
3611
    """
3731
3671
        view_info = _get_view_info_for_change_reporter(tree)
3732
3672
        change_reporter = delta._ChangeReporter(
3733
3673
            unversioned_filter=tree.is_ignored, view_info=view_info)
3734
 
        pb = ui.ui_factory.nested_progress_bar()
3735
 
        self.add_cleanup(pb.finished)
3736
 
        tree.lock_write()
3737
 
        self.add_cleanup(tree.unlock)
3738
 
        if location is not None:
3739
 
            try:
3740
 
                mergeable = bundle.read_mergeable_from_url(location,
3741
 
                    possible_transports=possible_transports)
3742
 
            except errors.NotABundle:
3743
 
                mergeable = None
 
3674
        cleanups = []
 
3675
        try:
 
3676
            pb = ui.ui_factory.nested_progress_bar()
 
3677
            cleanups.append(pb.finished)
 
3678
            tree.lock_write()
 
3679
            cleanups.append(tree.unlock)
 
3680
            if location is not None:
 
3681
                try:
 
3682
                    mergeable = bundle.read_mergeable_from_url(location,
 
3683
                        possible_transports=possible_transports)
 
3684
                except errors.NotABundle:
 
3685
                    mergeable = None
 
3686
                else:
 
3687
                    if uncommitted:
 
3688
                        raise errors.BzrCommandError('Cannot use --uncommitted'
 
3689
                            ' with bundles or merge directives.')
 
3690
 
 
3691
                    if revision is not None:
 
3692
                        raise errors.BzrCommandError(
 
3693
                            'Cannot use -r with merge directives or bundles')
 
3694
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3695
                       mergeable, pb)
 
3696
 
 
3697
            if merger is None and uncommitted:
 
3698
                if revision is not None and len(revision) > 0:
 
3699
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3700
                        ' --revision at the same time.')
 
3701
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3702
                                                          cleanups)
 
3703
                allow_pending = False
 
3704
 
 
3705
            if merger is None:
 
3706
                merger, allow_pending = self._get_merger_from_branch(tree,
 
3707
                    location, revision, remember, possible_transports, pb)
 
3708
 
 
3709
            merger.merge_type = merge_type
 
3710
            merger.reprocess = reprocess
 
3711
            merger.show_base = show_base
 
3712
            self.sanity_check_merger(merger)
 
3713
            if (merger.base_rev_id == merger.other_rev_id and
 
3714
                merger.other_rev_id is not None):
 
3715
                note('Nothing to do.')
 
3716
                return 0
 
3717
            if pull:
 
3718
                if merger.interesting_files is not None:
 
3719
                    raise errors.BzrCommandError('Cannot pull individual files')
 
3720
                if (merger.base_rev_id == tree.last_revision()):
 
3721
                    result = tree.pull(merger.other_branch, False,
 
3722
                                       merger.other_rev_id)
 
3723
                    result.report(self.outf)
 
3724
                    return 0
 
3725
            if merger.this_basis is None:
 
3726
                raise errors.BzrCommandError(
 
3727
                    "This branch has no commits."
 
3728
                    " (perhaps you would prefer 'bzr pull')")
 
3729
            if preview:
 
3730
                return self._do_preview(merger, cleanups)
 
3731
            elif interactive:
 
3732
                return self._do_interactive(merger, cleanups)
3744
3733
            else:
3745
 
                if uncommitted:
3746
 
                    raise errors.BzrCommandError('Cannot use --uncommitted'
3747
 
                        ' with bundles or merge directives.')
3748
 
 
3749
 
                if revision is not None:
3750
 
                    raise errors.BzrCommandError(
3751
 
                        'Cannot use -r with merge directives or bundles')
3752
 
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
3753
 
                   mergeable, pb)
3754
 
 
3755
 
        if merger is None and uncommitted:
3756
 
            if revision is not None and len(revision) > 0:
3757
 
                raise errors.BzrCommandError('Cannot use --uncommitted and'
3758
 
                    ' --revision at the same time.')
3759
 
            merger = self.get_merger_from_uncommitted(tree, location, pb)
3760
 
            allow_pending = False
3761
 
 
3762
 
        if merger is None:
3763
 
            merger, allow_pending = self._get_merger_from_branch(tree,
3764
 
                location, revision, remember, possible_transports, pb)
3765
 
 
3766
 
        merger.merge_type = merge_type
3767
 
        merger.reprocess = reprocess
3768
 
        merger.show_base = show_base
3769
 
        self.sanity_check_merger(merger)
3770
 
        if (merger.base_rev_id == merger.other_rev_id and
3771
 
            merger.other_rev_id is not None):
3772
 
            note('Nothing to do.')
3773
 
            return 0
3774
 
        if pull:
3775
 
            if merger.interesting_files is not None:
3776
 
                raise errors.BzrCommandError('Cannot pull individual files')
3777
 
            if (merger.base_rev_id == tree.last_revision()):
3778
 
                result = tree.pull(merger.other_branch, False,
3779
 
                                   merger.other_rev_id)
3780
 
                result.report(self.outf)
3781
 
                return 0
3782
 
        if merger.this_basis is None:
3783
 
            raise errors.BzrCommandError(
3784
 
                "This branch has no commits."
3785
 
                " (perhaps you would prefer 'bzr pull')")
3786
 
        if preview:
3787
 
            return self._do_preview(merger)
3788
 
        elif interactive:
3789
 
            return self._do_interactive(merger)
3790
 
        else:
3791
 
            return self._do_merge(merger, change_reporter, allow_pending,
3792
 
                                  verified)
3793
 
 
3794
 
    def _get_preview(self, merger):
 
3734
                return self._do_merge(merger, change_reporter, allow_pending,
 
3735
                                      verified)
 
3736
        finally:
 
3737
            for cleanup in reversed(cleanups):
 
3738
                cleanup()
 
3739
 
 
3740
    def _get_preview(self, merger, cleanups):
3795
3741
        tree_merger = merger.make_merger()
3796
3742
        tt = tree_merger.make_preview_transform()
3797
 
        self.add_cleanup(tt.finalize)
 
3743
        cleanups.append(tt.finalize)
3798
3744
        result_tree = tt.get_preview_tree()
3799
3745
        return result_tree
3800
3746
 
3801
 
    def _do_preview(self, merger):
 
3747
    def _do_preview(self, merger, cleanups):
3802
3748
        from bzrlib.diff import show_diff_trees
3803
 
        result_tree = self._get_preview(merger)
 
3749
        result_tree = self._get_preview(merger, cleanups)
3804
3750
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3805
3751
                        old_label='', new_label='')
3806
3752
 
3816
3762
        else:
3817
3763
            return 0
3818
3764
 
3819
 
    def _do_interactive(self, merger):
 
3765
    def _do_interactive(self, merger, cleanups):
3820
3766
        """Perform an interactive merge.
3821
3767
 
3822
3768
        This works by generating a preview tree of the merge, then using
3824
3770
        and the preview tree.
3825
3771
        """
3826
3772
        from bzrlib import shelf_ui
3827
 
        result_tree = self._get_preview(merger)
 
3773
        result_tree = self._get_preview(merger, cleanups)
3828
3774
        writer = bzrlib.option.diff_writer_registry.get()
3829
3775
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3830
3776
                                   reporter=shelf_ui.ApplyReporter(),
3831
3777
                                   diff_writer=writer(sys.stdout))
3832
 
        try:
3833
 
            shelver.run()
3834
 
        finally:
3835
 
            shelver.finalize()
 
3778
        shelver.run()
3836
3779
 
3837
3780
    def sanity_check_merger(self, merger):
3838
3781
        if (merger.show_base and
3898
3841
            allow_pending = True
3899
3842
        return merger, allow_pending
3900
3843
 
3901
 
    def get_merger_from_uncommitted(self, tree, location, pb):
 
3844
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
3902
3845
        """Get a merger for uncommitted changes.
3903
3846
 
3904
3847
        :param tree: The tree the merger should apply to.
3905
3848
        :param location: The location containing uncommitted changes.
3906
3849
        :param pb: The progress bar to use for showing progress.
 
3850
        :param cleanups: A list of operations to perform to clean up the
 
3851
            temporary directories, unfinalized objects, etc.
3907
3852
        """
3908
3853
        location = self._select_branch_location(tree, location)[0]
3909
3854
        other_tree, other_path = WorkingTree.open_containing(location)
3996
3941
            merge_type = _mod_merge.Merge3Merger
3997
3942
        tree, file_list = tree_files(file_list)
3998
3943
        tree.lock_write()
3999
 
        self.add_cleanup(tree.unlock)
4000
 
        parents = tree.get_parent_ids()
4001
 
        if len(parents) != 2:
4002
 
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
4003
 
                                         " merges.  Not cherrypicking or"
4004
 
                                         " multi-merges.")
4005
 
        repository = tree.branch.repository
4006
 
        interesting_ids = None
4007
 
        new_conflicts = []
4008
 
        conflicts = tree.conflicts()
4009
 
        if file_list is not None:
4010
 
            interesting_ids = set()
4011
 
            for filename in file_list:
4012
 
                file_id = tree.path2id(filename)
4013
 
                if file_id is None:
4014
 
                    raise errors.NotVersionedError(filename)
4015
 
                interesting_ids.add(file_id)
4016
 
                if tree.kind(file_id) != "directory":
4017
 
                    continue
 
3944
        try:
 
3945
            parents = tree.get_parent_ids()
 
3946
            if len(parents) != 2:
 
3947
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
3948
                                             " merges.  Not cherrypicking or"
 
3949
                                             " multi-merges.")
 
3950
            repository = tree.branch.repository
 
3951
            interesting_ids = None
 
3952
            new_conflicts = []
 
3953
            conflicts = tree.conflicts()
 
3954
            if file_list is not None:
 
3955
                interesting_ids = set()
 
3956
                for filename in file_list:
 
3957
                    file_id = tree.path2id(filename)
 
3958
                    if file_id is None:
 
3959
                        raise errors.NotVersionedError(filename)
 
3960
                    interesting_ids.add(file_id)
 
3961
                    if tree.kind(file_id) != "directory":
 
3962
                        continue
4018
3963
 
4019
 
                for name, ie in tree.inventory.iter_entries(file_id):
4020
 
                    interesting_ids.add(ie.file_id)
4021
 
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4022
 
        else:
4023
 
            # Remerge only supports resolving contents conflicts
4024
 
            allowed_conflicts = ('text conflict', 'contents conflict')
4025
 
            restore_files = [c.path for c in conflicts
4026
 
                             if c.typestring in allowed_conflicts]
4027
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4028
 
        tree.set_conflicts(ConflictList(new_conflicts))
4029
 
        if file_list is not None:
4030
 
            restore_files = file_list
4031
 
        for filename in restore_files:
 
3964
                    for name, ie in tree.inventory.iter_entries(file_id):
 
3965
                        interesting_ids.add(ie.file_id)
 
3966
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
3967
            else:
 
3968
                # Remerge only supports resolving contents conflicts
 
3969
                allowed_conflicts = ('text conflict', 'contents conflict')
 
3970
                restore_files = [c.path for c in conflicts
 
3971
                                 if c.typestring in allowed_conflicts]
 
3972
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
3973
            tree.set_conflicts(ConflictList(new_conflicts))
 
3974
            if file_list is not None:
 
3975
                restore_files = file_list
 
3976
            for filename in restore_files:
 
3977
                try:
 
3978
                    restore(tree.abspath(filename))
 
3979
                except errors.NotConflicted:
 
3980
                    pass
 
3981
            # Disable pending merges, because the file texts we are remerging
 
3982
            # have not had those merges performed.  If we use the wrong parents
 
3983
            # list, we imply that the working tree text has seen and rejected
 
3984
            # all the changes from the other tree, when in fact those changes
 
3985
            # have not yet been seen.
 
3986
            pb = ui.ui_factory.nested_progress_bar()
 
3987
            tree.set_parent_ids(parents[:1])
4032
3988
            try:
4033
 
                restore(tree.abspath(filename))
4034
 
            except errors.NotConflicted:
4035
 
                pass
4036
 
        # Disable pending merges, because the file texts we are remerging
4037
 
        # have not had those merges performed.  If we use the wrong parents
4038
 
        # list, we imply that the working tree text has seen and rejected
4039
 
        # all the changes from the other tree, when in fact those changes
4040
 
        # have not yet been seen.
4041
 
        pb = ui.ui_factory.nested_progress_bar()
4042
 
        tree.set_parent_ids(parents[:1])
4043
 
        try:
4044
 
            merger = _mod_merge.Merger.from_revision_ids(pb,
4045
 
                                                         tree, parents[1])
4046
 
            merger.interesting_ids = interesting_ids
4047
 
            merger.merge_type = merge_type
4048
 
            merger.show_base = show_base
4049
 
            merger.reprocess = reprocess
4050
 
            conflicts = merger.do_merge()
 
3989
                merger = _mod_merge.Merger.from_revision_ids(pb,
 
3990
                                                             tree, parents[1])
 
3991
                merger.interesting_ids = interesting_ids
 
3992
                merger.merge_type = merge_type
 
3993
                merger.show_base = show_base
 
3994
                merger.reprocess = reprocess
 
3995
                conflicts = merger.do_merge()
 
3996
            finally:
 
3997
                tree.set_parent_ids(parents)
 
3998
                pb.finished()
4051
3999
        finally:
4052
 
            tree.set_parent_ids(parents)
4053
 
            pb.finished()
 
4000
            tree.unlock()
4054
4001
        if conflicts > 0:
4055
4002
            return 1
4056
4003
        else:
4078
4025
    name.  If you name a directory, all the contents of that directory will be
4079
4026
    reverted.
4080
4027
 
4081
 
    If you have newly added files since the target revision, they will be
4082
 
    removed.  If the files to be removed have been changed, backups will be
4083
 
    created as above.  Directories containing unknown files will not be
4084
 
    deleted.
 
4028
    Any files that have been newly added since that revision will be deleted,
 
4029
    with a backup kept if appropriate.  Directories containing unknown files
 
4030
    will not be deleted.
4085
4031
 
4086
4032
    The working tree contains a list of pending merged revisions, which will
4087
4033
    be included as parents in the next commit.  Normally, revert clears that
4090
4036
    revert ." in the tree root to revert all files but keep the merge record,
4091
4037
    and "bzr revert --forget-merges" to clear the pending merge list without
4092
4038
    reverting any files.
4093
 
 
4094
 
    Using "bzr revert --forget-merges", it is possible to apply the changes
4095
 
    from an arbitrary merge as a single revision.  To do this, perform the
4096
 
    merge as desired.  Then doing revert with the "--forget-merges" option will
4097
 
    keep the content of the tree as it was, but it will clear the list of
4098
 
    pending merges.  The next commit will then contain all of the changes that
4099
 
    would have been in the merge, but without any mention of the other parent
4100
 
    revisions.  Because this technique forgets where these changes originated,
4101
 
    it may cause additional conflicts on later merges involving the source and
4102
 
    target branches.
4103
4039
    """
4104
4040
 
4105
4041
    _see_also = ['cat', 'export']
4115
4051
            forget_merges=None):
4116
4052
        tree, file_list = tree_files(file_list)
4117
4053
        tree.lock_write()
4118
 
        self.add_cleanup(tree.unlock)
4119
 
        if forget_merges:
4120
 
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4121
 
        else:
4122
 
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4054
        try:
 
4055
            if forget_merges:
 
4056
                tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4057
            else:
 
4058
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4059
        finally:
 
4060
            tree.unlock()
4123
4061
 
4124
4062
    @staticmethod
4125
4063
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4184
4122
    To filter on a range of revisions, you can use the command -r begin..end
4185
4123
    -r revision requests a specific revision, -r ..end or -r begin.. are
4186
4124
    also valid.
4187
 
            
4188
 
    :Exit values:
4189
 
        1 - some missing revisions
4190
 
        0 - no missing revisions
4191
4125
 
4192
4126
    :Examples:
4193
4127
 
4276
4210
        if remote_branch.base == local_branch.base:
4277
4211
            remote_branch = local_branch
4278
4212
 
4279
 
        local_branch.lock_read()
4280
 
        self.add_cleanup(local_branch.unlock)
4281
4213
        local_revid_range = _revision_range_to_revid_range(
4282
4214
            _get_revision_range(my_revision, local_branch,
4283
4215
                self.name()))
4284
4216
 
4285
 
        remote_branch.lock_read()
4286
 
        self.add_cleanup(remote_branch.unlock)
4287
4217
        remote_revid_range = _revision_range_to_revid_range(
4288
4218
            _get_revision_range(revision,
4289
4219
                remote_branch, self.name()))
4290
4220
 
4291
 
        local_extra, remote_extra = find_unmerged(
4292
 
            local_branch, remote_branch, restrict,
4293
 
            backward=not reverse,
4294
 
            include_merges=include_merges,
4295
 
            local_revid_range=local_revid_range,
4296
 
            remote_revid_range=remote_revid_range)
4297
 
 
4298
 
        if log_format is None:
4299
 
            registry = log.log_formatter_registry
4300
 
            log_format = registry.get_default(local_branch)
4301
 
        lf = log_format(to_file=self.outf,
4302
 
                        show_ids=show_ids,
4303
 
                        show_timezone='original')
4304
 
 
4305
 
        status_code = 0
4306
 
        if local_extra and not theirs_only:
4307
 
            message("You have %d extra revision(s):\n" %
4308
 
                len(local_extra))
4309
 
            for revision in iter_log_revisions(local_extra,
4310
 
                                local_branch.repository,
4311
 
                                verbose):
4312
 
                lf.log_revision(revision)
4313
 
            printed_local = True
4314
 
            status_code = 1
4315
 
        else:
4316
 
            printed_local = False
4317
 
 
4318
 
        if remote_extra and not mine_only:
4319
 
            if printed_local is True:
4320
 
                message("\n\n\n")
4321
 
            message("You are missing %d revision(s):\n" %
4322
 
                len(remote_extra))
4323
 
            for revision in iter_log_revisions(remote_extra,
4324
 
                                remote_branch.repository,
4325
 
                                verbose):
4326
 
                lf.log_revision(revision)
4327
 
            status_code = 1
4328
 
 
4329
 
        if mine_only and not local_extra:
4330
 
            # We checked local, and found nothing extra
4331
 
            message('This branch is up to date.\n')
4332
 
        elif theirs_only and not remote_extra:
4333
 
            # We checked remote, and found nothing extra
4334
 
            message('Other branch is up to date.\n')
4335
 
        elif not (mine_only or theirs_only or local_extra or
4336
 
                  remote_extra):
4337
 
            # We checked both branches, and neither one had extra
4338
 
            # revisions
4339
 
            message("Branches are up to date.\n")
4340
 
        self.cleanup_now()
 
4221
        local_branch.lock_read()
 
4222
        try:
 
4223
            remote_branch.lock_read()
 
4224
            try:
 
4225
                local_extra, remote_extra = find_unmerged(
 
4226
                    local_branch, remote_branch, restrict,
 
4227
                    backward=not reverse,
 
4228
                    include_merges=include_merges,
 
4229
                    local_revid_range=local_revid_range,
 
4230
                    remote_revid_range=remote_revid_range)
 
4231
 
 
4232
                if log_format is None:
 
4233
                    registry = log.log_formatter_registry
 
4234
                    log_format = registry.get_default(local_branch)
 
4235
                lf = log_format(to_file=self.outf,
 
4236
                                show_ids=show_ids,
 
4237
                                show_timezone='original')
 
4238
 
 
4239
                status_code = 0
 
4240
                if local_extra and not theirs_only:
 
4241
                    message("You have %d extra revision(s):\n" %
 
4242
                        len(local_extra))
 
4243
                    for revision in iter_log_revisions(local_extra,
 
4244
                                        local_branch.repository,
 
4245
                                        verbose):
 
4246
                        lf.log_revision(revision)
 
4247
                    printed_local = True
 
4248
                    status_code = 1
 
4249
                else:
 
4250
                    printed_local = False
 
4251
 
 
4252
                if remote_extra and not mine_only:
 
4253
                    if printed_local is True:
 
4254
                        message("\n\n\n")
 
4255
                    message("You are missing %d revision(s):\n" %
 
4256
                        len(remote_extra))
 
4257
                    for revision in iter_log_revisions(remote_extra,
 
4258
                                        remote_branch.repository,
 
4259
                                        verbose):
 
4260
                        lf.log_revision(revision)
 
4261
                    status_code = 1
 
4262
 
 
4263
                if mine_only and not local_extra:
 
4264
                    # We checked local, and found nothing extra
 
4265
                    message('This branch is up to date.\n')
 
4266
                elif theirs_only and not remote_extra:
 
4267
                    # We checked remote, and found nothing extra
 
4268
                    message('Other branch is up to date.\n')
 
4269
                elif not (mine_only or theirs_only or local_extra or
 
4270
                          remote_extra):
 
4271
                    # We checked both branches, and neither one had extra
 
4272
                    # revisions
 
4273
                    message("Branches are up to date.\n")
 
4274
            finally:
 
4275
                remote_branch.unlock()
 
4276
        finally:
 
4277
            local_branch.unlock()
4341
4278
        if not status_code and parent is None and other_branch is not None:
4342
4279
            local_branch.lock_write()
4343
 
            self.add_cleanup(local_branch.unlock)
4344
 
            # handle race conditions - a parent might be set while we run.
4345
 
            if local_branch.get_parent() is None:
4346
 
                local_branch.set_parent(remote_branch.base)
 
4280
            try:
 
4281
                # handle race conditions - a parent might be set while we run.
 
4282
                if local_branch.get_parent() is None:
 
4283
                    local_branch.set_parent(remote_branch.base)
 
4284
            finally:
 
4285
                local_branch.unlock()
4347
4286
        return status_code
4348
4287
 
4349
4288
 
4428
4367
        else:
4429
4368
            b = Branch.open(branch)
4430
4369
        b.lock_read()
4431
 
        self.add_cleanup(b.unlock)
4432
 
        if revision is None:
4433
 
            rev_id = b.last_revision()
4434
 
        else:
4435
 
            rev_id = revision[0].as_revision_id(b)
4436
 
        t = testament_class.from_revision(b.repository, rev_id)
4437
 
        if long:
4438
 
            sys.stdout.writelines(t.as_text_lines())
4439
 
        else:
4440
 
            sys.stdout.write(t.as_short_text())
 
4370
        try:
 
4371
            if revision is None:
 
4372
                rev_id = b.last_revision()
 
4373
            else:
 
4374
                rev_id = revision[0].as_revision_id(b)
 
4375
            t = testament_class.from_revision(b.repository, rev_id)
 
4376
            if long:
 
4377
                sys.stdout.writelines(t.as_text_lines())
 
4378
            else:
 
4379
                sys.stdout.write(t.as_short_text())
 
4380
        finally:
 
4381
            b.unlock()
4441
4382
 
4442
4383
 
4443
4384
class cmd_annotate(Command):
4469
4410
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4470
4411
        if wt is not None:
4471
4412
            wt.lock_read()
4472
 
            self.add_cleanup(wt.unlock)
4473
4413
        else:
4474
4414
            branch.lock_read()
4475
 
            self.add_cleanup(branch.unlock)
4476
 
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
4477
 
        tree.lock_read()
4478
 
        self.add_cleanup(tree.unlock)
4479
 
        if wt is not None:
4480
 
            file_id = wt.path2id(relpath)
4481
 
        else:
4482
 
            file_id = tree.path2id(relpath)
4483
 
        if file_id is None:
4484
 
            raise errors.NotVersionedError(filename)
4485
 
        file_version = tree.inventory[file_id].revision
4486
 
        if wt is not None and revision is None:
4487
 
            # If there is a tree and we're not annotating historical
4488
 
            # versions, annotate the working tree's content.
4489
 
            annotate_file_tree(wt, file_id, self.outf, long, all,
4490
 
                show_ids=show_ids)
4491
 
        else:
4492
 
            annotate_file(branch, file_version, file_id, long, all, self.outf,
4493
 
                          show_ids=show_ids)
 
4415
        try:
 
4416
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4417
            if wt is not None:
 
4418
                file_id = wt.path2id(relpath)
 
4419
            else:
 
4420
                file_id = tree.path2id(relpath)
 
4421
            if file_id is None:
 
4422
                raise errors.NotVersionedError(filename)
 
4423
            file_version = tree.inventory[file_id].revision
 
4424
            if wt is not None and revision is None:
 
4425
                # If there is a tree and we're not annotating historical
 
4426
                # versions, annotate the working tree's content.
 
4427
                annotate_file_tree(wt, file_id, self.outf, long, all,
 
4428
                    show_ids=show_ids)
 
4429
            else:
 
4430
                annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4431
                              show_ids=show_ids)
 
4432
        finally:
 
4433
            if wt is not None:
 
4434
                wt.unlock()
 
4435
            else:
 
4436
                branch.unlock()
4494
4437
 
4495
4438
 
4496
4439
class cmd_re_sign(Command):
4508
4451
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4509
4452
        b = WorkingTree.open_containing(u'.')[0].branch
4510
4453
        b.lock_write()
4511
 
        self.add_cleanup(b.unlock)
4512
 
        return self._run(b, revision_id_list, revision)
 
4454
        try:
 
4455
            return self._run(b, revision_id_list, revision)
 
4456
        finally:
 
4457
            b.unlock()
4513
4458
 
4514
4459
    def _run(self, b, revision_id_list, revision):
4515
4460
        import bzrlib.gpg as gpg
4566
4511
    before they will be applied to the local branch.
4567
4512
 
4568
4513
    Bound branches use the nickname of its master branch unless it is set
4569
 
    locally, in which case binding will update the local nickname to be
 
4514
    locally, in which case binding will update the the local nickname to be
4570
4515
    that of the master.
4571
4516
    """
4572
4517
 
4661
4606
 
4662
4607
        if tree is not None:
4663
4608
            tree.lock_write()
4664
 
            self.add_cleanup(tree.unlock)
4665
4609
        else:
4666
4610
            b.lock_write()
4667
 
            self.add_cleanup(b.unlock)
4668
 
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
 
4611
        try:
 
4612
            return self._run(b, tree, dry_run, verbose, revision, force,
 
4613
                             local=local)
 
4614
        finally:
 
4615
            if tree is not None:
 
4616
                tree.unlock()
 
4617
            else:
 
4618
                b.unlock()
4669
4619
 
4670
4620
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4671
4621
        from bzrlib.log import log_formatter, show_log
4728
4678
    CAUTION: Locks should only be broken when you are sure that the process
4729
4679
    holding the lock has been stopped.
4730
4680
 
4731
 
    You can get information on what locks are open via the 'bzr info
4732
 
    [location]' command.
 
4681
    You can get information on what locks are open via the 'bzr info' command.
4733
4682
 
4734
4683
    :Examples:
4735
4684
        bzr break-lock
4736
 
        bzr break-lock bzr+ssh://example.com/bzr/foo
4737
4685
    """
4738
4686
    takes_args = ['location?']
4739
4687
 
4769
4717
    takes_options = [
4770
4718
        Option('inet',
4771
4719
               help='Serve on stdin/out for use from inetd or sshd.'),
4772
 
        RegistryOption('protocol',
4773
 
               help="Protocol to serve.",
 
4720
        RegistryOption('protocol', 
 
4721
               help="Protocol to serve.", 
4774
4722
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
4775
4723
               value_switches=True),
4776
4724
        Option('port',
4785
4733
        Option('allow-writes',
4786
4734
               help='By default the server is a readonly server.  Supplying '
4787
4735
                    '--allow-writes enables write access to the contents of '
4788
 
                    'the served directory and below.  Note that ``bzr serve`` '
4789
 
                    'does not perform authentication, so unless some form of '
4790
 
                    'external authentication is arranged supplying this '
4791
 
                    'option leads to global uncontrolled write access to your '
4792
 
                    'file system.'
 
4736
                    'the served directory and below.'
4793
4737
                ),
4794
4738
        ]
4795
4739
 
5011
4955
      directly from the merge directive, without retrieving data from a
5012
4956
      branch.
5013
4957
 
5014
 
    `bzr send` creates a compact data set that, when applied using bzr
5015
 
    merge, has the same effect as merging from the source branch.  
5016
 
    
5017
 
    By default the merge directive is self-contained and can be applied to any
5018
 
    branch containing submit_branch in its ancestory without needing access to
5019
 
    the source branch.
5020
 
    
5021
 
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
5022
 
    revisions, but only a structured request to merge from the
5023
 
    public_location.  In that case the public_branch is needed and it must be
5024
 
    up-to-date and accessible to the recipient.  The public_branch is always
5025
 
    included if known, so that people can check it later.
5026
 
 
5027
 
    The submit branch defaults to the parent of the source branch, but can be
5028
 
    overridden.  Both submit branch and public branch will be remembered in
5029
 
    branch.conf the first time they are used for a particular branch.  The
5030
 
    source branch defaults to that containing the working directory, but can
5031
 
    be changed using --from.
5032
 
 
5033
 
    In order to calculate those changes, bzr must analyse the submit branch.
5034
 
    Therefore it is most efficient for the submit branch to be a local mirror.
5035
 
    If a public location is known for the submit_branch, that location is used
5036
 
    in the merge directive.
5037
 
 
5038
 
    The default behaviour is to send the merge directive by mail, unless -o is
5039
 
    given, in which case it is sent to a file.
 
4958
    If --no-bundle is specified, then public_branch is needed (and must be
 
4959
    up-to-date), so that the receiver can perform the merge using the
 
4960
    public_branch.  The public_branch is always included if known, so that
 
4961
    people can check it later.
 
4962
 
 
4963
    The submit branch defaults to the parent, but can be overridden.  Both
 
4964
    submit branch and public branch will be remembered if supplied.
 
4965
 
 
4966
    If a public_branch is known for the submit_branch, that public submit
 
4967
    branch is used in the merge instructions.  This means that a local mirror
 
4968
    can be used as your actual submit branch, once you have set public_branch
 
4969
    for that mirror.
5040
4970
 
5041
4971
    Mail is sent using your preferred mail program.  This should be transparent
5042
4972
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
5062
4992
 
5063
4993
    The merge directives created by bzr send may be applied using bzr merge or
5064
4994
    bzr pull by specifying a file containing a merge directive as the location.
5065
 
 
5066
 
    bzr send makes extensive use of public locations to map local locations into
5067
 
    URLs that can be used by other people.  See `bzr help configuration` to
5068
 
    set them, and use `bzr info` to display them.
5069
4995
    """
5070
4996
 
5071
4997
    encoding_type = 'exact'
5230
5156
            ):
5231
5157
        branch, relpath = Branch.open_containing(directory)
5232
5158
        branch.lock_write()
5233
 
        self.add_cleanup(branch.unlock)
5234
 
        if delete:
5235
 
            branch.tags.delete_tag(tag_name)
5236
 
            self.outf.write('Deleted tag %s.\n' % tag_name)
5237
 
        else:
5238
 
            if revision:
5239
 
                if len(revision) != 1:
5240
 
                    raise errors.BzrCommandError(
5241
 
                        "Tags can only be placed on a single revision, "
5242
 
                        "not on a range")
5243
 
                revision_id = revision[0].as_revision_id(branch)
 
5159
        try:
 
5160
            if delete:
 
5161
                branch.tags.delete_tag(tag_name)
 
5162
                self.outf.write('Deleted tag %s.\n' % tag_name)
5244
5163
            else:
5245
 
                revision_id = branch.last_revision()
5246
 
            if (not force) and branch.tags.has_tag(tag_name):
5247
 
                raise errors.TagAlreadyExists(tag_name)
5248
 
            branch.tags.set_tag(tag_name, revision_id)
5249
 
            self.outf.write('Created tag %s.\n' % tag_name)
 
5164
                if revision:
 
5165
                    if len(revision) != 1:
 
5166
                        raise errors.BzrCommandError(
 
5167
                            "Tags can only be placed on a single revision, "
 
5168
                            "not on a range")
 
5169
                    revision_id = revision[0].as_revision_id(branch)
 
5170
                else:
 
5171
                    revision_id = branch.last_revision()
 
5172
                if (not force) and branch.tags.has_tag(tag_name):
 
5173
                    raise errors.TagAlreadyExists(tag_name)
 
5174
                branch.tags.set_tag(tag_name, revision_id)
 
5175
                self.outf.write('Created tag %s.\n' % tag_name)
 
5176
        finally:
 
5177
            branch.unlock()
5250
5178
 
5251
5179
 
5252
5180
class cmd_tags(Command):
5285
5213
            return
5286
5214
 
5287
5215
        branch.lock_read()
5288
 
        self.add_cleanup(branch.unlock)
5289
 
        if revision:
5290
 
            graph = branch.repository.get_graph()
5291
 
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
5292
 
            revid1, revid2 = rev1.rev_id, rev2.rev_id
5293
 
            # only show revisions between revid1 and revid2 (inclusive)
5294
 
            tags = [(tag, revid) for tag, revid in tags if
5295
 
                graph.is_between(revid, revid1, revid2)]
5296
 
        if sort == 'alpha':
5297
 
            tags.sort()
5298
 
        elif sort == 'time':
5299
 
            timestamps = {}
5300
 
            for tag, revid in tags:
5301
 
                try:
5302
 
                    revobj = branch.repository.get_revision(revid)
5303
 
                except errors.NoSuchRevision:
5304
 
                    timestamp = sys.maxint # place them at the end
5305
 
                else:
5306
 
                    timestamp = revobj.timestamp
5307
 
                timestamps[revid] = timestamp
5308
 
            tags.sort(key=lambda x: timestamps[x[1]])
5309
 
        if not show_ids:
5310
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5311
 
            for index, (tag, revid) in enumerate(tags):
5312
 
                try:
5313
 
                    revno = branch.revision_id_to_dotted_revno(revid)
5314
 
                    if isinstance(revno, tuple):
5315
 
                        revno = '.'.join(map(str, revno))
5316
 
                except errors.NoSuchRevision:
5317
 
                    # Bad tag data/merges can lead to tagged revisions
5318
 
                    # which are not in this branch. Fail gracefully ...
5319
 
                    revno = '?'
5320
 
                tags[index] = (tag, revno)
5321
 
        self.cleanup_now()
 
5216
        try:
 
5217
            if revision:
 
5218
                graph = branch.repository.get_graph()
 
5219
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5220
                revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5221
                # only show revisions between revid1 and revid2 (inclusive)
 
5222
                tags = [(tag, revid) for tag, revid in tags if
 
5223
                    graph.is_between(revid, revid1, revid2)]
 
5224
            if sort == 'alpha':
 
5225
                tags.sort()
 
5226
            elif sort == 'time':
 
5227
                timestamps = {}
 
5228
                for tag, revid in tags:
 
5229
                    try:
 
5230
                        revobj = branch.repository.get_revision(revid)
 
5231
                    except errors.NoSuchRevision:
 
5232
                        timestamp = sys.maxint # place them at the end
 
5233
                    else:
 
5234
                        timestamp = revobj.timestamp
 
5235
                    timestamps[revid] = timestamp
 
5236
                tags.sort(key=lambda x: timestamps[x[1]])
 
5237
            if not show_ids:
 
5238
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5239
                for index, (tag, revid) in enumerate(tags):
 
5240
                    try:
 
5241
                        revno = branch.revision_id_to_dotted_revno(revid)
 
5242
                        if isinstance(revno, tuple):
 
5243
                            revno = '.'.join(map(str, revno))
 
5244
                    except errors.NoSuchRevision:
 
5245
                        # Bad tag data/merges can lead to tagged revisions
 
5246
                        # which are not in this branch. Fail gracefully ...
 
5247
                        revno = '?'
 
5248
                    tags[index] = (tag, revno)
 
5249
        finally:
 
5250
            branch.unlock()
5322
5251
        for tag, revspec in tags:
5323
5252
            self.outf.write('%-20s %s\n' % (tag, revspec))
5324
5253
 
5433
5362
    /path/to/newbranch.
5434
5363
 
5435
5364
    Bound branches use the nickname of its master branch unless it is set
5436
 
    locally, in which case switching will update the local nickname to be
 
5365
    locally, in which case switching will update the the local nickname to be
5437
5366
    that of the master.
5438
5367
    """
5439
5368
 
5440
 
    takes_args = ['to_location?']
 
5369
    takes_args = ['to_location']
5441
5370
    takes_options = [Option('force',
5442
5371
                        help='Switch even if local commits will be lost.'),
5443
 
                     'revision',
5444
5372
                     Option('create-branch', short_name='b',
5445
5373
                        help='Create the target branch from this one before'
5446
5374
                             ' switching to it.'),
5447
 
                    ]
 
5375
                     ]
5448
5376
 
5449
 
    def run(self, to_location=None, force=False, create_branch=False,
5450
 
            revision=None):
 
5377
    def run(self, to_location, force=False, create_branch=False):
5451
5378
        from bzrlib import switch
5452
5379
        tree_location = '.'
5453
 
        revision = _get_one_revision('switch', revision)
5454
5380
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5455
 
        if to_location is None:
5456
 
            if revision is None:
5457
 
                raise errors.BzrCommandError('You must supply either a'
5458
 
                                             ' revision or a location')
5459
 
            to_location = '.'
5460
5381
        try:
5461
5382
            branch = control_dir.open_branch()
5462
5383
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5467
5388
            if branch is None:
5468
5389
                raise errors.BzrCommandError('cannot create branch without'
5469
5390
                                             ' source branch')
5470
 
            to_location = directory_service.directories.dereference(
5471
 
                              to_location)
5472
5391
            if '/' not in to_location and '\\' not in to_location:
5473
5392
                # This path is meant to be relative to the existing branch
5474
5393
                this_url = self._get_branch_location(control_dir)
5476
5395
            to_branch = branch.bzrdir.sprout(to_location,
5477
5396
                                 possible_transports=[branch.bzrdir.root_transport],
5478
5397
                                 source_branch=branch).open_branch()
 
5398
            # try:
 
5399
            #     from_branch = control_dir.open_branch()
 
5400
            # except errors.NotBranchError:
 
5401
            #     raise BzrCommandError('Cannot create a branch from this'
 
5402
            #         ' location when we cannot open this branch')
 
5403
            # from_branch.bzrdir.sprout(
 
5404
            pass
5479
5405
        else:
5480
5406
            try:
5481
5407
                to_branch = Branch.open(to_location)
5483
5409
                this_url = self._get_branch_location(control_dir)
5484
5410
                to_branch = Branch.open(
5485
5411
                    urlutils.join(this_url, '..', to_location))
5486
 
        if revision is not None:
5487
 
            revision = revision.as_revision_id(to_branch)
5488
 
        switch.switch(control_dir, to_branch, force, revision_id=revision)
 
5412
        switch.switch(control_dir, to_branch, force)
5489
5413
        if had_explicit_nick:
5490
5414
            branch = control_dir.open_branch() #get the new branch!
5491
5415
            branch.nick = to_branch.nick
5738
5662
            try:
5739
5663
                shelver.run()
5740
5664
            finally:
5741
 
                shelver.finalize()
 
5665
                shelver.work_tree.unlock()
5742
5666
        except errors.UserAbort:
5743
5667
            return 0
5744
5668
 
5745
5669
    def run_for_list(self):
5746
5670
        tree = WorkingTree.open_containing('.')[0]
5747
5671
        tree.lock_read()
5748
 
        self.add_cleanup(tree.unlock)
5749
 
        manager = tree.get_shelf_manager()
5750
 
        shelves = manager.active_shelves()
5751
 
        if len(shelves) == 0:
5752
 
            note('No shelved changes.')
5753
 
            return 0
5754
 
        for shelf_id in reversed(shelves):
5755
 
            message = manager.get_metadata(shelf_id).get('message')
5756
 
            if message is None:
5757
 
                message = '<no message>'
5758
 
            self.outf.write('%3d: %s\n' % (shelf_id, message))
5759
 
        return 1
 
5672
        try:
 
5673
            manager = tree.get_shelf_manager()
 
5674
            shelves = manager.active_shelves()
 
5675
            if len(shelves) == 0:
 
5676
                note('No shelved changes.')
 
5677
                return 0
 
5678
            for shelf_id in reversed(shelves):
 
5679
                message = manager.get_metadata(shelf_id).get('message')
 
5680
                if message is None:
 
5681
                    message = '<no message>'
 
5682
                self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5683
            return 1
 
5684
        finally:
 
5685
            tree.unlock()
5760
5686
 
5761
5687
 
5762
5688
class cmd_unshelve(Command):
5774
5700
            enum_switch=False, value_switches=True,
5775
5701
            apply="Apply changes and remove from the shelf.",
5776
5702
            dry_run="Show changes, but do not apply or remove them.",
5777
 
            preview="Instead of unshelving the changes, show the diff that "
5778
 
                    "would result from unshelving.",
5779
 
            delete_only="Delete changes without applying them.",
5780
 
            keep="Apply changes but don't delete them.",
 
5703
            delete_only="Delete changes without applying them."
5781
5704
        )
5782
5705
    ]
5783
5706
    _see_also = ['shelve']