14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# DO NOT change this to cStringIO - it results in control files
19
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
21
from StringIO import StringIO
26
22
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
23
from bzrlib.commands import Command
28
24
from bzrlib.branch import Branch
29
25
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
30
26
from bzrlib.errors import DivergedBranches
35
31
from bzrlib.workingtree import WorkingTree
38
def branch_files(file_list, default_branch='.'):
40
Return a branch and list of branch-relative paths.
41
If supplied file_list is empty or None, the branch default will be used,
42
and returned file_list will match the original.
44
if file_list is None or len(file_list) == 0:
45
return Branch.open_containing(default_branch)[0], file_list
46
b = Branch.open_containing(file_list[0])[0]
48
# note that if this is a remote branch, we would want
49
# relpath against the transport. RBC 20051018
50
# Most branch ops can't meaningfully operate on files in remote branches;
51
# the above comment was in cmd_status. ADHB 20051026
52
tree = WorkingTree(b.base, b)
54
for filename in file_list:
56
new_list.append(tree.relpath(filename))
57
except NotBranchError:
58
raise BzrCommandError("%s is not in the same branch as %s" %
59
(filename, file_list[0]))
63
# TODO: Make sure no commands unconditionally use the working directory as a
64
# branch. If a filename argument is used, the first of them should be used to
65
# specify the branch. (Perhaps this can be factored out into some kind of
66
# Argument class, representing a file in a branch, where the first occurrence
69
34
class cmd_status(Command):
70
35
"""Display status summary.
105
70
If a revision argument is given, the status is calculated against
106
71
that revision, or between two revisions if two are provided.
109
73
# XXX: FIXME: bzr status should accept a -r option to show changes
110
74
# relative to a revision, or between revisions
112
# TODO: --no-recurse, --recurse options
114
76
takes_args = ['file*']
115
77
takes_options = ['all', 'show-ids']
116
78
aliases = ['st', 'stat']
119
80
def run(self, all=False, show_ids=False, file_list=None, revision=None):
120
b, file_list = branch_files(file_list)
82
b = Branch.open_containing(file_list[0])
83
tree = WorkingTree(b.base, b)
84
file_list = [tree.relpath(x) for x in file_list]
85
# special case: only one path was given and it's the root
90
b = Branch.open_containing('.')
122
92
from bzrlib.status import show_status
123
93
show_status(b, show_unchanged=all, show_ids=show_ids,
135
105
takes_args = ['revision_id?']
136
106
takes_options = ['revision']
139
108
def run(self, revision_id=None, revision=None):
141
110
if revision_id is not None and revision is not None:
142
111
raise BzrCommandError('You can only supply one of revision_id or --revision')
143
112
if revision_id is None and revision is None:
144
113
raise BzrCommandError('You must supply either --revision or a revision_id')
145
b = Branch.open_containing('.')[0]
114
b = Branch.open_containing('.')
146
115
if revision_id is not None:
147
116
sys.stdout.write(b.get_revision_xml_file(revision_id).read())
148
117
elif revision is not None:
248
215
takes_args = ['filename']
252
218
def run(self, filename):
253
branch, relpath = Branch.open_containing(filename)
219
branch = Branch.open_containing(filename)
220
print WorkingTree(branch.base, branch).relpath(filename)
257
223
class cmd_inventory(Command):
258
224
"""Show inventory of the current working copy or a revision."""
259
225
takes_options = ['revision', 'show-ids']
262
227
def run(self, revision=None, show_ids=False):
263
b = Branch.open_containing('.')[0]
228
b = Branch.open_containing('.')
264
229
if revision is None:
265
230
inv = b.read_working_inventory()
287
252
takes_args = ['source$', 'dest']
288
253
def run(self, source_list, dest):
289
b, source_list = branch_files(source_list)
254
b = Branch.open_containing('.')
291
256
# TODO: glob expansion on windows?
292
257
tree = WorkingTree(b.base, b)
293
b.move(source_list, tree.relpath(dest))
258
b.move([tree.relpath(s) for s in source_list], tree.relpath(dest))
296
261
class cmd_rename(Command):
310
275
takes_args = ['from_name', 'to_name']
312
277
def run(self, from_name, to_name):
313
b, (from_name, to_name) = branch_files((from_name, to_name))
314
b.rename_one(from_name, to_name)
278
b = Branch.open_containing('.')
279
tree = WorkingTree(b.base, b)
280
b.rename_one(tree.relpath(from_name), tree.relpath(to_name))
317
283
class cmd_mv(Command):
331
297
def run(self, names_list):
332
298
if len(names_list) < 2:
333
299
raise BzrCommandError("missing file argument")
334
b, rel_names = branch_files(names_list)
300
b = Branch.open_containing(names_list[0])
301
tree = WorkingTree(b.base, b)
302
rel_names = [tree.relpath(x) for x in names_list]
336
304
if os.path.isdir(names_list[-1]):
337
305
# move into existing directory
345
313
print "%s => %s" % (rel_names[0], rel_names[1])
348
318
class cmd_pull(Command):
349
319
"""Pull any changes from another branch into the current one.
351
If there is no default location set, the first pull will set it. After
352
that, you can omit the location to use the default. To change the
353
default, use --remember.
321
If the location is omitted, the last-used location will be used.
322
Both the revision history and the working directory will be
355
325
This command only works on branches that have not diverged. Branches are
356
326
considered diverged if both branches have had commits without first
357
327
pulling from the other.
359
329
If branches have diverged, you can use 'bzr merge' to pull the text changes
360
from one into the other. Once one branch has merged, the other should
361
be able to pull it again.
363
If you want to forget your local changes and just update your branch to
364
match the remote one, use --overwrite.
330
from one into the other.
366
takes_options = ['remember', 'overwrite']
332
takes_options = ['remember']
367
333
takes_args = ['location?']
369
def run(self, location=None, remember=False, overwrite=False):
335
def run(self, location=None, remember=False):
370
336
from bzrlib.merge import merge
371
338
from shutil import rmtree
374
br_to = Branch.open_containing('.')[0]
341
br_to = Branch.open_containing('.')
375
342
stored_loc = br_to.get_parent()
376
343
if location is None:
377
344
if stored_loc is None:
380
347
print "Using saved location: %s" % stored_loc
381
348
location = stored_loc
382
if br_to.get_parent() is None or remember:
383
br_to.set_parent(location)
349
cache_root = tempfile.mkdtemp()
384
350
br_from = Branch.open(location)
386
br_to.working_tree().pull(br_from, overwrite)
387
except DivergedBranches:
388
raise BzrCommandError("These branches have diverged."
392
class cmd_push(Command):
393
"""Push this branch into another branch.
395
The remote branch will not have its working tree populated because this
396
is both expensive, and may not be supported on the remote file system.
398
Some smart servers or protocols *may* put the working tree in place.
400
If there is no default push location set, the first push will set it.
401
After that, you can omit the location to use the default. To change the
402
default, use --remember.
404
This command only works on branches that have not diverged. Branches are
405
considered diverged if the branch being pushed to is not an older version
408
If branches have diverged, you can use 'bzr push --overwrite' to replace
409
the other branch completely.
411
If you want to ensure you have the different changes in the other branch,
412
do a merge (see bzr help merge) from the other branch, and commit that
413
before doing a 'push --overwrite'.
415
takes_options = ['remember', 'overwrite']
416
takes_args = ['location?']
418
def run(self, location=None, remember=False, overwrite=False):
420
from shutil import rmtree
421
from bzrlib.transport import get_transport
423
br_from = Branch.open_containing('.')[0]
424
stored_loc = br_from.get_push_location()
426
if stored_loc is None:
427
raise BzrCommandError("No push location known or specified.")
429
print "Using saved location: %s" % stored_loc
430
location = stored_loc
431
if br_from.get_push_location() is None or remember:
432
br_from.set_push_location(location)
434
br_to = Branch.open(location)
435
except NotBranchError:
437
transport = get_transport(location).clone('..')
438
transport.mkdir(transport.relpath(location))
439
br_to = Branch.initialize(location)
441
br_to.pull(br_from, overwrite)
442
except DivergedBranches:
443
raise BzrCommandError("These branches have diverged."
444
" Try a merge then push with overwrite.")
353
br_from.setup_caching(cache_root)
354
location = br_from.base
355
old_revno = br_to.revno()
356
old_revision_history = br_to.revision_history()
358
br_to.update_revisions(br_from)
359
except DivergedBranches:
360
raise BzrCommandError("These branches have diverged."
362
new_revision_history = br_to.revision_history()
363
if new_revision_history != old_revision_history:
364
merge(('.', -1), ('.', old_revno), check_clean=False)
365
if stored_loc is None or remember:
366
br_to.set_parent(location)
447
373
class cmd_branch(Command):
508
434
copy_branch(br_from, to_location, revision_id, basis_branch)
509
435
except bzrlib.errors.NoSuchRevision:
510
436
rmtree(to_location)
511
msg = "The branch %s has no revision %s." % (from_location, revision[0])
437
msg = "The branch %s has no revision %d." % (from_location, revision[0])
512
438
raise BzrCommandError(msg)
513
439
except bzrlib.errors.UnlistableBranch:
515
440
msg = "The branch %s cannot be used as a --basis"
516
raise BzrCommandError(msg)
518
branch = Branch.open(to_location)
519
name = StringIO(name)
520
branch.put_controlfile('branch-name', name)
525
446
class cmd_renames(Command):
566
485
def run(self, file_list, verbose=False):
567
b, file_list = branch_files(file_list)
568
tree = b.working_tree()
569
tree.remove(file_list, verbose=verbose)
486
b = Branch.open_containing(file_list[0])
487
tree = WorkingTree(b.base, b)
488
b.remove([tree.relpath(f) for f in file_list], verbose=verbose)
572
491
class cmd_file_id(Command):
595
514
starting at the branch root."""
597
516
takes_args = ['filename']
599
517
def run(self, filename):
600
b, relpath = Branch.open_containing(filename)
518
b = Branch.open_containing(filename)
601
519
inv = b.inventory
602
fid = inv.path2id(relpath)
520
tree = WorkingTree(b.base, b)
521
fid = inv.path2id(tree.relpath(filename))
604
523
raise BzrError("%r is not a versioned file" % filename)
605
524
for fip in inv.get_idpath(fid):
609
528
class cmd_revision_history(Command):
610
529
"""Display list of revision ids on this branch."""
614
for patchid in Branch.open_containing('.')[0].revision_history():
532
for patchid in Branch.open_containing('.').revision_history():
618
536
class cmd_ancestry(Command):
619
537
"""List all revisions merged into this branch."""
623
b = Branch.open_containing('.')[0]
624
541
for revision_id in b.get_ancestry(b.last_revision()):
625
542
print revision_id
628
545
class cmd_directories(Command):
629
546
"""Display list of versioned directories in this branch."""
632
for name, ie in Branch.open_containing('.')[0].read_working_inventory().directories():
548
for name, ie in Branch.open_containing('.').read_working_inventory().directories():
682
598
takes_options = ['revision', 'diff-options']
683
599
aliases = ['di', 'dif']
686
601
def run(self, revision=None, file_list=None, diff_options=None):
687
602
from bzrlib.diff import show_diff
689
b, file_list = branch_files(file_list)
605
b = Branch.open_containing(file_list[0])
606
tree = WorkingTree(b.base, b)
607
file_list = [tree.relpath(f) for f in file_list]
608
if file_list == ['']:
609
# just pointing to top-of-tree
612
b = Branch.open_containing('.')
690
614
if revision is not None:
691
615
if len(revision) == 1:
692
return show_diff(b, revision[0], specific_files=file_list,
693
external_diff_options=diff_options)
616
show_diff(b, revision[0], specific_files=file_list,
617
external_diff_options=diff_options)
694
618
elif len(revision) == 2:
695
return show_diff(b, revision[0], specific_files=file_list,
696
external_diff_options=diff_options,
697
revision2=revision[1])
619
show_diff(b, revision[0], specific_files=file_list,
620
external_diff_options=diff_options,
621
revision2=revision[1])
699
623
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
701
return show_diff(b, None, specific_files=file_list,
702
external_diff_options=diff_options)
625
show_diff(b, None, specific_files=file_list,
626
external_diff_options=diff_options)
705
631
class cmd_deleted(Command):
711
637
# directories with readdir, rather than stating each one. Same
712
638
# level of effort but possibly much less IO. (Or possibly not,
713
639
# if the directories are very large...)
715
640
def run(self, show_ids=False):
716
b = Branch.open_containing('.')[0]
641
b = Branch.open_containing('.')
717
642
old = b.basis_tree()
718
643
new = b.working_tree()
719
644
for path, ie in old.inventory.iter_entries():
727
652
class cmd_modified(Command):
728
653
"""List files modified in working tree."""
732
656
from bzrlib.delta import compare_trees
734
b = Branch.open_containing('.')[0]
658
b = Branch.open_containing('.')
735
659
td = compare_trees(b.basis_tree(), b.working_tree())
737
661
for path, id, kind, text_modified, meta_modified in td.modified:
810
732
direction = (forward and 'forward') or 'reverse'
813
b, fp = Branch.open_containing(filename)
735
b = Branch.open_containing(filename)
736
tree = WorkingTree(b.base, b)
737
fp = tree.relpath(filename)
815
739
file_id = b.read_working_inventory().path2id(fp)
817
741
file_id = None # points to branch root
819
b, relpath = Branch.open_containing('.')
743
b = Branch.open_containing('.')
822
746
if revision is None:
868
792
A more user-friendly interface is "bzr log FILE"."""
870
794
takes_args = ["filename"]
872
795
def run(self, filename):
873
b, relpath = Branch.open_containing(filename)[0]
796
b = Branch.open_containing(filename)
874
797
inv = b.read_working_inventory()
875
file_id = inv.path2id(relpath)
798
tree = WorkingTree(b.base, b)
799
file_id = inv.path2id(tree.relpath(filename))
876
800
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
877
801
print "%6d %s" % (revno, what)
883
807
# TODO: Take a revision or remote path and list that tree instead.
885
takes_options = ['verbose', 'revision',
886
Option('non-recursive',
887
help='don\'t recurse into sub-directories'),
889
help='Print all paths from the root of the branch.'),
890
Option('unknown', help='Print unknown files'),
891
Option('versioned', help='Print versioned files'),
892
Option('ignored', help='Print ignored files'),
894
Option('null', help='Null separate the files'),
897
def run(self, revision=None, verbose=False,
898
non_recursive=False, from_root=False,
899
unknown=False, versioned=False, ignored=False,
903
raise BzrCommandError('Cannot set both --verbose and --null')
904
all = not (unknown or versioned or ignored)
906
selection = {'I':ignored, '?':unknown, 'V':versioned}
908
b, relpath = Branch.open_containing('.')
809
def run(self, revision=None, verbose=False):
810
b = Branch.open_containing('.')
913
811
if revision == None:
914
812
tree = b.working_tree()
916
tree = b.revision_tree(revision[0].in_history(b).rev_id)
814
tree = b.revision_tree(revision.in_history(b).rev_id)
917
815
for fp, fc, kind, fid, entry in tree.list_files():
918
if fp.startswith(relpath):
919
fp = fp[len(relpath):]
920
if non_recursive and '/' in fp:
922
if not all and not selection[fc]:
925
kindch = entry.kind_character()
926
print '%-8s %s%s' % (fc, fp, kindch)
929
sys.stdout.write('\0')
817
kindch = entry.kind_character()
818
print '%-8s %s%s' % (fc, fp, kindch)
936
824
class cmd_unknowns(Command):
937
825
"""List unknown files."""
940
827
from bzrlib.osutils import quotefn
941
for f in Branch.open_containing('.')[0].unknowns():
828
for f in Branch.open_containing('.').unknowns():
1087
972
takes_options = ['revision']
1088
973
takes_args = ['filename']
1091
975
def run(self, filename, revision=None):
1092
976
if revision is None:
1093
977
raise BzrCommandError("bzr cat requires a revision number")
1094
978
elif len(revision) != 1:
1095
979
raise BzrCommandError("bzr cat --revision takes exactly one number")
1096
b, relpath = Branch.open_containing(filename)
1097
b.print_file(relpath, revision[0].in_history(b).revno)
980
b = Branch.open_containing('.')
981
tree = WorkingTree(b.base, b)
982
b.print_file(tree.relpath(filename), revision[0].in_history(b).revno)
1100
985
class cmd_local_time_offset(Command):
1101
986
"""Show the offset in seconds from GMT to local time."""
1105
989
print bzrlib.osutils.local_time_offset()
1135
1017
Option('file', type=str,
1136
1018
argname='msgfile',
1137
1019
help='file containing commit message'),
1139
help="refuse to commit if there are unknown "
1140
"files in the working tree."),
1142
1021
aliases = ['ci', 'checkin']
1144
1023
def run(self, message=None, file=None, verbose=True, selected_list=None,
1145
unchanged=False, strict=False):
1146
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1025
from bzrlib.errors import PointlessCommit, ConflictsInTree
1148
1026
from bzrlib.msgeditor import edit_commit_message
1149
1027
from bzrlib.status import show_status
1150
1028
from cStringIO import StringIO
1152
b, selected_list = branch_files(selected_list)
1030
b = Branch.open_containing('.')
1031
tree = WorkingTree(b.base, b)
1033
selected_list = [tree.relpath(s) for s in selected_list]
1153
1034
if message is None and not file:
1154
1035
catcher = StringIO()
1155
1036
show_status(b, specific_files=selected_list,
1170
1051
raise BzrCommandError("empty commit message specified")
1173
b.commit(message, specific_files=selected_list,
1174
allow_pointless=unchanged, strict=strict)
1055
specific_files=selected_list,
1056
allow_pointless=unchanged)
1175
1057
except PointlessCommit:
1176
1058
# FIXME: This should really happen before the file is read in;
1177
1059
# perhaps prepare the commit; get the message; then actually commit
1333
1209
takes_args = ['branch', 'other']
1337
1212
def run(self, branch, other):
1338
1213
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1340
branch1 = Branch.open_containing(branch)[0]
1341
branch2 = Branch.open_containing(other)[0]
1215
branch1 = Branch.open_containing(branch)
1216
branch2 = Branch.open_containing(other)
1343
1218
history_1 = branch1.revision_history()
1344
1219
history_2 = branch2.revision_history()
1393
1268
--force is given.
1395
1270
takes_args = ['branch?']
1396
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1397
Option('show-base', help="Show base revision text in "
1271
takes_options = ['revision', 'force', 'merge-type']
1400
def run(self, branch=None, revision=None, force=False, merge_type=None,
1401
show_base=False, reprocess=False):
1273
def run(self, branch=None, revision=None, force=False,
1402
1275
from bzrlib.merge import merge
1403
1276
from bzrlib.merge_core import ApplyMerge3
1404
1277
if merge_type is None:
1405
1278
merge_type = ApplyMerge3
1406
1279
if branch is None:
1407
branch = Branch.open_containing('.')[0].get_parent()
1280
branch = Branch.open_containing('.').get_parent()
1408
1281
if branch is None:
1409
1282
raise BzrCommandError("No merge location known or specified.")
1416
1289
if len(revision) == 1:
1417
1290
base = [None, None]
1418
other_branch = Branch.open_containing(branch)[0]
1419
revno = revision[0].in_history(other_branch).revno
1420
other = [branch, revno]
1291
other = [branch, revision[0].in_history(branch).revno]
1422
1293
assert len(revision) == 2
1423
1294
if None in revision:
1424
1295
raise BzrCommandError(
1425
1296
"Merge doesn't permit that revision specifier.")
1426
b = Branch.open_containing(branch)[0]
1297
b = Branch.open(branch)
1428
1299
base = [branch, revision[0].in_history(b).revno]
1429
1300
other = [branch, revision[1].in_history(b).revno]
1432
conflict_count = merge(other, base, check_clean=(not force),
1433
merge_type=merge_type, reprocess=reprocess,
1434
show_base=show_base)
1435
if conflict_count != 0:
1303
merge(other, base, check_clean=(not force), merge_type=merge_type)
1439
1304
except bzrlib.errors.AmbiguousBase, e:
1440
1305
m = ("sorry, bzr can't determine the right merge base yet\n"
1441
1306
"candidates are:\n "
1572
1427
# We only update parent if it did not exist, missing
1573
1428
# should not change the parent
1574
1429
b.set_parent(remote)
1575
br_remote = Branch.open_containing(remote)[0]
1430
br_remote = Branch.open_containing(remote)
1576
1431
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1579
1434
class cmd_plugins(Command):
1580
1435
"""List plugins"""
1584
1438
import bzrlib.plugin
1585
1439
from inspect import getdoc
1638
1491
Option('long', help='show date in annotations'),
1642
1494
def run(self, filename, all=False, long=False):
1643
1495
from bzrlib.annotate import annotate_file
1644
b, relpath = Branch.open_containing(filename)
1496
b = Branch.open_containing(filename)
1647
1499
tree = WorkingTree(b.base, b)
1500
rp = tree.relpath(filename)
1648
1501
tree = b.revision_tree(b.last_revision())
1649
file_id = tree.inventory.path2id(relpath)
1502
file_id = tree.inventory.path2id(rp)
1650
1503
file_version = tree.inventory[file_id].revision
1651
1504
annotate_file(b, file_version, file_id, long, all, sys.stdout)
1668
1521
raise BzrCommandError('You can only supply one of revision_id or --revision')
1669
1522
if revision_id is None and revision is None:
1670
1523
raise BzrCommandError('You must supply either --revision or a revision_id')
1671
b = Branch.open_containing('.')[0]
1524
b = Branch.open_containing('.')
1672
1525
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1673
1526
if revision_id is not None:
1674
1527
b.sign_revision(revision_id, gpg_strategy)
1675
1528
elif revision is not None:
1676
if len(revision) == 1:
1677
revno, rev_id = revision[0].in_history(b)
1529
for rev in revision:
1531
raise BzrCommandError('You cannot specify a NULL revision.')
1532
revno, rev_id = rev.in_history(b)
1678
1533
b.sign_revision(rev_id, gpg_strategy)
1679
elif len(revision) == 2:
1680
# are they both on rh- if so we can walk between them
1681
# might be nice to have a range helper for arbitrary
1682
# revision paths. hmm.
1683
from_revno, from_revid = revision[0].in_history(b)
1684
to_revno, to_revid = revision[1].in_history(b)
1685
if to_revid is None:
1686
to_revno = b.revno()
1687
if from_revno is None or to_revno is None:
1688
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1689
for revno in range(from_revno, to_revno + 1):
1690
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1692
raise BzrCommandError('Please supply either one revision, or a range.')
1695
1536
# these get imported and then picked up by the scan for cmd_*