~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-28 07:08:42 UTC
  • mfrom: (2380 +trunk)
  • mto: (2018.5.146 hpss)
  • mto: This revision was merged to the branch mainline in revision 2414.
  • Revision ID: andrew.bennetts@canonical.com-20070328070842-r843houy668oxb9o
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007 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
17
17
"""builtin bzr commands"""
18
18
 
19
19
import os
 
20
from StringIO import StringIO
20
21
 
21
22
from bzrlib.lazy_import import lazy_import
22
23
lazy_import(globals(), """
23
24
import codecs
24
25
import errno
 
26
import smtplib
25
27
import sys
26
28
import tempfile
 
29
import time
27
30
 
28
31
import bzrlib
29
32
from bzrlib import (
33
36
    delta,
34
37
    config,
35
38
    errors,
 
39
    globbing,
36
40
    ignores,
37
41
    log,
38
42
    merge as _mod_merge,
 
43
    merge_directive,
39
44
    osutils,
 
45
    registry,
40
46
    repository,
41
47
    symbol_versioning,
42
48
    transport,
142
148
    unknown
143
149
        Not versioned and not matching an ignore pattern.
144
150
 
145
 
    To see ignored files use 'bzr ignored'.  For details in the
 
151
    To see ignored files use 'bzr ignored'.  For details on the
146
152
    changes to file texts, use 'bzr diff'.
147
153
    
148
154
    --short gives a status flags for each item, similar to the SVN's status
177
183
    # TODO: --no-recurse, --recurse options
178
184
    
179
185
    takes_args = ['file*']
180
 
    takes_options = ['show-ids', 'revision', 'short']
 
186
    takes_options = ['show-ids', 'revision',
 
187
                     Option('short', help='Give short SVN-style status lines'),
 
188
                     Option('versioned', help='Only show versioned files')]
181
189
    aliases = ['st', 'stat']
182
190
 
183
191
    encoding_type = 'replace'
184
192
    
185
193
    @display_command
186
 
    def run(self, show_ids=False, file_list=None, revision=None, short=False):
 
194
    def run(self, show_ids=False, file_list=None, revision=None, short=False,
 
195
            versioned=False):
187
196
        from bzrlib.status import show_tree_status
188
197
 
189
198
        tree, file_list = tree_files(file_list)
190
199
            
191
200
        show_tree_status(tree, show_ids=show_ids,
192
201
                         specific_files=file_list, revision=revision,
193
 
                         to_file=self.outf,
194
 
                         short=short)
 
202
                         to_file=self.outf, short=short, versioned=versioned)
195
203
 
196
204
 
197
205
class cmd_cat_revision(Command):
237
245
    this will refuse to run against one.
238
246
    """
239
247
 
240
 
    hidden = True
241
 
 
242
248
    takes_args = ['location?']
243
249
 
244
250
    def run(self, location='.'):
344
350
            file_ids_from=None):
345
351
        import bzrlib.add
346
352
 
 
353
        base_tree = None
347
354
        if file_ids_from is not None:
348
355
            try:
349
356
                base_tree, base_path = WorkingTree.open_containing(
359
366
            action = bzrlib.add.AddAction(to_file=self.outf,
360
367
                should_print=(not is_quiet()))
361
368
 
362
 
        added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
363
 
                                              action=action, save=not dry_run)
 
369
        if base_tree:
 
370
            base_tree.lock_read()
 
371
        try:
 
372
            added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
 
373
                action=action, save=not dry_run)
 
374
        finally:
 
375
            if base_tree is not None:
 
376
                base_tree.unlock()
364
377
        if len(ignored) > 0:
365
378
            if verbose:
366
379
                for glob in sorted(ignored.keys()):
432
445
            raise errors.BzrCommandError('invalid kind specified')
433
446
 
434
447
        work_tree, file_list = tree_files(file_list)
435
 
 
436
 
        if revision is not None:
437
 
            if len(revision) > 1:
438
 
                raise errors.BzrCommandError('bzr inventory --revision takes'
439
 
                                             ' exactly one revision identifier')
440
 
            revision_id = revision[0].in_history(work_tree.branch).rev_id
441
 
            tree = work_tree.branch.repository.revision_tree(revision_id)
442
 
                        
443
 
            # We include work_tree as well as 'tree' here
444
 
            # So that doing '-r 10 path/foo' will lookup whatever file
445
 
            # exists now at 'path/foo' even if it has been renamed, as
446
 
            # well as whatever files existed in revision 10 at path/foo
447
 
            trees = [tree, work_tree]
448
 
        else:
449
 
            tree = work_tree
450
 
            trees = [tree]
451
 
 
452
 
        if file_list is not None:
453
 
            file_ids = _mod_tree.find_ids_across_trees(file_list, trees,
454
 
                                                      require_versioned=True)
455
 
            # find_ids_across_trees may include some paths that don't
456
 
            # exist in 'tree'.
457
 
            entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
458
 
                             for file_id in file_ids if file_id in tree)
459
 
        else:
460
 
            entries = tree.inventory.entries()
 
448
        work_tree.lock_read()
 
449
        try:
 
450
            if revision is not None:
 
451
                if len(revision) > 1:
 
452
                    raise errors.BzrCommandError(
 
453
                        'bzr inventory --revision takes exactly one revision'
 
454
                        ' identifier')
 
455
                revision_id = revision[0].in_history(work_tree.branch).rev_id
 
456
                tree = work_tree.branch.repository.revision_tree(revision_id)
 
457
 
 
458
                extra_trees = [work_tree]
 
459
                tree.lock_read()
 
460
            else:
 
461
                tree = work_tree
 
462
                extra_trees = []
 
463
 
 
464
            if file_list is not None:
 
465
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
466
                                          require_versioned=True)
 
467
                # find_ids_across_trees may include some paths that don't
 
468
                # exist in 'tree'.
 
469
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
470
                                 for file_id in file_ids if file_id in tree)
 
471
            else:
 
472
                entries = tree.inventory.entries()
 
473
        finally:
 
474
            tree.unlock()
 
475
            if tree is not work_tree:
 
476
                work_tree.unlock()
461
477
 
462
478
        for path, entry in entries:
463
479
            if kind and kind != entry.kind:
537
553
    location can be accessed.
538
554
    """
539
555
 
540
 
    takes_options = ['remember', 'overwrite', 'revision', 'verbose']
 
556
    takes_options = ['remember', 'overwrite', 'revision', 'verbose',
 
557
        Option('directory',
 
558
            help='branch to pull into, '
 
559
                 'rather than the one containing the working directory',
 
560
            short_name='d',
 
561
            type=unicode,
 
562
            ),
 
563
        ]
541
564
    takes_args = ['location?']
542
565
    encoding_type = 'replace'
543
566
 
544
 
    def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
 
567
    def run(self, location=None, remember=False, overwrite=False,
 
568
            revision=None, verbose=False,
 
569
            directory=None):
 
570
        from bzrlib.tag import _merge_tags_if_possible
545
571
        # FIXME: too much stuff is in the command class
 
572
        if directory is None:
 
573
            directory = u'.'
546
574
        try:
547
 
            tree_to = WorkingTree.open_containing(u'.')[0]
 
575
            tree_to = WorkingTree.open_containing(directory)[0]
548
576
            branch_to = tree_to.branch
549
577
        except errors.NoWorkingTree:
550
578
            tree_to = None
551
 
            branch_to = Branch.open_containing(u'.')[0]
 
579
            branch_to = Branch.open_containing(directory)[0]
552
580
 
553
581
        reader = None
554
582
        if location is not None:
568
596
                self.outf.write("Using saved location: %s\n" % display_url)
569
597
                location = stored_loc
570
598
 
571
 
 
572
599
        if reader is not None:
573
600
            install_bundle(branch_to.repository, reader)
574
601
            branch_from = branch_to
589
616
 
590
617
        old_rh = branch_to.revision_history()
591
618
        if tree_to is not None:
592
 
            count = tree_to.pull(branch_from, overwrite, rev_id)
 
619
            result = tree_to.pull(branch_from, overwrite, rev_id,
 
620
                delta._ChangeReporter(unversioned_filter=tree_to.is_ignored))
593
621
        else:
594
 
            count = branch_to.pull(branch_from, overwrite, rev_id)
595
 
        note('%d revision(s) pulled.' % (count,))
 
622
            result = branch_to.pull(branch_from, overwrite, rev_id)
596
623
 
 
624
        result.report(self.outf)
597
625
        if verbose:
 
626
            from bzrlib.log import show_changed_revisions
598
627
            new_rh = branch_to.revision_history()
599
 
            if old_rh != new_rh:
600
 
                # Something changed
601
 
                from bzrlib.log import show_changed_revisions
602
 
                show_changed_revisions(branch_to, old_rh, new_rh,
603
 
                                       to_file=self.outf)
 
628
            show_changed_revisions(branch_to, old_rh, new_rh, to_file=self.outf)
604
629
 
605
630
 
606
631
class cmd_push(Command):
630
655
    """
631
656
 
632
657
    takes_options = ['remember', 'overwrite', 'verbose',
633
 
                     Option('create-prefix',
634
 
                            help='Create the path leading up to the branch '
635
 
                                 'if it does not already exist'),
636
 
                     Option('use-existing-dir',
637
 
                            help='By default push will fail if the target'
638
 
                                 ' directory exists, but does not already'
639
 
                                 ' have a control directory. This flag will'
640
 
                                 ' allow push to proceed.'),
641
 
                     ]
 
658
        Option('create-prefix',
 
659
               help='Create the path leading up to the branch '
 
660
                    'if it does not already exist'),
 
661
        Option('directory',
 
662
            help='branch to push from, '
 
663
                 'rather than the one containing the working directory',
 
664
            short_name='d',
 
665
            type=unicode,
 
666
            ),
 
667
        Option('use-existing-dir',
 
668
               help='By default push will fail if the target'
 
669
                    ' directory exists, but does not already'
 
670
                    ' have a control directory. This flag will'
 
671
                    ' allow push to proceed.'),
 
672
        ]
642
673
    takes_args = ['location?']
643
674
    encoding_type = 'replace'
644
675
 
645
676
    def run(self, location=None, remember=False, overwrite=False,
646
 
            create_prefix=False, verbose=False, use_existing_dir=False):
 
677
            create_prefix=False, verbose=False,
 
678
            use_existing_dir=False,
 
679
            directory=None):
647
680
        # FIXME: Way too big!  Put this into a function called from the
648
681
        # command.
649
 
        
650
 
        br_from = Branch.open_containing('.')[0]
 
682
        if directory is None:
 
683
            directory = '.'
 
684
        br_from = Branch.open_containing(directory)[0]
651
685
        stored_loc = br_from.get_push_location()
652
686
        if location is None:
653
687
            if stored_loc is None:
661
695
        to_transport = transport.get_transport(location)
662
696
        location_url = to_transport.base
663
697
 
664
 
        old_rh = []
665
 
        count = 0
666
 
 
667
698
        br_to = repository_to = dir_to = None
668
699
        try:
669
700
            dir_to = bzrdir.BzrDir.open_from_transport(to_transport)
683
714
            else:
684
715
                # Found a branch, so we must have found a repository
685
716
                repository_to = br_to.repository
686
 
 
 
717
        push_result = None
687
718
        old_rh = []
688
719
        if dir_to is None:
 
720
            # The destination doesn't exist; create it.
689
721
            # XXX: Refactor the create_prefix/no_create_prefix code into a
690
722
            #      common helper function
691
723
            try:
732
764
            dir_to = br_from.bzrdir.clone(location_url,
733
765
                revision_id=br_from.last_revision())
734
766
            br_to = dir_to.open_branch()
735
 
            count = br_to.last_revision_info()[0]
 
767
            # TODO: Some more useful message about what was copied
 
768
            note('Created new branch.')
736
769
            # We successfully created the target, remember it
737
770
            if br_from.get_push_location() is None or remember:
738
771
                br_from.set_push_location(br_to.base)
751
784
            repository_to.fetch(br_from.repository,
752
785
                                revision_id=last_revision_id)
753
786
            br_to = br_from.clone(dir_to, revision_id=last_revision_id)
754
 
            count = len(br_to.revision_history())
 
787
            note('Created new branch.')
755
788
            if br_from.get_push_location() is None or remember:
756
789
                br_from.set_push_location(br_to.base)
757
790
        else: # We have a valid to branch
766
799
                except errors.NotLocalUrl:
767
800
                    warning('This transport does not update the working '
768
801
                            'tree of: %s' % (br_to.base,))
769
 
                    count = br_from.push(br_to, overwrite)
 
802
                    push_result = br_from.push(br_to, overwrite)
770
803
                except errors.NoWorkingTree:
771
 
                    count = br_from.push(br_to, overwrite)
 
804
                    push_result = br_from.push(br_to, overwrite)
772
805
                else:
773
806
                    tree_to.lock_write()
774
807
                    try:
775
 
                        count = br_from.push(tree_to.branch, overwrite)
 
808
                        push_result = br_from.push(tree_to.branch, overwrite)
776
809
                        tree_to.update()
777
810
                    finally:
778
811
                        tree_to.unlock()
779
812
            except errors.DivergedBranches:
780
813
                raise errors.BzrCommandError('These branches have diverged.'
781
814
                                        '  Try using "merge" and then "push".')
782
 
        note('%d revision(s) pushed.' % (count,))
783
 
 
784
 
        if verbose:
 
815
        if push_result is not None:
 
816
            push_result.report(self.outf)
 
817
        elif verbose:
785
818
            new_rh = br_to.revision_history()
786
819
            if old_rh != new_rh:
787
820
                # Something changed
788
821
                from bzrlib.log import show_changed_revisions
789
822
                show_changed_revisions(br_to, old_rh, new_rh,
790
823
                                       to_file=self.outf)
 
824
        else:
 
825
            # we probably did a clone rather than a push, so a message was
 
826
            # emitted above
 
827
            pass
791
828
 
792
829
 
793
830
class cmd_branch(Command):
808
845
    aliases = ['get', 'clone']
809
846
 
810
847
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
848
        from bzrlib.tag import _merge_tags_if_possible
811
849
        if revision is None:
812
850
            revision = [None]
813
851
        elif len(revision) > 1:
814
852
            raise errors.BzrCommandError(
815
853
                'bzr branch --revision takes exactly 1 revision value')
816
 
        try:
817
 
            br_from = Branch.open(from_location)
818
 
        except OSError, e:
819
 
            if e.errno == errno.ENOENT:
820
 
                raise errors.BzrCommandError('Source location "%s" does not'
821
 
                                             ' exist.' % to_location)
822
 
            else:
823
 
                raise
 
854
 
 
855
        br_from = Branch.open(from_location)
824
856
        br_from.lock_read()
825
857
        try:
826
858
            if basis is not None:
864
896
                raise errors.BzrCommandError(msg)
865
897
            if name:
866
898
                branch.control_files.put_utf8('branch-name', name)
 
899
            _merge_tags_if_possible(br_from, branch)
867
900
            note('Branched %d revision(s).' % branch.revno())
868
901
        finally:
869
902
            br_from.unlock()
885
918
    out of date [so you cannot commit] but it may be useful (i.e. to examine old
886
919
    code.)
887
920
 
888
 
    --basis is to speed up checking out from remote branches.  When specified, it
889
 
    uses the inventory and file contents from the basis branch in preference to the
890
 
    branch being checked out.
891
 
 
892
921
    See "help checkouts" for more information on checkouts.
893
922
    """
894
923
    takes_args = ['branch_location?', 'to_location?']
955
984
    @display_command
956
985
    def run(self, dir=u'.'):
957
986
        tree = WorkingTree.open_containing(dir)[0]
958
 
        old_inv = tree.basis_tree().inventory
959
 
        new_inv = tree.read_working_inventory()
960
 
        renames = list(_mod_tree.find_renames(old_inv, new_inv))
961
 
        renames.sort()
962
 
        for old_name, new_name in renames:
963
 
            self.outf.write("%s => %s\n" % (old_name, new_name))
 
987
        tree.lock_read()
 
988
        try:
 
989
            new_inv = tree.inventory
 
990
            old_tree = tree.basis_tree()
 
991
            old_tree.lock_read()
 
992
            try:
 
993
                old_inv = old_tree.inventory
 
994
                renames = list(_mod_tree.find_renames(old_inv, new_inv))
 
995
                renames.sort()
 
996
                for old_name, new_name in renames:
 
997
                    self.outf.write("%s => %s\n" % (old_name, new_name))
 
998
            finally:
 
999
                old_tree.unlock()
 
1000
        finally:
 
1001
            tree.unlock()
964
1002
 
965
1003
 
966
1004
class cmd_update(Command):
1071
1109
    @display_command
1072
1110
    def run(self, filename):
1073
1111
        tree, relpath = WorkingTree.open_containing(filename)
1074
 
        i = tree.inventory.path2id(relpath)
 
1112
        i = tree.path2id(relpath)
1075
1113
        if i is None:
1076
1114
            raise errors.NotVersionedError(filename)
1077
1115
        else:
1091
1129
    @display_command
1092
1130
    def run(self, filename):
1093
1131
        tree, relpath = WorkingTree.open_containing(filename)
1094
 
        inv = tree.inventory
1095
 
        fid = inv.path2id(relpath)
 
1132
        fid = tree.path2id(relpath)
1096
1133
        if fid is None:
1097
1134
            raise errors.NotVersionedError(filename)
1098
 
        for fip in inv.get_idpath(fid):
1099
 
            self.outf.write(fip + '\n')
 
1135
        segments = osutils.splitpath(relpath)
 
1136
        for pos in range(1, len(segments) + 1):
 
1137
            path = osutils.joinpath(segments[:pos])
 
1138
            self.outf.write("%s\n" % tree.path2id(path))
1100
1139
 
1101
1140
 
1102
1141
class cmd_reconcile(Command):
1171
1210
 
1172
1211
    If there is a repository in a parent directory of the location, then 
1173
1212
    the history of the branch will be stored in the repository.  Otherwise
1174
 
    init creates a standalone branch which carries its own history in 
1175
 
    .bzr.
 
1213
    init creates a standalone branch which carries its own history
 
1214
    in the .bzr directory.
1176
1215
 
1177
1216
    If there is already a branch at the location but it has no working tree,
1178
1217
    the tree can be populated with 'bzr checkout'.
1186
1225
    """
1187
1226
    takes_args = ['location?']
1188
1227
    takes_options = [
1189
 
                     RegistryOption('format',
1190
 
                            help='Specify a format for this branch. See "bzr '
1191
 
                            'help formats" for details',
1192
 
                            converter=bzrdir.format_registry.make_bzrdir,
1193
 
                            registry=bzrdir.format_registry,
1194
 
                            value_switches=True, title="Branch Format"),
1195
 
                     ]
1196
 
    def run(self, location=None, format=None):
 
1228
         RegistryOption('format',
 
1229
                help='Specify a format for this branch. '
 
1230
                'See "help formats".',
 
1231
                registry=bzrdir.format_registry,
 
1232
                converter=bzrdir.format_registry.make_bzrdir,
 
1233
                value_switches=True,
 
1234
                title="Branch Format",
 
1235
                ),
 
1236
         Option('append-revisions-only',
 
1237
                help='Never change revnos or the existing log.'
 
1238
                '  Append revisions to it only.')
 
1239
         ]
 
1240
    def run(self, location=None, format=None, append_revisions_only=False):
1197
1241
        if format is None:
1198
1242
            format = bzrdir.format_registry.make_bzrdir('default')
1199
1243
        if location is None:
1216
1260
            existing_bzrdir = bzrdir.BzrDir.open(location)
1217
1261
        except errors.NotBranchError:
1218
1262
            # really a NotBzrDir error...
1219
 
            bzrdir.BzrDir.create_branch_convenience(location, format=format)
 
1263
            branch = bzrdir.BzrDir.create_branch_convenience(to_transport.base,
 
1264
                                                             format=format)
1220
1265
        else:
1221
1266
            from bzrlib.transport.local import LocalTransport
1222
1267
            if existing_bzrdir.has_branch():
1225
1270
                        raise errors.BranchExistsWithoutWorkingTree(location)
1226
1271
                raise errors.AlreadyBranchError(location)
1227
1272
            else:
1228
 
                existing_bzrdir.create_branch()
 
1273
                branch = existing_bzrdir.create_branch()
1229
1274
                existing_bzrdir.create_workingtree()
 
1275
        if append_revisions_only:
 
1276
            try:
 
1277
                branch.set_append_revisions_only(True)
 
1278
            except errors.UpgradeRequired:
 
1279
                raise errors.BzrCommandError('This branch format cannot be set'
 
1280
                    ' to append-revisions-only.  Try --experimental-branch6')
1230
1281
 
1231
1282
 
1232
1283
class cmd_init_repository(Command):
1233
1284
    """Create a shared repository to hold branches.
1234
1285
 
1235
1286
    New branches created under the repository directory will store their revisions
1236
 
    in the repository, not in the branch directory, if the branch format supports
1237
 
    shared storage.
 
1287
    in the repository, not in the branch directory.
1238
1288
 
1239
1289
    example:
1240
 
        bzr init-repo repo
 
1290
        bzr init-repo --no-trees repo
1241
1291
        bzr init repo/trunk
1242
1292
        bzr checkout --lightweight repo/trunk trunk-checkout
1243
1293
        cd trunk-checkout
1244
1294
        (add files here)
1245
1295
    """
1246
 
    takes_args = ["location"] 
 
1296
 
 
1297
    takes_args = ["location"]
1247
1298
    takes_options = [RegistryOption('format',
1248
1299
                            help='Specify a format for this repository. See'
1249
1300
                                 ' "bzr help formats" for details',
1250
1301
                            registry=bzrdir.format_registry,
1251
1302
                            converter=bzrdir.format_registry.make_bzrdir,
1252
1303
                            value_switches=True, title='Repository format'),
1253
 
                     Option('trees',
1254
 
                             help='Allows branches in repository to have'
1255
 
                             ' a working tree')]
 
1304
                     Option('no-trees',
 
1305
                             help='Branches in the repository will default to'
 
1306
                                  ' not having a working tree'),
 
1307
                    ]
1256
1308
    aliases = ["init-repo"]
1257
 
    def run(self, location, format=None, trees=False):
 
1309
 
 
1310
    def run(self, location, format=None, no_trees=False):
1258
1311
        if format is None:
1259
1312
            format = bzrdir.format_registry.make_bzrdir('default')
1260
1313
 
1269
1322
 
1270
1323
        newdir = format.initialize_on_transport(to_transport)
1271
1324
        repo = newdir.create_repository(shared=True)
1272
 
        repo.set_make_working_trees(trees)
 
1325
        repo.set_make_working_trees(not no_trees)
1273
1326
 
1274
1327
 
1275
1328
class cmd_diff(Command):
1288
1341
            Difference between the working tree and revision 1
1289
1342
        bzr diff -r1..2
1290
1343
            Difference between revision 2 and revision 1
1291
 
        bzr diff --diff-prefix old/:new/
 
1344
        bzr diff --prefix old/:new/
1292
1345
            Same as 'bzr diff' but prefix paths with old/ and new/
1293
1346
        bzr diff bzr.mine bzr.dev
1294
1347
            Show the differences between the two working trees
1311
1364
        Option('prefix', type=str,
1312
1365
               short_name='p',
1313
1366
               help='Set prefixes to added to old and new filenames, as '
1314
 
                    'two values separated by a colon.'),
 
1367
                    'two values separated by a colon. (eg "old/:new/")'),
1315
1368
        ]
1316
1369
    aliases = ['di', 'dif']
1317
1370
    encoding_type = 'exact'
1331
1384
        elif ':' in prefix:
1332
1385
            old_label, new_label = prefix.split(":")
1333
1386
        else:
1334
 
            raise BzrCommandError(
1335
 
                "--prefix expects two values separated by a colon")
 
1387
            raise errors.BzrCommandError(
 
1388
                '--prefix expects two values separated by a colon'
 
1389
                ' (eg "old/:new/")')
1336
1390
 
1337
1391
        if revision and len(revision) > 2:
1338
1392
            raise errors.BzrCommandError('bzr diff --revision takes exactly'
1339
1393
                                         ' one or two revision specifiers')
1340
 
        
 
1394
 
1341
1395
        try:
1342
1396
            tree1, file_list = internal_tree_files(file_list)
1343
1397
            tree2 = None
1395
1449
    @display_command
1396
1450
    def run(self, show_ids=False):
1397
1451
        tree = WorkingTree.open_containing(u'.')[0]
1398
 
        old = tree.basis_tree()
1399
 
        for path, ie in old.inventory.iter_entries():
1400
 
            if not tree.has_id(ie.file_id):
1401
 
                self.outf.write(path)
1402
 
                if show_ids:
1403
 
                    self.outf.write(' ')
1404
 
                    self.outf.write(ie.file_id)
1405
 
                self.outf.write('\n')
 
1452
        tree.lock_read()
 
1453
        try:
 
1454
            old = tree.basis_tree()
 
1455
            old.lock_read()
 
1456
            try:
 
1457
                for path, ie in old.inventory.iter_entries():
 
1458
                    if not tree.has_id(ie.file_id):
 
1459
                        self.outf.write(path)
 
1460
                        if show_ids:
 
1461
                            self.outf.write(' ')
 
1462
                            self.outf.write(ie.file_id)
 
1463
                        self.outf.write('\n')
 
1464
            finally:
 
1465
                old.unlock()
 
1466
        finally:
 
1467
            tree.unlock()
1406
1468
 
1407
1469
 
1408
1470
class cmd_modified(Command):
1432
1494
    @display_command
1433
1495
    def run(self):
1434
1496
        wt = WorkingTree.open_containing(u'.')[0]
1435
 
        basis_inv = wt.basis_tree().inventory
1436
 
        inv = wt.inventory
1437
 
        for file_id in inv:
1438
 
            if file_id in basis_inv:
1439
 
                continue
1440
 
            if inv.is_root(file_id) and len(basis_inv) == 0:
1441
 
                continue
1442
 
            path = inv.id2path(file_id)
1443
 
            if not os.access(osutils.abspath(path), os.F_OK):
1444
 
                continue
1445
 
            self.outf.write(path + '\n')
 
1497
        wt.lock_read()
 
1498
        try:
 
1499
            basis = wt.basis_tree()
 
1500
            basis.lock_read()
 
1501
            try:
 
1502
                basis_inv = basis.inventory
 
1503
                inv = wt.inventory
 
1504
                for file_id in inv:
 
1505
                    if file_id in basis_inv:
 
1506
                        continue
 
1507
                    if inv.is_root(file_id) and len(basis_inv) == 0:
 
1508
                        continue
 
1509
                    path = inv.id2path(file_id)
 
1510
                    if not os.access(osutils.abspath(path), os.F_OK):
 
1511
                        continue
 
1512
                    self.outf.write(path + '\n')
 
1513
            finally:
 
1514
                basis.unlock()
 
1515
        finally:
 
1516
            wt.unlock()
1446
1517
 
1447
1518
 
1448
1519
class cmd_root(Command):
1509
1580
        if location:
1510
1581
            # find the file id to log:
1511
1582
 
1512
 
            dir, fp = bzrdir.BzrDir.open_containing(location)
1513
 
            b = dir.open_branch()
 
1583
            tree, b, fp = bzrdir.BzrDir.open_containing_tree_or_branch(
 
1584
                location)
1514
1585
            if fp != '':
1515
 
                try:
1516
 
                    # might be a tree:
1517
 
                    inv = dir.open_workingtree().inventory
1518
 
                except (errors.NotBranchError, errors.NotLocalUrl):
1519
 
                    # either no tree, or is remote.
1520
 
                    inv = b.basis_tree().inventory
1521
 
                file_id = inv.path2id(fp)
 
1586
                if tree is None:
 
1587
                    tree = b.basis_tree()
 
1588
                file_id = tree.path2id(fp)
1522
1589
                if file_id is None:
1523
1590
                    raise errors.BzrCommandError(
1524
1591
                        "Path does not have any revision history: %s" %
1534
1601
            dir, relpath = bzrdir.BzrDir.open_containing(location)
1535
1602
            b = dir.open_branch()
1536
1603
 
1537
 
        if revision is None:
1538
 
            rev1 = None
1539
 
            rev2 = None
1540
 
        elif len(revision) == 1:
1541
 
            rev1 = rev2 = revision[0].in_history(b).revno
1542
 
        elif len(revision) == 2:
1543
 
            if revision[1].get_branch() != revision[0].get_branch():
1544
 
                # b is taken from revision[0].get_branch(), and
1545
 
                # show_log will use its revision_history. Having
1546
 
                # different branches will lead to weird behaviors.
 
1604
        b.lock_read()
 
1605
        try:
 
1606
            if revision is None:
 
1607
                rev1 = None
 
1608
                rev2 = None
 
1609
            elif len(revision) == 1:
 
1610
                rev1 = rev2 = revision[0].in_history(b).revno
 
1611
            elif len(revision) == 2:
 
1612
                if revision[1].get_branch() != revision[0].get_branch():
 
1613
                    # b is taken from revision[0].get_branch(), and
 
1614
                    # show_log will use its revision_history. Having
 
1615
                    # different branches will lead to weird behaviors.
 
1616
                    raise errors.BzrCommandError(
 
1617
                        "Log doesn't accept two revisions in different"
 
1618
                        " branches.")
 
1619
                if revision[0].spec is None:
 
1620
                    # missing begin-range means first revision
 
1621
                    rev1 = 1
 
1622
                else:
 
1623
                    rev1 = revision[0].in_history(b).revno
 
1624
 
 
1625
                if revision[1].spec is None:
 
1626
                    # missing end-range means last known revision
 
1627
                    rev2 = b.revno()
 
1628
                else:
 
1629
                    rev2 = revision[1].in_history(b).revno
 
1630
            else:
1547
1631
                raise errors.BzrCommandError(
1548
 
                    "Log doesn't accept two revisions in different branches.")
1549
 
            if revision[0].spec is None:
1550
 
                # missing begin-range means first revision
1551
 
                rev1 = 1
1552
 
            else:
1553
 
                rev1 = revision[0].in_history(b).revno
1554
 
 
1555
 
            if revision[1].spec is None:
1556
 
                # missing end-range means last known revision
1557
 
                rev2 = b.revno()
1558
 
            else:
1559
 
                rev2 = revision[1].in_history(b).revno
1560
 
        else:
1561
 
            raise errors.BzrCommandError('bzr log --revision takes one or two values.')
1562
 
 
1563
 
        # By this point, the revision numbers are converted to the +ve
1564
 
        # form if they were supplied in the -ve form, so we can do
1565
 
        # this comparison in relative safety
1566
 
        if rev1 > rev2:
1567
 
            (rev2, rev1) = (rev1, rev2)
1568
 
 
1569
 
        if log_format is None:
1570
 
            log_format = log.log_formatter_registry.get_default(b)
1571
 
 
1572
 
        lf = log_format(show_ids=show_ids, to_file=self.outf,
1573
 
                        show_timezone=timezone)
1574
 
 
1575
 
        show_log(b,
1576
 
                 lf,
1577
 
                 file_id,
1578
 
                 verbose=verbose,
1579
 
                 direction=direction,
1580
 
                 start_revision=rev1,
1581
 
                 end_revision=rev2,
1582
 
                 search=message)
 
1632
                    'bzr log --revision takes one or two values.')
 
1633
 
 
1634
            # By this point, the revision numbers are converted to the +ve
 
1635
            # form if they were supplied in the -ve form, so we can do
 
1636
            # this comparison in relative safety
 
1637
            if rev1 > rev2:
 
1638
                (rev2, rev1) = (rev1, rev2)
 
1639
 
 
1640
            if log_format is None:
 
1641
                log_format = log.log_formatter_registry.get_default(b)
 
1642
 
 
1643
            lf = log_format(show_ids=show_ids, to_file=self.outf,
 
1644
                            show_timezone=timezone)
 
1645
 
 
1646
            show_log(b,
 
1647
                     lf,
 
1648
                     file_id,
 
1649
                     verbose=verbose,
 
1650
                     direction=direction,
 
1651
                     start_revision=rev1,
 
1652
                     end_revision=rev2,
 
1653
                     search=message)
 
1654
        finally:
 
1655
            b.unlock()
1583
1656
 
1584
1657
 
1585
1658
def get_log_format(long=False, short=False, line=False, default='long'):
1606
1679
    def run(self, filename):
1607
1680
        tree, relpath = WorkingTree.open_containing(filename)
1608
1681
        b = tree.branch
1609
 
        inv = tree.read_working_inventory()
1610
 
        file_id = inv.path2id(relpath)
 
1682
        file_id = tree.path2id(relpath)
1611
1683
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
1612
1684
            self.outf.write("%6d %s\n" % (revno, what))
1613
1685
 
1666
1738
        elif tree is None:
1667
1739
            tree = branch.basis_tree()
1668
1740
 
1669
 
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
1670
 
            if fp.startswith(relpath):
1671
 
                fp = osutils.pathjoin(prefix, fp[len(relpath):])
1672
 
                if non_recursive and '/' in fp:
1673
 
                    continue
1674
 
                if not all and not selection[fc]:
1675
 
                    continue
1676
 
                if kind is not None and fkind != kind:
1677
 
                    continue
1678
 
                if verbose:
1679
 
                    kindch = entry.kind_character()
1680
 
                    outstring = '%-8s %s%s' % (fc, fp, kindch)
1681
 
                    if show_ids and fid is not None:
1682
 
                        outstring = "%-50s %s" % (outstring, fid)
1683
 
                    self.outf.write(outstring + '\n')
1684
 
                elif null:
1685
 
                    self.outf.write(fp + '\0')
1686
 
                    if show_ids:
 
1741
        tree.lock_read()
 
1742
        try:
 
1743
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
 
1744
                if fp.startswith(relpath):
 
1745
                    fp = osutils.pathjoin(prefix, fp[len(relpath):])
 
1746
                    if non_recursive and '/' in fp:
 
1747
                        continue
 
1748
                    if not all and not selection[fc]:
 
1749
                        continue
 
1750
                    if kind is not None and fkind != kind:
 
1751
                        continue
 
1752
                    if verbose:
 
1753
                        kindch = entry.kind_character()
 
1754
                        outstring = '%-8s %s%s' % (fc, fp, kindch)
 
1755
                        if show_ids and fid is not None:
 
1756
                            outstring = "%-50s %s" % (outstring, fid)
 
1757
                        self.outf.write(outstring + '\n')
 
1758
                    elif null:
 
1759
                        self.outf.write(fp + '\0')
 
1760
                        if show_ids:
 
1761
                            if fid is not None:
 
1762
                                self.outf.write(fid)
 
1763
                            self.outf.write('\0')
 
1764
                        self.outf.flush()
 
1765
                    else:
1687
1766
                        if fid is not None:
1688
 
                            self.outf.write(fid)
1689
 
                        self.outf.write('\0')
1690
 
                    self.outf.flush()
1691
 
                else:
1692
 
                    if fid is not None:
1693
 
                        my_id = fid
1694
 
                    else:
1695
 
                        my_id = ''
1696
 
                    if show_ids:
1697
 
                        self.outf.write('%-50s %s\n' % (fp, my_id))
1698
 
                    else:
1699
 
                        self.outf.write(fp + '\n')
 
1767
                            my_id = fid
 
1768
                        else:
 
1769
                            my_id = ''
 
1770
                        if show_ids:
 
1771
                            self.outf.write('%-50s %s\n' % (fp, my_id))
 
1772
                        else:
 
1773
                            self.outf.write(fp + '\n')
 
1774
        finally:
 
1775
            tree.unlock()
1700
1776
 
1701
1777
 
1702
1778
class cmd_unknowns(Command):
1762
1838
        if not name_pattern_list:
1763
1839
            raise errors.BzrCommandError("ignore requires at least one "
1764
1840
                                  "NAME_PATTERN or --old-default-rules")
 
1841
        name_pattern_list = [globbing.normalize_pattern(p) 
 
1842
                             for p in name_pattern_list]
1765
1843
        for name_pattern in name_pattern_list:
1766
 
            if name_pattern[0] == '/':
 
1844
            if (name_pattern[0] == '/' or 
 
1845
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
1767
1846
                raise errors.BzrCommandError(
1768
1847
                    "NAME_PATTERN should not be an absolute path")
1769
1848
        tree, relpath = WorkingTree.open_containing(u'.')
1783
1862
        if igns and igns[-1] != '\n':
1784
1863
            igns += '\n'
1785
1864
        for name_pattern in name_pattern_list:
1786
 
            igns += name_pattern.rstrip('/') + '\n'
 
1865
            igns += name_pattern + '\n'
1787
1866
 
1788
1867
        f = AtomicFile(ifn, 'wb')
1789
1868
        try:
1792
1871
        finally:
1793
1872
            f.close()
1794
1873
 
1795
 
        inv = tree.inventory
1796
 
        if inv.path2id('.bzrignore'):
1797
 
            mutter('.bzrignore is already versioned')
1798
 
        else:
1799
 
            mutter('need to make new .bzrignore file versioned')
 
1874
        if not tree.path2id('.bzrignore'):
1800
1875
            tree.add(['.bzrignore'])
1801
1876
 
1802
1877
 
1807
1882
    @display_command
1808
1883
    def run(self):
1809
1884
        tree = WorkingTree.open_containing(u'.')[0]
1810
 
        for path, file_class, kind, file_id, entry in tree.list_files():
1811
 
            if file_class != 'I':
1812
 
                continue
1813
 
            ## XXX: Slightly inefficient since this was already calculated
1814
 
            pat = tree.is_ignored(path)
1815
 
            print '%-50s %s' % (path, pat)
 
1885
        tree.lock_read()
 
1886
        try:
 
1887
            for path, file_class, kind, file_id, entry in tree.list_files():
 
1888
                if file_class != 'I':
 
1889
                    continue
 
1890
                ## XXX: Slightly inefficient since this was already calculated
 
1891
                pat = tree.is_ignored(path)
 
1892
                print '%-50s %s' % (path, pat)
 
1893
        finally:
 
1894
            tree.unlock()
1816
1895
 
1817
1896
 
1818
1897
class cmd_lookup_revision(Command):
1835
1914
 
1836
1915
 
1837
1916
class cmd_export(Command):
1838
 
    """Export past revision to destination directory.
 
1917
    """Export current or past revision to a destination directory or archive.
1839
1918
 
1840
1919
    If no revision is specified this exports the last committed revision.
1841
1920
 
1846
1925
    Root may be the top directory for tar, tgz and tbz2 formats. If none
1847
1926
    is given, the top directory will be the root name of the file.
1848
1927
 
1849
 
    If branch is omitted then the branch containing the CWD will be used.
 
1928
    If branch is omitted then the branch containing the current working
 
1929
    directory will be used.
1850
1930
 
1851
1931
    Note: export of tree with non-ascii filenames to zip is not supported.
1852
1932
 
1884
1964
 
1885
1965
 
1886
1966
class cmd_cat(Command):
1887
 
    """Write a file's text from a previous revision."""
 
1967
    """Write the contents of a file as of a given revision to standard output.
 
1968
 
 
1969
    If no revision is nominated, the last revision is used.
 
1970
 
 
1971
    Note: Take care to redirect standard output when using this command on a
 
1972
    binary file. 
 
1973
    """
1888
1974
 
1889
1975
    takes_options = ['revision', 'name-from-revision']
1890
1976
    takes_args = ['filename']
1898
1984
 
1899
1985
        tree = None
1900
1986
        try:
1901
 
            tree, relpath = WorkingTree.open_containing(filename)
1902
 
            b = tree.branch
1903
 
        except (errors.NotBranchError, errors.NotLocalUrl):
 
1987
            tree, b, relpath = \
 
1988
                    bzrdir.BzrDir.open_containing_tree_or_branch(filename)
 
1989
        except errors.NotBranchError:
1904
1990
            pass
1905
1991
 
1906
1992
        if revision is not None and revision[0].get_branch() is not None:
1907
1993
            b = Branch.open(revision[0].get_branch())
1908
1994
        if tree is None:
1909
 
            b, relpath = Branch.open_containing(filename)
1910
1995
            tree = b.basis_tree()
1911
1996
        if revision is None:
1912
1997
            revision_id = b.last_revision()
2090
2175
                        value_switches=True, title='Branch format'),
2091
2176
                    ]
2092
2177
 
2093
 
 
2094
2178
    def run(self, url='.', format=None):
2095
2179
        from bzrlib.upgrade import upgrade
2096
2180
        if format is None:
2159
2243
 
2160
2244
    @display_command
2161
2245
    def printme(self, branch):
2162
 
        print branch.nick 
 
2246
        print branch.nick
2163
2247
 
2164
2248
 
2165
2249
class cmd_selftest(Command):
2188
2272
            run only tests relating to 'ignore'
2189
2273
        bzr --no-plugins selftest -v
2190
2274
            disable plugins and list tests as they're run
 
2275
 
 
2276
    For each test, that needs actual disk access, bzr create their own
 
2277
    subdirectory in the temporary testing directory (testXXXX.tmp).
 
2278
    By default the name of such subdirectory is based on the name of the test.
 
2279
    If option '--numbered-dirs' is given, bzr will use sequent numbers
 
2280
    of running tests to create such subdirectories. This is default behavior
 
2281
    on Windows because of path length limitation.
2191
2282
    """
2192
2283
    # TODO: --list should give a list of all available tests
2193
2284
 
2212
2303
    takes_args = ['testspecs*']
2213
2304
    takes_options = ['verbose',
2214
2305
                     Option('one', help='stop when one test fails'),
2215
 
                     Option('keep-output', 
 
2306
                     Option('keep-output',
2216
2307
                            help='keep output directories when tests fail'),
2217
 
                     Option('transport', 
 
2308
                     Option('transport',
2218
2309
                            help='Use a different transport by default '
2219
2310
                                 'throughout the test suite.',
2220
2311
                            type=get_transport_type),
2221
 
                     Option('benchmark', help='run the bzr bencharks.'),
 
2312
                     Option('benchmark', help='run the bzr benchmarks.'),
2222
2313
                     Option('lsprof-timed',
2223
2314
                            help='generate lsprof output for benchmarked'
2224
2315
                                 ' sections of code.'),
2229
2320
                            help='clean temporary tests directories'
2230
2321
                                 ' without running tests'),
2231
2322
                     Option('first',
2232
 
                            help='run all tests, but run specified tests first',
2233
 
                            )
 
2323
                            help='run all tests, but run specified tests first'
 
2324
                            ),
 
2325
                     Option('numbered-dirs',
 
2326
                            help='use numbered dirs for TestCaseInTempDir'),
2234
2327
                     ]
2235
2328
    encoding_type = 'replace'
2236
2329
 
2237
2330
    def run(self, testspecs_list=None, verbose=None, one=False,
2238
2331
            keep_output=False, transport=None, benchmark=None,
2239
2332
            lsprof_timed=None, cache_dir=None, clean_output=False,
2240
 
            first=False):
 
2333
            first=False, numbered_dirs=None):
2241
2334
        import bzrlib.ui
2242
2335
        from bzrlib.tests import selftest
2243
2336
        import bzrlib.benchmarks as benchmarks
2248
2341
            clean_selftest_output()
2249
2342
            return 0
2250
2343
 
 
2344
        if numbered_dirs is None and sys.platform == 'win32':
 
2345
            numbered_dirs = True
 
2346
 
2251
2347
        if cache_dir is not None:
2252
2348
            tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir)
2253
2349
        print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
2278
2374
                              lsprof_timed=lsprof_timed,
2279
2375
                              bench_history=benchfile,
2280
2376
                              matching_tests_first=first,
 
2377
                              numbered_dirs=numbered_dirs,
2281
2378
                              )
2282
2379
        finally:
2283
2380
            if benchfile is not None:
2305
2402
 
2306
2403
    @display_command
2307
2404
    def run(self):
2308
 
        print "it sure does!"
 
2405
        print "It sure does!"
2309
2406
 
2310
2407
 
2311
2408
class cmd_find_merge_base(Command):
2358
2455
    default, use --remember. The value will only be saved if the remote
2359
2456
    location can be accessed.
2360
2457
 
 
2458
    The results of the merge are placed into the destination working
 
2459
    directory, where they can be reviewed (with bzr diff), tested, and then
 
2460
    committed to record the result of the merge.
 
2461
 
2361
2462
    Examples:
2362
2463
 
2363
2464
    To merge the latest revision from bzr.dev
2371
2472
    
2372
2473
    merge refuses to run if there are any uncommitted changes, unless
2373
2474
    --force is given.
2374
 
 
2375
 
    The following merge types are available:
2376
2475
    """
2377
2476
    takes_args = ['branch?']
2378
2477
    takes_options = ['revision', 'force', 'merge-type', 'reprocess', 'remember',
2379
 
                     Option('show-base', help="Show base revision text in "
2380
 
                            "conflicts"),
2381
 
                     Option('uncommitted', help='Apply uncommitted changes'
2382
 
                            ' from a working copy, instead of branch changes'),
2383
 
                     Option('pull', help='If the destination is already'
2384
 
                             ' completely merged into the source, pull from the'
2385
 
                             ' source rather than merging. When this happens,'
2386
 
                             ' you do not need to commit the result.'),
2387
 
                     ]
 
2478
        Option('show-base', help="Show base revision text in "
 
2479
               "conflicts"),
 
2480
        Option('uncommitted', help='Apply uncommitted changes'
 
2481
               ' from a working copy, instead of branch changes'),
 
2482
        Option('pull', help='If the destination is already'
 
2483
                ' completely merged into the source, pull from the'
 
2484
                ' source rather than merging. When this happens,'
 
2485
                ' you do not need to commit the result.'),
 
2486
        Option('directory',
 
2487
            help='Branch to merge into, '
 
2488
                 'rather than the one containing the working directory',
 
2489
            short_name='d',
 
2490
            type=unicode,
 
2491
            ),
 
2492
    ]
2388
2493
 
2389
2494
    def run(self, branch=None, revision=None, force=False, merge_type=None,
2390
 
            show_base=False, reprocess=False, remember=False, 
2391
 
            uncommitted=False, pull=False):
 
2495
            show_base=False, reprocess=False, remember=False,
 
2496
            uncommitted=False, pull=False,
 
2497
            directory=None,
 
2498
            ):
 
2499
        from bzrlib.tag import _merge_tags_if_possible
2392
2500
        if merge_type is None:
2393
2501
            merge_type = _mod_merge.Merge3Merger
2394
2502
 
2395
 
        tree = WorkingTree.open_containing(u'.')[0]
 
2503
        if directory is None: directory = u'.'
 
2504
        # XXX: jam 20070225 WorkingTree should be locked before you extract its
 
2505
        #      inventory. Because merge is a mutating operation, it really
 
2506
        #      should be a lock_write() for the whole cmd_merge operation.
 
2507
        #      However, cmd_merge open's its own tree in _merge_helper, which
 
2508
        #      means if we lock here, the later lock_write() will always block.
 
2509
        #      Either the merge helper code should be updated to take a tree,
 
2510
        #      (What about tree.merge_from_branch?)
 
2511
        tree = WorkingTree.open_containing(directory)[0]
 
2512
        change_reporter = delta._ChangeReporter(
 
2513
            unversioned_filter=tree.is_ignored)
2396
2514
 
2397
2515
        if branch is not None:
2398
2516
            try:
2401
2519
                pass # Continue on considering this url a Branch
2402
2520
            else:
2403
2521
                conflicts = merge_bundle(reader, tree, not force, merge_type,
2404
 
                                            reprocess, show_base)
 
2522
                                         reprocess, show_base, change_reporter)
2405
2523
                if conflicts == 0:
2406
2524
                    return 0
2407
2525
                else:
2447
2565
        if tree.branch.get_parent() is None or remember:
2448
2566
            tree.branch.set_parent(other_branch.base)
2449
2567
 
 
2568
        # pull tags now... it's a bit inconsistent to do it ahead of copying
 
2569
        # the history but that's done inside the merge code
 
2570
        _merge_tags_if_possible(other_branch, tree.branch)
 
2571
 
2450
2572
        if path != "":
2451
2573
            interesting_files = [path]
2452
2574
        else:
2460
2582
                    reprocess=reprocess,
2461
2583
                    show_base=show_base,
2462
2584
                    pull=pull,
2463
 
                    pb=pb, file_list=interesting_files)
 
2585
                    this_dir=directory,
 
2586
                    pb=pb, file_list=interesting_files,
 
2587
                    change_reporter=change_reporter)
2464
2588
            finally:
2465
2589
                pb.finished()
2466
2590
            if conflict_count != 0:
2512
2636
    $ bzr remerge --merge-type weave --reprocess foobar
2513
2637
        Re-do the merge of "foobar", using the weave merge algorithm, with
2514
2638
        additional processing to reduce the size of conflict regions.
2515
 
    
2516
 
    The following merge types are available:"""
 
2639
    """
2517
2640
    takes_args = ['file*']
2518
2641
    takes_options = ['merge-type', 'reprocess',
2519
2642
                     Option('show-base', help="Show base revision text in "
2605
2728
    """
2606
2729
    takes_options = ['revision', 'no-backup']
2607
2730
    takes_args = ['file*']
2608
 
    aliases = ['merge-revert']
2609
2731
 
2610
2732
    def run(self, revision=None, no_backup=False, file_list=None):
2611
2733
        if file_list is not None:
2856
2978
                raise errors.BzrCommandError('bzr annotate --revision takes exactly 1 argument')
2857
2979
            else:
2858
2980
                revision_id = revision[0].in_history(branch).rev_id
2859
 
            file_id = tree.inventory.path2id(relpath)
 
2981
            file_id = tree.path2id(relpath)
2860
2982
            tree = branch.repository.revision_tree(revision_id)
2861
2983
            file_version = tree.inventory[file_id].revision
2862
2984
            annotate_file(branch, file_version, file_id, long, all, sys.stdout,
2914
3036
    See "help checkouts" for more information on checkouts.
2915
3037
    """
2916
3038
 
2917
 
    takes_args = ['location']
 
3039
    takes_args = ['location?']
2918
3040
    takes_options = []
2919
3041
 
2920
3042
    def run(self, location=None):
2921
3043
        b, relpath = Branch.open_containing(u'.')
 
3044
        if location is None:
 
3045
            try:
 
3046
                location = b.get_old_bound_location()
 
3047
            except errors.UpgradeRequired:
 
3048
                raise errors.BzrCommandError('No location supplied.  '
 
3049
                    'This format does not remember old locations.')
 
3050
            else:
 
3051
                if location is None:
 
3052
                    raise errors.BzrCommandError('No location supplied and no '
 
3053
                        'previous location known')
2922
3054
        b_other = Branch.open(location)
2923
3055
        try:
2924
3056
            b.bind(b_other)
3075
3207
        Option('port',
3076
3208
               help='listen for connections on nominated port of the form '
3077
3209
                    '[hostname:]portnumber. Passing 0 as the port number will '
3078
 
                    'result in a dynamically allocated port.',
 
3210
                    'result in a dynamically allocated port. Default port is '
 
3211
                    '4155.',
3079
3212
               type=str),
3080
3213
        Option('directory',
3081
3214
               help='serve contents of directory',
3098
3231
        t = get_transport(url)
3099
3232
        if inet:
3100
3233
            server = smart.SmartServerPipeStreamMedium(sys.stdin, sys.stdout, t)
3101
 
        elif port is not None:
3102
 
            if ':' in port:
3103
 
                host, port = port.split(':')
3104
 
            else:
 
3234
        else:
 
3235
            if port is None:
 
3236
                port = smart.BZR_DEFAULT_PORT
3105
3237
                host = '127.0.0.1'
3106
 
            server = smart.SmartTCPServer(t, host=host, port=int(port))
 
3238
            else:
 
3239
                if ':' in port:
 
3240
                    host, port = port.split(':')
 
3241
                else:
 
3242
                    host = '127.0.0.1'
 
3243
                port = int(port)
 
3244
            server = smart.SmartTCPServer(t, host=host, port=port)
3107
3245
            print 'listening on port: ', server.port
3108
3246
            sys.stdout.flush()
3109
 
        else:
3110
 
            raise errors.BzrCommandError("bzr serve requires one of --inet or --port")
3111
3247
        server.serve()
3112
3248
 
 
3249
class cmd_join(Command):
 
3250
    """Combine a subtree into its containing tree.
 
3251
    
 
3252
    This command is for experimental use only.  It requires the target tree
 
3253
    to be in dirstate-with-subtree format, which cannot be converted into
 
3254
    earlier formats.
 
3255
 
 
3256
    The TREE argument should be an independent tree, inside another tree, but
 
3257
    not part of it.  (Such trees can be produced by "bzr split", but also by
 
3258
    running "bzr branch" with the target inside a tree.)
 
3259
 
 
3260
    The result is a combined tree, with the subtree no longer an independant
 
3261
    part.  This is marked as a merge of the subtree into the containing tree,
 
3262
    and all history is preserved.
 
3263
 
 
3264
    If --reference is specified, the subtree retains its independence.  It can
 
3265
    be branched by itself, and can be part of multiple projects at the same
 
3266
    time.  But operations performed in the containing tree, such as commit
 
3267
    and merge, will recurse into the subtree.
 
3268
    """
 
3269
 
 
3270
    takes_args = ['tree']
 
3271
    takes_options = [Option('reference', 'join by reference')]
 
3272
    hidden = True
 
3273
 
 
3274
    def run(self, tree, reference=False):
 
3275
        sub_tree = WorkingTree.open(tree)
 
3276
        parent_dir = osutils.dirname(sub_tree.basedir)
 
3277
        containing_tree = WorkingTree.open_containing(parent_dir)[0]
 
3278
        repo = containing_tree.branch.repository
 
3279
        if not repo.supports_rich_root():
 
3280
            raise errors.BzrCommandError(
 
3281
                "Can't join trees because %s doesn't support rich root data.\n"
 
3282
                "You can use bzr upgrade on the repository."
 
3283
                % (repo,))
 
3284
        if reference:
 
3285
            try:
 
3286
                containing_tree.add_reference(sub_tree)
 
3287
            except errors.BadReferenceTarget, e:
 
3288
                # XXX: Would be better to just raise a nicely printable
 
3289
                # exception from the real origin.  Also below.  mbp 20070306
 
3290
                raise errors.BzrCommandError("Cannot join %s.  %s" %
 
3291
                                             (tree, e.reason))
 
3292
        else:
 
3293
            try:
 
3294
                containing_tree.subsume(sub_tree)
 
3295
            except errors.BadSubsumeSource, e:
 
3296
                raise errors.BzrCommandError("Cannot join %s.  %s" % 
 
3297
                                             (tree, e.reason))
 
3298
 
 
3299
 
 
3300
class cmd_split(Command):
 
3301
    """Split a tree into two trees.
 
3302
 
 
3303
    This command is for experimental use only.  It requires the target tree
 
3304
    to be in dirstate-with-subtree format, which cannot be converted into
 
3305
    earlier formats.
 
3306
 
 
3307
    The TREE argument should be a subdirectory of a working tree.  That
 
3308
    subdirectory will be converted into an independent tree, with its own
 
3309
    branch.  Commits in the top-level tree will not apply to the new subtree.
 
3310
    If you want that behavior, do "bzr join --reference TREE".
 
3311
 
 
3312
    To undo this operation, do "bzr join TREE".
 
3313
    """
 
3314
 
 
3315
    takes_args = ['tree']
 
3316
 
 
3317
    hidden = True
 
3318
 
 
3319
    def run(self, tree):
 
3320
        containing_tree, subdir = WorkingTree.open_containing(tree)
 
3321
        sub_id = containing_tree.path2id(subdir)
 
3322
        if sub_id is None:
 
3323
            raise errors.NotVersionedError(subdir)
 
3324
        try:
 
3325
            containing_tree.extract(sub_id)
 
3326
        except errors.RootNotRich:
 
3327
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
3328
 
 
3329
 
 
3330
 
 
3331
class cmd_merge_directive(Command):
 
3332
    """Generate a merge directive for auto-merge tools.
 
3333
 
 
3334
    A directive requests a merge to be performed, and also provides all the
 
3335
    information necessary to do so.  This means it must either include a
 
3336
    revision bundle, or the location of a branch containing the desired
 
3337
    revision.
 
3338
 
 
3339
    A submit branch (the location to merge into) must be supplied the first
 
3340
    time the command is issued.  After it has been supplied once, it will
 
3341
    be remembered as the default.
 
3342
 
 
3343
    A public branch is optional if a revision bundle is supplied, but required
 
3344
    if --diff or --plain is specified.  It will be remembered as the default
 
3345
    after the first use.
 
3346
    """
 
3347
 
 
3348
    takes_args = ['submit_branch?', 'public_branch?']
 
3349
 
 
3350
    takes_options = [
 
3351
        RegistryOption.from_kwargs('patch-type',
 
3352
            'The type of patch to include in the directive',
 
3353
            title='Patch type', value_switches=True, enum_switch=False,
 
3354
            bundle='Bazaar revision bundle (default)',
 
3355
            diff='Normal unified diff',
 
3356
            plain='No patch, just directive'),
 
3357
        Option('sign', help='GPG-sign the directive'), 'revision',
 
3358
        Option('mail-to', type=str,
 
3359
            help='Instead of printing the directive, email to this address'),
 
3360
        Option('message', type=str, short_name='m',
 
3361
            help='Message to use when committing this merge')
 
3362
        ]
 
3363
 
 
3364
    def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
 
3365
            sign=False, revision=None, mail_to=None, message=None):
 
3366
        if patch_type == 'plain':
 
3367
            patch_type = None
 
3368
        branch = Branch.open('.')
 
3369
        stored_submit_branch = branch.get_submit_branch()
 
3370
        if submit_branch is None:
 
3371
            submit_branch = stored_submit_branch
 
3372
        else:
 
3373
            if stored_submit_branch is None:
 
3374
                branch.set_submit_branch(submit_branch)
 
3375
        if submit_branch is None:
 
3376
            submit_branch = branch.get_parent()
 
3377
        if submit_branch is None:
 
3378
            raise errors.BzrCommandError('No submit branch specified or known')
 
3379
 
 
3380
        stored_public_branch = branch.get_public_branch()
 
3381
        if public_branch is None:
 
3382
            public_branch = stored_public_branch
 
3383
        elif stored_public_branch is None:
 
3384
            branch.set_public_branch(public_branch)
 
3385
        if patch_type != "bundle" and public_branch is None:
 
3386
            raise errors.BzrCommandError('No public branch specified or'
 
3387
                                         ' known')
 
3388
        if revision is not None:
 
3389
            if len(revision) != 1:
 
3390
                raise errors.BzrCommandError('bzr merge-directive takes '
 
3391
                    'exactly one revision identifier')
 
3392
            else:
 
3393
                revision_id = revision[0].in_history(branch).rev_id
 
3394
        else:
 
3395
            revision_id = branch.last_revision()
 
3396
        directive = merge_directive.MergeDirective.from_objects(
 
3397
            branch.repository, revision_id, time.time(),
 
3398
            osutils.local_time_offset(), submit_branch,
 
3399
            public_branch=public_branch, patch_type=patch_type,
 
3400
            message=message)
 
3401
        if mail_to is None:
 
3402
            if sign:
 
3403
                self.outf.write(directive.to_signed(branch))
 
3404
            else:
 
3405
                self.outf.writelines(directive.to_lines())
 
3406
        else:
 
3407
            message = directive.to_email(mail_to, branch, sign)
 
3408
            s = smtplib.SMTP()
 
3409
            server = branch.get_config().get_user_option('smtp_server')
 
3410
            if not server:
 
3411
                server = 'localhost'
 
3412
            s.connect(server)
 
3413
            s.sendmail(message['From'], message['To'], message.as_string())
 
3414
 
 
3415
 
 
3416
class cmd_tag(Command):
 
3417
    """Create a tag naming a revision.
 
3418
    
 
3419
    Tags give human-meaningful names to revisions.  Commands that take a -r
 
3420
    (--revision) option can be given -rtag:X, where X is any previously
 
3421
    created tag.
 
3422
 
 
3423
    Tags are stored in the branch.  Tags are copied from one branch to another
 
3424
    along when you branch, push, pull or merge.
 
3425
 
 
3426
    It is an error to give a tag name that already exists unless you pass 
 
3427
    --force, in which case the tag is moved to point to the new revision.
 
3428
    """
 
3429
 
 
3430
    takes_args = ['tag_name']
 
3431
    takes_options = [
 
3432
        Option('delete',
 
3433
            help='Delete this tag rather than placing it.',
 
3434
            ),
 
3435
        Option('directory',
 
3436
            help='Branch in which to place the tag.',
 
3437
            short_name='d',
 
3438
            type=unicode,
 
3439
            ),
 
3440
        Option('force',
 
3441
            help='Replace existing tags',
 
3442
            ),
 
3443
        'revision',
 
3444
        ]
 
3445
 
 
3446
    def run(self, tag_name,
 
3447
            delete=None,
 
3448
            directory='.',
 
3449
            force=None,
 
3450
            revision=None,
 
3451
            ):
 
3452
        branch, relpath = Branch.open_containing(directory)
 
3453
        branch.lock_write()
 
3454
        try:
 
3455
            if delete:
 
3456
                branch.tags.delete_tag(tag_name)
 
3457
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
3458
            else:
 
3459
                if revision:
 
3460
                    if len(revision) != 1:
 
3461
                        raise errors.BzrCommandError(
 
3462
                            "Tags can only be placed on a single revision, "
 
3463
                            "not on a range")
 
3464
                    revision_id = revision[0].in_history(branch).rev_id
 
3465
                else:
 
3466
                    revision_id = branch.last_revision()
 
3467
                if (not force) and branch.tags.has_tag(tag_name):
 
3468
                    raise errors.TagAlreadyExists(tag_name)
 
3469
                branch.tags.set_tag(tag_name, revision_id)
 
3470
                self.outf.write('Created tag %s.\n' % tag_name)
 
3471
        finally:
 
3472
            branch.unlock()
 
3473
 
 
3474
 
 
3475
class cmd_tags(Command):
 
3476
    """List tags.
 
3477
 
 
3478
    This tag shows a table of tag names and the revisions they reference.
 
3479
    """
 
3480
 
 
3481
    takes_options = [
 
3482
        Option('directory',
 
3483
            help='Branch whose tags should be displayed',
 
3484
            short_name='d',
 
3485
            type=unicode,
 
3486
            ),
 
3487
    ]
 
3488
 
 
3489
    @display_command
 
3490
    def run(self,
 
3491
            directory='.',
 
3492
            ):
 
3493
        branch, relpath = Branch.open_containing(directory)
 
3494
        for tag_name, target in sorted(branch.tags.get_tag_dict().items()):
 
3495
            self.outf.write('%-20s %s\n' % (tag_name, target))
 
3496
 
3113
3497
 
3114
3498
# command-line interpretation helper for merge-related commands
3115
3499
def _merge_helper(other_revision, base_revision,
3118
3502
                  merge_type=None,
3119
3503
                  file_list=None, show_base=False, reprocess=False,
3120
3504
                  pull=False,
3121
 
                  pb=DummyProgress()):
 
3505
                  pb=DummyProgress(),
 
3506
                  change_reporter=None):
3122
3507
    """Merge changes into a tree.
3123
3508
 
3124
3509
    base_revision
3160
3545
                                     " type %s." % merge_type)
3161
3546
    if reprocess and show_base:
3162
3547
        raise errors.BzrCommandError("Cannot do conflict reduction and show base.")
 
3548
    # TODO: jam 20070226 We should really lock these trees earlier. However, we
 
3549
    #       only want to take out a lock_tree_write() if we don't have to pull
 
3550
    #       any ancestry. But merge might fetch ancestry in the middle, in
 
3551
    #       which case we would need a lock_write().
 
3552
    #       Because we cannot upgrade locks, for now we live with the fact that
 
3553
    #       the tree will be locked multiple times during a merge. (Maybe
 
3554
    #       read-only some of the time, but it means things will get read
 
3555
    #       multiple times.)
3163
3556
    try:
3164
3557
        merger = _mod_merge.Merger(this_tree.branch, this_tree=this_tree,
3165
 
                                   pb=pb)
 
3558
                                   pb=pb, change_reporter=change_reporter)
3166
3559
        merger.pp = ProgressPhase("Merge phase", 5, pb)
3167
3560
        merger.pp.next_phase()
3168
3561
        merger.check_basis(check_clean)
3174
3567
            return 0
3175
3568
        if file_list is None:
3176
3569
            if pull and merger.base_rev_id == merger.this_rev_id:
3177
 
                count = merger.this_tree.pull(merger.this_branch,
 
3570
                # FIXME: deduplicate with pull
 
3571
                result = merger.this_tree.pull(merger.this_branch,
3178
3572
                        False, merger.other_rev_id)
3179
 
                note('%d revision(s) pulled.' % (count,))
 
3573
                if result.old_revid == result.new_revid:
 
3574
                    note('No revisions to pull.')
 
3575
                else:
 
3576
                    note('Now on revision %d.' % result.new_revno)
3180
3577
                return 0
3181
3578
        merger.backup_files = backup_files
3182
3579
        merger.merge_type = merge_type