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
from bzrlib.branch import find_branch
25
from bzrlib import BZRDIR
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
51
52
assert cmd.startswith("cmd_")
52
53
return cmd[4:].replace('_','-')
55
55
def _parse_revision_str(revstr):
56
"""This handles a revision string -> revno.
58
This always returns a list. The list will have one element for
60
It supports integers directly, but everything else it
61
defers for passing to Branch.get_revision_info()
63
>>> _parse_revision_str('234')
65
>>> _parse_revision_str('234..567')
67
>>> _parse_revision_str('..')
69
>>> _parse_revision_str('..234')
71
>>> _parse_revision_str('234..')
73
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
75
>>> _parse_revision_str('234....789') # Error?
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')
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')
93
>>> _parse_revision_str('-5')
95
>>> _parse_revision_str('123a')
97
>>> _parse_revision_str('abc')
56
"""This handles a revision string -> revno.
58
There are several possibilities:
61
'234:345' -> [234, 345]
65
In the future we will also support:
66
'uuid:blah-blah-blah' -> ?
67
'hash:blahblahblah' -> ?
101
old_format_re = re.compile('\d*:\d*')
102
m = old_format_re.match(revstr)
104
warning('Colon separator for revision numbers is deprecated.'
107
for rev in revstr.split(':'):
109
revs.append(int(rev))
114
for x in revstr.split('..'):
71
if revstr.find(':') != -1:
72
revs = revstr.split(':')
74
raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
79
revs[0] = int(revs[0])
84
revs[1] = int(revs[1])
332
297
directory is shown. Otherwise, only the status of the specified
333
298
files or directories is reported. If a directory is given, status
334
299
is reported for everything inside that directory.
336
If a revision is specified, the changes since that revision are shown.
338
301
takes_args = ['file*']
339
takes_options = ['all', 'show-ids', 'revision']
302
takes_options = ['all', 'show-ids']
340
303
aliases = ['st', 'stat']
342
305
def run(self, all=False, show_ids=False, file_list=None):
344
b = find_branch(file_list[0])
307
b = Branch(file_list[0])
345
308
file_list = [b.relpath(x) for x in file_list]
346
309
# special case: only one path was given and it's the root
348
311
if file_list == ['']:
353
from bzrlib.status import show_status
354
show_status(b, show_unchanged=all, show_ids=show_ids,
355
specific_files=file_list)
316
status.show_status(b, show_unchanged=all, show_ids=show_ids,
317
specific_files=file_list)
358
320
class cmd_cat_revision(Command):
372
333
This is equal to the number of revisions on this branch."""
374
print find_branch('.').revno()
376
class cmd_revision_info(Command):
377
"""Show revision number and revision id for a given revision identifier.
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
386
if revision is not None:
387
revs.extend(revision)
388
if revision_info_list is not None:
389
revs.extend(revision_info_list)
391
raise BzrCommandError('You must supply a revision identifier')
396
print '%4d %s' % b.get_revision_info(rev)
335
print Branch('.').revno()
399
338
class cmd_add(Command):
421
360
takes_options = ['verbose', 'no-recurse']
423
362
def run(self, file_list, verbose=False, no_recurse=False):
424
from bzrlib.add import smart_add
425
smart_add(file_list, verbose, not no_recurse)
363
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
458
399
takes_options = ['revision', 'show-ids']
460
401
def run(self, revision=None, show_ids=False):
462
403
if revision == None:
463
404
inv = b.read_working_inventory()
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]))
406
inv = b.get_revision_inventory(b.lookup_revision(revision))
470
408
for path, entry in inv.entries():
549
485
print "Using last location: %s" % stored_loc
550
486
location = stored_loc
551
cache_root = tempfile.mkdtemp()
552
from bzrlib.branch import DivergedBranches
487
from branch import find_branch, DivergedBranches
553
488
br_from = find_branch(location)
554
489
location = pull_loc(br_from)
555
490
old_revno = br_to.revno()
557
from branch import find_cached_branch, DivergedBranches
558
br_from = find_cached_branch(location, cache_root)
559
location = pull_loc(br_from)
560
old_revno = br_to.revno()
562
br_to.update_revisions(br_from)
563
except DivergedBranches:
564
raise BzrCommandError("These branches have diverged."
567
merge(('.', -1), ('.', old_revno), check_clean=False)
568
if location != stored_loc:
569
br_to.controlfile("x-pull", "wb").write(location + "\n")
492
br_to.update_revisions(br_from)
493
except DivergedBranches:
494
raise BzrCommandError("These branches have diverged. Try merge.")
496
merge(('.', -1), ('.', old_revno), check_clean=False)
497
if location != stored_loc:
498
br_to.controlfile("x-pull", "wb").write(location + "\n")
587
514
def run(self, from_location, to_location=None, revision=None):
589
516
from bzrlib.merge import merge
590
from bzrlib.branch import DivergedBranches, NoSuchRevision, \
591
find_cached_branch, Branch
517
from branch import find_branch, DivergedBranches, NoSuchRevision
592
518
from shutil import rmtree
593
from meta_store import CachedStore
595
cache_root = tempfile.mkdtemp()
599
elif len(revision) > 1:
600
raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
604
br_from = find_cached_branch(from_location, cache_root)
606
if e.errno == errno.ENOENT:
607
raise BzrCommandError('Source location "%s" does not'
608
' exist.' % to_location)
612
if to_location is None:
613
to_location = os.path.basename(from_location.rstrip("/\\"))
616
os.mkdir(to_location)
618
if e.errno == errno.EEXIST:
619
raise BzrCommandError('Target directory "%s" already'
620
' exists.' % to_location)
621
if e.errno == errno.ENOENT:
622
raise BzrCommandError('Parent of "%s" does not exist.' %
626
br_to = Branch(to_location, init=True)
628
br_to.set_root_id(br_from.get_root_id())
631
if revision[0] is None:
632
revno = br_from.revno()
634
revno, rev_id = br_from.get_revision_info(revision[0])
636
br_to.update_revisions(br_from, stop_revision=revno)
637
except NoSuchRevision:
639
msg = "The branch %s has no revision %d." % (from_location,
641
raise BzrCommandError(msg)
643
merge((to_location, -1), (to_location, 0), this_dir=to_location,
644
check_clean=False, ignore_zero=True)
645
from_location = pull_loc(br_from)
646
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
520
br_from = find_branch(from_location)
522
if e.errno == errno.ENOENT:
523
raise BzrCommandError('Source location "%s" does not exist.' %
528
if to_location is None:
529
to_location = os.path.basename(from_location.rstrip("/\\"))
532
os.mkdir(to_location)
534
if e.errno == errno.EEXIST:
535
raise BzrCommandError('Target directory "%s" already exists.' %
537
if e.errno == errno.ENOENT:
538
raise BzrCommandError('Parent of "%s" does not exist.' %
542
br_to = Branch(to_location, init=True)
545
br_to.update_revisions(br_from, stop_revision=revision)
546
except NoSuchRevision:
548
msg = "The branch %s has no revision %d." % (from_location,
550
raise BzrCommandError(msg)
551
merge((to_location, -1), (to_location, 0), this_dir=to_location,
552
check_clean=False, ignore_zero=True)
553
from_location = pull_loc(br_from)
554
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
651
557
def pull_loc(branch):
742
649
"""Display list of revision ids on this branch."""
745
for patchid in find_branch('.').revision_history():
652
for patchid in Branch('.').revision_history():
749
656
class cmd_directories(Command):
750
657
"""Display list of versioned directories in this branch."""
752
for name, ie in find_branch('.').read_working_inventory().directories():
659
for name, ie in Branch('.').read_working_inventory().directories():
812
719
# just pointing to top-of-tree
817
# TODO: Make show_diff support taking 2 arguments
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]
824
show_diff(b, base_rev, specific_files=file_list,
724
show_diff(b, revision, specific_files=file_list,
825
725
external_diff_options=diff_options)
855
755
"""List files modified in working tree."""
858
from bzrlib.diff import compare_trees
861
td = compare_trees(b.basis_tree(), b.working_tree())
863
for path, id, kind in td.modified:
760
inv = b.read_working_inventory()
761
sc = statcache.update_cache(b, inv)
762
basis = b.basis_tree()
763
basis_inv = basis.inventory
765
# We used to do this through iter_entries(), but that's slow
766
# when most of the files are unmodified, as is usually the
767
# case. So instead we iterate by inventory entry, and only
768
# calculate paths as necessary.
770
for file_id in basis_inv:
771
cacheentry = sc.get(file_id)
772
if not cacheentry: # deleted
774
ie = basis_inv[file_id]
775
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
776
path = inv.id2path(file_id)
902
816
-r revision requests a specific revision, -r :end or -r begin: are
905
--message allows you to give a regular expression, which will be evaluated
906
so that only matching entries will be displayed.
908
819
TODO: Make --revision support uuid: and hash: [future tag:] notation.
912
823
takes_args = ['filename?']
913
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
824
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
915
826
def run(self, filename=None, timezone='original',
922
from bzrlib.branch import find_branch
923
from bzrlib.log import log_formatter, show_log
831
from bzrlib import show_log, find_branch
926
834
direction = (forward and 'forward') or 'reverse'
936
844
b = find_branch('.')
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]
848
revision = [None, None]
849
elif isinstance(revision, int):
850
revision = [revision, revision]
948
raise BzrCommandError('bzr log --revision takes one or two values.')
855
assert len(revision) == 2
955
857
mutter('encoding log as %r' % bzrlib.user_encoding)
1125
1017
If no revision is specified this exports the last committed revision.
1127
1019
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).
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."""
1020
given, exports to a directory (equivalent to --format=dir)."""
1133
1021
# TODO: list known exporters
1134
1022
takes_args = ['dest']
1135
takes_options = ['revision', 'format', 'root']
1136
def run(self, dest, revision=None, format=None, root=None):
1138
b = find_branch('.')
1139
if revision is None:
1140
rev_id = b.last_patch()
1023
takes_options = ['revision', 'format']
1024
def run(self, dest, revision=None, format='dir'):
1026
if revision == None:
1027
rh = b.revision_history()[-1]
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)
1148
if ext in (".tar",):
1150
elif ext in (".gz", ".tgz"):
1152
elif ext in (".bz2", ".tbz2"):
1156
t.export(dest, format, root)
1029
rh = b.lookup_revision(int(revision))
1030
t = b.revision_tree(rh)
1031
t.export(dest, format)
1159
1034
class cmd_cat(Command):
1195
1068
TODO: Strict commit that fails if there are unknown or deleted files.
1197
1070
takes_args = ['selected*']
1198
takes_options = ['message', 'file', 'verbose', 'unchanged']
1071
takes_options = ['message', 'file', 'verbose']
1199
1072
aliases = ['ci', 'checkin']
1201
def run(self, message=None, file=None, verbose=True, selected_list=None,
1203
from bzrlib.errors import PointlessCommit
1204
from bzrlib.osutils import get_text_message
1074
def run(self, message=None, file=None, verbose=True, selected_list=None):
1075
from bzrlib.commit import commit
1206
1077
## Warning: shadows builtin file()
1207
1078
if not message and not file:
1210
catcher = cStringIO.StringIO()
1211
sys.stdout = catcher
1212
cmd_status({"file_list":selected_list}, {})
1213
info = catcher.getvalue()
1215
message = get_text_message(info)
1218
raise BzrCommandError("please specify a commit message",
1219
["use either --message or --file"])
1079
raise BzrCommandError("please specify a commit message",
1080
["use either --message or --file"])
1220
1081
elif message and file:
1221
1082
raise BzrCommandError("please specify either --message or --file")
1225
1086
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1227
b = find_branch('.')
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"])
1089
commit(b, message, verbose=verbose, specific_files=selected_list)
1240
1092
class cmd_check(Command):
1249
1101
takes_args = ['dir?']
1251
1103
def run(self, dir='.'):
1252
from bzrlib.check import check
1253
check(find_branch(dir))
1257
class cmd_scan_cache(Command):
1260
from bzrlib.hashcache import HashCache
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
1105
bzrlib.check.check(Branch(dir))
1278
1109
class cmd_upgrade(Command):
1398
1222
check_clean=(not force))
1402
1225
class cmd_revert(Command):
1403
"""Restore selected files from a previous revision.
1405
takes_args = ['file+']
1406
def run(self, file_list):
1407
from bzrlib.branch import find_branch
1412
b = find_branch(file_list[0])
1414
b.revert([b.relpath(f) for f in file_list])
1417
class cmd_merge_revert(Command):
1418
1226
"""Reverse all changes since the last commit.
1420
1228
Only versioned files are affected.
1425
1233
takes_options = ['revision']
1427
def run(self, revision=None):
1235
def run(self, revision=-1):
1428
1236
from bzrlib.merge import merge
1429
if revision is None:
1431
elif len(revision) != 1:
1432
raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1433
merge(('.', revision[0]), parse_spec('.'),
1237
merge(('.', revision), parse_spec('.'),
1434
1238
check_clean=False,
1435
1239
ignore_zero=True)
1526
1329
>>> parse_args('commit --message=biter'.split())
1527
1330
(['commit'], {'message': u'biter'})
1528
1331
>>> parse_args('log -r 500'.split())
1529
(['log'], {'revision': [500]})
1530
>>> parse_args('log -r500..600'.split())
1332
(['log'], {'revision': 500})
1333
>>> parse_args('log -r500:600'.split())
1531
1334
(['log'], {'revision': [500, 600]})
1532
>>> parse_args('log -vr500..600'.split())
1335
>>> parse_args('log -vr500:600'.split())
1533
1336
(['log'], {'verbose': True, 'revision': [500, 600]})
1534
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
1535
(['log'], {'revision': ['v500', 600]})
1337
>>> parse_args('log -rv500:600'.split()) #the r takes an argument
1338
Traceback (most recent call last):
1340
ValueError: invalid literal for int(): v500