~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-07-22 23:43:26 UTC
  • Revision ID: mbp@sourcefrog.net-20050722234326-13104af9d837ed2b
todo

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
 
22
from bzrlib.trace import mutter, note, log_error, warning
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
24
from bzrlib.branch import find_branch
25
25
from bzrlib import BZRDIR
51
51
    assert cmd.startswith("cmd_")
52
52
    return cmd[4:].replace('_','-')
53
53
 
 
54
 
54
55
def _parse_revision_str(revstr):
55
 
    """This handles a revision string -> revno. 
56
 
 
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'             -> ?
 
56
    """This handles a revision string -> revno.
 
57
 
 
58
    This always returns a list.  The list will have one element for 
 
59
 
 
60
    It supports integers directly, but everything else it
 
61
    defers for passing to Branch.get_revision_info()
 
62
 
 
63
    >>> _parse_revision_str('234')
 
64
    [234]
 
65
    >>> _parse_revision_str('234..567')
 
66
    [234, 567]
 
67
    >>> _parse_revision_str('..')
 
68
    [None, None]
 
69
    >>> _parse_revision_str('..234')
 
70
    [None, 234]
 
71
    >>> _parse_revision_str('234..')
 
72
    [234, None]
 
73
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
74
    [234, 456, 789]
 
75
    >>> _parse_revision_str('234....789') # Error?
 
76
    [234, None, 789]
 
77
    >>> _parse_revision_str('revid:test@other.com-234234')
 
78
    ['revid:test@other.com-234234']
 
79
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
80
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
 
81
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
82
    ['revid:test@other.com-234234', 23]
 
83
    >>> _parse_revision_str('date:2005-04-12')
 
84
    ['date:2005-04-12']
 
85
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
86
    ['date:2005-04-12 12:24:33']
 
87
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
88
    ['date:2005-04-12T12:24:33']
 
89
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
90
    ['date:2005-04-12,12:24:33']
 
91
    >>> _parse_revision_str('-5..23')
 
92
    [-5, 23]
 
93
    >>> _parse_revision_str('-5')
 
94
    [-5]
 
95
    >>> _parse_revision_str('123a')
 
96
    ['123a']
 
97
    >>> _parse_revision_str('abc')
 
98
    ['abc']
69
99
    """
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)
 
100
    import re
 
101
    old_format_re = re.compile('\d*:\d*')
 
102
    m = old_format_re.match(revstr)
 
103
    if m:
 
104
        warning('Colon separator for revision numbers is deprecated.'
 
105
                ' Use .. instead')
 
106
        revs = []
 
107
        for rev in revstr.split(':'):
 
108
            if rev:
 
109
                revs.append(int(rev))
 
110
            else:
 
111
                revs.append(None)
 
112
        return revs
 
113
    revs = []
 
114
    for x in revstr.split('..'):
 
115
        if not x:
 
116
            revs.append(None)
 
117
        else:
 
118
            try:
 
119
                revs.append(int(x))
 
120
            except ValueError:
 
121
                revs.append(x)
86
122
    return revs
87
123
 
88
124
 
296
332
    directory is shown.  Otherwise, only the status of the specified
297
333
    files or directories is reported.  If a directory is given, status
298
334
    is reported for everything inside that directory.
 
335
 
 
336
    If a revision is specified, the changes since that revision are shown.
299
337
    """
300
338
    takes_args = ['file*']
301
 
    takes_options = ['all', 'show-ids']
 
339
    takes_options = ['all', 'show-ids', 'revision']
302
340
    aliases = ['st', 'stat']
303
341
    
304
342
    def run(self, all=False, show_ids=False, file_list=None):
311
349
                file_list = None
312
350
        else:
313
351
            b = find_branch('.')
314
 
        import status
315
 
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
316
 
                           specific_files=file_list)
 
352
            
 
353
        from bzrlib.status import show_status
 
354
        show_status(b, show_unchanged=all, show_ids=show_ids,
 
355
                    specific_files=file_list)
317
356
 
318
357
 
319
358
class cmd_cat_revision(Command):
334
373
    def run(self):
335
374
        print find_branch('.').revno()
336
375
 
 
376
class cmd_revision_info(Command):
 
377
    """Show revision number and revision id for a given revision identifier.
 
378
    """
 
379
    hidden = True
 
380
    takes_args = ['revision_info*']
 
381
    takes_options = ['revision']
 
382
    def run(self, revision=None, revision_info_list=None):
 
383
        from bzrlib.branch import find_branch
 
384
 
 
385
        revs = []
 
386
        if revision is not None:
 
387
            revs.extend(revision)
 
388
        if revision_info_list is not None:
 
389
            revs.extend(revision_info_list)
 
390
        if len(revs) == 0:
 
391
            raise BzrCommandError('You must supply a revision identifier')
 
392
 
 
393
        b = find_branch('.')
 
394
 
 
395
        for rev in revs:
 
396
            print '%4d %s' % b.get_revision_info(rev)
 
397
 
337
398
    
338
399
class cmd_add(Command):
339
400
    """Add specified files or directories.
401
462
        if revision == None:
402
463
            inv = b.read_working_inventory()
403
464
        else:
404
 
            inv = b.get_revision_inventory(b.lookup_revision(revision))
 
465
            if len(revision) > 1:
 
466
                raise BzrCommandError('bzr inventory --revision takes'
 
467
                    ' exactly one revision identifier')
 
468
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
405
469
 
406
470
        for path, entry in inv.entries():
407
471
            if show_ids:
529
593
        from meta_store import CachedStore
530
594
        import tempfile
531
595
        cache_root = tempfile.mkdtemp()
 
596
 
 
597
        if revision is None:
 
598
            revision = [None]
 
599
        elif len(revision) > 1:
 
600
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
 
601
 
532
602
        try:
533
603
            try:
534
604
                br_from = find_cached_branch(from_location, cache_root)
555
625
                    raise
556
626
            br_to = Branch(to_location, init=True)
557
627
 
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)
 
628
            br_to.set_root_id(br_from.get_root_id())
 
629
 
 
630
            if revision:
 
631
                if revision[0] is None:
 
632
                    revno = br_from.revno()
 
633
                else:
 
634
                    revno, rev_id = br_from.get_revision_info(revision[0])
 
635
                try:
 
636
                    br_to.update_revisions(br_from, stop_revision=revno)
 
637
                except NoSuchRevision:
 
638
                    rmtree(to_location)
 
639
                    msg = "The branch %s has no revision %d." % (from_location,
 
640
                                                                 revno)
 
641
                    raise BzrCommandError(msg)
 
642
            
565
643
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
566
644
                  check_clean=False, ignore_zero=True)
567
645
            from_location = pull_loc(br_from)
735
813
                file_list = None
736
814
        else:
737
815
            b = find_branch('.')
 
816
 
 
817
        # TODO: Make show_diff support taking 2 arguments
 
818
        base_rev = None
 
819
        if revision is not None:
 
820
            if len(revision) != 1:
 
821
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
 
822
            base_rev = revision[0]
738
823
    
739
 
        show_diff(b, revision, specific_files=file_list,
 
824
        show_diff(b, base_rev, specific_files=file_list,
740
825
                  external_diff_options=diff_options)
741
826
 
742
827
 
770
855
    """List files modified in working tree."""
771
856
    hidden = True
772
857
    def run(self):
773
 
        from bzrlib.statcache import update_cache, SC_SHA1
 
858
        from bzrlib.diff import compare_trees
 
859
 
774
860
        b = find_branch('.')
775
 
        inv = b.read_working_inventory()
776
 
        sc = 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.
 
861
        td = compare_trees(b.basis_tree(), b.working_tree())
784
862
 
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[SC_SHA1] != ie.text_sha1:
791
 
                path = inv.id2path(file_id)
792
 
                print path
 
863
        for path, id, kind in td.modified:
 
864
            print path
793
865
 
794
866
 
795
867
 
830
902
    -r revision requests a specific revision, -r :end or -r begin: are
831
903
    also valid.
832
904
 
 
905
    --message allows you to give a regular expression, which will be evaluated
 
906
    so that only matching entries will be displayed.
 
907
 
833
908
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
834
909
  
835
910
    """
836
911
 
837
912
    takes_args = ['filename?']
838
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long']
 
913
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
839
914
    
840
915
    def run(self, filename=None, timezone='original',
841
916
            verbose=False,
842
917
            show_ids=False,
843
918
            forward=False,
844
919
            revision=None,
 
920
            message=None,
845
921
            long=False):
846
922
        from bzrlib.branch import find_branch
847
923
        from bzrlib.log import log_formatter, show_log
860
936
            b = find_branch('.')
861
937
            file_id = None
862
938
 
863
 
        if revision == None:
864
 
            revision = [None, None]
865
 
        elif isinstance(revision, int):
866
 
            revision = [revision, revision]
 
939
        if revision is None:
 
940
            rev1 = None
 
941
            rev2 = None
 
942
        elif len(revision) == 1:
 
943
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
 
944
        elif len(revision) == 2:
 
945
            rev1 = b.get_revision_info(revision[0])[0]
 
946
            rev2 = b.get_revision_info(revision[1])[0]
867
947
        else:
868
 
            # pair of revisions?
869
 
            pass
870
 
            
871
 
        assert len(revision) == 2
 
948
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
949
 
 
950
        if rev1 == 0:
 
951
            rev1 = None
 
952
        if rev2 == 0:
 
953
            rev2 = None
872
954
 
873
955
        mutter('encoding log as %r' % bzrlib.user_encoding)
874
956
 
890
972
                 file_id,
891
973
                 verbose=verbose,
892
974
                 direction=direction,
893
 
                 start_revision=revision[0],
894
 
                 end_revision=revision[1])
 
975
                 start_revision=rev1,
 
976
                 end_revision=rev2,
 
977
                 search=message)
895
978
 
896
979
 
897
980
 
1042
1125
    If no revision is specified this exports the last committed revision.
1043
1126
 
1044
1127
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1045
 
    given, exports to a directory (equivalent to --format=dir)."""
 
1128
    given, try to find the format with the extension. If no extension
 
1129
    is found exports to a directory (equivalent to --format=dir).
 
1130
 
 
1131
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1132
    is given, the top directory will be the root name of the file."""
1046
1133
    # TODO: list known exporters
1047
1134
    takes_args = ['dest']
1048
 
    takes_options = ['revision', 'format']
1049
 
    def run(self, dest, revision=None, format='dir'):
 
1135
    takes_options = ['revision', 'format', 'root']
 
1136
    def run(self, dest, revision=None, format=None, root=None):
 
1137
        import os.path
1050
1138
        b = find_branch('.')
1051
 
        if revision == None:
1052
 
            rh = b.revision_history()[-1]
 
1139
        if revision is None:
 
1140
            rev_id = b.last_patch()
1053
1141
        else:
1054
 
            rh = b.lookup_revision(int(revision))
1055
 
        t = b.revision_tree(rh)
1056
 
        t.export(dest, format)
 
1142
            if len(revision) != 1:
 
1143
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
1144
            revno, rev_id = b.get_revision_info(revision[0])
 
1145
        t = b.revision_tree(rev_id)
 
1146
        root, ext = os.path.splitext(dest)
 
1147
        if not format:
 
1148
            if ext in (".tar",):
 
1149
                format = "tar"
 
1150
            elif ext in (".gz", ".tgz"):
 
1151
                format = "tgz"
 
1152
            elif ext in (".bz2", ".tbz2"):
 
1153
                format = "tbz2"
 
1154
            else:
 
1155
                format = "dir"
 
1156
        t.export(dest, format, root)
1057
1157
 
1058
1158
 
1059
1159
class cmd_cat(Command):
1065
1165
    def run(self, filename, revision=None):
1066
1166
        if revision == None:
1067
1167
            raise BzrCommandError("bzr cat requires a revision number")
 
1168
        elif len(revision) != 1:
 
1169
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1068
1170
        b = find_branch('.')
1069
 
        b.print_file(b.relpath(filename), int(revision))
 
1171
        b.print_file(b.relpath(filename), revision[0])
1070
1172
 
1071
1173
 
1072
1174
class cmd_local_time_offset(Command):
1093
1195
    TODO: Strict commit that fails if there are unknown or deleted files.
1094
1196
    """
1095
1197
    takes_args = ['selected*']
1096
 
    takes_options = ['message', 'file', 'verbose']
 
1198
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1097
1199
    aliases = ['ci', 'checkin']
1098
1200
 
1099
 
    def run(self, message=None, file=None, verbose=True, selected_list=None):
1100
 
        from bzrlib.commit import commit
 
1201
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1202
            unchanged=False):
 
1203
        from bzrlib.errors import PointlessCommit
1101
1204
        from bzrlib.osutils import get_text_message
1102
1205
 
1103
1206
        ## Warning: shadows builtin file()
1122
1225
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1123
1226
 
1124
1227
        b = find_branch('.')
1125
 
        commit(b, message, verbose=verbose, specific_files=selected_list)
 
1228
 
 
1229
        try:
 
1230
            b.commit(message, verbose=verbose,
 
1231
                     specific_files=selected_list,
 
1232
                     allow_pointless=unchanged)
 
1233
        except PointlessCommit:
 
1234
            # FIXME: This should really happen before the file is read in;
 
1235
            # perhaps prepare the commit; get the message; then actually commit
 
1236
            raise BzrCommandError("no changes to commit",
 
1237
                                  ["use --unchanged to commit anyhow"])
1126
1238
 
1127
1239
 
1128
1240
class cmd_check(Command):
1142
1254
 
1143
1255
 
1144
1256
 
 
1257
class cmd_scan_cache(Command):
 
1258
    hidden = True
 
1259
    def run(self):
 
1260
        from bzrlib.hashcache import HashCache
 
1261
        import os
 
1262
 
 
1263
        c = HashCache('.')
 
1264
        c.read()
 
1265
        c.scan()
 
1266
            
 
1267
        print '%6d stats' % c.stat_count
 
1268
        print '%6d in hashcache' % len(c._cache)
 
1269
        print '%6d files removed from cache' % c.removed_count
 
1270
        print '%6d hashes updated' % c.update_count
 
1271
        print '%6d files changed too recently to cache' % c.danger_count
 
1272
 
 
1273
        if c.needs_write:
 
1274
            c.write()
 
1275
            
 
1276
 
 
1277
 
1145
1278
class cmd_upgrade(Command):
1146
1279
    """Upgrade branch storage to current format.
1147
1280
 
1170
1303
class cmd_selftest(Command):
1171
1304
    """Run internal test suite"""
1172
1305
    hidden = True
1173
 
    def run(self):
 
1306
    takes_options = ['verbose']
 
1307
    def run(self, verbose=False):
1174
1308
        from bzrlib.selftest import selftest
1175
 
        return int(not selftest())
 
1309
        return int(not selftest(verbose=verbose))
1176
1310
 
1177
1311
 
1178
1312
class cmd_version(Command):
1210
1344
    ['..', -1]
1211
1345
    >>> parse_spec("../f/@35")
1212
1346
    ['../f', 35]
 
1347
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
1348
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1213
1349
    """
1214
1350
    if spec is None:
1215
1351
        return [None, None]
1219
1355
        if parsed[1] == "":
1220
1356
            parsed[1] = -1
1221
1357
        else:
1222
 
            parsed[1] = int(parsed[1])
1223
 
            assert parsed[1] >=0
 
1358
            try:
 
1359
                parsed[1] = int(parsed[1])
 
1360
            except ValueError:
 
1361
                pass # We can allow stuff like ./@revid:blahblahblah
 
1362
            else:
 
1363
                assert parsed[1] >=0
1224
1364
    else:
1225
1365
        parsed = [spec, None]
1226
1366
    return parsed
1284
1424
    """
1285
1425
    takes_options = ['revision']
1286
1426
 
1287
 
    def run(self, revision=-1):
 
1427
    def run(self, revision=None):
1288
1428
        from bzrlib.merge import merge
1289
 
        merge(('.', revision), parse_spec('.'),
 
1429
        if revision is None:
 
1430
            revision = [-1]
 
1431
        elif len(revision) != 1:
 
1432
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
 
1433
        merge(('.', revision[0]), parse_spec('.'),
1290
1434
              check_clean=False,
1291
1435
              ignore_zero=True)
1292
1436
 
1310
1454
        help.help(topic)
1311
1455
 
1312
1456
 
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
 
        from bzrlib.statcache import update_cache
1320
 
        b = find_branch('.')
1321
 
        update_cache(b.base, b.read_working_inventory())
1322
 
 
1323
1457
 
1324
1458
 
1325
1459
class cmd_plugins(Command):
1327
1461
    hidden = True
1328
1462
    def run(self):
1329
1463
        import bzrlib.plugin
 
1464
        from inspect import getdoc
1330
1465
        from pprint import pprint
1331
 
        pprint(bzrlib.plugin.all_plugins)
 
1466
        for plugin in bzrlib.plugin.all_plugins:
 
1467
            print plugin.__path__[0]
 
1468
            d = getdoc(plugin)
 
1469
            if d:
 
1470
                print '\t', d.split('\n')[0]
 
1471
 
 
1472
        #pprint(bzrlib.plugin.all_plugins)
1332
1473
 
1333
1474
 
1334
1475
 
1352
1493
    'verbose':                None,
1353
1494
    'version':                None,
1354
1495
    'email':                  None,
 
1496
    'unchanged':              None,
1355
1497
    'update':                 None,
1356
1498
    'long':                   None,
 
1499
    'root':                   str,
1357
1500
    }
1358
1501
 
1359
1502
SHORT_OPTIONS = {
1383
1526
    >>> parse_args('commit --message=biter'.split())
1384
1527
    (['commit'], {'message': u'biter'})
1385
1528
    >>> parse_args('log -r 500'.split())
1386
 
    (['log'], {'revision': 500})
1387
 
    >>> parse_args('log -r500:600'.split())
 
1529
    (['log'], {'revision': [500]})
 
1530
    >>> parse_args('log -r500..600'.split())
1388
1531
    (['log'], {'revision': [500, 600]})
1389
 
    >>> parse_args('log -vr500:600'.split())
 
1532
    >>> parse_args('log -vr500..600'.split())
1390
1533
    (['log'], {'verbose': True, 'revision': [500, 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
 
1534
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
1535
    (['log'], {'revision': ['v500', 600]})
1395
1536
    """
1396
1537
    args = []
1397
1538
    opts = {}