~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2009-11-16 02:26:32 UTC
  • mto: This revision was merged to the branch mainline in revision 4880.
  • Revision ID: mbp@sourcefrog.net-20091116022632-261trs2osy8oupe3
Convert version-info to use TextUIOutputStream

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
            tree, file_list = tree_files_for_add(file_list)
 
659
            added, ignored = tree.smart_add(file_list, not
 
660
                no_recurse, action=action, save=not dry_run)
 
661
        finally:
 
662
            if base_tree is not None:
 
663
                base_tree.unlock()
661
664
        if len(ignored) > 0:
662
665
            if verbose:
663
666
                for glob in sorted(ignored.keys()):
727
730
        revision = _get_one_revision('inventory', revision)
728
731
        work_tree, file_list = tree_files(file_list)
729
732
        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()
 
733
        try:
 
734
            if revision is not None:
 
735
                tree = revision.as_tree(work_tree.branch)
 
736
 
 
737
                extra_trees = [work_tree]
 
738
                tree.lock_read()
 
739
            else:
 
740
                tree = work_tree
 
741
                extra_trees = []
 
742
 
 
743
            if file_list is not None:
 
744
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
745
                                          require_versioned=True)
 
746
                # find_ids_across_trees may include some paths that don't
 
747
                # exist in 'tree'.
 
748
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
749
                                 for file_id in file_ids if file_id in tree)
 
750
            else:
 
751
                entries = tree.inventory.entries()
 
752
        finally:
 
753
            tree.unlock()
 
754
            if tree is not work_tree:
 
755
                work_tree.unlock()
 
756
 
752
757
        for path, entry in entries:
753
758
            if kind and kind != entry.kind:
754
759
                continue
800
805
            raise errors.BzrCommandError("missing file argument")
801
806
        tree, rel_names = tree_files(names_list, canonicalize=False)
802
807
        tree.lock_tree_write()
803
 
        self.add_cleanup(tree.unlock)
804
 
        self._run(tree, names_list, rel_names, after)
 
808
        try:
 
809
            self._run(tree, names_list, rel_names, after)
 
810
        finally:
 
811
            tree.unlock()
805
812
 
806
813
    def run_auto(self, names_list, after, dry_run):
807
814
        if names_list is not None and len(names_list) > 1:
812
819
                                         ' --auto.')
813
820
        work_tree, file_list = tree_files(names_list, default_branch='.')
814
821
        work_tree.lock_tree_write()
815
 
        self.add_cleanup(work_tree.unlock)
816
 
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
822
        try:
 
823
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
824
        finally:
 
825
            work_tree.unlock()
817
826
 
818
827
    def _run(self, tree, names_list, rel_names, after):
819
828
        into_existing = osutils.isdir(names_list[-1])
840
849
            # All entries reference existing inventory items, so fix them up
841
850
            # for cicp file-systems.
842
851
            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))
 
852
            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
 
853
                self.outf.write("%s => %s\n" % pair)
846
854
        else:
847
855
            if len(names_list) != 2:
848
856
                raise errors.BzrCommandError('to mv multiple files the'
892
900
            dest = osutils.pathjoin(dest_parent, dest_tail)
893
901
            mutter("attempting to move %s => %s", src, dest)
894
902
            tree.rename_one(src, dest, after=after)
895
 
            if not is_quiet():
896
 
                self.outf.write("%s => %s\n" % (src, dest))
 
903
            self.outf.write("%s => %s\n" % (src, dest))
897
904
 
898
905
 
899
906
class cmd_pull(Command):
900
907
    """Turn this branch into a mirror of another branch.
901
908
 
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.
 
909
    This command only works on branches that have not diverged.  Branches are
 
910
    considered diverged if the destination branch's most recent commit is one
 
911
    that has not been merged (directly or indirectly) into the parent.
906
912
 
907
913
    If branches have diverged, you can use 'bzr merge' to integrate the changes
908
914
    from one into the other.  Once one branch has merged, the other should
909
915
    be able to pull it again.
910
916
 
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.
 
917
    If you want to forget your local changes and just update your branch to
 
918
    match the remote one, use pull --overwrite.
914
919
 
915
920
    If there is no default location set, the first pull will set it.  After
916
921
    that, you can omit the location to use the default.  To change the
997
1002
 
998
1003
        if branch_from is not branch_to:
999
1004
            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)
 
1005
        try:
 
1006
            if revision is not None:
 
1007
                revision_id = revision.as_revision_id(branch_from)
 
1008
 
 
1009
            branch_to.lock_write()
 
1010
            try:
 
1011
                if tree_to is not None:
 
1012
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1013
                    change_reporter = delta._ChangeReporter(
 
1014
                        unversioned_filter=tree_to.is_ignored,
 
1015
                        view_info=view_info)
 
1016
                    result = tree_to.pull(
 
1017
                        branch_from, overwrite, revision_id, change_reporter,
 
1018
                        possible_transports=possible_transports, local=local)
 
1019
                else:
 
1020
                    result = branch_to.pull(
 
1021
                        branch_from, overwrite, revision_id, local=local)
 
1022
 
 
1023
                result.report(self.outf)
 
1024
                if verbose and result.old_revid != result.new_revid:
 
1025
                    log.show_branch_change(
 
1026
                        branch_to, self.outf, result.old_revno,
 
1027
                        result.old_revid)
 
1028
            finally:
 
1029
                branch_to.unlock()
 
1030
        finally:
 
1031
            if branch_from is not branch_to:
 
1032
                branch_from.unlock()
1023
1033
 
1024
1034
 
1025
1035
class cmd_push(Command):
1180
1190
                    ' directory exists, but does not already'
1181
1191
                    ' have a control directory.  This flag will'
1182
1192
                    ' allow branch to proceed.'),
1183
 
        Option('bind',
1184
 
            help="Bind new branch to from location."),
1185
1193
        ]
1186
1194
    aliases = ['get', 'clone']
1187
1195
 
1188
1196
    def run(self, from_location, to_location=None, revision=None,
1189
1197
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1190
 
            use_existing_dir=False, switch=False, bind=False):
 
1198
            use_existing_dir=False, switch=False):
1191
1199
        from bzrlib import switch as _mod_switch
1192
1200
        from bzrlib.tag import _merge_tags_if_possible
1193
1201
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1194
1202
            from_location)
 
1203
        if (accelerator_tree is not None and
 
1204
            accelerator_tree.supports_content_filtering()):
 
1205
            accelerator_tree = None
1195
1206
        revision = _get_one_revision('branch', revision)
1196
1207
        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
1208
        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)
 
1209
            if revision is not None:
 
1210
                revision_id = revision.as_revision_id(br_from)
1214
1211
            else:
1215
 
                try:
1216
 
                    bzrdir.BzrDir.open_from_transport(to_transport)
1217
 
                except errors.NotBranchError:
1218
 
                    pass
 
1212
                # FIXME - wt.last_revision, fallback to branch, fall back to
 
1213
                # None or perhaps NULL_REVISION to mean copy nothing
 
1214
                # RBC 20060209
 
1215
                revision_id = br_from.last_revision()
 
1216
            if to_location is None:
 
1217
                to_location = urlutils.derive_to_location(from_location)
 
1218
            to_transport = transport.get_transport(to_location)
 
1219
            try:
 
1220
                to_transport.mkdir('.')
 
1221
            except errors.FileExists:
 
1222
                if not use_existing_dir:
 
1223
                    raise errors.BzrCommandError('Target directory "%s" '
 
1224
                        'already exists.' % to_location)
1219
1225
                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'))
 
1226
                    try:
 
1227
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1228
                    except errors.NotBranchError:
 
1229
                        pass
 
1230
                    else:
 
1231
                        raise errors.AlreadyBranchError(to_location)
 
1232
            except errors.NoSuchFile:
 
1233
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
1234
                                             % to_location)
 
1235
            try:
 
1236
                # preserve whatever source format we have.
 
1237
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
 
1238
                                            possible_transports=[to_transport],
 
1239
                                            accelerator_tree=accelerator_tree,
 
1240
                                            hardlink=hardlink, stacked=stacked,
 
1241
                                            force_new_repo=standalone,
 
1242
                                            create_tree_if_local=not no_tree,
 
1243
                                            source_branch=br_from)
 
1244
                branch = dir.open_branch()
 
1245
            except errors.NoSuchRevision:
 
1246
                to_transport.delete_tree('.')
 
1247
                msg = "The branch %s has no revision %s." % (from_location,
 
1248
                    revision)
 
1249
                raise errors.BzrCommandError(msg)
 
1250
            _merge_tags_if_possible(br_from, branch)
 
1251
            # If the source branch is stacked, the new branch may
 
1252
            # be stacked whether we asked for that explicitly or not.
 
1253
            # We therefore need a try/except here and not just 'if stacked:'
 
1254
            try:
 
1255
                note('Created new stacked branch referring to %s.' %
 
1256
                    branch.get_stacked_on_url())
 
1257
            except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1258
                errors.UnstackableRepositoryFormat), e:
 
1259
                note('Branched %d revision(s).' % branch.revno())
 
1260
            if switch:
 
1261
                # Switch to the new branch
 
1262
                wt, _ = WorkingTree.open_containing('.')
 
1263
                _mod_switch.switch(wt.bzrdir, branch)
 
1264
                note('Switched to branch: %s',
 
1265
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1266
        finally:
 
1267
            br_from.unlock()
1260
1268
 
1261
1269
 
1262
1270
class cmd_checkout(Command):
1341
1349
    def run(self, dir=u'.'):
1342
1350
        tree = WorkingTree.open_containing(dir)[0]
1343
1351
        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))
 
1352
        try:
 
1353
            new_inv = tree.inventory
 
1354
            old_tree = tree.basis_tree()
 
1355
            old_tree.lock_read()
 
1356
            try:
 
1357
                old_inv = old_tree.inventory
 
1358
                renames = []
 
1359
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1360
                for f, paths, c, v, p, n, k, e in iterator:
 
1361
                    if paths[0] == paths[1]:
 
1362
                        continue
 
1363
                    if None in (paths):
 
1364
                        continue
 
1365
                    renames.append(paths)
 
1366
                renames.sort()
 
1367
                for old_name, new_name in renames:
 
1368
                    self.outf.write("%s => %s\n" % (old_name, new_name))
 
1369
            finally:
 
1370
                old_tree.unlock()
 
1371
        finally:
 
1372
            tree.unlock()
1361
1373
 
1362
1374
 
1363
1375
class cmd_update(Command):
1369
1381
 
1370
1382
    If you want to discard your local changes, you can just do a
1371
1383
    '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
1384
    """
1376
1385
 
1377
1386
    _see_also = ['pull', 'working-trees', 'status-flags']
1378
1387
    takes_args = ['dir?']
1379
 
    takes_options = ['revision']
1380
1388
    aliases = ['up']
1381
1389
 
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")
 
1390
    def run(self, dir='.'):
1386
1391
        tree = WorkingTree.open_containing(dir)[0]
1387
 
        branch = tree.branch
1388
1392
        possible_transports = []
1389
 
        master = branch.get_master_branch(
 
1393
        master = tree.branch.get_master_branch(
1390
1394
            possible_transports=possible_transports)
1391
1395
        if master is not None:
1392
1396
            tree.lock_write()
1393
 
            branch_location = master.base
1394
1397
        else:
1395
1398
            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
1399
        try:
 
1400
            existing_pending_merges = tree.get_parent_ids()[1:]
 
1401
            last_rev = _mod_revision.ensure_null(tree.last_revision())
 
1402
            if last_rev == _mod_revision.ensure_null(
 
1403
                tree.branch.last_revision()):
 
1404
                # may be up to date, check master too.
 
1405
                if master is None or last_rev == _mod_revision.ensure_null(
 
1406
                    master.last_revision()):
 
1407
                    revno = tree.branch.revision_id_to_revno(last_rev)
 
1408
                    note("Tree is up to date at revision %d." % (revno,))
 
1409
                    return 0
 
1410
            view_info = _get_view_info_for_change_reporter(tree)
1423
1411
            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
 
1412
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
 
1413
                view_info=view_info), possible_transports=possible_transports)
 
1414
            revno = tree.branch.revision_id_to_revno(
 
1415
                _mod_revision.ensure_null(tree.last_revision()))
 
1416
            note('Updated to revision %d.' % (revno,))
 
1417
            if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1418
                note('Your local commits will now show as pending merges with '
 
1419
                     "'bzr status', and can be committed with 'bzr commit'.")
 
1420
            if conflicts != 0:
 
1421
                return 1
 
1422
            else:
 
1423
                return 0
 
1424
        finally:
 
1425
            tree.unlock()
1445
1426
 
1446
1427
 
1447
1428
class cmd_info(Command):
1518
1499
            file_list = [f for f in file_list]
1519
1500
 
1520
1501
        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')
 
1502
        try:
 
1503
            # Heuristics should probably all move into tree.remove_smart or
 
1504
            # some such?
 
1505
            if new:
 
1506
                added = tree.changes_from(tree.basis_tree(),
 
1507
                    specific_files=file_list).added
 
1508
                file_list = sorted([f[0] for f in added], reverse=True)
 
1509
                if len(file_list) == 0:
 
1510
                    raise errors.BzrCommandError('No matching files.')
 
1511
            elif file_list is None:
 
1512
                # missing files show up in iter_changes(basis) as
 
1513
                # versioned-with-no-kind.
 
1514
                missing = []
 
1515
                for change in tree.iter_changes(tree.basis_tree()):
 
1516
                    # Find paths in the working tree that have no kind:
 
1517
                    if change[1][1] is not None and change[6][1] is None:
 
1518
                        missing.append(change[1][1])
 
1519
                file_list = sorted(missing, reverse=True)
 
1520
                file_deletion_strategy = 'keep'
 
1521
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1522
                keep_files=file_deletion_strategy=='keep',
 
1523
                force=file_deletion_strategy=='force')
 
1524
        finally:
 
1525
            tree.unlock()
1543
1526
 
1544
1527
 
1545
1528
class cmd_file_id(Command):
1765
1748
 
1766
1749
 
1767
1750
class cmd_init_repository(Command):
1768
 
    """Create a shared repository for branches to share storage space.
 
1751
    """Create a shared repository to hold branches.
1769
1752
 
1770
1753
    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.
 
1754
    revisions in the repository, not in the branch directory.
1774
1755
 
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.
 
1756
    If the --no-trees option is used then the branches in the repository
 
1757
    will not have working trees by default.
1781
1758
 
1782
1759
    :Examples:
1783
 
        Create a shared repository holding just branches::
 
1760
        Create a shared repositories holding just branches::
1784
1761
 
1785
1762
            bzr init-repo --no-trees repo
1786
1763
            bzr init repo/trunk
1852
1829
 
1853
1830
            bzr diff -r1
1854
1831
 
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
 
1832
        Difference between revision 2 and revision 1::
 
1833
 
 
1834
            bzr diff -r1..2
 
1835
 
 
1836
        Difference between revision 2 and revision 1 for branch xxx::
 
1837
 
 
1838
            bzr diff -r1..2 xxx
1876
1839
 
1877
1840
        Show just the differences for file NEWS::
1878
1841
 
1971
1934
    def run(self, show_ids=False):
1972
1935
        tree = WorkingTree.open_containing(u'.')[0]
1973
1936
        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')
 
1937
        try:
 
1938
            old = tree.basis_tree()
 
1939
            old.lock_read()
 
1940
            try:
 
1941
                for path, ie in old.inventory.iter_entries():
 
1942
                    if not tree.has_id(ie.file_id):
 
1943
                        self.outf.write(path)
 
1944
                        if show_ids:
 
1945
                            self.outf.write(' ')
 
1946
                            self.outf.write(ie.file_id)
 
1947
                        self.outf.write('\n')
 
1948
            finally:
 
1949
                old.unlock()
 
1950
        finally:
 
1951
            tree.unlock()
1985
1952
 
1986
1953
 
1987
1954
class cmd_modified(Command):
2023
1990
    def run(self, null=False):
2024
1991
        wt = WorkingTree.open_containing(u'.')[0]
2025
1992
        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')
 
1993
        try:
 
1994
            basis = wt.basis_tree()
 
1995
            basis.lock_read()
 
1996
            try:
 
1997
                basis_inv = basis.inventory
 
1998
                inv = wt.inventory
 
1999
                for file_id in inv:
 
2000
                    if file_id in basis_inv:
 
2001
                        continue
 
2002
                    if inv.is_root(file_id) and len(basis_inv) == 0:
 
2003
                        continue
 
2004
                    path = inv.id2path(file_id)
 
2005
                    if not os.access(osutils.abspath(path), os.F_OK):
 
2006
                        continue
 
2007
                    if null:
 
2008
                        self.outf.write(path + '\0')
 
2009
                    else:
 
2010
                        self.outf.write(osutils.quotefn(path) + '\n')
 
2011
            finally:
 
2012
                basis.unlock()
 
2013
        finally:
 
2014
            wt.unlock()
2044
2015
 
2045
2016
 
2046
2017
class cmd_root(Command):
2191
2162
    :Tips & tricks:
2192
2163
 
2193
2164
      GUI tools and IDEs are often better at exploring history than command
2194
 
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2195
 
      bzr-explorer shell, or the Loggerhead web interface.  See the Plugin
2196
 
      Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2197
 
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
 
2165
      line tools. You may prefer qlog or glog from the QBzr and Bzr-Gtk packages
 
2166
      respectively for example. (TortoiseBzr uses qlog for displaying logs.) See
 
2167
      http://bazaar-vcs.org/BzrPlugins and http://bazaar-vcs.org/IDEIntegration.
 
2168
 
 
2169
      Web interfaces are often better at exploring history than command line
 
2170
      tools, particularly for branches on servers. You may prefer Loggerhead
 
2171
      or one of its alternatives. See http://bazaar-vcs.org/WebInterface.
2198
2172
 
2199
2173
      You may find it useful to add the aliases below to ``bazaar.conf``::
2200
2174
 
2301
2275
 
2302
2276
        file_ids = []
2303
2277
        filter_by_dir = False
2304
 
        if file_list:
2305
 
            # find the file ids to log and check for directory filtering
2306
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2307
 
                revision, file_list)
2308
 
            self.add_cleanup(b.unlock)
2309
 
            for relpath, file_id, kind in file_info_list:
2310
 
                if file_id is None:
2311
 
                    raise errors.BzrCommandError(
2312
 
                        "Path unknown at end or start of revision range: %s" %
2313
 
                        relpath)
2314
 
                # If the relpath is the top of the tree, we log everything
2315
 
                if relpath == '':
2316
 
                    file_ids = []
2317
 
                    break
 
2278
        b = None
 
2279
        try:
 
2280
            if file_list:
 
2281
                # find the file ids to log and check for directory filtering
 
2282
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2283
                    revision, file_list)
 
2284
                for relpath, file_id, kind in file_info_list:
 
2285
                    if file_id is None:
 
2286
                        raise errors.BzrCommandError(
 
2287
                            "Path unknown at end or start of revision range: %s" %
 
2288
                            relpath)
 
2289
                    # If the relpath is the top of the tree, we log everything
 
2290
                    if relpath == '':
 
2291
                        file_ids = []
 
2292
                        break
 
2293
                    else:
 
2294
                        file_ids.append(file_id)
 
2295
                    filter_by_dir = filter_by_dir or (
 
2296
                        kind in ['directory', 'tree-reference'])
 
2297
            else:
 
2298
                # log everything
 
2299
                # FIXME ? log the current subdir only RBC 20060203
 
2300
                if revision is not None \
 
2301
                        and len(revision) > 0 and revision[0].get_branch():
 
2302
                    location = revision[0].get_branch()
2318
2303
                else:
2319
 
                    file_ids.append(file_id)
2320
 
                filter_by_dir = filter_by_dir or (
2321
 
                    kind in ['directory', 'tree-reference'])
2322
 
        else:
2323
 
            # log everything
2324
 
            # FIXME ? log the current subdir only RBC 20060203
2325
 
            if revision is not None \
2326
 
                    and len(revision) > 0 and revision[0].get_branch():
2327
 
                location = revision[0].get_branch()
2328
 
            else:
2329
 
                location = '.'
2330
 
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2331
 
            b = dir.open_branch()
2332
 
            b.lock_read()
2333
 
            self.add_cleanup(b.unlock)
2334
 
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2335
 
 
2336
 
        # Decide on the type of delta & diff filtering to use
2337
 
        # TODO: add an --all-files option to make this configurable & consistent
2338
 
        if not verbose:
2339
 
            delta_type = None
2340
 
        else:
2341
 
            delta_type = 'full'
2342
 
        if not show_diff:
2343
 
            diff_type = None
2344
 
        elif file_ids:
2345
 
            diff_type = 'partial'
2346
 
        else:
2347
 
            diff_type = 'full'
2348
 
 
2349
 
        # Build the log formatter
2350
 
        if log_format is None:
2351
 
            log_format = log.log_formatter_registry.get_default(b)
2352
 
        # Make a non-encoding output to include the diffs - bug 328007
2353
 
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2354
 
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2355
 
                        to_exact_file=unencoded_output,
2356
 
                        show_timezone=timezone,
2357
 
                        delta_format=get_verbosity_level(),
2358
 
                        levels=levels,
2359
 
                        show_advice=levels is None)
2360
 
 
2361
 
        # Choose the algorithm for doing the logging. It's annoying
2362
 
        # having multiple code paths like this but necessary until
2363
 
        # the underlying repository format is faster at generating
2364
 
        # deltas or can provide everything we need from the indices.
2365
 
        # The default algorithm - match-using-deltas - works for
2366
 
        # multiple files and directories and is faster for small
2367
 
        # amounts of history (200 revisions say). However, it's too
2368
 
        # slow for logging a single file in a repository with deep
2369
 
        # history, i.e. > 10K revisions. In the spirit of "do no
2370
 
        # evil when adding features", we continue to use the
2371
 
        # original algorithm - per-file-graph - for the "single
2372
 
        # file that isn't a directory without showing a delta" case.
2373
 
        partial_history = revision and b.repository._format.supports_chks
2374
 
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2375
 
            or delta_type or partial_history)
2376
 
 
2377
 
        # Build the LogRequest and execute it
2378
 
        if len(file_ids) == 0:
2379
 
            file_ids = None
2380
 
        rqst = make_log_request_dict(
2381
 
            direction=direction, specific_fileids=file_ids,
2382
 
            start_revision=rev1, end_revision=rev2, limit=limit,
2383
 
            message_search=message, delta_type=delta_type,
2384
 
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
2385
 
        Logger(b, rqst).show(lf)
 
2304
                    location = '.'
 
2305
                dir, relpath = bzrdir.BzrDir.open_containing(location)
 
2306
                b = dir.open_branch()
 
2307
                b.lock_read()
 
2308
                rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2309
 
 
2310
            # Decide on the type of delta & diff filtering to use
 
2311
            # TODO: add an --all-files option to make this configurable & consistent
 
2312
            if not verbose:
 
2313
                delta_type = None
 
2314
            else:
 
2315
                delta_type = 'full'
 
2316
            if not show_diff:
 
2317
                diff_type = None
 
2318
            elif file_ids:
 
2319
                diff_type = 'partial'
 
2320
            else:
 
2321
                diff_type = 'full'
 
2322
 
 
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
            if b is not None:
 
2359
                b.unlock()
2386
2360
 
2387
2361
 
2388
2362
def _get_revision_range(revisionspec_list, branch, command_name):
2455
2429
        file_id = tree.path2id(relpath)
2456
2430
        b = tree.branch
2457
2431
        b.lock_read()
2458
 
        self.add_cleanup(b.unlock)
2459
 
        touching_revs = log.find_touching_revisions(b, file_id)
2460
 
        for revno, revision_id, what in touching_revs:
2461
 
            self.outf.write("%6d %s\n" % (revno, what))
 
2432
        try:
 
2433
            touching_revs = log.find_touching_revisions(b, file_id)
 
2434
            for revno, revision_id, what in touching_revs:
 
2435
                self.outf.write("%6d %s\n" % (revno, what))
 
2436
        finally:
 
2437
            b.unlock()
2462
2438
 
2463
2439
 
2464
2440
class cmd_ls(Command):
2516
2492
        if from_root:
2517
2493
            if relpath:
2518
2494
                prefix = relpath + '/'
2519
 
        elif fs_path != '.' and not fs_path.endswith('/'):
 
2495
        elif fs_path != '.':
2520
2496
            prefix = fs_path + '/'
2521
2497
 
2522
2498
        if revision is not None or tree is None:
2531
2507
                note("Ignoring files outside view. View is %s" % view_str)
2532
2508
 
2533
2509
        tree.lock_read()
2534
 
        self.add_cleanup(tree.unlock)
2535
 
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2536
 
            from_dir=relpath, recursive=recursive):
2537
 
            # Apply additional masking
2538
 
            if not all and not selection[fc]:
2539
 
                continue
2540
 
            if kind is not None and fkind != kind:
2541
 
                continue
2542
 
            if apply_view:
2543
 
                try:
2544
 
                    if relpath:
2545
 
                        fullpath = osutils.pathjoin(relpath, fp)
2546
 
                    else:
2547
 
                        fullpath = fp
2548
 
                    views.check_path_in_view(tree, fullpath)
2549
 
                except errors.FileOutsideView:
2550
 
                    continue
 
2510
        try:
 
2511
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2512
                from_dir=relpath, recursive=recursive):
 
2513
                # Apply additional masking
 
2514
                if not all and not selection[fc]:
 
2515
                    continue
 
2516
                if kind is not None and fkind != kind:
 
2517
                    continue
 
2518
                if apply_view:
 
2519
                    try:
 
2520
                        if relpath:
 
2521
                            fullpath = osutils.pathjoin(relpath, fp)
 
2522
                        else:
 
2523
                            fullpath = fp
 
2524
                        views.check_path_in_view(tree, fullpath)
 
2525
                    except errors.FileOutsideView:
 
2526
                        continue
2551
2527
 
2552
 
            # Output the entry
2553
 
            if prefix:
2554
 
                fp = osutils.pathjoin(prefix, fp)
2555
 
            kindch = entry.kind_character()
2556
 
            outstring = fp + kindch
2557
 
            ui.ui_factory.clear_term()
2558
 
            if verbose:
2559
 
                outstring = '%-8s %s' % (fc, outstring)
2560
 
                if show_ids and fid is not None:
2561
 
                    outstring = "%-50s %s" % (outstring, fid)
2562
 
                self.outf.write(outstring + '\n')
2563
 
            elif null:
2564
 
                self.outf.write(fp + '\0')
2565
 
                if show_ids:
2566
 
                    if fid is not None:
2567
 
                        self.outf.write(fid)
2568
 
                    self.outf.write('\0')
2569
 
                self.outf.flush()
2570
 
            else:
2571
 
                if show_ids:
2572
 
                    if fid is not None:
2573
 
                        my_id = fid
2574
 
                    else:
2575
 
                        my_id = ''
2576
 
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
2577
 
                else:
 
2528
                # Output the entry
 
2529
                if prefix:
 
2530
                    fp = osutils.pathjoin(prefix, fp)
 
2531
                kindch = entry.kind_character()
 
2532
                outstring = fp + kindch
 
2533
                ui.ui_factory.clear_term()
 
2534
                if verbose:
 
2535
                    outstring = '%-8s %s' % (fc, outstring)
 
2536
                    if show_ids and fid is not None:
 
2537
                        outstring = "%-50s %s" % (outstring, fid)
2578
2538
                    self.outf.write(outstring + '\n')
 
2539
                elif null:
 
2540
                    self.outf.write(fp + '\0')
 
2541
                    if show_ids:
 
2542
                        if fid is not None:
 
2543
                            self.outf.write(fid)
 
2544
                        self.outf.write('\0')
 
2545
                    self.outf.flush()
 
2546
                else:
 
2547
                    if show_ids:
 
2548
                        if fid is not None:
 
2549
                            my_id = fid
 
2550
                        else:
 
2551
                            my_id = ''
 
2552
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2553
                    else:
 
2554
                        self.outf.write(outstring + '\n')
 
2555
        finally:
 
2556
            tree.unlock()
2579
2557
 
2580
2558
 
2581
2559
class cmd_unknowns(Command):
2596
2574
 
2597
2575
    See ``bzr help patterns`` for details on the syntax of patterns.
2598
2576
 
2599
 
    If a .bzrignore file does not exist, the ignore command
2600
 
    will create one and add the specified files or patterns to the newly
2601
 
    created file. The ignore command will also automatically add the 
2602
 
    .bzrignore file to be versioned. Creating a .bzrignore file without
2603
 
    the use of the ignore command will require an explicit add command.
2604
 
 
2605
2577
    To remove patterns from the ignore list, edit the .bzrignore file.
2606
2578
    After adding, editing or deleting that file either indirectly by
2607
2579
    using this command or directly by using an editor, be sure to commit
2608
2580
    it.
2609
 
    
2610
 
    Patterns prefixed with '!' are exceptions to ignore patterns and take
2611
 
    precedence over regular ignores.  Such exceptions are used to specify
2612
 
    files that should be versioned which would otherwise be ignored.
2613
 
    
2614
 
    Patterns prefixed with '!!' act as regular ignore patterns, but have
2615
 
    precedence over the '!' exception patterns.
2616
2581
 
2617
2582
    Note: ignore patterns containing shell wildcards must be quoted from
2618
2583
    the shell on Unix.
2622
2587
 
2623
2588
            bzr ignore ./Makefile
2624
2589
 
2625
 
        Ignore .class files in all directories...::
 
2590
        Ignore class files in all directories::
2626
2591
 
2627
2592
            bzr ignore "*.class"
2628
2593
 
2629
 
        ...but do not ignore "special.class"::
2630
 
 
2631
 
            bzr ignore "!special.class"
2632
 
 
2633
2594
        Ignore .o files under the lib directory::
2634
2595
 
2635
2596
            bzr ignore "lib/**/*.o"
2641
2602
        Ignore everything but the "debian" toplevel directory::
2642
2603
 
2643
2604
            bzr ignore "RE:(?!debian/).*"
2644
 
        
2645
 
        Ignore everything except the "local" toplevel directory,
2646
 
        but always ignore "*~" autosave files, even under local/::
2647
 
        
2648
 
            bzr ignore "*"
2649
 
            bzr ignore "!./local"
2650
 
            bzr ignore "!!*~"
2651
2605
    """
2652
2606
 
2653
2607
    _see_also = ['status', 'ignored', 'patterns']
2711
2665
    def run(self):
2712
2666
        tree = WorkingTree.open_containing(u'.')[0]
2713
2667
        tree.lock_read()
2714
 
        self.add_cleanup(tree.unlock)
2715
 
        for path, file_class, kind, file_id, entry in tree.list_files():
2716
 
            if file_class != 'I':
2717
 
                continue
2718
 
            ## XXX: Slightly inefficient since this was already calculated
2719
 
            pat = tree.is_ignored(path)
2720
 
            self.outf.write('%-50s %s\n' % (path, pat))
 
2668
        try:
 
2669
            for path, file_class, kind, file_id, entry in tree.list_files():
 
2670
                if file_class != 'I':
 
2671
                    continue
 
2672
                ## XXX: Slightly inefficient since this was already calculated
 
2673
                pat = tree.is_ignored(path)
 
2674
                self.outf.write('%-50s %s\n' % (path, pat))
 
2675
        finally:
 
2676
            tree.unlock()
2721
2677
 
2722
2678
 
2723
2679
class cmd_lookup_revision(Command):
2826
2782
        tree, branch, relpath = \
2827
2783
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2828
2784
        branch.lock_read()
2829
 
        self.add_cleanup(branch.unlock)
2830
 
        return self._run(tree, branch, relpath, filename, revision,
2831
 
                         name_from_revision, filters)
 
2785
        try:
 
2786
            return self._run(tree, branch, relpath, filename, revision,
 
2787
                             name_from_revision, filters)
 
2788
        finally:
 
2789
            branch.unlock()
2832
2790
 
2833
2791
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2834
2792
        filtered):
2835
2793
        if tree is None:
2836
2794
            tree = b.basis_tree()
2837
2795
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2838
 
        rev_tree.lock_read()
2839
 
        self.add_cleanup(rev_tree.unlock)
2840
2796
 
2841
2797
        old_file_id = rev_tree.path2id(relpath)
2842
2798
 
2877
2833
            chunks = content.splitlines(True)
2878
2834
            content = filtered_output_bytes(chunks, filters,
2879
2835
                ContentFilterContext(relpath, rev_tree))
2880
 
            self.cleanup_now()
2881
2836
            self.outf.writelines(content)
2882
2837
        else:
2883
 
            self.cleanup_now()
2884
2838
            self.outf.write(content)
2885
2839
 
2886
2840
 
2993
2947
             Option('strict',
2994
2948
                    help="Refuse to commit if there are unknown "
2995
2949
                    "files in the working tree."),
2996
 
             Option('commit-time', type=str,
2997
 
                    help="Manually set a commit time using commit date "
2998
 
                    "format, e.g. '2009-10-10 08:00:00 +0100'."),
2999
2950
             ListOption('fixes', type=str,
3000
2951
                    help="Mark a bug as being fixed by this revision "
3001
2952
                         "(see \"bzr help bugs\")."),
3008
2959
                         "the master branch until a normal commit "
3009
2960
                         "is performed."
3010
2961
                    ),
3011
 
             Option('show-diff',
3012
 
                    help='When no message is supplied, show the diff along'
3013
 
                    ' with the status summary in the message editor.'),
 
2962
              Option('show-diff',
 
2963
                     help='When no message is supplied, show the diff along'
 
2964
                     ' with the status summary in the message editor.'),
3014
2965
             ]
3015
2966
    aliases = ['ci', 'checkin']
3016
2967
 
3035
2986
 
3036
2987
    def run(self, message=None, file=None, verbose=False, selected_list=None,
3037
2988
            unchanged=False, strict=False, local=False, fixes=None,
3038
 
            author=None, show_diff=False, exclude=None, commit_time=None):
 
2989
            author=None, show_diff=False, exclude=None):
3039
2990
        from bzrlib.errors import (
3040
2991
            PointlessCommit,
3041
2992
            ConflictsInTree,
3047
2998
            make_commit_message_template_encoded
3048
2999
        )
3049
3000
 
3050
 
        commit_stamp = offset = None
3051
 
        if commit_time is not None:
3052
 
            try:
3053
 
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
3054
 
            except ValueError, e:
3055
 
                raise errors.BzrCommandError(
3056
 
                    "Could not parse --commit-time: " + str(e))
3057
 
 
3058
3001
        # TODO: Need a blackbox test for invoking the external editor; may be
3059
3002
        # slightly problematic to run this cross-platform.
3060
3003
 
3080
3023
        if local and not tree.branch.get_bound_location():
3081
3024
            raise errors.LocalRequiresBoundBranch()
3082
3025
 
3083
 
        if message is not None:
3084
 
            try:
3085
 
                file_exists = osutils.lexists(message)
3086
 
            except UnicodeError:
3087
 
                # The commit message contains unicode characters that can't be
3088
 
                # represented in the filesystem encoding, so that can't be a
3089
 
                # file.
3090
 
                file_exists = False
3091
 
            if file_exists:
3092
 
                warning_msg = (
3093
 
                    'The commit message is a file name: "%(f)s".\n'
3094
 
                    '(use --file "%(f)s" to take commit message from that file)'
3095
 
                    % { 'f': message })
3096
 
                ui.ui_factory.show_warning(warning_msg)
3097
 
 
3098
3026
        def get_message(commit_obj):
3099
3027
            """Callback to get commit message"""
3100
3028
            my_message = message
3130
3058
                        specific_files=selected_list,
3131
3059
                        allow_pointless=unchanged, strict=strict, local=local,
3132
3060
                        reporter=None, verbose=verbose, revprops=properties,
3133
 
                        authors=author, timestamp=commit_stamp,
3134
 
                        timezone=offset,
 
3061
                        authors=author,
3135
3062
                        exclude=safe_relpath_files(tree, exclude))
3136
3063
        except PointlessCommit:
3137
3064
            # FIXME: This should really happen before the file is read in;
3547
3474
            verbose = not is_quiet()
3548
3475
            # TODO: should possibly lock the history file...
3549
3476
            benchfile = open(".perf_history", "at", buffering=1)
3550
 
            self.add_cleanup(benchfile.close)
3551
3477
        else:
3552
3478
            test_suite_factory = None
3553
3479
            benchfile = None
3554
 
        selftest_kwargs = {"verbose": verbose,
3555
 
                          "pattern": pattern,
3556
 
                          "stop_on_failure": one,
3557
 
                          "transport": transport,
3558
 
                          "test_suite_factory": test_suite_factory,
3559
 
                          "lsprof_timed": lsprof_timed,
3560
 
                          "lsprof_tests": lsprof_tests,
3561
 
                          "bench_history": benchfile,
3562
 
                          "matching_tests_first": first,
3563
 
                          "list_only": list_only,
3564
 
                          "random_seed": randomize,
3565
 
                          "exclude_pattern": exclude,
3566
 
                          "strict": strict,
3567
 
                          "load_list": load_list,
3568
 
                          "debug_flags": debugflag,
3569
 
                          "starting_with": starting_with
3570
 
                          }
3571
 
        selftest_kwargs.update(self.additional_selftest_args)
3572
 
        result = selftest(**selftest_kwargs)
 
3480
        try:
 
3481
            selftest_kwargs = {"verbose": verbose,
 
3482
                              "pattern": pattern,
 
3483
                              "stop_on_failure": one,
 
3484
                              "transport": transport,
 
3485
                              "test_suite_factory": test_suite_factory,
 
3486
                              "lsprof_timed": lsprof_timed,
 
3487
                              "lsprof_tests": lsprof_tests,
 
3488
                              "bench_history": benchfile,
 
3489
                              "matching_tests_first": first,
 
3490
                              "list_only": list_only,
 
3491
                              "random_seed": randomize,
 
3492
                              "exclude_pattern": exclude,
 
3493
                              "strict": strict,
 
3494
                              "load_list": load_list,
 
3495
                              "debug_flags": debugflag,
 
3496
                              "starting_with": starting_with
 
3497
                              }
 
3498
            selftest_kwargs.update(self.additional_selftest_args)
 
3499
            result = selftest(**selftest_kwargs)
 
3500
        finally:
 
3501
            if benchfile is not None:
 
3502
                benchfile.close()
3573
3503
        return int(not result)
3574
3504
 
3575
3505
 
3614
3544
        branch1 = Branch.open_containing(branch)[0]
3615
3545
        branch2 = Branch.open_containing(other)[0]
3616
3546
        branch1.lock_read()
3617
 
        self.add_cleanup(branch1.unlock)
3618
 
        branch2.lock_read()
3619
 
        self.add_cleanup(branch2.unlock)
3620
 
        last1 = ensure_null(branch1.last_revision())
3621
 
        last2 = ensure_null(branch2.last_revision())
3622
 
 
3623
 
        graph = branch1.repository.get_graph(branch2.repository)
3624
 
        base_rev_id = graph.find_unique_lca(last1, last2)
3625
 
 
3626
 
        print 'merge base is revision %s' % base_rev_id
 
3547
        try:
 
3548
            branch2.lock_read()
 
3549
            try:
 
3550
                last1 = ensure_null(branch1.last_revision())
 
3551
                last2 = ensure_null(branch2.last_revision())
 
3552
 
 
3553
                graph = branch1.repository.get_graph(branch2.repository)
 
3554
                base_rev_id = graph.find_unique_lca(last1, last2)
 
3555
 
 
3556
                print 'merge base is revision %s' % base_rev_id
 
3557
            finally:
 
3558
                branch2.unlock()
 
3559
        finally:
 
3560
            branch1.unlock()
3627
3561
 
3628
3562
 
3629
3563
class cmd_merge(Command):
3662
3596
    committed to record the result of the merge.
3663
3597
 
3664
3598
    merge refuses to run if there are any uncommitted changes, unless
3665
 
    --force is given. The --force option can also be used to create a
3666
 
    merge revision which has more than two parents.
3667
 
 
3668
 
    If one would like to merge changes from the working tree of the other
3669
 
    branch without merging any committed revisions, the --uncommitted option
3670
 
    can be given.
 
3599
    --force is given.
3671
3600
 
3672
3601
    To select only some changes to merge, use "merge -i", which will prompt
3673
3602
    you to apply each diff hunk and file change, similar to "shelve".
3685
3614
 
3686
3615
            bzr merge -r 81..82 ../bzr.dev
3687
3616
 
3688
 
        To apply a merge directive contained in /tmp/merge::
 
3617
        To apply a merge directive contained in /tmp/merge:
3689
3618
 
3690
3619
            bzr merge /tmp/merge
3691
 
 
3692
 
        To create a merge revision with three parents from two branches
3693
 
        feature1a and feature1b:
3694
 
 
3695
 
            bzr merge ../feature1a
3696
 
            bzr merge ../feature1b --force
3697
 
            bzr commit -m 'revision with three parents'
3698
3620
    """
3699
3621
 
3700
3622
    encoding_type = 'exact'
3758
3680
        view_info = _get_view_info_for_change_reporter(tree)
3759
3681
        change_reporter = delta._ChangeReporter(
3760
3682
            unversioned_filter=tree.is_ignored, view_info=view_info)
3761
 
        pb = ui.ui_factory.nested_progress_bar()
3762
 
        self.add_cleanup(pb.finished)
3763
 
        tree.lock_write()
3764
 
        self.add_cleanup(tree.unlock)
3765
 
        if location is not None:
3766
 
            try:
3767
 
                mergeable = bundle.read_mergeable_from_url(location,
3768
 
                    possible_transports=possible_transports)
3769
 
            except errors.NotABundle:
3770
 
                mergeable = None
 
3683
        cleanups = []
 
3684
        try:
 
3685
            pb = ui.ui_factory.nested_progress_bar()
 
3686
            cleanups.append(pb.finished)
 
3687
            tree.lock_write()
 
3688
            cleanups.append(tree.unlock)
 
3689
            if location is not None:
 
3690
                try:
 
3691
                    mergeable = bundle.read_mergeable_from_url(location,
 
3692
                        possible_transports=possible_transports)
 
3693
                except errors.NotABundle:
 
3694
                    mergeable = None
 
3695
                else:
 
3696
                    if uncommitted:
 
3697
                        raise errors.BzrCommandError('Cannot use --uncommitted'
 
3698
                            ' with bundles or merge directives.')
 
3699
 
 
3700
                    if revision is not None:
 
3701
                        raise errors.BzrCommandError(
 
3702
                            'Cannot use -r with merge directives or bundles')
 
3703
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3704
                       mergeable, pb)
 
3705
 
 
3706
            if merger is None and uncommitted:
 
3707
                if revision is not None and len(revision) > 0:
 
3708
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3709
                        ' --revision at the same time.')
 
3710
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3711
                                                          cleanups)
 
3712
                allow_pending = False
 
3713
 
 
3714
            if merger is None:
 
3715
                merger, allow_pending = self._get_merger_from_branch(tree,
 
3716
                    location, revision, remember, possible_transports, pb)
 
3717
 
 
3718
            merger.merge_type = merge_type
 
3719
            merger.reprocess = reprocess
 
3720
            merger.show_base = show_base
 
3721
            self.sanity_check_merger(merger)
 
3722
            if (merger.base_rev_id == merger.other_rev_id and
 
3723
                merger.other_rev_id is not None):
 
3724
                note('Nothing to do.')
 
3725
                return 0
 
3726
            if pull:
 
3727
                if merger.interesting_files is not None:
 
3728
                    raise errors.BzrCommandError('Cannot pull individual files')
 
3729
                if (merger.base_rev_id == tree.last_revision()):
 
3730
                    result = tree.pull(merger.other_branch, False,
 
3731
                                       merger.other_rev_id)
 
3732
                    result.report(self.outf)
 
3733
                    return 0
 
3734
            if merger.this_basis is None:
 
3735
                raise errors.BzrCommandError(
 
3736
                    "This branch has no commits."
 
3737
                    " (perhaps you would prefer 'bzr pull')")
 
3738
            if preview:
 
3739
                return self._do_preview(merger, cleanups)
 
3740
            elif interactive:
 
3741
                return self._do_interactive(merger, cleanups)
3771
3742
            else:
3772
 
                if uncommitted:
3773
 
                    raise errors.BzrCommandError('Cannot use --uncommitted'
3774
 
                        ' with bundles or merge directives.')
3775
 
 
3776
 
                if revision is not None:
3777
 
                    raise errors.BzrCommandError(
3778
 
                        'Cannot use -r with merge directives or bundles')
3779
 
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
3780
 
                   mergeable, pb)
3781
 
 
3782
 
        if merger is None and uncommitted:
3783
 
            if revision is not None and len(revision) > 0:
3784
 
                raise errors.BzrCommandError('Cannot use --uncommitted and'
3785
 
                    ' --revision at the same time.')
3786
 
            merger = self.get_merger_from_uncommitted(tree, location, pb)
3787
 
            allow_pending = False
3788
 
 
3789
 
        if merger is None:
3790
 
            merger, allow_pending = self._get_merger_from_branch(tree,
3791
 
                location, revision, remember, possible_transports, pb)
3792
 
 
3793
 
        merger.merge_type = merge_type
3794
 
        merger.reprocess = reprocess
3795
 
        merger.show_base = show_base
3796
 
        self.sanity_check_merger(merger)
3797
 
        if (merger.base_rev_id == merger.other_rev_id and
3798
 
            merger.other_rev_id is not None):
3799
 
            note('Nothing to do.')
3800
 
            return 0
3801
 
        if pull:
3802
 
            if merger.interesting_files is not None:
3803
 
                raise errors.BzrCommandError('Cannot pull individual files')
3804
 
            if (merger.base_rev_id == tree.last_revision()):
3805
 
                result = tree.pull(merger.other_branch, False,
3806
 
                                   merger.other_rev_id)
3807
 
                result.report(self.outf)
3808
 
                return 0
3809
 
        if merger.this_basis is None:
3810
 
            raise errors.BzrCommandError(
3811
 
                "This branch has no commits."
3812
 
                " (perhaps you would prefer 'bzr pull')")
3813
 
        if preview:
3814
 
            return self._do_preview(merger)
3815
 
        elif interactive:
3816
 
            return self._do_interactive(merger)
3817
 
        else:
3818
 
            return self._do_merge(merger, change_reporter, allow_pending,
3819
 
                                  verified)
3820
 
 
3821
 
    def _get_preview(self, merger):
 
3743
                return self._do_merge(merger, change_reporter, allow_pending,
 
3744
                                      verified)
 
3745
        finally:
 
3746
            for cleanup in reversed(cleanups):
 
3747
                cleanup()
 
3748
 
 
3749
    def _get_preview(self, merger, cleanups):
3822
3750
        tree_merger = merger.make_merger()
3823
3751
        tt = tree_merger.make_preview_transform()
3824
 
        self.add_cleanup(tt.finalize)
 
3752
        cleanups.append(tt.finalize)
3825
3753
        result_tree = tt.get_preview_tree()
3826
3754
        return result_tree
3827
3755
 
3828
 
    def _do_preview(self, merger):
 
3756
    def _do_preview(self, merger, cleanups):
3829
3757
        from bzrlib.diff import show_diff_trees
3830
 
        result_tree = self._get_preview(merger)
 
3758
        result_tree = self._get_preview(merger, cleanups)
3831
3759
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3832
3760
                        old_label='', new_label='')
3833
3761
 
3843
3771
        else:
3844
3772
            return 0
3845
3773
 
3846
 
    def _do_interactive(self, merger):
 
3774
    def _do_interactive(self, merger, cleanups):
3847
3775
        """Perform an interactive merge.
3848
3776
 
3849
3777
        This works by generating a preview tree of the merge, then using
3851
3779
        and the preview tree.
3852
3780
        """
3853
3781
        from bzrlib import shelf_ui
3854
 
        result_tree = self._get_preview(merger)
 
3782
        result_tree = self._get_preview(merger, cleanups)
3855
3783
        writer = bzrlib.option.diff_writer_registry.get()
3856
3784
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3857
3785
                                   reporter=shelf_ui.ApplyReporter(),
3858
3786
                                   diff_writer=writer(sys.stdout))
3859
 
        try:
3860
 
            shelver.run()
3861
 
        finally:
3862
 
            shelver.finalize()
 
3787
        shelver.run()
3863
3788
 
3864
3789
    def sanity_check_merger(self, merger):
3865
3790
        if (merger.show_base and
3925
3850
            allow_pending = True
3926
3851
        return merger, allow_pending
3927
3852
 
3928
 
    def get_merger_from_uncommitted(self, tree, location, pb):
 
3853
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
3929
3854
        """Get a merger for uncommitted changes.
3930
3855
 
3931
3856
        :param tree: The tree the merger should apply to.
3932
3857
        :param location: The location containing uncommitted changes.
3933
3858
        :param pb: The progress bar to use for showing progress.
 
3859
        :param cleanups: A list of operations to perform to clean up the
 
3860
            temporary directories, unfinalized objects, etc.
3934
3861
        """
3935
3862
        location = self._select_branch_location(tree, location)[0]
3936
3863
        other_tree, other_path = WorkingTree.open_containing(location)
4023
3950
            merge_type = _mod_merge.Merge3Merger
4024
3951
        tree, file_list = tree_files(file_list)
4025
3952
        tree.lock_write()
4026
 
        self.add_cleanup(tree.unlock)
4027
 
        parents = tree.get_parent_ids()
4028
 
        if len(parents) != 2:
4029
 
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
4030
 
                                         " merges.  Not cherrypicking or"
4031
 
                                         " multi-merges.")
4032
 
        repository = tree.branch.repository
4033
 
        interesting_ids = None
4034
 
        new_conflicts = []
4035
 
        conflicts = tree.conflicts()
4036
 
        if file_list is not None:
4037
 
            interesting_ids = set()
4038
 
            for filename in file_list:
4039
 
                file_id = tree.path2id(filename)
4040
 
                if file_id is None:
4041
 
                    raise errors.NotVersionedError(filename)
4042
 
                interesting_ids.add(file_id)
4043
 
                if tree.kind(file_id) != "directory":
4044
 
                    continue
 
3953
        try:
 
3954
            parents = tree.get_parent_ids()
 
3955
            if len(parents) != 2:
 
3956
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
3957
                                             " merges.  Not cherrypicking or"
 
3958
                                             " multi-merges.")
 
3959
            repository = tree.branch.repository
 
3960
            interesting_ids = None
 
3961
            new_conflicts = []
 
3962
            conflicts = tree.conflicts()
 
3963
            if file_list is not None:
 
3964
                interesting_ids = set()
 
3965
                for filename in file_list:
 
3966
                    file_id = tree.path2id(filename)
 
3967
                    if file_id is None:
 
3968
                        raise errors.NotVersionedError(filename)
 
3969
                    interesting_ids.add(file_id)
 
3970
                    if tree.kind(file_id) != "directory":
 
3971
                        continue
4045
3972
 
4046
 
                for name, ie in tree.inventory.iter_entries(file_id):
4047
 
                    interesting_ids.add(ie.file_id)
4048
 
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4049
 
        else:
4050
 
            # Remerge only supports resolving contents conflicts
4051
 
            allowed_conflicts = ('text conflict', 'contents conflict')
4052
 
            restore_files = [c.path for c in conflicts
4053
 
                             if c.typestring in allowed_conflicts]
4054
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4055
 
        tree.set_conflicts(ConflictList(new_conflicts))
4056
 
        if file_list is not None:
4057
 
            restore_files = file_list
4058
 
        for filename in restore_files:
 
3973
                    for name, ie in tree.inventory.iter_entries(file_id):
 
3974
                        interesting_ids.add(ie.file_id)
 
3975
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
3976
            else:
 
3977
                # Remerge only supports resolving contents conflicts
 
3978
                allowed_conflicts = ('text conflict', 'contents conflict')
 
3979
                restore_files = [c.path for c in conflicts
 
3980
                                 if c.typestring in allowed_conflicts]
 
3981
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
3982
            tree.set_conflicts(ConflictList(new_conflicts))
 
3983
            if file_list is not None:
 
3984
                restore_files = file_list
 
3985
            for filename in restore_files:
 
3986
                try:
 
3987
                    restore(tree.abspath(filename))
 
3988
                except errors.NotConflicted:
 
3989
                    pass
 
3990
            # Disable pending merges, because the file texts we are remerging
 
3991
            # have not had those merges performed.  If we use the wrong parents
 
3992
            # list, we imply that the working tree text has seen and rejected
 
3993
            # all the changes from the other tree, when in fact those changes
 
3994
            # have not yet been seen.
 
3995
            pb = ui.ui_factory.nested_progress_bar()
 
3996
            tree.set_parent_ids(parents[:1])
4059
3997
            try:
4060
 
                restore(tree.abspath(filename))
4061
 
            except errors.NotConflicted:
4062
 
                pass
4063
 
        # Disable pending merges, because the file texts we are remerging
4064
 
        # have not had those merges performed.  If we use the wrong parents
4065
 
        # list, we imply that the working tree text has seen and rejected
4066
 
        # all the changes from the other tree, when in fact those changes
4067
 
        # have not yet been seen.
4068
 
        pb = ui.ui_factory.nested_progress_bar()
4069
 
        tree.set_parent_ids(parents[:1])
4070
 
        try:
4071
 
            merger = _mod_merge.Merger.from_revision_ids(pb,
4072
 
                                                         tree, parents[1])
4073
 
            merger.interesting_ids = interesting_ids
4074
 
            merger.merge_type = merge_type
4075
 
            merger.show_base = show_base
4076
 
            merger.reprocess = reprocess
4077
 
            conflicts = merger.do_merge()
 
3998
                merger = _mod_merge.Merger.from_revision_ids(pb,
 
3999
                                                             tree, parents[1])
 
4000
                merger.interesting_ids = interesting_ids
 
4001
                merger.merge_type = merge_type
 
4002
                merger.show_base = show_base
 
4003
                merger.reprocess = reprocess
 
4004
                conflicts = merger.do_merge()
 
4005
            finally:
 
4006
                tree.set_parent_ids(parents)
 
4007
                pb.finished()
4078
4008
        finally:
4079
 
            tree.set_parent_ids(parents)
4080
 
            pb.finished()
 
4009
            tree.unlock()
4081
4010
        if conflicts > 0:
4082
4011
            return 1
4083
4012
        else:
4105
4034
    name.  If you name a directory, all the contents of that directory will be
4106
4035
    reverted.
4107
4036
 
4108
 
    If you have newly added files since the target revision, they will be
4109
 
    removed.  If the files to be removed have been changed, backups will be
4110
 
    created as above.  Directories containing unknown files will not be
4111
 
    deleted.
 
4037
    Any files that have been newly added since that revision will be deleted,
 
4038
    with a backup kept if appropriate.  Directories containing unknown files
 
4039
    will not be deleted.
4112
4040
 
4113
 
    The working tree contains a list of revisions that have been merged but
4114
 
    not yet committed. These revisions will be included as additional parents
4115
 
    of the next commit.  Normally, using revert clears that list as well as
4116
 
    reverting the files.  If any files are specified, revert leaves the list
4117
 
    of uncommitted merges alone and reverts only the files.  Use ``bzr revert
4118
 
    .`` in the tree root to revert all files but keep the recorded merges,
4119
 
    and ``bzr revert --forget-merges`` to clear the pending merge list without
 
4041
    The working tree contains a list of pending merged revisions, which will
 
4042
    be included as parents in the next commit.  Normally, revert clears that
 
4043
    list as well as reverting the files.  If any files are specified, revert
 
4044
    leaves the pending merge list alone and reverts only the files.  Use "bzr
 
4045
    revert ." in the tree root to revert all files but keep the merge record,
 
4046
    and "bzr revert --forget-merges" to clear the pending merge list without
4120
4047
    reverting any files.
4121
 
 
4122
 
    Using "bzr revert --forget-merges", it is possible to apply all of the
4123
 
    changes from a branch in a single revision.  To do this, perform the merge
4124
 
    as desired.  Then doing revert with the "--forget-merges" option will keep
4125
 
    the content of the tree as it was, but it will clear the list of pending
4126
 
    merges.  The next commit will then contain all of the changes that are
4127
 
    present in the other branch, but without any other parent revisions.
4128
 
    Because this technique forgets where these changes originated, it may
4129
 
    cause additional conflicts on later merges involving the same source and
4130
 
    target branches.
4131
4048
    """
4132
4049
 
4133
4050
    _see_also = ['cat', 'export']
4143
4060
            forget_merges=None):
4144
4061
        tree, file_list = tree_files(file_list)
4145
4062
        tree.lock_write()
4146
 
        self.add_cleanup(tree.unlock)
4147
 
        if forget_merges:
4148
 
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4149
 
        else:
4150
 
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4063
        try:
 
4064
            if forget_merges:
 
4065
                tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4066
            else:
 
4067
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4068
        finally:
 
4069
            tree.unlock()
4151
4070
 
4152
4071
    @staticmethod
4153
4072
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4212
4131
    To filter on a range of revisions, you can use the command -r begin..end
4213
4132
    -r revision requests a specific revision, -r ..end or -r begin.. are
4214
4133
    also valid.
4215
 
            
4216
 
    :Exit values:
4217
 
        1 - some missing revisions
4218
 
        0 - no missing revisions
4219
4134
 
4220
4135
    :Examples:
4221
4136
 
4304
4219
        if remote_branch.base == local_branch.base:
4305
4220
            remote_branch = local_branch
4306
4221
 
4307
 
        local_branch.lock_read()
4308
 
        self.add_cleanup(local_branch.unlock)
4309
4222
        local_revid_range = _revision_range_to_revid_range(
4310
4223
            _get_revision_range(my_revision, local_branch,
4311
4224
                self.name()))
4312
4225
 
4313
 
        remote_branch.lock_read()
4314
 
        self.add_cleanup(remote_branch.unlock)
4315
4226
        remote_revid_range = _revision_range_to_revid_range(
4316
4227
            _get_revision_range(revision,
4317
4228
                remote_branch, self.name()))
4318
4229
 
4319
 
        local_extra, remote_extra = find_unmerged(
4320
 
            local_branch, remote_branch, restrict,
4321
 
            backward=not reverse,
4322
 
            include_merges=include_merges,
4323
 
            local_revid_range=local_revid_range,
4324
 
            remote_revid_range=remote_revid_range)
4325
 
 
4326
 
        if log_format is None:
4327
 
            registry = log.log_formatter_registry
4328
 
            log_format = registry.get_default(local_branch)
4329
 
        lf = log_format(to_file=self.outf,
4330
 
                        show_ids=show_ids,
4331
 
                        show_timezone='original')
4332
 
 
4333
 
        status_code = 0
4334
 
        if local_extra and not theirs_only:
4335
 
            message("You have %d extra revision(s):\n" %
4336
 
                len(local_extra))
4337
 
            for revision in iter_log_revisions(local_extra,
4338
 
                                local_branch.repository,
4339
 
                                verbose):
4340
 
                lf.log_revision(revision)
4341
 
            printed_local = True
4342
 
            status_code = 1
4343
 
        else:
4344
 
            printed_local = False
4345
 
 
4346
 
        if remote_extra and not mine_only:
4347
 
            if printed_local is True:
4348
 
                message("\n\n\n")
4349
 
            message("You are missing %d revision(s):\n" %
4350
 
                len(remote_extra))
4351
 
            for revision in iter_log_revisions(remote_extra,
4352
 
                                remote_branch.repository,
4353
 
                                verbose):
4354
 
                lf.log_revision(revision)
4355
 
            status_code = 1
4356
 
 
4357
 
        if mine_only and not local_extra:
4358
 
            # We checked local, and found nothing extra
4359
 
            message('This branch is up to date.\n')
4360
 
        elif theirs_only and not remote_extra:
4361
 
            # We checked remote, and found nothing extra
4362
 
            message('Other branch is up to date.\n')
4363
 
        elif not (mine_only or theirs_only or local_extra or
4364
 
                  remote_extra):
4365
 
            # We checked both branches, and neither one had extra
4366
 
            # revisions
4367
 
            message("Branches are up to date.\n")
4368
 
        self.cleanup_now()
 
4230
        local_branch.lock_read()
 
4231
        try:
 
4232
            remote_branch.lock_read()
 
4233
            try:
 
4234
                local_extra, remote_extra = find_unmerged(
 
4235
                    local_branch, remote_branch, restrict,
 
4236
                    backward=not reverse,
 
4237
                    include_merges=include_merges,
 
4238
                    local_revid_range=local_revid_range,
 
4239
                    remote_revid_range=remote_revid_range)
 
4240
 
 
4241
                if log_format is None:
 
4242
                    registry = log.log_formatter_registry
 
4243
                    log_format = registry.get_default(local_branch)
 
4244
                lf = log_format(to_file=self.outf,
 
4245
                                show_ids=show_ids,
 
4246
                                show_timezone='original')
 
4247
 
 
4248
                status_code = 0
 
4249
                if local_extra and not theirs_only:
 
4250
                    message("You have %d extra revision(s):\n" %
 
4251
                        len(local_extra))
 
4252
                    for revision in iter_log_revisions(local_extra,
 
4253
                                        local_branch.repository,
 
4254
                                        verbose):
 
4255
                        lf.log_revision(revision)
 
4256
                    printed_local = True
 
4257
                    status_code = 1
 
4258
                else:
 
4259
                    printed_local = False
 
4260
 
 
4261
                if remote_extra and not mine_only:
 
4262
                    if printed_local is True:
 
4263
                        message("\n\n\n")
 
4264
                    message("You are missing %d revision(s):\n" %
 
4265
                        len(remote_extra))
 
4266
                    for revision in iter_log_revisions(remote_extra,
 
4267
                                        remote_branch.repository,
 
4268
                                        verbose):
 
4269
                        lf.log_revision(revision)
 
4270
                    status_code = 1
 
4271
 
 
4272
                if mine_only and not local_extra:
 
4273
                    # We checked local, and found nothing extra
 
4274
                    message('This branch is up to date.\n')
 
4275
                elif theirs_only and not remote_extra:
 
4276
                    # We checked remote, and found nothing extra
 
4277
                    message('Other branch is up to date.\n')
 
4278
                elif not (mine_only or theirs_only or local_extra or
 
4279
                          remote_extra):
 
4280
                    # We checked both branches, and neither one had extra
 
4281
                    # revisions
 
4282
                    message("Branches are up to date.\n")
 
4283
            finally:
 
4284
                remote_branch.unlock()
 
4285
        finally:
 
4286
            local_branch.unlock()
4369
4287
        if not status_code and parent is None and other_branch is not None:
4370
4288
            local_branch.lock_write()
4371
 
            self.add_cleanup(local_branch.unlock)
4372
 
            # handle race conditions - a parent might be set while we run.
4373
 
            if local_branch.get_parent() is None:
4374
 
                local_branch.set_parent(remote_branch.base)
 
4289
            try:
 
4290
                # handle race conditions - a parent might be set while we run.
 
4291
                if local_branch.get_parent() is None:
 
4292
                    local_branch.set_parent(remote_branch.base)
 
4293
            finally:
 
4294
                local_branch.unlock()
4375
4295
        return status_code
4376
4296
 
4377
4297
 
4405
4325
    adding new commands, providing additional network transports and
4406
4326
    customizing log output.
4407
4327
 
4408
 
    See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
4409
 
    for further information on plugins including where to find them and how to
4410
 
    install them. Instructions are also provided there on how to write new
4411
 
    plugins using the Python programming language.
 
4328
    See the Bazaar web site, http://bazaar-vcs.org, for further
 
4329
    information on plugins including where to find them and how to
 
4330
    install them. Instructions are also provided there on how to
 
4331
    write new plugins using the Python programming language.
4412
4332
    """
4413
4333
    takes_options = ['verbose']
4414
4334
 
4456
4376
        else:
4457
4377
            b = Branch.open(branch)
4458
4378
        b.lock_read()
4459
 
        self.add_cleanup(b.unlock)
4460
 
        if revision is None:
4461
 
            rev_id = b.last_revision()
4462
 
        else:
4463
 
            rev_id = revision[0].as_revision_id(b)
4464
 
        t = testament_class.from_revision(b.repository, rev_id)
4465
 
        if long:
4466
 
            sys.stdout.writelines(t.as_text_lines())
4467
 
        else:
4468
 
            sys.stdout.write(t.as_short_text())
 
4379
        try:
 
4380
            if revision is None:
 
4381
                rev_id = b.last_revision()
 
4382
            else:
 
4383
                rev_id = revision[0].as_revision_id(b)
 
4384
            t = testament_class.from_revision(b.repository, rev_id)
 
4385
            if long:
 
4386
                sys.stdout.writelines(t.as_text_lines())
 
4387
            else:
 
4388
                sys.stdout.write(t.as_short_text())
 
4389
        finally:
 
4390
            b.unlock()
4469
4391
 
4470
4392
 
4471
4393
class cmd_annotate(Command):
4497
4419
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4498
4420
        if wt is not None:
4499
4421
            wt.lock_read()
4500
 
            self.add_cleanup(wt.unlock)
4501
4422
        else:
4502
4423
            branch.lock_read()
4503
 
            self.add_cleanup(branch.unlock)
4504
 
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
4505
 
        tree.lock_read()
4506
 
        self.add_cleanup(tree.unlock)
4507
 
        if wt is not None:
4508
 
            file_id = wt.path2id(relpath)
4509
 
        else:
4510
 
            file_id = tree.path2id(relpath)
4511
 
        if file_id is None:
4512
 
            raise errors.NotVersionedError(filename)
4513
 
        file_version = tree.inventory[file_id].revision
4514
 
        if wt is not None and revision is None:
4515
 
            # If there is a tree and we're not annotating historical
4516
 
            # versions, annotate the working tree's content.
4517
 
            annotate_file_tree(wt, file_id, self.outf, long, all,
4518
 
                show_ids=show_ids)
4519
 
        else:
4520
 
            annotate_file(branch, file_version, file_id, long, all, self.outf,
4521
 
                          show_ids=show_ids)
 
4424
        try:
 
4425
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4426
            if wt is not None:
 
4427
                file_id = wt.path2id(relpath)
 
4428
            else:
 
4429
                file_id = tree.path2id(relpath)
 
4430
            if file_id is None:
 
4431
                raise errors.NotVersionedError(filename)
 
4432
            file_version = tree.inventory[file_id].revision
 
4433
            if wt is not None and revision is None:
 
4434
                # If there is a tree and we're not annotating historical
 
4435
                # versions, annotate the working tree's content.
 
4436
                annotate_file_tree(wt, file_id, self.outf, long, all,
 
4437
                    show_ids=show_ids)
 
4438
            else:
 
4439
                annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4440
                              show_ids=show_ids)
 
4441
        finally:
 
4442
            if wt is not None:
 
4443
                wt.unlock()
 
4444
            else:
 
4445
                branch.unlock()
4522
4446
 
4523
4447
 
4524
4448
class cmd_re_sign(Command):
4536
4460
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4537
4461
        b = WorkingTree.open_containing(u'.')[0].branch
4538
4462
        b.lock_write()
4539
 
        self.add_cleanup(b.unlock)
4540
 
        return self._run(b, revision_id_list, revision)
 
4463
        try:
 
4464
            return self._run(b, revision_id_list, revision)
 
4465
        finally:
 
4466
            b.unlock()
4541
4467
 
4542
4468
    def _run(self, b, revision_id_list, revision):
4543
4469
        import bzrlib.gpg as gpg
4689
4615
 
4690
4616
        if tree is not None:
4691
4617
            tree.lock_write()
4692
 
            self.add_cleanup(tree.unlock)
4693
4618
        else:
4694
4619
            b.lock_write()
4695
 
            self.add_cleanup(b.unlock)
4696
 
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
 
4620
        try:
 
4621
            return self._run(b, tree, dry_run, verbose, revision, force,
 
4622
                             local=local)
 
4623
        finally:
 
4624
            if tree is not None:
 
4625
                tree.unlock()
 
4626
            else:
 
4627
                b.unlock()
4697
4628
 
4698
4629
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4699
4630
        from bzrlib.log import log_formatter, show_log
4756
4687
    CAUTION: Locks should only be broken when you are sure that the process
4757
4688
    holding the lock has been stopped.
4758
4689
 
4759
 
    You can get information on what locks are open via the 'bzr info
4760
 
    [location]' command.
 
4690
    You can get information on what locks are open via the 'bzr info' command.
4761
4691
 
4762
4692
    :Examples:
4763
4693
        bzr break-lock
4764
 
        bzr break-lock bzr+ssh://example.com/bzr/foo
4765
4694
    """
4766
4695
    takes_args = ['location?']
4767
4696
 
4797
4726
    takes_options = [
4798
4727
        Option('inet',
4799
4728
               help='Serve on stdin/out for use from inetd or sshd.'),
4800
 
        RegistryOption('protocol',
4801
 
               help="Protocol to serve.",
 
4729
        RegistryOption('protocol', 
 
4730
               help="Protocol to serve.", 
4802
4731
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
4803
4732
               value_switches=True),
4804
4733
        Option('port',
4813
4742
        Option('allow-writes',
4814
4743
               help='By default the server is a readonly server.  Supplying '
4815
4744
                    '--allow-writes enables write access to the contents of '
4816
 
                    'the served directory and below.  Note that ``bzr serve`` '
4817
 
                    'does not perform authentication, so unless some form of '
4818
 
                    'external authentication is arranged supplying this '
4819
 
                    'option leads to global uncontrolled write access to your '
4820
 
                    'file system.'
 
4745
                    'the served directory and below.'
4821
4746
                ),
4822
4747
        ]
4823
4748
 
5039
4964
      directly from the merge directive, without retrieving data from a
5040
4965
      branch.
5041
4966
 
5042
 
    `bzr send` creates a compact data set that, when applied using bzr
5043
 
    merge, has the same effect as merging from the source branch.  
5044
 
    
5045
 
    By default the merge directive is self-contained and can be applied to any
5046
 
    branch containing submit_branch in its ancestory without needing access to
5047
 
    the source branch.
5048
 
    
5049
 
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
5050
 
    revisions, but only a structured request to merge from the
5051
 
    public_location.  In that case the public_branch is needed and it must be
5052
 
    up-to-date and accessible to the recipient.  The public_branch is always
5053
 
    included if known, so that people can check it later.
5054
 
 
5055
 
    The submit branch defaults to the parent of the source branch, but can be
5056
 
    overridden.  Both submit branch and public branch will be remembered in
5057
 
    branch.conf the first time they are used for a particular branch.  The
5058
 
    source branch defaults to that containing the working directory, but can
5059
 
    be changed using --from.
5060
 
 
5061
 
    In order to calculate those changes, bzr must analyse the submit branch.
5062
 
    Therefore it is most efficient for the submit branch to be a local mirror.
5063
 
    If a public location is known for the submit_branch, that location is used
5064
 
    in the merge directive.
5065
 
 
5066
 
    The default behaviour is to send the merge directive by mail, unless -o is
5067
 
    given, in which case it is sent to a file.
 
4967
    If --no-bundle is specified, then public_branch is needed (and must be
 
4968
    up-to-date), so that the receiver can perform the merge using the
 
4969
    public_branch.  The public_branch is always included if known, so that
 
4970
    people can check it later.
 
4971
 
 
4972
    The submit branch defaults to the parent, but can be overridden.  Both
 
4973
    submit branch and public branch will be remembered if supplied.
 
4974
 
 
4975
    If a public_branch is known for the submit_branch, that public submit
 
4976
    branch is used in the merge instructions.  This means that a local mirror
 
4977
    can be used as your actual submit branch, once you have set public_branch
 
4978
    for that mirror.
5068
4979
 
5069
4980
    Mail is sent using your preferred mail program.  This should be transparent
5070
4981
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
5090
5001
 
5091
5002
    The merge directives created by bzr send may be applied using bzr merge or
5092
5003
    bzr pull by specifying a file containing a merge directive as the location.
5093
 
 
5094
 
    bzr send makes extensive use of public locations to map local locations into
5095
 
    URLs that can be used by other people.  See `bzr help configuration` to
5096
 
    set them, and use `bzr info` to display them.
5097
5004
    """
5098
5005
 
5099
5006
    encoding_type = 'exact'
5258
5165
            ):
5259
5166
        branch, relpath = Branch.open_containing(directory)
5260
5167
        branch.lock_write()
5261
 
        self.add_cleanup(branch.unlock)
5262
 
        if delete:
5263
 
            branch.tags.delete_tag(tag_name)
5264
 
            self.outf.write('Deleted tag %s.\n' % tag_name)
5265
 
        else:
5266
 
            if revision:
5267
 
                if len(revision) != 1:
5268
 
                    raise errors.BzrCommandError(
5269
 
                        "Tags can only be placed on a single revision, "
5270
 
                        "not on a range")
5271
 
                revision_id = revision[0].as_revision_id(branch)
 
5168
        try:
 
5169
            if delete:
 
5170
                branch.tags.delete_tag(tag_name)
 
5171
                self.outf.write('Deleted tag %s.\n' % tag_name)
5272
5172
            else:
5273
 
                revision_id = branch.last_revision()
5274
 
            if (not force) and branch.tags.has_tag(tag_name):
5275
 
                raise errors.TagAlreadyExists(tag_name)
5276
 
            branch.tags.set_tag(tag_name, revision_id)
5277
 
            self.outf.write('Created tag %s.\n' % tag_name)
 
5173
                if revision:
 
5174
                    if len(revision) != 1:
 
5175
                        raise errors.BzrCommandError(
 
5176
                            "Tags can only be placed on a single revision, "
 
5177
                            "not on a range")
 
5178
                    revision_id = revision[0].as_revision_id(branch)
 
5179
                else:
 
5180
                    revision_id = branch.last_revision()
 
5181
                if (not force) and branch.tags.has_tag(tag_name):
 
5182
                    raise errors.TagAlreadyExists(tag_name)
 
5183
                branch.tags.set_tag(tag_name, revision_id)
 
5184
                self.outf.write('Created tag %s.\n' % tag_name)
 
5185
        finally:
 
5186
            branch.unlock()
5278
5187
 
5279
5188
 
5280
5189
class cmd_tags(Command):
5313
5222
            return
5314
5223
 
5315
5224
        branch.lock_read()
5316
 
        self.add_cleanup(branch.unlock)
5317
 
        if revision:
5318
 
            graph = branch.repository.get_graph()
5319
 
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
5320
 
            revid1, revid2 = rev1.rev_id, rev2.rev_id
5321
 
            # only show revisions between revid1 and revid2 (inclusive)
5322
 
            tags = [(tag, revid) for tag, revid in tags if
5323
 
                graph.is_between(revid, revid1, revid2)]
5324
 
        if sort == 'alpha':
5325
 
            tags.sort()
5326
 
        elif sort == 'time':
5327
 
            timestamps = {}
5328
 
            for tag, revid in tags:
5329
 
                try:
5330
 
                    revobj = branch.repository.get_revision(revid)
5331
 
                except errors.NoSuchRevision:
5332
 
                    timestamp = sys.maxint # place them at the end
5333
 
                else:
5334
 
                    timestamp = revobj.timestamp
5335
 
                timestamps[revid] = timestamp
5336
 
            tags.sort(key=lambda x: timestamps[x[1]])
5337
 
        if not show_ids:
5338
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5339
 
            for index, (tag, revid) in enumerate(tags):
5340
 
                try:
5341
 
                    revno = branch.revision_id_to_dotted_revno(revid)
5342
 
                    if isinstance(revno, tuple):
5343
 
                        revno = '.'.join(map(str, revno))
5344
 
                except errors.NoSuchRevision:
5345
 
                    # Bad tag data/merges can lead to tagged revisions
5346
 
                    # which are not in this branch. Fail gracefully ...
5347
 
                    revno = '?'
5348
 
                tags[index] = (tag, revno)
5349
 
        self.cleanup_now()
 
5225
        try:
 
5226
            if revision:
 
5227
                graph = branch.repository.get_graph()
 
5228
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5229
                revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5230
                # only show revisions between revid1 and revid2 (inclusive)
 
5231
                tags = [(tag, revid) for tag, revid in tags if
 
5232
                    graph.is_between(revid, revid1, revid2)]
 
5233
            if sort == 'alpha':
 
5234
                tags.sort()
 
5235
            elif sort == 'time':
 
5236
                timestamps = {}
 
5237
                for tag, revid in tags:
 
5238
                    try:
 
5239
                        revobj = branch.repository.get_revision(revid)
 
5240
                    except errors.NoSuchRevision:
 
5241
                        timestamp = sys.maxint # place them at the end
 
5242
                    else:
 
5243
                        timestamp = revobj.timestamp
 
5244
                    timestamps[revid] = timestamp
 
5245
                tags.sort(key=lambda x: timestamps[x[1]])
 
5246
            if not show_ids:
 
5247
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5248
                for index, (tag, revid) in enumerate(tags):
 
5249
                    try:
 
5250
                        revno = branch.revision_id_to_dotted_revno(revid)
 
5251
                        if isinstance(revno, tuple):
 
5252
                            revno = '.'.join(map(str, revno))
 
5253
                    except errors.NoSuchRevision:
 
5254
                        # Bad tag data/merges can lead to tagged revisions
 
5255
                        # which are not in this branch. Fail gracefully ...
 
5256
                        revno = '?'
 
5257
                    tags[index] = (tag, revno)
 
5258
        finally:
 
5259
            branch.unlock()
5350
5260
        for tag, revspec in tags:
5351
5261
            self.outf.write('%-20s %s\n' % (tag, revspec))
5352
5262
 
5465
5375
    that of the master.
5466
5376
    """
5467
5377
 
5468
 
    takes_args = ['to_location?']
 
5378
    takes_args = ['to_location']
5469
5379
    takes_options = [Option('force',
5470
5380
                        help='Switch even if local commits will be lost.'),
5471
 
                     'revision',
5472
5381
                     Option('create-branch', short_name='b',
5473
5382
                        help='Create the target branch from this one before'
5474
5383
                             ' switching to it.'),
5475
 
                    ]
 
5384
                     ]
5476
5385
 
5477
 
    def run(self, to_location=None, force=False, create_branch=False,
5478
 
            revision=None):
 
5386
    def run(self, to_location, force=False, create_branch=False):
5479
5387
        from bzrlib import switch
5480
5388
        tree_location = '.'
5481
 
        revision = _get_one_revision('switch', revision)
5482
5389
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5483
 
        if to_location is None:
5484
 
            if revision is None:
5485
 
                raise errors.BzrCommandError('You must supply either a'
5486
 
                                             ' revision or a location')
5487
 
            to_location = '.'
5488
5390
        try:
5489
5391
            branch = control_dir.open_branch()
5490
5392
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5495
5397
            if branch is None:
5496
5398
                raise errors.BzrCommandError('cannot create branch without'
5497
5399
                                             ' source branch')
5498
 
            to_location = directory_service.directories.dereference(
5499
 
                              to_location)
5500
5400
            if '/' not in to_location and '\\' not in to_location:
5501
5401
                # This path is meant to be relative to the existing branch
5502
5402
                this_url = self._get_branch_location(control_dir)
5504
5404
            to_branch = branch.bzrdir.sprout(to_location,
5505
5405
                                 possible_transports=[branch.bzrdir.root_transport],
5506
5406
                                 source_branch=branch).open_branch()
 
5407
            # try:
 
5408
            #     from_branch = control_dir.open_branch()
 
5409
            # except errors.NotBranchError:
 
5410
            #     raise BzrCommandError('Cannot create a branch from this'
 
5411
            #         ' location when we cannot open this branch')
 
5412
            # from_branch.bzrdir.sprout(
 
5413
            pass
5507
5414
        else:
5508
5415
            try:
5509
5416
                to_branch = Branch.open(to_location)
5511
5418
                this_url = self._get_branch_location(control_dir)
5512
5419
                to_branch = Branch.open(
5513
5420
                    urlutils.join(this_url, '..', to_location))
5514
 
        if revision is not None:
5515
 
            revision = revision.as_revision_id(to_branch)
5516
 
        switch.switch(control_dir, to_branch, force, revision_id=revision)
 
5421
        switch.switch(control_dir, to_branch, force)
5517
5422
        if had_explicit_nick:
5518
5423
            branch = control_dir.open_branch() #get the new branch!
5519
5424
            branch.nick = to_branch.nick
5766
5671
            try:
5767
5672
                shelver.run()
5768
5673
            finally:
5769
 
                shelver.finalize()
 
5674
                shelver.work_tree.unlock()
5770
5675
        except errors.UserAbort:
5771
5676
            return 0
5772
5677
 
5773
5678
    def run_for_list(self):
5774
5679
        tree = WorkingTree.open_containing('.')[0]
5775
5680
        tree.lock_read()
5776
 
        self.add_cleanup(tree.unlock)
5777
 
        manager = tree.get_shelf_manager()
5778
 
        shelves = manager.active_shelves()
5779
 
        if len(shelves) == 0:
5780
 
            note('No shelved changes.')
5781
 
            return 0
5782
 
        for shelf_id in reversed(shelves):
5783
 
            message = manager.get_metadata(shelf_id).get('message')
5784
 
            if message is None:
5785
 
                message = '<no message>'
5786
 
            self.outf.write('%3d: %s\n' % (shelf_id, message))
5787
 
        return 1
 
5681
        try:
 
5682
            manager = tree.get_shelf_manager()
 
5683
            shelves = manager.active_shelves()
 
5684
            if len(shelves) == 0:
 
5685
                note('No shelved changes.')
 
5686
                return 0
 
5687
            for shelf_id in reversed(shelves):
 
5688
                message = manager.get_metadata(shelf_id).get('message')
 
5689
                if message is None:
 
5690
                    message = '<no message>'
 
5691
                self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5692
            return 1
 
5693
        finally:
 
5694
            tree.unlock()
5788
5695
 
5789
5696
 
5790
5697
class cmd_unshelve(Command):
5802
5709
            enum_switch=False, value_switches=True,
5803
5710
            apply="Apply changes and remove from the shelf.",
5804
5711
            dry_run="Show changes, but do not apply or remove them.",
5805
 
            preview="Instead of unshelving the changes, show the diff that "
5806
 
                    "would result from unshelving.",
5807
 
            delete_only="Delete changes without applying them.",
5808
 
            keep="Apply changes but don't delete them.",
 
5712
            delete_only="Delete changes without applying them."
5809
5713
        )
5810
5714
    ]
5811
5715
    _see_also = ['shelve']