~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-06-28 05:33:40 UTC
  • Revision ID: mbp@sourcefrog.net-20050628053340-ea73b03fbcde9c46
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
  called as needed.  

- Avoid importing xml and ElementTree library unless needed.

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
51
51
    assert cmd.startswith("cmd_")
52
52
    return cmd[4:].replace('_','-')
53
53
 
54
 
 
55
54
def _parse_revision_str(revstr):
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']
 
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'             -> ?
99
69
    """
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)
 
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)
122
86
    return revs
123
87
 
124
88
 
332
296
    directory is shown.  Otherwise, only the status of the specified
333
297
    files or directories is reported.  If a directory is given, status
334
298
    is reported for everything inside that directory.
335
 
 
336
 
    If a revision is specified, the changes since that revision are shown.
337
299
    """
338
300
    takes_args = ['file*']
339
 
    takes_options = ['all', 'show-ids', 'revision']
 
301
    takes_options = ['all', 'show-ids']
340
302
    aliases = ['st', 'stat']
341
303
    
342
304
    def run(self, all=False, show_ids=False, file_list=None):
349
311
                file_list = None
350
312
        else:
351
313
            b = find_branch('.')
352
 
            
353
 
        from bzrlib.status import show_status
354
 
        show_status(b, show_unchanged=all, show_ids=show_ids,
355
 
                    specific_files=file_list)
 
314
        import status
 
315
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
 
316
                           specific_files=file_list)
356
317
 
357
318
 
358
319
class cmd_cat_revision(Command):
373
334
    def run(self):
374
335
        print find_branch('.').revno()
375
336
 
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
 
 
398
337
    
399
338
class cmd_add(Command):
400
339
    """Add specified files or directories.
462
401
        if revision == None:
463
402
            inv = b.read_working_inventory()
464
403
        else:
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]))
 
404
            inv = b.get_revision_inventory(b.lookup_revision(revision))
469
405
 
470
406
        for path, entry in inv.entries():
471
407
            if show_ids:
593
529
        from meta_store import CachedStore
594
530
        import tempfile
595
531
        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
 
 
602
532
        try:
603
533
            try:
604
534
                br_from = find_cached_branch(from_location, cache_root)
625
555
                    raise
626
556
            br_to = Branch(to_location, init=True)
627
557
 
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
 
            
 
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)
643
565
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
644
566
                  check_clean=False, ignore_zero=True)
645
567
            from_location = pull_loc(br_from)
813
735
                file_list = None
814
736
        else:
815
737
            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]
823
738
    
824
 
        show_diff(b, base_rev, specific_files=file_list,
 
739
        show_diff(b, revision, specific_files=file_list,
825
740
                  external_diff_options=diff_options)
826
741
 
827
742
 
855
770
    """List files modified in working tree."""
856
771
    hidden = True
857
772
    def run(self):
858
 
        from bzrlib.diff import compare_trees
859
 
 
 
773
        import statcache
860
774
        b = find_branch('.')
861
 
        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.
862
784
 
863
 
        for path, id, kind in td.modified:
864
 
            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
865
793
 
866
794
 
867
795
 
902
830
    -r revision requests a specific revision, -r :end or -r begin: are
903
831
    also valid.
904
832
 
905
 
    --message allows you to give a regular expression, which will be evaluated
906
 
    so that only matching entries will be displayed.
907
 
 
908
833
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
909
834
  
910
835
    """
911
836
 
912
837
    takes_args = ['filename?']
913
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
 
838
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
914
839
    
915
840
    def run(self, filename=None, timezone='original',
916
841
            verbose=False,
917
842
            show_ids=False,
918
843
            forward=False,
919
 
            revision=None,
920
 
            message=None,
921
 
            long=False):
 
844
            revision=None):
922
845
        from bzrlib.branch import find_branch
923
846
        from bzrlib.log import log_formatter, show_log
924
847
        import codecs
936
859
            b = find_branch('.')
937
860
            file_id = None
938
861
 
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]
 
862
        if revision == None:
 
863
            revision = [None, None]
 
864
        elif isinstance(revision, int):
 
865
            revision = [revision, revision]
947
866
        else:
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
 
867
            # pair of revisions?
 
868
            pass
 
869
            
 
870
        assert len(revision) == 2
954
871
 
955
872
        mutter('encoding log as %r' % bzrlib.user_encoding)
956
873
 
958
875
        # in e.g. the default C locale.
959
876
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
960
877
 
961
 
        if long:
962
 
            log_format = 'long'
963
 
        else:
964
 
            log_format = 'short'
965
 
        lf = log_formatter(log_format,
 
878
        lf = log_formatter('short',
966
879
                           show_ids=show_ids,
967
880
                           to_file=outf,
968
881
                           show_timezone=timezone)
972
885
                 file_id,
973
886
                 verbose=verbose,
974
887
                 direction=direction,
975
 
                 start_revision=rev1,
976
 
                 end_revision=rev2,
977
 
                 search=message)
 
888
                 start_revision=revision[0],
 
889
                 end_revision=revision[1])
978
890
 
979
891
 
980
892
 
1125
1037
    If no revision is specified this exports the last committed revision.
1126
1038
 
1127
1039
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
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."""
 
1040
    given, exports to a directory (equivalent to --format=dir)."""
1133
1041
    # TODO: list known exporters
1134
1042
    takes_args = ['dest']
1135
 
    takes_options = ['revision', 'format', 'root']
1136
 
    def run(self, dest, revision=None, format=None, root=None):
1137
 
        import os.path
 
1043
    takes_options = ['revision', 'format']
 
1044
    def run(self, dest, revision=None, format='dir'):
1138
1045
        b = find_branch('.')
1139
 
        if revision is None:
1140
 
            rev_id = b.last_patch()
 
1046
        if revision == None:
 
1047
            rh = b.revision_history()[-1]
1141
1048
        else:
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)
 
1049
            rh = b.lookup_revision(int(revision))
 
1050
        t = b.revision_tree(rh)
 
1051
        t.export(dest, format)
1157
1052
 
1158
1053
 
1159
1054
class cmd_cat(Command):
1165
1060
    def run(self, filename, revision=None):
1166
1061
        if revision == None:
1167
1062
            raise BzrCommandError("bzr cat requires a revision number")
1168
 
        elif len(revision) != 1:
1169
 
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1170
1063
        b = find_branch('.')
1171
 
        b.print_file(b.relpath(filename), revision[0])
 
1064
        b.print_file(b.relpath(filename), int(revision))
1172
1065
 
1173
1066
 
1174
1067
class cmd_local_time_offset(Command):
1195
1088
    TODO: Strict commit that fails if there are unknown or deleted files.
1196
1089
    """
1197
1090
    takes_args = ['selected*']
1198
 
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
1091
    takes_options = ['message', 'file', 'verbose']
1199
1092
    aliases = ['ci', 'checkin']
1200
1093
 
1201
 
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1202
 
            unchanged=False):
1203
 
        from bzrlib.errors import PointlessCommit
 
1094
    def run(self, message=None, file=None, verbose=True, selected_list=None):
 
1095
        from bzrlib.commit import commit
1204
1096
        from bzrlib.osutils import get_text_message
1205
1097
 
1206
1098
        ## Warning: shadows builtin file()
1225
1117
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1226
1118
 
1227
1119
        b = find_branch('.')
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"])
 
1120
        commit(b, message, verbose=verbose, specific_files=selected_list)
1238
1121
 
1239
1122
 
1240
1123
class cmd_check(Command):
1254
1137
 
1255
1138
 
1256
1139
 
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
 
 
1278
1140
class cmd_upgrade(Command):
1279
1141
    """Upgrade branch storage to current format.
1280
1142
 
1343
1205
    ['..', -1]
1344
1206
    >>> parse_spec("../f/@35")
1345
1207
    ['../f', 35]
1346
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1347
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1348
1208
    """
1349
1209
    if spec is None:
1350
1210
        return [None, None]
1354
1214
        if parsed[1] == "":
1355
1215
            parsed[1] = -1
1356
1216
        else:
1357
 
            try:
1358
 
                parsed[1] = int(parsed[1])
1359
 
            except ValueError:
1360
 
                pass # We can allow stuff like ./@revid:blahblahblah
1361
 
            else:
1362
 
                assert parsed[1] >=0
 
1217
            parsed[1] = int(parsed[1])
 
1218
            assert parsed[1] >=0
1363
1219
    else:
1364
1220
        parsed = [spec, None]
1365
1221
    return parsed
1423
1279
    """
1424
1280
    takes_options = ['revision']
1425
1281
 
1426
 
    def run(self, revision=None):
 
1282
    def run(self, revision=-1):
1427
1283
        from bzrlib.merge import merge
1428
 
        if revision is None:
1429
 
            revision = [-1]
1430
 
        elif len(revision) != 1:
1431
 
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1432
 
        merge(('.', revision[0]), parse_spec('.'),
 
1284
        merge(('.', revision), parse_spec('.'),
1433
1285
              check_clean=False,
1434
1286
              ignore_zero=True)
1435
1287
 
1453
1305
        help.help(topic)
1454
1306
 
1455
1307
 
 
1308
class cmd_update_stat_cache(Command):
 
1309
    """Update stat-cache mapping inodes to SHA-1 hashes.
 
1310
 
 
1311
    For testing only."""
 
1312
    hidden = True
 
1313
    def run(self):
 
1314
        import statcache
 
1315
        b = find_branch('.')
 
1316
        statcache.update_cache(b.base, b.read_working_inventory())
 
1317
 
1456
1318
 
1457
1319
 
1458
1320
class cmd_plugins(Command):
1460
1322
    hidden = True
1461
1323
    def run(self):
1462
1324
        import bzrlib.plugin
1463
 
        from inspect import getdoc
1464
1325
        from pprint import pprint
1465
 
        for plugin in bzrlib.plugin.all_plugins:
1466
 
            print plugin.__path__[0]
1467
 
            d = getdoc(plugin)
1468
 
            if d:
1469
 
                print '\t', d.split('\n')[0]
1470
 
 
1471
 
        #pprint(bzrlib.plugin.all_plugins)
 
1326
        pprint(bzrlib.plugin.all_plugins)
1472
1327
 
1473
1328
 
1474
1329
 
1492
1347
    'verbose':                None,
1493
1348
    'version':                None,
1494
1349
    'email':                  None,
1495
 
    'unchanged':              None,
1496
1350
    'update':                 None,
1497
 
    'long':                   None,
1498
 
    'root':                   str,
1499
1351
    }
1500
1352
 
1501
1353
SHORT_OPTIONS = {
1504
1356
    'm':                      'message',
1505
1357
    'r':                      'revision',
1506
1358
    'v':                      'verbose',
1507
 
    'l':                      'long',
1508
1359
}
1509
1360
 
1510
1361
 
1525
1376
    >>> parse_args('commit --message=biter'.split())
1526
1377
    (['commit'], {'message': u'biter'})
1527
1378
    >>> parse_args('log -r 500'.split())
1528
 
    (['log'], {'revision': [500]})
1529
 
    >>> parse_args('log -r500..600'.split())
 
1379
    (['log'], {'revision': 500})
 
1380
    >>> parse_args('log -r500:600'.split())
1530
1381
    (['log'], {'revision': [500, 600]})
1531
 
    >>> parse_args('log -vr500..600'.split())
 
1382
    >>> parse_args('log -vr500:600'.split())
1532
1383
    (['log'], {'verbose': True, 'revision': [500, 600]})
1533
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
1534
 
    (['log'], {'revision': ['v500', 600]})
 
1384
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
 
1385
    Traceback (most recent call last):
 
1386
    ...
 
1387
    ValueError: invalid literal for int(): v500
1535
1388
    """
1536
1389
    args = []
1537
1390
    opts = {}