~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-07-06 00:37:03 UTC
  • Revision ID: mbp@sourcefrog.net-20050706003703-7ae9be78690ee6d9
- more merge tests from john

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import sys, os
20
20
 
21
21
import bzrlib
22
 
from bzrlib.trace import mutter, note, log_error, warning
 
22
from bzrlib.trace import mutter, note, log_error
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
24
from bzrlib.branch import find_branch
25
25
from bzrlib import BZRDIR
54
54
def _parse_revision_str(revstr):
55
55
    """This handles a revision string -> revno. 
56
56
 
57
 
    It supports integers directly, but everything else it
58
 
    defers for passing to Branch.get_revision_info()
59
 
 
60
 
    >>> _parse_revision_str('234')
61
 
    [234]
62
 
    >>> _parse_revision_str('234..567')
63
 
    [234, 567]
64
 
    >>> _parse_revision_str('..')
65
 
    [None, None]
66
 
    >>> _parse_revision_str('..234')
67
 
    [None, 234]
68
 
    >>> _parse_revision_str('234..')
69
 
    [234, None]
70
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
71
 
    [234, 456, 789]
72
 
    >>> _parse_revision_str('234....789') # Error?
73
 
    [234, None, 789]
74
 
    >>> _parse_revision_str('revid:test@other.com-234234')
75
 
    ['revid:test@other.com-234234']
76
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
77
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
78
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
79
 
    ['revid:test@other.com-234234', 23]
80
 
    >>> _parse_revision_str('date:2005-04-12')
81
 
    ['date:2005-04-12']
82
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
83
 
    ['date:2005-04-12 12:24:33']
84
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
85
 
    ['date:2005-04-12T12:24:33']
86
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
87
 
    ['date:2005-04-12,12:24:33']
88
 
    >>> _parse_revision_str('-5..23')
89
 
    [-5, 23]
90
 
    >>> _parse_revision_str('-5')
91
 
    [-5]
92
 
    >>> _parse_revision_str('123a')
93
 
    ['123a']
94
 
    >>> _parse_revision_str('abc')
95
 
    ['abc']
 
57
    There are several possibilities:
 
58
 
 
59
        '234'       -> 234
 
60
        '234:345'   -> [234, 345]
 
61
        ':234'      -> [None, 234]
 
62
        '234:'      -> [234, None]
 
63
 
 
64
    In the future we will also support:
 
65
        'uuid:blah-blah-blah'   -> ?
 
66
        'hash:blahblahblah'     -> ?
 
67
        potentially:
 
68
        'tag:mytag'             -> ?
96
69
    """
97
 
    import re
98
 
    old_format_re = re.compile('\d*:\d*')
99
 
    m = old_format_re.match(revstr)
100
 
    if m:
101
 
        warning('Colon separator for revision numbers is deprecated.'
102
 
                ' Use .. instead')
103
 
        revs = []
104
 
        for rev in revstr.split(':'):
105
 
            if rev:
106
 
                revs.append(int(rev))
107
 
            else:
108
 
                revs.append(None)
109
 
        return revs
110
 
    revs = []
111
 
    for x in revstr.split('..'):
112
 
        if not x:
113
 
            revs.append(None)
114
 
        else:
115
 
            try:
116
 
                revs.append(int(x))
117
 
            except ValueError:
118
 
                revs.append(x)
 
70
    if revstr.find(':') != -1:
 
71
        revs = revstr.split(':')
 
72
        if len(revs) > 2:
 
73
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
 
74
 
 
75
        if not revs[0]:
 
76
            revs[0] = None
 
77
        else:
 
78
            revs[0] = int(revs[0])
 
79
 
 
80
        if not revs[1]:
 
81
            revs[1] = None
 
82
        else:
 
83
            revs[1] = int(revs[1])
 
84
    else:
 
85
        revs = int(revstr)
119
86
    return revs
120
87
 
121
88
 
367
334
    def run(self):
368
335
        print find_branch('.').revno()
369
336
 
370
 
class cmd_revision_info(Command):
371
 
    """Show revision number and revision id for a given revision identifier.
372
 
    """
373
 
    hidden = True
374
 
    takes_args = ['revision_info*']
375
 
    takes_options = ['revision']
376
 
    def run(self, revision=None, revision_info_list=None):
377
 
        from bzrlib.branch import find_branch
378
 
 
379
 
        revs = []
380
 
        if revision is not None:
381
 
            revs.extend(revision)
382
 
        if revision_info_list is not None:
383
 
            revs.extend(revision_info_list)
384
 
        if len(revs) == 0:
385
 
            raise BzrCommandError('You must supply a revision identifier')
386
 
 
387
 
        b = find_branch('.')
388
 
 
389
 
        for rev in revs:
390
 
            print '%4d %s' % b.get_revision_info(rev)
391
 
 
392
337
    
393
338
class cmd_add(Command):
394
339
    """Add specified files or directories.
456
401
        if revision == None:
457
402
            inv = b.read_working_inventory()
458
403
        else:
459
 
            if len(revision) > 1:
460
 
                raise BzrCommandError('bzr inventory --revision takes'
461
 
                    ' exactly one revision identifier')
462
 
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
 
404
            inv = b.get_revision_inventory(b.lookup_revision(revision))
463
405
 
464
406
        for path, entry in inv.entries():
465
407
            if show_ids:
587
529
        from meta_store import CachedStore
588
530
        import tempfile
589
531
        cache_root = tempfile.mkdtemp()
590
 
 
591
 
        if revision is not None:
592
 
            if len(revision) > 1:
593
 
                raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
594
532
        try:
595
533
            try:
596
534
                br_from = find_cached_branch(from_location, cache_root)
617
555
                    raise
618
556
            br_to = Branch(to_location, init=True)
619
557
 
620
 
            br_to.set_root_id(br_from.get_root_id())
621
 
 
622
 
            if revision:
623
 
                revno = br_to.lookup_revision(revision[0])
624
 
                try:
625
 
                    br_to.update_revisions(br_from, stop_revision=revno)
626
 
                except NoSuchRevision:
627
 
                    rmtree(to_location)
628
 
                    msg = "The branch %s has no revision %d." % (from_location,
629
 
                                                                 revno)
630
 
                    raise BzrCommandError(msg)
631
 
            
 
558
            try:
 
559
                br_to.update_revisions(br_from, stop_revision=revision)
 
560
            except NoSuchRevision:
 
561
                rmtree(to_location)
 
562
                msg = "The branch %s has no revision %d." % (from_location,
 
563
                                                             revision)
 
564
                raise BzrCommandError(msg)
632
565
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
633
566
                  check_clean=False, ignore_zero=True)
634
567
            from_location = pull_loc(br_from)
802
735
                file_list = None
803
736
        else:
804
737
            b = find_branch('.')
805
 
 
806
 
        # TODO: Make show_diff support taking 2 arguments
807
 
        base_rev = None
808
 
        if revision is not None:
809
 
            if len(revision) != 1:
810
 
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
811
 
            base_rev = revision[0]
812
738
    
813
 
        show_diff(b, base_rev, specific_files=file_list,
 
739
        show_diff(b, revision, specific_files=file_list,
814
740
                  external_diff_options=diff_options)
815
741
 
816
742
 
844
770
    """List files modified in working tree."""
845
771
    hidden = True
846
772
    def run(self):
847
 
        from bzrlib.diff import compare_trees
848
 
 
 
773
        import statcache
849
774
        b = find_branch('.')
850
 
        td = compare_trees(b.basis_tree(), b.working_tree())
 
775
        inv = b.read_working_inventory()
 
776
        sc = statcache.update_cache(b, inv)
 
777
        basis = b.basis_tree()
 
778
        basis_inv = basis.inventory
 
779
        
 
780
        # We used to do this through iter_entries(), but that's slow
 
781
        # when most of the files are unmodified, as is usually the
 
782
        # case.  So instead we iterate by inventory entry, and only
 
783
        # calculate paths as necessary.
851
784
 
852
 
        for path, id, kind in td.modified:
853
 
            print path
 
785
        for file_id in basis_inv:
 
786
            cacheentry = sc.get(file_id)
 
787
            if not cacheentry:                 # deleted
 
788
                continue
 
789
            ie = basis_inv[file_id]
 
790
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
 
791
                path = inv.id2path(file_id)
 
792
                print path
854
793
 
855
794
 
856
795
 
891
830
    -r revision requests a specific revision, -r :end or -r begin: are
892
831
    also valid.
893
832
 
894
 
    --message allows you to give a regular expression, which will be evaluated
895
 
    so that only matching entries will be displayed.
896
 
 
897
833
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
898
834
  
899
835
    """
900
836
 
901
837
    takes_args = ['filename?']
902
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
 
838
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long']
903
839
    
904
840
    def run(self, filename=None, timezone='original',
905
841
            verbose=False,
906
842
            show_ids=False,
907
843
            forward=False,
908
844
            revision=None,
909
 
            message=None,
910
845
            long=False):
911
846
        from bzrlib.branch import find_branch
912
847
        from bzrlib.log import log_formatter, show_log
925
860
            b = find_branch('.')
926
861
            file_id = None
927
862
 
928
 
        if revision is None:
929
 
            rev1 = None
930
 
            rev2 = None
931
 
        elif len(revision) == 1:
932
 
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
933
 
        elif len(revision) == 2:
934
 
            rev1 = b.get_revision_info(revision[0])[0]
935
 
            rev2 = b.get_revision_info(revision[1])[0]
 
863
        if revision == None:
 
864
            revision = [None, None]
 
865
        elif isinstance(revision, int):
 
866
            revision = [revision, revision]
936
867
        else:
937
 
            raise BzrCommandError('bzr log --revision takes one or two values.')
938
 
 
939
 
        if rev1 == 0:
940
 
            rev1 = None
941
 
        if rev2 == 0:
942
 
            rev2 = None
 
868
            # pair of revisions?
 
869
            pass
 
870
            
 
871
        assert len(revision) == 2
943
872
 
944
873
        mutter('encoding log as %r' % bzrlib.user_encoding)
945
874
 
961
890
                 file_id,
962
891
                 verbose=verbose,
963
892
                 direction=direction,
964
 
                 start_revision=rev1,
965
 
                 end_revision=rev2,
966
 
                 search=message)
 
893
                 start_revision=revision[0],
 
894
                 end_revision=revision[1])
967
895
 
968
896
 
969
897
 
1114
1042
    If no revision is specified this exports the last committed revision.
1115
1043
 
1116
1044
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1117
 
    given, try to find the format with the extension. If no extension
1118
 
    is found exports to a directory (equivalent to --format=dir).
1119
 
 
1120
 
    Root may be the top directory for tar, tgz and tbz2 formats. If none
1121
 
    is given, the top directory will be the root name of the file."""
 
1045
    given, exports to a directory (equivalent to --format=dir)."""
1122
1046
    # TODO: list known exporters
1123
1047
    takes_args = ['dest']
1124
 
    takes_options = ['revision', 'format', 'root']
1125
 
    def run(self, dest, revision=None, format=None, root=None):
1126
 
        import os.path
 
1048
    takes_options = ['revision', 'format']
 
1049
    def run(self, dest, revision=None, format='dir'):
1127
1050
        b = find_branch('.')
1128
 
        if revision is None:
1129
 
            rev_id = b.last_patch()
 
1051
        if revision == None:
 
1052
            rh = b.revision_history()[-1]
1130
1053
        else:
1131
 
            if len(revision) != 1:
1132
 
                raise BzrError('bzr export --revision takes exactly 1 argument')
1133
 
            revno, rev_id = b.get_revision_info(revision[0])
1134
 
        t = b.revision_tree(rev_id)
1135
 
        root, ext = os.path.splitext(dest)
1136
 
        if not format:
1137
 
            if ext in (".tar",):
1138
 
                format = "tar"
1139
 
            elif ext in (".gz", ".tgz"):
1140
 
                format = "tgz"
1141
 
            elif ext in (".bz2", ".tbz2"):
1142
 
                format = "tbz2"
1143
 
            else:
1144
 
                format = "dir"
1145
 
        t.export(dest, format, root)
 
1054
            rh = b.lookup_revision(int(revision))
 
1055
        t = b.revision_tree(rh)
 
1056
        t.export(dest, format)
1146
1057
 
1147
1058
 
1148
1059
class cmd_cat(Command):
1154
1065
    def run(self, filename, revision=None):
1155
1066
        if revision == None:
1156
1067
            raise BzrCommandError("bzr cat requires a revision number")
1157
 
        elif len(revision) != 1:
1158
 
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1159
1068
        b = find_branch('.')
1160
 
        b.print_file(b.relpath(filename), revision[0])
 
1069
        b.print_file(b.relpath(filename), int(revision))
1161
1070
 
1162
1071
 
1163
1072
class cmd_local_time_offset(Command):
1184
1093
    TODO: Strict commit that fails if there are unknown or deleted files.
1185
1094
    """
1186
1095
    takes_args = ['selected*']
1187
 
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
1096
    takes_options = ['message', 'file', 'verbose']
1188
1097
    aliases = ['ci', 'checkin']
1189
1098
 
1190
 
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1191
 
            unchanged=False):
1192
 
        from bzrlib.errors import PointlessCommit
 
1099
    def run(self, message=None, file=None, verbose=True, selected_list=None):
 
1100
        from bzrlib.commit import commit
1193
1101
        from bzrlib.osutils import get_text_message
1194
1102
 
1195
1103
        ## Warning: shadows builtin file()
1214
1122
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1215
1123
 
1216
1124
        b = find_branch('.')
1217
 
 
1218
 
        try:
1219
 
            b.commit(message, verbose=verbose,
1220
 
                     specific_files=selected_list,
1221
 
                     allow_pointless=unchanged)
1222
 
        except PointlessCommit:
1223
 
            # FIXME: This should really happen before the file is read in;
1224
 
            # perhaps prepare the commit; get the message; then actually commit
1225
 
            raise BzrCommandError("no changes to commit",
1226
 
                                  ["use --unchanged to commit anyhow"])
 
1125
        commit(b, message, verbose=verbose, specific_files=selected_list)
1227
1126
 
1228
1127
 
1229
1128
class cmd_check(Command):
1311
1210
    ['..', -1]
1312
1211
    >>> parse_spec("../f/@35")
1313
1212
    ['../f', 35]
1314
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1315
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1316
1213
    """
1317
1214
    if spec is None:
1318
1215
        return [None, None]
1322
1219
        if parsed[1] == "":
1323
1220
            parsed[1] = -1
1324
1221
        else:
1325
 
            try:
1326
 
                parsed[1] = int(parsed[1])
1327
 
            except ValueError:
1328
 
                pass # We can allow stuff like ./@revid:blahblahblah
1329
 
            else:
1330
 
                assert parsed[1] >=0
 
1222
            parsed[1] = int(parsed[1])
 
1223
            assert parsed[1] >=0
1331
1224
    else:
1332
1225
        parsed = [spec, None]
1333
1226
    return parsed
1391
1284
    """
1392
1285
    takes_options = ['revision']
1393
1286
 
1394
 
    def run(self, revision=None):
 
1287
    def run(self, revision=-1):
1395
1288
        from bzrlib.merge import merge
1396
 
        if revision is None:
1397
 
            revision = -1
1398
 
        elif len(revision) != 1:
1399
 
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1400
 
        merge(('.', revision[0]), parse_spec('.'),
 
1289
        merge(('.', revision), parse_spec('.'),
1401
1290
              check_clean=False,
1402
1291
              ignore_zero=True)
1403
1292
 
1421
1310
        help.help(topic)
1422
1311
 
1423
1312
 
 
1313
class cmd_update_stat_cache(Command):
 
1314
    """Update stat-cache mapping inodes to SHA-1 hashes.
 
1315
 
 
1316
    For testing only."""
 
1317
    hidden = True
 
1318
    def run(self):
 
1319
        import statcache
 
1320
        b = find_branch('.')
 
1321
        statcache.update_cache(b.base, b.read_working_inventory())
 
1322
 
1424
1323
 
1425
1324
 
1426
1325
class cmd_plugins(Command):
1428
1327
    hidden = True
1429
1328
    def run(self):
1430
1329
        import bzrlib.plugin
1431
 
        from inspect import getdoc
1432
1330
        from pprint import pprint
1433
 
        for plugin in bzrlib.plugin.all_plugins:
1434
 
            print plugin.__path__[0]
1435
 
            d = getdoc(plugin)
1436
 
            if d:
1437
 
                print '\t', d.split('\n')[0]
1438
 
 
1439
 
        #pprint(bzrlib.plugin.all_plugins)
 
1331
        pprint(bzrlib.plugin.all_plugins)
1440
1332
 
1441
1333
 
1442
1334
 
1460
1352
    'verbose':                None,
1461
1353
    'version':                None,
1462
1354
    'email':                  None,
1463
 
    'unchanged':              None,
1464
1355
    'update':                 None,
1465
1356
    'long':                   None,
1466
 
    'root':                   str,
1467
1357
    }
1468
1358
 
1469
1359
SHORT_OPTIONS = {
1493
1383
    >>> parse_args('commit --message=biter'.split())
1494
1384
    (['commit'], {'message': u'biter'})
1495
1385
    >>> parse_args('log -r 500'.split())
1496
 
    (['log'], {'revision': [500]})
1497
 
    >>> parse_args('log -r500..600'.split())
 
1386
    (['log'], {'revision': 500})
 
1387
    >>> parse_args('log -r500:600'.split())
1498
1388
    (['log'], {'revision': [500, 600]})
1499
 
    >>> parse_args('log -vr500..600'.split())
 
1389
    >>> parse_args('log -vr500:600'.split())
1500
1390
    (['log'], {'verbose': True, 'revision': [500, 600]})
1501
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
1502
 
    (['log'], {'revision': ['v500', 600]})
 
1391
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
 
1392
    Traceback (most recent call last):
 
1393
    ...
 
1394
    ValueError: invalid literal for int(): v500
1503
1395
    """
1504
1396
    args = []
1505
1397
    opts = {}