~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-17 17:11:16 UTC
  • mfrom: (4797.2.17 2.1)
  • mto: (4797.2.18 2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: john@arbash-meinel.com-20100217171116-h7t9223ystbnx5h8
merge bzr.2.1 in preparation for NEWS entry.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
    bundle,
32
32
    btree_index,
33
33
    bzrdir,
 
34
    directory_service,
34
35
    delta,
35
36
    config,
36
37
    errors,
43
44
    reconfigure,
44
45
    rename_map,
45
46
    revision as _mod_revision,
 
47
    static_tuple,
46
48
    symbol_versioning,
 
49
    timestamp,
47
50
    transport,
48
51
    ui,
49
52
    urlutils,
257
260
    unknown
258
261
        Not versioned and not matching an ignore pattern.
259
262
 
 
263
    Additionally for directories, symlinks and files with an executable
 
264
    bit, Bazaar indicates their type using a trailing character: '/', '@'
 
265
    or '*' respectively.
 
266
 
260
267
    To see ignored files use 'bzr ignored'.  For details on the
261
268
    changes to file texts, use 'bzr diff'.
262
269
 
431
438
        for node in bt.iter_all_entries():
432
439
            # Node is made up of:
433
440
            # (index, key, value, [references])
434
 
            refs_as_tuples = tuple([tuple([tuple(ref) for ref in ref_list])
435
 
                                   for ref_list in node[3]])
 
441
            refs_as_tuples = static_tuple.as_tuples(node[3])
436
442
            as_tuple = (tuple(node[1]), node[2], refs_as_tuples)
437
443
            self.outf.write('%s\n' % (as_tuple,))
438
444
 
496
502
                wt.lock_read()
497
503
            except (errors.NoWorkingTree, errors.NotLocalUrl):
498
504
                raise errors.NoWorkingTree(location)
 
505
            self.add_cleanup(wt.unlock)
 
506
            revid = wt.last_revision()
499
507
            try:
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()
 
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)
508
512
        else:
509
513
            b = Branch.open_containing(location)[0]
510
514
            b.lock_read()
511
 
            try:
512
 
                revno = b.revno()
513
 
            finally:
514
 
                b.unlock()
515
 
 
 
515
            self.add_cleanup(b.unlock)
 
516
            revno = b.revno()
 
517
        self.cleanup_now()
516
518
        self.outf.write(str(revno) + '\n')
517
519
 
518
520
 
540
542
            wt = WorkingTree.open_containing(directory)[0]
541
543
            b = wt.branch
542
544
            wt.lock_read()
 
545
            self.add_cleanup(wt.unlock)
543
546
        except (errors.NoWorkingTree, errors.NotLocalUrl):
544
547
            wt = None
545
548
            b = Branch.open_containing(directory)[0]
546
549
            b.lock_read()
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()
 
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())
577
564
            else:
578
 
                wt.unlock()
579
 
 
 
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()
580
579
        for ri in revinfos:
581
580
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
582
581
 
654
653
 
655
654
        if base_tree:
656
655
            base_tree.lock_read()
657
 
        try:
658
 
            file_list = self._maybe_expand_globs(file_list)
659
 
            tree, file_list = tree_files_for_add(file_list)
660
 
            added, ignored = tree.smart_add(file_list, not
661
 
                no_recurse, action=action, save=not dry_run)
662
 
        finally:
663
 
            if base_tree is not None:
664
 
                base_tree.unlock()
 
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()
665
661
        if len(ignored) > 0:
666
662
            if verbose:
667
663
                for glob in sorted(ignored.keys()):
731
727
        revision = _get_one_revision('inventory', revision)
732
728
        work_tree, file_list = tree_files(file_list)
733
729
        work_tree.lock_read()
734
 
        try:
735
 
            if revision is not None:
736
 
                tree = revision.as_tree(work_tree.branch)
737
 
 
738
 
                extra_trees = [work_tree]
739
 
                tree.lock_read()
740
 
            else:
741
 
                tree = work_tree
742
 
                extra_trees = []
743
 
 
744
 
            if file_list is not None:
745
 
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
746
 
                                          require_versioned=True)
747
 
                # find_ids_across_trees may include some paths that don't
748
 
                # exist in 'tree'.
749
 
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
750
 
                                 for file_id in file_ids if file_id in tree)
751
 
            else:
752
 
                entries = tree.inventory.entries()
753
 
        finally:
754
 
            tree.unlock()
755
 
            if tree is not work_tree:
756
 
                work_tree.unlock()
757
 
 
 
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()
758
752
        for path, entry in entries:
759
753
            if kind and kind != entry.kind:
760
754
                continue
806
800
            raise errors.BzrCommandError("missing file argument")
807
801
        tree, rel_names = tree_files(names_list, canonicalize=False)
808
802
        tree.lock_tree_write()
809
 
        try:
810
 
            self._run(tree, names_list, rel_names, after)
811
 
        finally:
812
 
            tree.unlock()
 
803
        self.add_cleanup(tree.unlock)
 
804
        self._run(tree, names_list, rel_names, after)
813
805
 
814
806
    def run_auto(self, names_list, after, dry_run):
815
807
        if names_list is not None and len(names_list) > 1:
820
812
                                         ' --auto.')
821
813
        work_tree, file_list = tree_files(names_list, default_branch='.')
822
814
        work_tree.lock_tree_write()
823
 
        try:
824
 
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
825
 
        finally:
826
 
            work_tree.unlock()
 
815
        self.add_cleanup(work_tree.unlock)
 
816
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
827
817
 
828
818
    def _run(self, tree, names_list, rel_names, after):
829
819
        into_existing = osutils.isdir(names_list[-1])
850
840
            # All entries reference existing inventory items, so fix them up
851
841
            # for cicp file-systems.
852
842
            rel_names = tree.get_canonical_inventory_paths(rel_names)
853
 
            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
854
 
                self.outf.write("%s => %s\n" % pair)
 
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))
855
846
        else:
856
847
            if len(names_list) != 2:
857
848
                raise errors.BzrCommandError('to mv multiple files the'
901
892
            dest = osutils.pathjoin(dest_parent, dest_tail)
902
893
            mutter("attempting to move %s => %s", src, dest)
903
894
            tree.rename_one(src, dest, after=after)
904
 
            self.outf.write("%s => %s\n" % (src, dest))
 
895
            if not is_quiet():
 
896
                self.outf.write("%s => %s\n" % (src, dest))
905
897
 
906
898
 
907
899
class cmd_pull(Command):
908
900
    """Turn this branch into a mirror of another branch.
909
901
 
910
 
    This command only works on branches that have not diverged.  Branches are
911
 
    considered diverged if the destination branch's most recent commit is one
912
 
    that has not been merged (directly or indirectly) into the parent.
 
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.
913
906
 
914
907
    If branches have diverged, you can use 'bzr merge' to integrate the changes
915
908
    from one into the other.  Once one branch has merged, the other should
916
909
    be able to pull it again.
917
910
 
918
 
    If you want to forget your local changes and just update your branch to
919
 
    match the remote one, use pull --overwrite.
 
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.
920
914
 
921
915
    If there is no default location set, the first pull will set it.  After
922
916
    that, you can omit the location to use the default.  To change the
1003
997
 
1004
998
        if branch_from is not branch_to:
1005
999
            branch_from.lock_read()
1006
 
        try:
1007
 
            if revision is not None:
1008
 
                revision_id = revision.as_revision_id(branch_from)
1009
 
 
1010
 
            branch_to.lock_write()
1011
 
            try:
1012
 
                if tree_to is not None:
1013
 
                    view_info = _get_view_info_for_change_reporter(tree_to)
1014
 
                    change_reporter = delta._ChangeReporter(
1015
 
                        unversioned_filter=tree_to.is_ignored,
1016
 
                        view_info=view_info)
1017
 
                    result = tree_to.pull(
1018
 
                        branch_from, overwrite, revision_id, change_reporter,
1019
 
                        possible_transports=possible_transports, local=local)
1020
 
                else:
1021
 
                    result = branch_to.pull(
1022
 
                        branch_from, overwrite, revision_id, local=local)
1023
 
 
1024
 
                result.report(self.outf)
1025
 
                if verbose and result.old_revid != result.new_revid:
1026
 
                    log.show_branch_change(
1027
 
                        branch_to, self.outf, result.old_revno,
1028
 
                        result.old_revid)
1029
 
            finally:
1030
 
                branch_to.unlock()
1031
 
        finally:
1032
 
            if branch_from is not branch_to:
1033
 
                branch_from.unlock()
 
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)
1034
1023
 
1035
1024
 
1036
1025
class cmd_push(Command):
1191
1180
                    ' directory exists, but does not already'
1192
1181
                    ' have a control directory.  This flag will'
1193
1182
                    ' allow branch to proceed.'),
 
1183
        Option('bind',
 
1184
            help="Bind new branch to from location."),
1194
1185
        ]
1195
1186
    aliases = ['get', 'clone']
1196
1187
 
1197
1188
    def run(self, from_location, to_location=None, revision=None,
1198
1189
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1199
 
            use_existing_dir=False, switch=False):
 
1190
            use_existing_dir=False, switch=False, bind=False):
1200
1191
        from bzrlib import switch as _mod_switch
1201
1192
        from bzrlib.tag import _merge_tags_if_possible
1202
1193
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1203
1194
            from_location)
1204
 
        if (accelerator_tree is not None and
1205
 
            accelerator_tree.supports_content_filtering()):
1206
 
            accelerator_tree = None
1207
1195
        revision = _get_one_revision('branch', revision)
1208
1196
        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)
1209
1208
        try:
1210
 
            if revision is not None:
1211
 
                revision_id = revision.as_revision_id(br_from)
 
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)
1212
1214
            else:
1213
 
                # FIXME - wt.last_revision, fallback to branch, fall back to
1214
 
                # None or perhaps NULL_REVISION to mean copy nothing
1215
 
                # RBC 20060209
1216
 
                revision_id = br_from.last_revision()
1217
 
            if to_location is None:
1218
 
                to_location = urlutils.derive_to_location(from_location)
1219
 
            to_transport = transport.get_transport(to_location)
1220
 
            try:
1221
 
                to_transport.mkdir('.')
1222
 
            except errors.FileExists:
1223
 
                if not use_existing_dir:
1224
 
                    raise errors.BzrCommandError('Target directory "%s" '
1225
 
                        'already exists.' % to_location)
 
1215
                try:
 
1216
                    bzrdir.BzrDir.open_from_transport(to_transport)
 
1217
                except errors.NotBranchError:
 
1218
                    pass
1226
1219
                else:
1227
 
                    try:
1228
 
                        bzrdir.BzrDir.open_from_transport(to_transport)
1229
 
                    except errors.NotBranchError:
1230
 
                        pass
1231
 
                    else:
1232
 
                        raise errors.AlreadyBranchError(to_location)
1233
 
            except errors.NoSuchFile:
1234
 
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1235
 
                                             % to_location)
1236
 
            try:
1237
 
                # preserve whatever source format we have.
1238
 
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1239
 
                                            possible_transports=[to_transport],
1240
 
                                            accelerator_tree=accelerator_tree,
1241
 
                                            hardlink=hardlink, stacked=stacked,
1242
 
                                            force_new_repo=standalone,
1243
 
                                            create_tree_if_local=not no_tree,
1244
 
                                            source_branch=br_from)
1245
 
                branch = dir.open_branch()
1246
 
            except errors.NoSuchRevision:
1247
 
                to_transport.delete_tree('.')
1248
 
                msg = "The branch %s has no revision %s." % (from_location,
1249
 
                    revision)
1250
 
                raise errors.BzrCommandError(msg)
1251
 
            _merge_tags_if_possible(br_from, branch)
1252
 
            # If the source branch is stacked, the new branch may
1253
 
            # be stacked whether we asked for that explicitly or not.
1254
 
            # We therefore need a try/except here and not just 'if stacked:'
1255
 
            try:
1256
 
                note('Created new stacked branch referring to %s.' %
1257
 
                    branch.get_stacked_on_url())
1258
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1259
 
                errors.UnstackableRepositoryFormat), e:
1260
 
                note('Branched %d revision(s).' % branch.revno())
1261
 
            if switch:
1262
 
                # Switch to the new branch
1263
 
                wt, _ = WorkingTree.open_containing('.')
1264
 
                _mod_switch.switch(wt.bzrdir, branch)
1265
 
                note('Switched to branch: %s',
1266
 
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
1267
 
        finally:
1268
 
            br_from.unlock()
 
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'))
1269
1260
 
1270
1261
 
1271
1262
class cmd_checkout(Command):
1350
1341
    def run(self, dir=u'.'):
1351
1342
        tree = WorkingTree.open_containing(dir)[0]
1352
1343
        tree.lock_read()
1353
 
        try:
1354
 
            new_inv = tree.inventory
1355
 
            old_tree = tree.basis_tree()
1356
 
            old_tree.lock_read()
1357
 
            try:
1358
 
                old_inv = old_tree.inventory
1359
 
                renames = []
1360
 
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
1361
 
                for f, paths, c, v, p, n, k, e in iterator:
1362
 
                    if paths[0] == paths[1]:
1363
 
                        continue
1364
 
                    if None in (paths):
1365
 
                        continue
1366
 
                    renames.append(paths)
1367
 
                renames.sort()
1368
 
                for old_name, new_name in renames:
1369
 
                    self.outf.write("%s => %s\n" % (old_name, new_name))
1370
 
            finally:
1371
 
                old_tree.unlock()
1372
 
        finally:
1373
 
            tree.unlock()
 
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))
1374
1361
 
1375
1362
 
1376
1363
class cmd_update(Command):
1382
1369
 
1383
1370
    If you want to discard your local changes, you can just do a
1384
1371
    '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.
1385
1375
    """
1386
1376
 
1387
1377
    _see_also = ['pull', 'working-trees', 'status-flags']
1388
1378
    takes_args = ['dir?']
 
1379
    takes_options = ['revision']
1389
1380
    aliases = ['up']
1390
1381
 
1391
 
    def run(self, dir='.'):
 
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")
1392
1386
        tree = WorkingTree.open_containing(dir)[0]
 
1387
        branch = tree.branch
1393
1388
        possible_transports = []
1394
 
        master = tree.branch.get_master_branch(
 
1389
        master = branch.get_master_branch(
1395
1390
            possible_transports=possible_transports)
1396
1391
        if master is not None:
1397
1392
            tree.lock_write()
 
1393
            branch_location = master.base
1398
1394
        else:
1399
1395
            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)
1400
1422
        try:
1401
 
            existing_pending_merges = tree.get_parent_ids()[1:]
1402
 
            last_rev = _mod_revision.ensure_null(tree.last_revision())
1403
 
            if last_rev == _mod_revision.ensure_null(
1404
 
                tree.branch.last_revision()):
1405
 
                # may be up to date, check master too.
1406
 
                if master is None or last_rev == _mod_revision.ensure_null(
1407
 
                    master.last_revision()):
1408
 
                    revno = tree.branch.revision_id_to_revno(last_rev)
1409
 
                    note("Tree is up to date at revision %d." % (revno,))
1410
 
                    return 0
1411
 
            view_info = _get_view_info_for_change_reporter(tree)
1412
1423
            conflicts = tree.update(
1413
 
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
1414
 
                view_info=view_info), possible_transports=possible_transports)
1415
 
            revno = tree.branch.revision_id_to_revno(
1416
 
                _mod_revision.ensure_null(tree.last_revision()))
1417
 
            note('Updated to revision %d.' % (revno,))
1418
 
            if tree.get_parent_ids()[1:] != existing_pending_merges:
1419
 
                note('Your local commits will now show as pending merges with '
1420
 
                     "'bzr status', and can be committed with 'bzr commit'.")
1421
 
            if conflicts != 0:
1422
 
                return 1
1423
 
            else:
1424
 
                return 0
1425
 
        finally:
1426
 
            tree.unlock()
 
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
1427
1445
 
1428
1446
 
1429
1447
class cmd_info(Command):
1500
1518
            file_list = [f for f in file_list]
1501
1519
 
1502
1520
        tree.lock_write()
1503
 
        try:
1504
 
            # Heuristics should probably all move into tree.remove_smart or
1505
 
            # some such?
1506
 
            if new:
1507
 
                added = tree.changes_from(tree.basis_tree(),
1508
 
                    specific_files=file_list).added
1509
 
                file_list = sorted([f[0] for f in added], reverse=True)
1510
 
                if len(file_list) == 0:
1511
 
                    raise errors.BzrCommandError('No matching files.')
1512
 
            elif file_list is None:
1513
 
                # missing files show up in iter_changes(basis) as
1514
 
                # versioned-with-no-kind.
1515
 
                missing = []
1516
 
                for change in tree.iter_changes(tree.basis_tree()):
1517
 
                    # Find paths in the working tree that have no kind:
1518
 
                    if change[1][1] is not None and change[6][1] is None:
1519
 
                        missing.append(change[1][1])
1520
 
                file_list = sorted(missing, reverse=True)
1521
 
                file_deletion_strategy = 'keep'
1522
 
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
1523
 
                keep_files=file_deletion_strategy=='keep',
1524
 
                force=file_deletion_strategy=='force')
1525
 
        finally:
1526
 
            tree.unlock()
 
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')
1527
1543
 
1528
1544
 
1529
1545
class cmd_file_id(Command):
1749
1765
 
1750
1766
 
1751
1767
class cmd_init_repository(Command):
1752
 
    """Create a shared repository to hold branches.
 
1768
    """Create a shared repository for branches to share storage space.
1753
1769
 
1754
1770
    New branches created under the repository directory will store their
1755
 
    revisions in the repository, not in the branch directory.
 
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.
1756
1774
 
1757
 
    If the --no-trees option is used then the branches in the repository
1758
 
    will not have working trees by default.
 
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.
1759
1781
 
1760
1782
    :Examples:
1761
 
        Create a shared repositories holding just branches::
 
1783
        Create a shared repository holding just branches::
1762
1784
 
1763
1785
            bzr init-repo --no-trees repo
1764
1786
            bzr init repo/trunk
1830
1852
 
1831
1853
            bzr diff -r1
1832
1854
 
1833
 
        Difference between revision 2 and revision 1::
1834
 
 
1835
 
            bzr diff -r1..2
1836
 
 
1837
 
        Difference between revision 2 and revision 1 for branch xxx::
1838
 
 
1839
 
            bzr diff -r1..2 xxx
 
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
1840
1876
 
1841
1877
        Show just the differences for file NEWS::
1842
1878
 
1935
1971
    def run(self, show_ids=False):
1936
1972
        tree = WorkingTree.open_containing(u'.')[0]
1937
1973
        tree.lock_read()
1938
 
        try:
1939
 
            old = tree.basis_tree()
1940
 
            old.lock_read()
1941
 
            try:
1942
 
                for path, ie in old.inventory.iter_entries():
1943
 
                    if not tree.has_id(ie.file_id):
1944
 
                        self.outf.write(path)
1945
 
                        if show_ids:
1946
 
                            self.outf.write(' ')
1947
 
                            self.outf.write(ie.file_id)
1948
 
                        self.outf.write('\n')
1949
 
            finally:
1950
 
                old.unlock()
1951
 
        finally:
1952
 
            tree.unlock()
 
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')
1953
1985
 
1954
1986
 
1955
1987
class cmd_modified(Command):
1991
2023
    def run(self, null=False):
1992
2024
        wt = WorkingTree.open_containing(u'.')[0]
1993
2025
        wt.lock_read()
1994
 
        try:
1995
 
            basis = wt.basis_tree()
1996
 
            basis.lock_read()
1997
 
            try:
1998
 
                basis_inv = basis.inventory
1999
 
                inv = wt.inventory
2000
 
                for file_id in inv:
2001
 
                    if file_id in basis_inv:
2002
 
                        continue
2003
 
                    if inv.is_root(file_id) and len(basis_inv) == 0:
2004
 
                        continue
2005
 
                    path = inv.id2path(file_id)
2006
 
                    if not os.access(osutils.abspath(path), os.F_OK):
2007
 
                        continue
2008
 
                    if null:
2009
 
                        self.outf.write(path + '\0')
2010
 
                    else:
2011
 
                        self.outf.write(osutils.quotefn(path) + '\n')
2012
 
            finally:
2013
 
                basis.unlock()
2014
 
        finally:
2015
 
            wt.unlock()
 
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')
2016
2044
 
2017
2045
 
2018
2046
class cmd_root(Command):
2278
2306
        filter_by_dir = False
2279
2307
        if file_list:
2280
2308
            # find the file ids to log and check for directory filtering
2281
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(revision,
2282
 
                file_list)
 
2309
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2310
                revision, file_list)
 
2311
            self.add_cleanup(b.unlock)
2283
2312
            for relpath, file_id, kind in file_info_list:
2284
2313
                if file_id is None:
2285
2314
                    raise errors.BzrCommandError(
2303
2332
                location = '.'
2304
2333
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2305
2334
            b = dir.open_branch()
 
2335
            b.lock_read()
 
2336
            self.add_cleanup(b.unlock)
2306
2337
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2307
2338
 
2308
2339
        # Decide on the type of delta & diff filtering to use
2318
2349
        else:
2319
2350
            diff_type = 'full'
2320
2351
 
2321
 
        b.lock_read()
2322
 
        try:
2323
 
            # Build the log formatter
2324
 
            if log_format is None:
2325
 
                log_format = log.log_formatter_registry.get_default(b)
2326
 
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2327
 
                            show_timezone=timezone,
2328
 
                            delta_format=get_verbosity_level(),
2329
 
                            levels=levels,
2330
 
                            show_advice=levels is None)
2331
 
 
2332
 
            # Choose the algorithm for doing the logging. It's annoying
2333
 
            # having multiple code paths like this but necessary until
2334
 
            # the underlying repository format is faster at generating
2335
 
            # deltas or can provide everything we need from the indices.
2336
 
            # The default algorithm - match-using-deltas - works for
2337
 
            # multiple files and directories and is faster for small
2338
 
            # amounts of history (200 revisions say). However, it's too
2339
 
            # slow for logging a single file in a repository with deep
2340
 
            # history, i.e. > 10K revisions. In the spirit of "do no
2341
 
            # evil when adding features", we continue to use the
2342
 
            # original algorithm - per-file-graph - for the "single
2343
 
            # file that isn't a directory without showing a delta" case.
2344
 
            partial_history = revision and b.repository._format.supports_chks
2345
 
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2346
 
                or delta_type or partial_history)
2347
 
 
2348
 
            # Build the LogRequest and execute it
2349
 
            if len(file_ids) == 0:
2350
 
                file_ids = None
2351
 
            rqst = make_log_request_dict(
2352
 
                direction=direction, specific_fileids=file_ids,
2353
 
                start_revision=rev1, end_revision=rev2, limit=limit,
2354
 
                message_search=message, delta_type=delta_type,
2355
 
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2356
 
            Logger(b, rqst).show(lf)
2357
 
        finally:
2358
 
            b.unlock()
 
2352
        # Build the log formatter
 
2353
        if log_format is None:
 
2354
            log_format = log.log_formatter_registry.get_default(b)
 
2355
        # Make a non-encoding output to include the diffs - bug 328007
 
2356
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2357
        lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2358
                        to_exact_file=unencoded_output,
 
2359
                        show_timezone=timezone,
 
2360
                        delta_format=get_verbosity_level(),
 
2361
                        levels=levels,
 
2362
                        show_advice=levels is None)
 
2363
 
 
2364
        # Choose the algorithm for doing the logging. It's annoying
 
2365
        # having multiple code paths like this but necessary until
 
2366
        # the underlying repository format is faster at generating
 
2367
        # deltas or can provide everything we need from the indices.
 
2368
        # The default algorithm - match-using-deltas - works for
 
2369
        # multiple files and directories and is faster for small
 
2370
        # amounts of history (200 revisions say). However, it's too
 
2371
        # slow for logging a single file in a repository with deep
 
2372
        # history, i.e. > 10K revisions. In the spirit of "do no
 
2373
        # evil when adding features", we continue to use the
 
2374
        # original algorithm - per-file-graph - for the "single
 
2375
        # file that isn't a directory without showing a delta" case.
 
2376
        partial_history = revision and b.repository._format.supports_chks
 
2377
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2378
            or delta_type or partial_history)
 
2379
 
 
2380
        # Build the LogRequest and execute it
 
2381
        if len(file_ids) == 0:
 
2382
            file_ids = None
 
2383
        rqst = make_log_request_dict(
 
2384
            direction=direction, specific_fileids=file_ids,
 
2385
            start_revision=rev1, end_revision=rev2, limit=limit,
 
2386
            message_search=message, delta_type=delta_type,
 
2387
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2388
        Logger(b, rqst).show(lf)
2359
2389
 
2360
2390
 
2361
2391
def _get_revision_range(revisionspec_list, branch, command_name):
2425
2455
    @display_command
2426
2456
    def run(self, filename):
2427
2457
        tree, relpath = WorkingTree.open_containing(filename)
 
2458
        file_id = tree.path2id(relpath)
2428
2459
        b = tree.branch
2429
 
        file_id = tree.path2id(relpath)
2430
 
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
 
2460
        b.lock_read()
 
2461
        self.add_cleanup(b.unlock)
 
2462
        touching_revs = log.find_touching_revisions(b, file_id)
 
2463
        for revno, revision_id, what in touching_revs:
2431
2464
            self.outf.write("%6d %s\n" % (revno, what))
2432
2465
 
2433
2466
 
2486
2519
        if from_root:
2487
2520
            if relpath:
2488
2521
                prefix = relpath + '/'
2489
 
        elif fs_path != '.':
 
2522
        elif fs_path != '.' and not fs_path.endswith('/'):
2490
2523
            prefix = fs_path + '/'
2491
2524
 
2492
2525
        if revision is not None or tree is None:
2501
2534
                note("Ignoring files outside view. View is %s" % view_str)
2502
2535
 
2503
2536
        tree.lock_read()
2504
 
        try:
2505
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2506
 
                from_dir=relpath, recursive=recursive):
2507
 
                # Apply additional masking
2508
 
                if not all and not selection[fc]:
2509
 
                    continue
2510
 
                if kind is not None and fkind != kind:
2511
 
                    continue
2512
 
                if apply_view:
2513
 
                    try:
2514
 
                        if relpath:
2515
 
                            fullpath = osutils.pathjoin(relpath, fp)
2516
 
                        else:
2517
 
                            fullpath = fp
2518
 
                        views.check_path_in_view(tree, fullpath)
2519
 
                    except errors.FileOutsideView:
2520
 
                        continue
 
2537
        self.add_cleanup(tree.unlock)
 
2538
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2539
            from_dir=relpath, recursive=recursive):
 
2540
            # Apply additional masking
 
2541
            if not all and not selection[fc]:
 
2542
                continue
 
2543
            if kind is not None and fkind != kind:
 
2544
                continue
 
2545
            if apply_view:
 
2546
                try:
 
2547
                    if relpath:
 
2548
                        fullpath = osutils.pathjoin(relpath, fp)
 
2549
                    else:
 
2550
                        fullpath = fp
 
2551
                    views.check_path_in_view(tree, fullpath)
 
2552
                except errors.FileOutsideView:
 
2553
                    continue
2521
2554
 
2522
 
                # Output the entry
2523
 
                if prefix:
2524
 
                    fp = osutils.pathjoin(prefix, fp)
2525
 
                kindch = entry.kind_character()
2526
 
                outstring = fp + kindch
2527
 
                ui.ui_factory.clear_term()
2528
 
                if verbose:
2529
 
                    outstring = '%-8s %s' % (fc, outstring)
2530
 
                    if show_ids and fid is not None:
2531
 
                        outstring = "%-50s %s" % (outstring, fid)
 
2555
            # Output the entry
 
2556
            if prefix:
 
2557
                fp = osutils.pathjoin(prefix, fp)
 
2558
            kindch = entry.kind_character()
 
2559
            outstring = fp + kindch
 
2560
            ui.ui_factory.clear_term()
 
2561
            if verbose:
 
2562
                outstring = '%-8s %s' % (fc, outstring)
 
2563
                if show_ids and fid is not None:
 
2564
                    outstring = "%-50s %s" % (outstring, fid)
 
2565
                self.outf.write(outstring + '\n')
 
2566
            elif null:
 
2567
                self.outf.write(fp + '\0')
 
2568
                if show_ids:
 
2569
                    if fid is not None:
 
2570
                        self.outf.write(fid)
 
2571
                    self.outf.write('\0')
 
2572
                self.outf.flush()
 
2573
            else:
 
2574
                if show_ids:
 
2575
                    if fid is not None:
 
2576
                        my_id = fid
 
2577
                    else:
 
2578
                        my_id = ''
 
2579
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2580
                else:
2532
2581
                    self.outf.write(outstring + '\n')
2533
 
                elif null:
2534
 
                    self.outf.write(fp + '\0')
2535
 
                    if show_ids:
2536
 
                        if fid is not None:
2537
 
                            self.outf.write(fid)
2538
 
                        self.outf.write('\0')
2539
 
                    self.outf.flush()
2540
 
                else:
2541
 
                    if show_ids:
2542
 
                        if fid is not None:
2543
 
                            my_id = fid
2544
 
                        else:
2545
 
                            my_id = ''
2546
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2547
 
                    else:
2548
 
                        self.outf.write(outstring + '\n')
2549
 
        finally:
2550
 
            tree.unlock()
2551
2582
 
2552
2583
 
2553
2584
class cmd_unknowns(Command):
2568
2599
 
2569
2600
    See ``bzr help patterns`` for details on the syntax of patterns.
2570
2601
 
 
2602
    If a .bzrignore file does not exist, the ignore command
 
2603
    will create one and add the specified files or patterns to the newly
 
2604
    created file. The ignore command will also automatically add the 
 
2605
    .bzrignore file to be versioned. Creating a .bzrignore file without
 
2606
    the use of the ignore command will require an explicit add command.
 
2607
 
2571
2608
    To remove patterns from the ignore list, edit the .bzrignore file.
2572
2609
    After adding, editing or deleting that file either indirectly by
2573
2610
    using this command or directly by using an editor, be sure to commit
2574
2611
    it.
 
2612
    
 
2613
    Patterns prefixed with '!' are exceptions to ignore patterns and take
 
2614
    precedence over regular ignores.  Such exceptions are used to specify
 
2615
    files that should be versioned which would otherwise be ignored.
 
2616
    
 
2617
    Patterns prefixed with '!!' act as regular ignore patterns, but have
 
2618
    precedence over the '!' exception patterns.
2575
2619
 
2576
2620
    Note: ignore patterns containing shell wildcards must be quoted from
2577
2621
    the shell on Unix.
2581
2625
 
2582
2626
            bzr ignore ./Makefile
2583
2627
 
2584
 
        Ignore class files in all directories::
 
2628
        Ignore .class files in all directories...::
2585
2629
 
2586
2630
            bzr ignore "*.class"
2587
2631
 
 
2632
        ...but do not ignore "special.class"::
 
2633
 
 
2634
            bzr ignore "!special.class"
 
2635
 
2588
2636
        Ignore .o files under the lib directory::
2589
2637
 
2590
2638
            bzr ignore "lib/**/*.o"
2596
2644
        Ignore everything but the "debian" toplevel directory::
2597
2645
 
2598
2646
            bzr ignore "RE:(?!debian/).*"
 
2647
        
 
2648
        Ignore everything except the "local" toplevel directory,
 
2649
        but always ignore "*~" autosave files, even under local/::
 
2650
        
 
2651
            bzr ignore "*"
 
2652
            bzr ignore "!./local"
 
2653
            bzr ignore "!!*~"
2599
2654
    """
2600
2655
 
2601
2656
    _see_also = ['status', 'ignored', 'patterns']
2659
2714
    def run(self):
2660
2715
        tree = WorkingTree.open_containing(u'.')[0]
2661
2716
        tree.lock_read()
2662
 
        try:
2663
 
            for path, file_class, kind, file_id, entry in tree.list_files():
2664
 
                if file_class != 'I':
2665
 
                    continue
2666
 
                ## XXX: Slightly inefficient since this was already calculated
2667
 
                pat = tree.is_ignored(path)
2668
 
                self.outf.write('%-50s %s\n' % (path, pat))
2669
 
        finally:
2670
 
            tree.unlock()
 
2717
        self.add_cleanup(tree.unlock)
 
2718
        for path, file_class, kind, file_id, entry in tree.list_files():
 
2719
            if file_class != 'I':
 
2720
                continue
 
2721
            ## XXX: Slightly inefficient since this was already calculated
 
2722
            pat = tree.is_ignored(path)
 
2723
            self.outf.write('%-50s %s\n' % (path, pat))
2671
2724
 
2672
2725
 
2673
2726
class cmd_lookup_revision(Command):
2776
2829
        tree, branch, relpath = \
2777
2830
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2778
2831
        branch.lock_read()
2779
 
        try:
2780
 
            return self._run(tree, branch, relpath, filename, revision,
2781
 
                             name_from_revision, filters)
2782
 
        finally:
2783
 
            branch.unlock()
 
2832
        self.add_cleanup(branch.unlock)
 
2833
        return self._run(tree, branch, relpath, filename, revision,
 
2834
                         name_from_revision, filters)
2784
2835
 
2785
2836
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2786
2837
        filtered):
2787
2838
        if tree is None:
2788
2839
            tree = b.basis_tree()
2789
2840
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
2841
        rev_tree.lock_read()
 
2842
        self.add_cleanup(rev_tree.unlock)
2790
2843
 
2791
2844
        old_file_id = rev_tree.path2id(relpath)
2792
2845
 
2827
2880
            chunks = content.splitlines(True)
2828
2881
            content = filtered_output_bytes(chunks, filters,
2829
2882
                ContentFilterContext(relpath, rev_tree))
 
2883
            self.cleanup_now()
2830
2884
            self.outf.writelines(content)
2831
2885
        else:
 
2886
            self.cleanup_now()
2832
2887
            self.outf.write(content)
2833
2888
 
2834
2889
 
2941
2996
             Option('strict',
2942
2997
                    help="Refuse to commit if there are unknown "
2943
2998
                    "files in the working tree."),
 
2999
             Option('commit-time', type=str,
 
3000
                    help="Manually set a commit time using commit date "
 
3001
                    "format, e.g. '2009-10-10 08:00:00 +0100'."),
2944
3002
             ListOption('fixes', type=str,
2945
3003
                    help="Mark a bug as being fixed by this revision "
2946
3004
                         "(see \"bzr help bugs\")."),
2953
3011
                         "the master branch until a normal commit "
2954
3012
                         "is performed."
2955
3013
                    ),
2956
 
              Option('show-diff',
2957
 
                     help='When no message is supplied, show the diff along'
2958
 
                     ' with the status summary in the message editor.'),
 
3014
             Option('show-diff',
 
3015
                    help='When no message is supplied, show the diff along'
 
3016
                    ' with the status summary in the message editor.'),
2959
3017
             ]
2960
3018
    aliases = ['ci', 'checkin']
2961
3019
 
2980
3038
 
2981
3039
    def run(self, message=None, file=None, verbose=False, selected_list=None,
2982
3040
            unchanged=False, strict=False, local=False, fixes=None,
2983
 
            author=None, show_diff=False, exclude=None):
 
3041
            author=None, show_diff=False, exclude=None, commit_time=None):
2984
3042
        from bzrlib.errors import (
2985
3043
            PointlessCommit,
2986
3044
            ConflictsInTree,
2992
3050
            make_commit_message_template_encoded
2993
3051
        )
2994
3052
 
 
3053
        commit_stamp = offset = None
 
3054
        if commit_time is not None:
 
3055
            try:
 
3056
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
 
3057
            except ValueError, e:
 
3058
                raise errors.BzrCommandError(
 
3059
                    "Could not parse --commit-time: " + str(e))
 
3060
 
2995
3061
        # TODO: Need a blackbox test for invoking the external editor; may be
2996
3062
        # slightly problematic to run this cross-platform.
2997
3063
 
3017
3083
        if local and not tree.branch.get_bound_location():
3018
3084
            raise errors.LocalRequiresBoundBranch()
3019
3085
 
 
3086
        if message is not None:
 
3087
            try:
 
3088
                file_exists = osutils.lexists(message)
 
3089
            except UnicodeError:
 
3090
                # The commit message contains unicode characters that can't be
 
3091
                # represented in the filesystem encoding, so that can't be a
 
3092
                # file.
 
3093
                file_exists = False
 
3094
            if file_exists:
 
3095
                warning_msg = (
 
3096
                    'The commit message is a file name: "%(f)s".\n'
 
3097
                    '(use --file "%(f)s" to take commit message from that file)'
 
3098
                    % { 'f': message })
 
3099
                ui.ui_factory.show_warning(warning_msg)
 
3100
 
3020
3101
        def get_message(commit_obj):
3021
3102
            """Callback to get commit message"""
3022
3103
            my_message = message
 
3104
            if my_message is not None and '\r' in my_message:
 
3105
                my_message = my_message.replace('\r\n', '\n')
 
3106
                my_message = my_message.replace('\r', '\n')
3023
3107
            if my_message is None and not file:
3024
3108
                t = make_commit_message_template_encoded(tree,
3025
3109
                        selected_list, diff=show_diff,
3049
3133
                        specific_files=selected_list,
3050
3134
                        allow_pointless=unchanged, strict=strict, local=local,
3051
3135
                        reporter=None, verbose=verbose, revprops=properties,
3052
 
                        authors=author,
 
3136
                        authors=author, timestamp=commit_stamp,
 
3137
                        timezone=offset,
3053
3138
                        exclude=safe_relpath_files(tree, exclude))
3054
3139
        except PointlessCommit:
3055
3140
            # FIXME: This should really happen before the file is read in;
3359
3444
    def get_transport_type(typestring):
3360
3445
        """Parse and return a transport specifier."""
3361
3446
        if typestring == "sftp":
3362
 
            from bzrlib.transport.sftp import SFTPAbsoluteServer
3363
 
            return SFTPAbsoluteServer
 
3447
            from bzrlib.tests import stub_sftp
 
3448
            return stub_sftp.SFTPAbsoluteServer
3364
3449
        if typestring == "memory":
3365
3450
            from bzrlib.transport.memory import MemoryServer
3366
3451
            return MemoryServer
3465
3550
            verbose = not is_quiet()
3466
3551
            # TODO: should possibly lock the history file...
3467
3552
            benchfile = open(".perf_history", "at", buffering=1)
 
3553
            self.add_cleanup(benchfile.close)
3468
3554
        else:
3469
3555
            test_suite_factory = None
3470
3556
            benchfile = None
3471
 
        try:
3472
 
            selftest_kwargs = {"verbose": verbose,
3473
 
                              "pattern": pattern,
3474
 
                              "stop_on_failure": one,
3475
 
                              "transport": transport,
3476
 
                              "test_suite_factory": test_suite_factory,
3477
 
                              "lsprof_timed": lsprof_timed,
3478
 
                              "lsprof_tests": lsprof_tests,
3479
 
                              "bench_history": benchfile,
3480
 
                              "matching_tests_first": first,
3481
 
                              "list_only": list_only,
3482
 
                              "random_seed": randomize,
3483
 
                              "exclude_pattern": exclude,
3484
 
                              "strict": strict,
3485
 
                              "load_list": load_list,
3486
 
                              "debug_flags": debugflag,
3487
 
                              "starting_with": starting_with
3488
 
                              }
3489
 
            selftest_kwargs.update(self.additional_selftest_args)
3490
 
            result = selftest(**selftest_kwargs)
3491
 
        finally:
3492
 
            if benchfile is not None:
3493
 
                benchfile.close()
 
3557
        selftest_kwargs = {"verbose": verbose,
 
3558
                          "pattern": pattern,
 
3559
                          "stop_on_failure": one,
 
3560
                          "transport": transport,
 
3561
                          "test_suite_factory": test_suite_factory,
 
3562
                          "lsprof_timed": lsprof_timed,
 
3563
                          "lsprof_tests": lsprof_tests,
 
3564
                          "bench_history": benchfile,
 
3565
                          "matching_tests_first": first,
 
3566
                          "list_only": list_only,
 
3567
                          "random_seed": randomize,
 
3568
                          "exclude_pattern": exclude,
 
3569
                          "strict": strict,
 
3570
                          "load_list": load_list,
 
3571
                          "debug_flags": debugflag,
 
3572
                          "starting_with": starting_with
 
3573
                          }
 
3574
        selftest_kwargs.update(self.additional_selftest_args)
 
3575
        result = selftest(**selftest_kwargs)
3494
3576
        return int(not result)
3495
3577
 
3496
3578
 
3535
3617
        branch1 = Branch.open_containing(branch)[0]
3536
3618
        branch2 = Branch.open_containing(other)[0]
3537
3619
        branch1.lock_read()
3538
 
        try:
3539
 
            branch2.lock_read()
3540
 
            try:
3541
 
                last1 = ensure_null(branch1.last_revision())
3542
 
                last2 = ensure_null(branch2.last_revision())
3543
 
 
3544
 
                graph = branch1.repository.get_graph(branch2.repository)
3545
 
                base_rev_id = graph.find_unique_lca(last1, last2)
3546
 
 
3547
 
                print 'merge base is revision %s' % base_rev_id
3548
 
            finally:
3549
 
                branch2.unlock()
3550
 
        finally:
3551
 
            branch1.unlock()
 
3620
        self.add_cleanup(branch1.unlock)
 
3621
        branch2.lock_read()
 
3622
        self.add_cleanup(branch2.unlock)
 
3623
        last1 = ensure_null(branch1.last_revision())
 
3624
        last2 = ensure_null(branch2.last_revision())
 
3625
 
 
3626
        graph = branch1.repository.get_graph(branch2.repository)
 
3627
        base_rev_id = graph.find_unique_lca(last1, last2)
 
3628
 
 
3629
        print 'merge base is revision %s' % base_rev_id
3552
3630
 
3553
3631
 
3554
3632
class cmd_merge(Command):
3587
3665
    committed to record the result of the merge.
3588
3666
 
3589
3667
    merge refuses to run if there are any uncommitted changes, unless
3590
 
    --force is given.
 
3668
    --force is given. The --force option can also be used to create a
 
3669
    merge revision which has more than two parents.
 
3670
 
 
3671
    If one would like to merge changes from the working tree of the other
 
3672
    branch without merging any committed revisions, the --uncommitted option
 
3673
    can be given.
3591
3674
 
3592
3675
    To select only some changes to merge, use "merge -i", which will prompt
3593
3676
    you to apply each diff hunk and file change, similar to "shelve".
3605
3688
 
3606
3689
            bzr merge -r 81..82 ../bzr.dev
3607
3690
 
3608
 
        To apply a merge directive contained in /tmp/merge:
 
3691
        To apply a merge directive contained in /tmp/merge::
3609
3692
 
3610
3693
            bzr merge /tmp/merge
 
3694
 
 
3695
        To create a merge revision with three parents from two branches
 
3696
        feature1a and feature1b:
 
3697
 
 
3698
            bzr merge ../feature1a
 
3699
            bzr merge ../feature1b --force
 
3700
            bzr commit -m 'revision with three parents'
3611
3701
    """
3612
3702
 
3613
3703
    encoding_type = 'exact'
3671
3761
        view_info = _get_view_info_for_change_reporter(tree)
3672
3762
        change_reporter = delta._ChangeReporter(
3673
3763
            unversioned_filter=tree.is_ignored, view_info=view_info)
3674
 
        cleanups = []
3675
 
        try:
3676
 
            pb = ui.ui_factory.nested_progress_bar()
3677
 
            cleanups.append(pb.finished)
3678
 
            tree.lock_write()
3679
 
            cleanups.append(tree.unlock)
3680
 
            if location is not None:
3681
 
                try:
3682
 
                    mergeable = bundle.read_mergeable_from_url(location,
3683
 
                        possible_transports=possible_transports)
3684
 
                except errors.NotABundle:
3685
 
                    mergeable = None
3686
 
                else:
3687
 
                    if uncommitted:
3688
 
                        raise errors.BzrCommandError('Cannot use --uncommitted'
3689
 
                            ' with bundles or merge directives.')
3690
 
 
3691
 
                    if revision is not None:
3692
 
                        raise errors.BzrCommandError(
3693
 
                            'Cannot use -r with merge directives or bundles')
3694
 
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
3695
 
                       mergeable, pb)
3696
 
 
3697
 
            if merger is None and uncommitted:
3698
 
                if revision is not None and len(revision) > 0:
3699
 
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3700
 
                        ' --revision at the same time.')
3701
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3702
 
                                                          cleanups)
3703
 
                allow_pending = False
3704
 
 
3705
 
            if merger is None:
3706
 
                merger, allow_pending = self._get_merger_from_branch(tree,
3707
 
                    location, revision, remember, possible_transports, pb)
3708
 
 
3709
 
            merger.merge_type = merge_type
3710
 
            merger.reprocess = reprocess
3711
 
            merger.show_base = show_base
3712
 
            self.sanity_check_merger(merger)
3713
 
            if (merger.base_rev_id == merger.other_rev_id and
3714
 
                merger.other_rev_id is not None):
3715
 
                note('Nothing to do.')
 
3764
        pb = ui.ui_factory.nested_progress_bar()
 
3765
        self.add_cleanup(pb.finished)
 
3766
        tree.lock_write()
 
3767
        self.add_cleanup(tree.unlock)
 
3768
        if location is not None:
 
3769
            try:
 
3770
                mergeable = bundle.read_mergeable_from_url(location,
 
3771
                    possible_transports=possible_transports)
 
3772
            except errors.NotABundle:
 
3773
                mergeable = None
 
3774
            else:
 
3775
                if uncommitted:
 
3776
                    raise errors.BzrCommandError('Cannot use --uncommitted'
 
3777
                        ' with bundles or merge directives.')
 
3778
 
 
3779
                if revision is not None:
 
3780
                    raise errors.BzrCommandError(
 
3781
                        'Cannot use -r with merge directives or bundles')
 
3782
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3783
                   mergeable, pb)
 
3784
 
 
3785
        if merger is None and uncommitted:
 
3786
            if revision is not None and len(revision) > 0:
 
3787
                raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3788
                    ' --revision at the same time.')
 
3789
            merger = self.get_merger_from_uncommitted(tree, location, pb)
 
3790
            allow_pending = False
 
3791
 
 
3792
        if merger is None:
 
3793
            merger, allow_pending = self._get_merger_from_branch(tree,
 
3794
                location, revision, remember, possible_transports, pb)
 
3795
 
 
3796
        merger.merge_type = merge_type
 
3797
        merger.reprocess = reprocess
 
3798
        merger.show_base = show_base
 
3799
        self.sanity_check_merger(merger)
 
3800
        if (merger.base_rev_id == merger.other_rev_id and
 
3801
            merger.other_rev_id is not None):
 
3802
            note('Nothing to do.')
 
3803
            return 0
 
3804
        if pull:
 
3805
            if merger.interesting_files is not None:
 
3806
                raise errors.BzrCommandError('Cannot pull individual files')
 
3807
            if (merger.base_rev_id == tree.last_revision()):
 
3808
                result = tree.pull(merger.other_branch, False,
 
3809
                                   merger.other_rev_id)
 
3810
                result.report(self.outf)
3716
3811
                return 0
3717
 
            if pull:
3718
 
                if merger.interesting_files is not None:
3719
 
                    raise errors.BzrCommandError('Cannot pull individual files')
3720
 
                if (merger.base_rev_id == tree.last_revision()):
3721
 
                    result = tree.pull(merger.other_branch, False,
3722
 
                                       merger.other_rev_id)
3723
 
                    result.report(self.outf)
3724
 
                    return 0
3725
 
            if merger.this_basis is None:
3726
 
                raise errors.BzrCommandError(
3727
 
                    "This branch has no commits."
3728
 
                    " (perhaps you would prefer 'bzr pull')")
3729
 
            if preview:
3730
 
                return self._do_preview(merger, cleanups)
3731
 
            elif interactive:
3732
 
                return self._do_interactive(merger, cleanups)
3733
 
            else:
3734
 
                return self._do_merge(merger, change_reporter, allow_pending,
3735
 
                                      verified)
3736
 
        finally:
3737
 
            for cleanup in reversed(cleanups):
3738
 
                cleanup()
 
3812
        if merger.this_basis is None:
 
3813
            raise errors.BzrCommandError(
 
3814
                "This branch has no commits."
 
3815
                " (perhaps you would prefer 'bzr pull')")
 
3816
        if preview:
 
3817
            return self._do_preview(merger)
 
3818
        elif interactive:
 
3819
            return self._do_interactive(merger)
 
3820
        else:
 
3821
            return self._do_merge(merger, change_reporter, allow_pending,
 
3822
                                  verified)
3739
3823
 
3740
 
    def _get_preview(self, merger, cleanups):
 
3824
    def _get_preview(self, merger):
3741
3825
        tree_merger = merger.make_merger()
3742
3826
        tt = tree_merger.make_preview_transform()
3743
 
        cleanups.append(tt.finalize)
 
3827
        self.add_cleanup(tt.finalize)
3744
3828
        result_tree = tt.get_preview_tree()
3745
3829
        return result_tree
3746
3830
 
3747
 
    def _do_preview(self, merger, cleanups):
 
3831
    def _do_preview(self, merger):
3748
3832
        from bzrlib.diff import show_diff_trees
3749
 
        result_tree = self._get_preview(merger, cleanups)
 
3833
        result_tree = self._get_preview(merger)
3750
3834
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3751
3835
                        old_label='', new_label='')
3752
3836
 
3762
3846
        else:
3763
3847
            return 0
3764
3848
 
3765
 
    def _do_interactive(self, merger, cleanups):
 
3849
    def _do_interactive(self, merger):
3766
3850
        """Perform an interactive merge.
3767
3851
 
3768
3852
        This works by generating a preview tree of the merge, then using
3770
3854
        and the preview tree.
3771
3855
        """
3772
3856
        from bzrlib import shelf_ui
3773
 
        result_tree = self._get_preview(merger, cleanups)
 
3857
        result_tree = self._get_preview(merger)
3774
3858
        writer = bzrlib.option.diff_writer_registry.get()
3775
3859
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3776
3860
                                   reporter=shelf_ui.ApplyReporter(),
3777
3861
                                   diff_writer=writer(sys.stdout))
3778
 
        shelver.run()
 
3862
        try:
 
3863
            shelver.run()
 
3864
        finally:
 
3865
            shelver.finalize()
3779
3866
 
3780
3867
    def sanity_check_merger(self, merger):
3781
3868
        if (merger.show_base and
3841
3928
            allow_pending = True
3842
3929
        return merger, allow_pending
3843
3930
 
3844
 
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3931
    def get_merger_from_uncommitted(self, tree, location, pb):
3845
3932
        """Get a merger for uncommitted changes.
3846
3933
 
3847
3934
        :param tree: The tree the merger should apply to.
3848
3935
        :param location: The location containing uncommitted changes.
3849
3936
        :param pb: The progress bar to use for showing progress.
3850
 
        :param cleanups: A list of operations to perform to clean up the
3851
 
            temporary directories, unfinalized objects, etc.
3852
3937
        """
3853
3938
        location = self._select_branch_location(tree, location)[0]
3854
3939
        other_tree, other_path = WorkingTree.open_containing(location)
3941
4026
            merge_type = _mod_merge.Merge3Merger
3942
4027
        tree, file_list = tree_files(file_list)
3943
4028
        tree.lock_write()
3944
 
        try:
3945
 
            parents = tree.get_parent_ids()
3946
 
            if len(parents) != 2:
3947
 
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
3948
 
                                             " merges.  Not cherrypicking or"
3949
 
                                             " multi-merges.")
3950
 
            repository = tree.branch.repository
3951
 
            interesting_ids = None
3952
 
            new_conflicts = []
3953
 
            conflicts = tree.conflicts()
3954
 
            if file_list is not None:
3955
 
                interesting_ids = set()
3956
 
                for filename in file_list:
3957
 
                    file_id = tree.path2id(filename)
3958
 
                    if file_id is None:
3959
 
                        raise errors.NotVersionedError(filename)
3960
 
                    interesting_ids.add(file_id)
3961
 
                    if tree.kind(file_id) != "directory":
3962
 
                        continue
 
4029
        self.add_cleanup(tree.unlock)
 
4030
        parents = tree.get_parent_ids()
 
4031
        if len(parents) != 2:
 
4032
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
4033
                                         " merges.  Not cherrypicking or"
 
4034
                                         " multi-merges.")
 
4035
        repository = tree.branch.repository
 
4036
        interesting_ids = None
 
4037
        new_conflicts = []
 
4038
        conflicts = tree.conflicts()
 
4039
        if file_list is not None:
 
4040
            interesting_ids = set()
 
4041
            for filename in file_list:
 
4042
                file_id = tree.path2id(filename)
 
4043
                if file_id is None:
 
4044
                    raise errors.NotVersionedError(filename)
 
4045
                interesting_ids.add(file_id)
 
4046
                if tree.kind(file_id) != "directory":
 
4047
                    continue
3963
4048
 
3964
 
                    for name, ie in tree.inventory.iter_entries(file_id):
3965
 
                        interesting_ids.add(ie.file_id)
3966
 
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
3967
 
            else:
3968
 
                # Remerge only supports resolving contents conflicts
3969
 
                allowed_conflicts = ('text conflict', 'contents conflict')
3970
 
                restore_files = [c.path for c in conflicts
3971
 
                                 if c.typestring in allowed_conflicts]
3972
 
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
3973
 
            tree.set_conflicts(ConflictList(new_conflicts))
3974
 
            if file_list is not None:
3975
 
                restore_files = file_list
3976
 
            for filename in restore_files:
3977
 
                try:
3978
 
                    restore(tree.abspath(filename))
3979
 
                except errors.NotConflicted:
3980
 
                    pass
3981
 
            # Disable pending merges, because the file texts we are remerging
3982
 
            # have not had those merges performed.  If we use the wrong parents
3983
 
            # list, we imply that the working tree text has seen and rejected
3984
 
            # all the changes from the other tree, when in fact those changes
3985
 
            # have not yet been seen.
3986
 
            pb = ui.ui_factory.nested_progress_bar()
3987
 
            tree.set_parent_ids(parents[:1])
 
4049
                for name, ie in tree.inventory.iter_entries(file_id):
 
4050
                    interesting_ids.add(ie.file_id)
 
4051
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4052
        else:
 
4053
            # Remerge only supports resolving contents conflicts
 
4054
            allowed_conflicts = ('text conflict', 'contents conflict')
 
4055
            restore_files = [c.path for c in conflicts
 
4056
                             if c.typestring in allowed_conflicts]
 
4057
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4058
        tree.set_conflicts(ConflictList(new_conflicts))
 
4059
        if file_list is not None:
 
4060
            restore_files = file_list
 
4061
        for filename in restore_files:
3988
4062
            try:
3989
 
                merger = _mod_merge.Merger.from_revision_ids(pb,
3990
 
                                                             tree, parents[1])
3991
 
                merger.interesting_ids = interesting_ids
3992
 
                merger.merge_type = merge_type
3993
 
                merger.show_base = show_base
3994
 
                merger.reprocess = reprocess
3995
 
                conflicts = merger.do_merge()
3996
 
            finally:
3997
 
                tree.set_parent_ids(parents)
3998
 
                pb.finished()
 
4063
                restore(tree.abspath(filename))
 
4064
            except errors.NotConflicted:
 
4065
                pass
 
4066
        # Disable pending merges, because the file texts we are remerging
 
4067
        # have not had those merges performed.  If we use the wrong parents
 
4068
        # list, we imply that the working tree text has seen and rejected
 
4069
        # all the changes from the other tree, when in fact those changes
 
4070
        # have not yet been seen.
 
4071
        pb = ui.ui_factory.nested_progress_bar()
 
4072
        tree.set_parent_ids(parents[:1])
 
4073
        try:
 
4074
            merger = _mod_merge.Merger.from_revision_ids(pb,
 
4075
                                                         tree, parents[1])
 
4076
            merger.interesting_ids = interesting_ids
 
4077
            merger.merge_type = merge_type
 
4078
            merger.show_base = show_base
 
4079
            merger.reprocess = reprocess
 
4080
            conflicts = merger.do_merge()
3999
4081
        finally:
4000
 
            tree.unlock()
 
4082
            tree.set_parent_ids(parents)
 
4083
            pb.finished()
4001
4084
        if conflicts > 0:
4002
4085
            return 1
4003
4086
        else:
4025
4108
    name.  If you name a directory, all the contents of that directory will be
4026
4109
    reverted.
4027
4110
 
4028
 
    Any files that have been newly added since that revision will be deleted,
4029
 
    with a backup kept if appropriate.  Directories containing unknown files
4030
 
    will not be deleted.
 
4111
    If you have newly added files since the target revision, they will be
 
4112
    removed.  If the files to be removed have been changed, backups will be
 
4113
    created as above.  Directories containing unknown files will not be
 
4114
    deleted.
4031
4115
 
4032
 
    The working tree contains a list of pending merged revisions, which will
4033
 
    be included as parents in the next commit.  Normally, revert clears that
4034
 
    list as well as reverting the files.  If any files are specified, revert
4035
 
    leaves the pending merge list alone and reverts only the files.  Use "bzr
4036
 
    revert ." in the tree root to revert all files but keep the merge record,
4037
 
    and "bzr revert --forget-merges" to clear the pending merge list without
 
4116
    The working tree contains a list of revisions that have been merged but
 
4117
    not yet committed. These revisions will be included as additional parents
 
4118
    of the next commit.  Normally, using revert clears that list as well as
 
4119
    reverting the files.  If any files are specified, revert leaves the list
 
4120
    of uncommitted merges alone and reverts only the files.  Use ``bzr revert
 
4121
    .`` in the tree root to revert all files but keep the recorded merges,
 
4122
    and ``bzr revert --forget-merges`` to clear the pending merge list without
4038
4123
    reverting any files.
 
4124
 
 
4125
    Using "bzr revert --forget-merges", it is possible to apply all of the
 
4126
    changes from a branch in a single revision.  To do this, perform the merge
 
4127
    as desired.  Then doing revert with the "--forget-merges" option will keep
 
4128
    the content of the tree as it was, but it will clear the list of pending
 
4129
    merges.  The next commit will then contain all of the changes that are
 
4130
    present in the other branch, but without any other parent revisions.
 
4131
    Because this technique forgets where these changes originated, it may
 
4132
    cause additional conflicts on later merges involving the same source and
 
4133
    target branches.
4039
4134
    """
4040
4135
 
4041
4136
    _see_also = ['cat', 'export']
4051
4146
            forget_merges=None):
4052
4147
        tree, file_list = tree_files(file_list)
4053
4148
        tree.lock_write()
4054
 
        try:
4055
 
            if forget_merges:
4056
 
                tree.set_parent_ids(tree.get_parent_ids()[:1])
4057
 
            else:
4058
 
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4059
 
        finally:
4060
 
            tree.unlock()
 
4149
        self.add_cleanup(tree.unlock)
 
4150
        if forget_merges:
 
4151
            tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4152
        else:
 
4153
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4061
4154
 
4062
4155
    @staticmethod
4063
4156
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4122
4215
    To filter on a range of revisions, you can use the command -r begin..end
4123
4216
    -r revision requests a specific revision, -r ..end or -r begin.. are
4124
4217
    also valid.
 
4218
            
 
4219
    :Exit values:
 
4220
        1 - some missing revisions
 
4221
        0 - no missing revisions
4125
4222
 
4126
4223
    :Examples:
4127
4224
 
4210
4307
        if remote_branch.base == local_branch.base:
4211
4308
            remote_branch = local_branch
4212
4309
 
 
4310
        local_branch.lock_read()
 
4311
        self.add_cleanup(local_branch.unlock)
4213
4312
        local_revid_range = _revision_range_to_revid_range(
4214
4313
            _get_revision_range(my_revision, local_branch,
4215
4314
                self.name()))
4216
4315
 
 
4316
        remote_branch.lock_read()
 
4317
        self.add_cleanup(remote_branch.unlock)
4217
4318
        remote_revid_range = _revision_range_to_revid_range(
4218
4319
            _get_revision_range(revision,
4219
4320
                remote_branch, self.name()))
4220
4321
 
4221
 
        local_branch.lock_read()
4222
 
        try:
4223
 
            remote_branch.lock_read()
4224
 
            try:
4225
 
                local_extra, remote_extra = find_unmerged(
4226
 
                    local_branch, remote_branch, restrict,
4227
 
                    backward=not reverse,
4228
 
                    include_merges=include_merges,
4229
 
                    local_revid_range=local_revid_range,
4230
 
                    remote_revid_range=remote_revid_range)
4231
 
 
4232
 
                if log_format is None:
4233
 
                    registry = log.log_formatter_registry
4234
 
                    log_format = registry.get_default(local_branch)
4235
 
                lf = log_format(to_file=self.outf,
4236
 
                                show_ids=show_ids,
4237
 
                                show_timezone='original')
4238
 
 
4239
 
                status_code = 0
4240
 
                if local_extra and not theirs_only:
4241
 
                    message("You have %d extra revision(s):\n" %
4242
 
                        len(local_extra))
4243
 
                    for revision in iter_log_revisions(local_extra,
4244
 
                                        local_branch.repository,
4245
 
                                        verbose):
4246
 
                        lf.log_revision(revision)
4247
 
                    printed_local = True
4248
 
                    status_code = 1
4249
 
                else:
4250
 
                    printed_local = False
4251
 
 
4252
 
                if remote_extra and not mine_only:
4253
 
                    if printed_local is True:
4254
 
                        message("\n\n\n")
4255
 
                    message("You are missing %d revision(s):\n" %
4256
 
                        len(remote_extra))
4257
 
                    for revision in iter_log_revisions(remote_extra,
4258
 
                                        remote_branch.repository,
4259
 
                                        verbose):
4260
 
                        lf.log_revision(revision)
4261
 
                    status_code = 1
4262
 
 
4263
 
                if mine_only and not local_extra:
4264
 
                    # We checked local, and found nothing extra
4265
 
                    message('This branch is up to date.\n')
4266
 
                elif theirs_only and not remote_extra:
4267
 
                    # We checked remote, and found nothing extra
4268
 
                    message('Other branch is up to date.\n')
4269
 
                elif not (mine_only or theirs_only or local_extra or
4270
 
                          remote_extra):
4271
 
                    # We checked both branches, and neither one had extra
4272
 
                    # revisions
4273
 
                    message("Branches are up to date.\n")
4274
 
            finally:
4275
 
                remote_branch.unlock()
4276
 
        finally:
4277
 
            local_branch.unlock()
 
4322
        local_extra, remote_extra = find_unmerged(
 
4323
            local_branch, remote_branch, restrict,
 
4324
            backward=not reverse,
 
4325
            include_merges=include_merges,
 
4326
            local_revid_range=local_revid_range,
 
4327
            remote_revid_range=remote_revid_range)
 
4328
 
 
4329
        if log_format is None:
 
4330
            registry = log.log_formatter_registry
 
4331
            log_format = registry.get_default(local_branch)
 
4332
        lf = log_format(to_file=self.outf,
 
4333
                        show_ids=show_ids,
 
4334
                        show_timezone='original')
 
4335
 
 
4336
        status_code = 0
 
4337
        if local_extra and not theirs_only:
 
4338
            message("You have %d extra revision(s):\n" %
 
4339
                len(local_extra))
 
4340
            for revision in iter_log_revisions(local_extra,
 
4341
                                local_branch.repository,
 
4342
                                verbose):
 
4343
                lf.log_revision(revision)
 
4344
            printed_local = True
 
4345
            status_code = 1
 
4346
        else:
 
4347
            printed_local = False
 
4348
 
 
4349
        if remote_extra and not mine_only:
 
4350
            if printed_local is True:
 
4351
                message("\n\n\n")
 
4352
            message("You are missing %d revision(s):\n" %
 
4353
                len(remote_extra))
 
4354
            for revision in iter_log_revisions(remote_extra,
 
4355
                                remote_branch.repository,
 
4356
                                verbose):
 
4357
                lf.log_revision(revision)
 
4358
            status_code = 1
 
4359
 
 
4360
        if mine_only and not local_extra:
 
4361
            # We checked local, and found nothing extra
 
4362
            message('This branch is up to date.\n')
 
4363
        elif theirs_only and not remote_extra:
 
4364
            # We checked remote, and found nothing extra
 
4365
            message('Other branch is up to date.\n')
 
4366
        elif not (mine_only or theirs_only or local_extra or
 
4367
                  remote_extra):
 
4368
            # We checked both branches, and neither one had extra
 
4369
            # revisions
 
4370
            message("Branches are up to date.\n")
 
4371
        self.cleanup_now()
4278
4372
        if not status_code and parent is None and other_branch is not None:
4279
4373
            local_branch.lock_write()
4280
 
            try:
4281
 
                # handle race conditions - a parent might be set while we run.
4282
 
                if local_branch.get_parent() is None:
4283
 
                    local_branch.set_parent(remote_branch.base)
4284
 
            finally:
4285
 
                local_branch.unlock()
 
4374
            self.add_cleanup(local_branch.unlock)
 
4375
            # handle race conditions - a parent might be set while we run.
 
4376
            if local_branch.get_parent() is None:
 
4377
                local_branch.set_parent(remote_branch.base)
4286
4378
        return status_code
4287
4379
 
4288
4380
 
4367
4459
        else:
4368
4460
            b = Branch.open(branch)
4369
4461
        b.lock_read()
4370
 
        try:
4371
 
            if revision is None:
4372
 
                rev_id = b.last_revision()
4373
 
            else:
4374
 
                rev_id = revision[0].as_revision_id(b)
4375
 
            t = testament_class.from_revision(b.repository, rev_id)
4376
 
            if long:
4377
 
                sys.stdout.writelines(t.as_text_lines())
4378
 
            else:
4379
 
                sys.stdout.write(t.as_short_text())
4380
 
        finally:
4381
 
            b.unlock()
 
4462
        self.add_cleanup(b.unlock)
 
4463
        if revision is None:
 
4464
            rev_id = b.last_revision()
 
4465
        else:
 
4466
            rev_id = revision[0].as_revision_id(b)
 
4467
        t = testament_class.from_revision(b.repository, rev_id)
 
4468
        if long:
 
4469
            sys.stdout.writelines(t.as_text_lines())
 
4470
        else:
 
4471
            sys.stdout.write(t.as_short_text())
4382
4472
 
4383
4473
 
4384
4474
class cmd_annotate(Command):
4410
4500
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4411
4501
        if wt is not None:
4412
4502
            wt.lock_read()
 
4503
            self.add_cleanup(wt.unlock)
4413
4504
        else:
4414
4505
            branch.lock_read()
4415
 
        try:
4416
 
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4417
 
            if wt is not None:
4418
 
                file_id = wt.path2id(relpath)
4419
 
            else:
4420
 
                file_id = tree.path2id(relpath)
4421
 
            if file_id is None:
4422
 
                raise errors.NotVersionedError(filename)
4423
 
            file_version = tree.inventory[file_id].revision
4424
 
            if wt is not None and revision is None:
4425
 
                # If there is a tree and we're not annotating historical
4426
 
                # versions, annotate the working tree's content.
4427
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4428
 
                    show_ids=show_ids)
4429
 
            else:
4430
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4431
 
                              show_ids=show_ids)
4432
 
        finally:
4433
 
            if wt is not None:
4434
 
                wt.unlock()
4435
 
            else:
4436
 
                branch.unlock()
 
4506
            self.add_cleanup(branch.unlock)
 
4507
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4508
        tree.lock_read()
 
4509
        self.add_cleanup(tree.unlock)
 
4510
        if wt is not None:
 
4511
            file_id = wt.path2id(relpath)
 
4512
        else:
 
4513
            file_id = tree.path2id(relpath)
 
4514
        if file_id is None:
 
4515
            raise errors.NotVersionedError(filename)
 
4516
        file_version = tree.inventory[file_id].revision
 
4517
        if wt is not None and revision is None:
 
4518
            # If there is a tree and we're not annotating historical
 
4519
            # versions, annotate the working tree's content.
 
4520
            annotate_file_tree(wt, file_id, self.outf, long, all,
 
4521
                show_ids=show_ids)
 
4522
        else:
 
4523
            annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4524
                          show_ids=show_ids)
4437
4525
 
4438
4526
 
4439
4527
class cmd_re_sign(Command):
4451
4539
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4452
4540
        b = WorkingTree.open_containing(u'.')[0].branch
4453
4541
        b.lock_write()
4454
 
        try:
4455
 
            return self._run(b, revision_id_list, revision)
4456
 
        finally:
4457
 
            b.unlock()
 
4542
        self.add_cleanup(b.unlock)
 
4543
        return self._run(b, revision_id_list, revision)
4458
4544
 
4459
4545
    def _run(self, b, revision_id_list, revision):
4460
4546
        import bzrlib.gpg as gpg
4511
4597
    before they will be applied to the local branch.
4512
4598
 
4513
4599
    Bound branches use the nickname of its master branch unless it is set
4514
 
    locally, in which case binding will update the the local nickname to be
 
4600
    locally, in which case binding will update the local nickname to be
4515
4601
    that of the master.
4516
4602
    """
4517
4603
 
4606
4692
 
4607
4693
        if tree is not None:
4608
4694
            tree.lock_write()
 
4695
            self.add_cleanup(tree.unlock)
4609
4696
        else:
4610
4697
            b.lock_write()
4611
 
        try:
4612
 
            return self._run(b, tree, dry_run, verbose, revision, force,
4613
 
                             local=local)
4614
 
        finally:
4615
 
            if tree is not None:
4616
 
                tree.unlock()
4617
 
            else:
4618
 
                b.unlock()
 
4698
            self.add_cleanup(b.unlock)
 
4699
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4619
4700
 
4620
4701
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4621
4702
        from bzrlib.log import log_formatter, show_log
4678
4759
    CAUTION: Locks should only be broken when you are sure that the process
4679
4760
    holding the lock has been stopped.
4680
4761
 
4681
 
    You can get information on what locks are open via the 'bzr info' command.
 
4762
    You can get information on what locks are open via the 'bzr info
 
4763
    [location]' command.
4682
4764
 
4683
4765
    :Examples:
4684
4766
        bzr break-lock
 
4767
        bzr break-lock bzr+ssh://example.com/bzr/foo
4685
4768
    """
4686
4769
    takes_args = ['location?']
4687
4770
 
4717
4800
    takes_options = [
4718
4801
        Option('inet',
4719
4802
               help='Serve on stdin/out for use from inetd or sshd.'),
4720
 
        RegistryOption('protocol', 
4721
 
               help="Protocol to serve.", 
 
4803
        RegistryOption('protocol',
 
4804
               help="Protocol to serve.",
4722
4805
               lazy_registry=('bzrlib.transport', 'transport_server_registry'),
4723
4806
               value_switches=True),
4724
4807
        Option('port',
4733
4816
        Option('allow-writes',
4734
4817
               help='By default the server is a readonly server.  Supplying '
4735
4818
                    '--allow-writes enables write access to the contents of '
4736
 
                    'the served directory and below.'
 
4819
                    'the served directory and below.  Note that ``bzr serve`` '
 
4820
                    'does not perform authentication, so unless some form of '
 
4821
                    'external authentication is arranged supplying this '
 
4822
                    'option leads to global uncontrolled write access to your '
 
4823
                    'file system.'
4737
4824
                ),
4738
4825
        ]
4739
4826
 
4955
5042
      directly from the merge directive, without retrieving data from a
4956
5043
      branch.
4957
5044
 
4958
 
    If --no-bundle is specified, then public_branch is needed (and must be
4959
 
    up-to-date), so that the receiver can perform the merge using the
4960
 
    public_branch.  The public_branch is always included if known, so that
4961
 
    people can check it later.
4962
 
 
4963
 
    The submit branch defaults to the parent, but can be overridden.  Both
4964
 
    submit branch and public branch will be remembered if supplied.
4965
 
 
4966
 
    If a public_branch is known for the submit_branch, that public submit
4967
 
    branch is used in the merge instructions.  This means that a local mirror
4968
 
    can be used as your actual submit branch, once you have set public_branch
4969
 
    for that mirror.
 
5045
    `bzr send` creates a compact data set that, when applied using bzr
 
5046
    merge, has the same effect as merging from the source branch.  
 
5047
    
 
5048
    By default the merge directive is self-contained and can be applied to any
 
5049
    branch containing submit_branch in its ancestory without needing access to
 
5050
    the source branch.
 
5051
    
 
5052
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5053
    revisions, but only a structured request to merge from the
 
5054
    public_location.  In that case the public_branch is needed and it must be
 
5055
    up-to-date and accessible to the recipient.  The public_branch is always
 
5056
    included if known, so that people can check it later.
 
5057
 
 
5058
    The submit branch defaults to the parent of the source branch, but can be
 
5059
    overridden.  Both submit branch and public branch will be remembered in
 
5060
    branch.conf the first time they are used for a particular branch.  The
 
5061
    source branch defaults to that containing the working directory, but can
 
5062
    be changed using --from.
 
5063
 
 
5064
    In order to calculate those changes, bzr must analyse the submit branch.
 
5065
    Therefore it is most efficient for the submit branch to be a local mirror.
 
5066
    If a public location is known for the submit_branch, that location is used
 
5067
    in the merge directive.
 
5068
 
 
5069
    The default behaviour is to send the merge directive by mail, unless -o is
 
5070
    given, in which case it is sent to a file.
4970
5071
 
4971
5072
    Mail is sent using your preferred mail program.  This should be transparent
4972
5073
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
4992
5093
 
4993
5094
    The merge directives created by bzr send may be applied using bzr merge or
4994
5095
    bzr pull by specifying a file containing a merge directive as the location.
 
5096
 
 
5097
    bzr send makes extensive use of public locations to map local locations into
 
5098
    URLs that can be used by other people.  See `bzr help configuration` to
 
5099
    set them, and use `bzr info` to display them.
4995
5100
    """
4996
5101
 
4997
5102
    encoding_type = 'exact'
5156
5261
            ):
5157
5262
        branch, relpath = Branch.open_containing(directory)
5158
5263
        branch.lock_write()
5159
 
        try:
5160
 
            if delete:
5161
 
                branch.tags.delete_tag(tag_name)
5162
 
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
5264
        self.add_cleanup(branch.unlock)
 
5265
        if delete:
 
5266
            branch.tags.delete_tag(tag_name)
 
5267
            self.outf.write('Deleted tag %s.\n' % tag_name)
 
5268
        else:
 
5269
            if revision:
 
5270
                if len(revision) != 1:
 
5271
                    raise errors.BzrCommandError(
 
5272
                        "Tags can only be placed on a single revision, "
 
5273
                        "not on a range")
 
5274
                revision_id = revision[0].as_revision_id(branch)
5163
5275
            else:
5164
 
                if revision:
5165
 
                    if len(revision) != 1:
5166
 
                        raise errors.BzrCommandError(
5167
 
                            "Tags can only be placed on a single revision, "
5168
 
                            "not on a range")
5169
 
                    revision_id = revision[0].as_revision_id(branch)
5170
 
                else:
5171
 
                    revision_id = branch.last_revision()
5172
 
                if (not force) and branch.tags.has_tag(tag_name):
5173
 
                    raise errors.TagAlreadyExists(tag_name)
5174
 
                branch.tags.set_tag(tag_name, revision_id)
5175
 
                self.outf.write('Created tag %s.\n' % tag_name)
5176
 
        finally:
5177
 
            branch.unlock()
 
5276
                revision_id = branch.last_revision()
 
5277
            if (not force) and branch.tags.has_tag(tag_name):
 
5278
                raise errors.TagAlreadyExists(tag_name)
 
5279
            branch.tags.set_tag(tag_name, revision_id)
 
5280
            self.outf.write('Created tag %s.\n' % tag_name)
5178
5281
 
5179
5282
 
5180
5283
class cmd_tags(Command):
5213
5316
            return
5214
5317
 
5215
5318
        branch.lock_read()
5216
 
        try:
5217
 
            if revision:
5218
 
                graph = branch.repository.get_graph()
5219
 
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5220
 
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5221
 
                # only show revisions between revid1 and revid2 (inclusive)
5222
 
                tags = [(tag, revid) for tag, revid in tags if
5223
 
                    graph.is_between(revid, revid1, revid2)]
5224
 
            if sort == 'alpha':
5225
 
                tags.sort()
5226
 
            elif sort == 'time':
5227
 
                timestamps = {}
5228
 
                for tag, revid in tags:
5229
 
                    try:
5230
 
                        revobj = branch.repository.get_revision(revid)
5231
 
                    except errors.NoSuchRevision:
5232
 
                        timestamp = sys.maxint # place them at the end
5233
 
                    else:
5234
 
                        timestamp = revobj.timestamp
5235
 
                    timestamps[revid] = timestamp
5236
 
                tags.sort(key=lambda x: timestamps[x[1]])
5237
 
            if not show_ids:
5238
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5239
 
                for index, (tag, revid) in enumerate(tags):
5240
 
                    try:
5241
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5242
 
                        if isinstance(revno, tuple):
5243
 
                            revno = '.'.join(map(str, revno))
5244
 
                    except errors.NoSuchRevision:
5245
 
                        # Bad tag data/merges can lead to tagged revisions
5246
 
                        # which are not in this branch. Fail gracefully ...
5247
 
                        revno = '?'
5248
 
                    tags[index] = (tag, revno)
5249
 
        finally:
5250
 
            branch.unlock()
 
5319
        self.add_cleanup(branch.unlock)
 
5320
        if revision:
 
5321
            graph = branch.repository.get_graph()
 
5322
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5323
            revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5324
            # only show revisions between revid1 and revid2 (inclusive)
 
5325
            tags = [(tag, revid) for tag, revid in tags if
 
5326
                graph.is_between(revid, revid1, revid2)]
 
5327
        if sort == 'alpha':
 
5328
            tags.sort()
 
5329
        elif sort == 'time':
 
5330
            timestamps = {}
 
5331
            for tag, revid in tags:
 
5332
                try:
 
5333
                    revobj = branch.repository.get_revision(revid)
 
5334
                except errors.NoSuchRevision:
 
5335
                    timestamp = sys.maxint # place them at the end
 
5336
                else:
 
5337
                    timestamp = revobj.timestamp
 
5338
                timestamps[revid] = timestamp
 
5339
            tags.sort(key=lambda x: timestamps[x[1]])
 
5340
        if not show_ids:
 
5341
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5342
            for index, (tag, revid) in enumerate(tags):
 
5343
                try:
 
5344
                    revno = branch.revision_id_to_dotted_revno(revid)
 
5345
                    if isinstance(revno, tuple):
 
5346
                        revno = '.'.join(map(str, revno))
 
5347
                except errors.NoSuchRevision:
 
5348
                    # Bad tag data/merges can lead to tagged revisions
 
5349
                    # which are not in this branch. Fail gracefully ...
 
5350
                    revno = '?'
 
5351
                tags[index] = (tag, revno)
 
5352
        self.cleanup_now()
5251
5353
        for tag, revspec in tags:
5252
5354
            self.outf.write('%-20s %s\n' % (tag, revspec))
5253
5355
 
5362
5464
    /path/to/newbranch.
5363
5465
 
5364
5466
    Bound branches use the nickname of its master branch unless it is set
5365
 
    locally, in which case switching will update the the local nickname to be
 
5467
    locally, in which case switching will update the local nickname to be
5366
5468
    that of the master.
5367
5469
    """
5368
5470
 
5369
 
    takes_args = ['to_location']
 
5471
    takes_args = ['to_location?']
5370
5472
    takes_options = [Option('force',
5371
5473
                        help='Switch even if local commits will be lost.'),
 
5474
                     'revision',
5372
5475
                     Option('create-branch', short_name='b',
5373
5476
                        help='Create the target branch from this one before'
5374
5477
                             ' switching to it.'),
5375
 
                     ]
 
5478
                    ]
5376
5479
 
5377
 
    def run(self, to_location, force=False, create_branch=False):
 
5480
    def run(self, to_location=None, force=False, create_branch=False,
 
5481
            revision=None):
5378
5482
        from bzrlib import switch
5379
5483
        tree_location = '.'
 
5484
        revision = _get_one_revision('switch', revision)
5380
5485
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
 
5486
        if to_location is None:
 
5487
            if revision is None:
 
5488
                raise errors.BzrCommandError('You must supply either a'
 
5489
                                             ' revision or a location')
 
5490
            to_location = '.'
5381
5491
        try:
5382
5492
            branch = control_dir.open_branch()
5383
5493
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5388
5498
            if branch is None:
5389
5499
                raise errors.BzrCommandError('cannot create branch without'
5390
5500
                                             ' source branch')
 
5501
            to_location = directory_service.directories.dereference(
 
5502
                              to_location)
5391
5503
            if '/' not in to_location and '\\' not in to_location:
5392
5504
                # This path is meant to be relative to the existing branch
5393
5505
                this_url = self._get_branch_location(control_dir)
5395
5507
            to_branch = branch.bzrdir.sprout(to_location,
5396
5508
                                 possible_transports=[branch.bzrdir.root_transport],
5397
5509
                                 source_branch=branch).open_branch()
5398
 
            # try:
5399
 
            #     from_branch = control_dir.open_branch()
5400
 
            # except errors.NotBranchError:
5401
 
            #     raise BzrCommandError('Cannot create a branch from this'
5402
 
            #         ' location when we cannot open this branch')
5403
 
            # from_branch.bzrdir.sprout(
5404
 
            pass
5405
5510
        else:
5406
5511
            try:
5407
5512
                to_branch = Branch.open(to_location)
5409
5514
                this_url = self._get_branch_location(control_dir)
5410
5515
                to_branch = Branch.open(
5411
5516
                    urlutils.join(this_url, '..', to_location))
5412
 
        switch.switch(control_dir, to_branch, force)
 
5517
        if revision is not None:
 
5518
            revision = revision.as_revision_id(to_branch)
 
5519
        switch.switch(control_dir, to_branch, force, revision_id=revision)
5413
5520
        if had_explicit_nick:
5414
5521
            branch = control_dir.open_branch() #get the new branch!
5415
5522
            branch.nick = to_branch.nick
5662
5769
            try:
5663
5770
                shelver.run()
5664
5771
            finally:
5665
 
                shelver.work_tree.unlock()
 
5772
                shelver.finalize()
5666
5773
        except errors.UserAbort:
5667
5774
            return 0
5668
5775
 
5669
5776
    def run_for_list(self):
5670
5777
        tree = WorkingTree.open_containing('.')[0]
5671
5778
        tree.lock_read()
5672
 
        try:
5673
 
            manager = tree.get_shelf_manager()
5674
 
            shelves = manager.active_shelves()
5675
 
            if len(shelves) == 0:
5676
 
                note('No shelved changes.')
5677
 
                return 0
5678
 
            for shelf_id in reversed(shelves):
5679
 
                message = manager.get_metadata(shelf_id).get('message')
5680
 
                if message is None:
5681
 
                    message = '<no message>'
5682
 
                self.outf.write('%3d: %s\n' % (shelf_id, message))
5683
 
            return 1
5684
 
        finally:
5685
 
            tree.unlock()
 
5779
        self.add_cleanup(tree.unlock)
 
5780
        manager = tree.get_shelf_manager()
 
5781
        shelves = manager.active_shelves()
 
5782
        if len(shelves) == 0:
 
5783
            note('No shelved changes.')
 
5784
            return 0
 
5785
        for shelf_id in reversed(shelves):
 
5786
            message = manager.get_metadata(shelf_id).get('message')
 
5787
            if message is None:
 
5788
                message = '<no message>'
 
5789
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5790
        return 1
5686
5791
 
5687
5792
 
5688
5793
class cmd_unshelve(Command):
5700
5805
            enum_switch=False, value_switches=True,
5701
5806
            apply="Apply changes and remove from the shelf.",
5702
5807
            dry_run="Show changes, but do not apply or remove them.",
5703
 
            delete_only="Delete changes without applying them."
 
5808
            preview="Instead of unshelving the changes, show the diff that "
 
5809
                    "would result from unshelving.",
 
5810
            delete_only="Delete changes without applying them.",
 
5811
            keep="Apply changes but don't delete them.",
5704
5812
        )
5705
5813
    ]
5706
5814
    _see_also = ['shelve']