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
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
28
from bzrlib.branch import Branch
29
from bzrlib.revision import common_ancestor
30
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
31
NotBranchError, DivergedBranches, NotConflicted,
32
NoSuchFile, NoWorkingTree, FileInWrongBranch)
33
from bzrlib.option import Option
34
from bzrlib.revisionspec import RevisionSpec
22
35
import bzrlib.trace
23
36
from bzrlib.trace import mutter, note, log_error, warning
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
25
from bzrlib.branch import Branch
26
from bzrlib import BZRDIR
27
from bzrlib.commands import Command
37
from bzrlib.workingtree import WorkingTree
40
def branch_files(file_list, default_branch='.'):
42
return inner_branch_files(file_list, default_branch)
43
except FileInWrongBranch, e:
44
raise BzrCommandError("%s is not in the same branch as %s" %
45
(e.path, file_list[0]))
47
def inner_branch_files(file_list, default_branch='.'):
49
Return a branch and list of branch-relative paths.
50
If supplied file_list is empty or None, the branch default will be used,
51
and returned file_list will match the original.
53
if file_list is None or len(file_list) == 0:
54
return Branch.open_containing(default_branch)[0], file_list
55
b = Branch.open_containing(file_list[0])[0]
57
# note that if this is a remote branch, we would want
58
# relpath against the transport. RBC 20051018
59
# Most branch ops can't meaningfully operate on files in remote branches;
60
# the above comment was in cmd_status. ADHB 20051026
61
tree = WorkingTree(b.base, b)
63
for filename in file_list:
65
new_list.append(tree.relpath(filename))
66
except NotBranchError:
67
raise FileInWrongBranch(b, filename)
71
# TODO: Make sure no commands unconditionally use the working directory as a
72
# branch. If a filename argument is used, the first of them should be used to
73
# specify the branch. (Perhaps this can be factored out into some kind of
74
# Argument class, representing a file in a branch, where the first occurrence
30
77
class cmd_status(Command):
31
78
"""Display status summary.
66
113
If a revision argument is given, the status is calculated against
67
114
that revision, or between two revisions if two are provided.
69
# XXX: FIXME: bzr status should accept a -r option to show changes
70
# relative to a revision, or between revisions
117
# TODO: --no-recurse, --recurse options
72
119
takes_args = ['file*']
73
takes_options = ['all', 'show-ids']
120
takes_options = ['all', 'show-ids', 'revision']
74
121
aliases = ['st', 'stat']
76
124
def run(self, all=False, show_ids=False, file_list=None, revision=None):
78
b = Branch.open_containing(file_list[0])
79
file_list = [b.relpath(x) for x in file_list]
80
# special case: only one path was given and it's the root
85
b = Branch.open_containing('.')
125
b, file_list = branch_files(file_list)
87
127
from bzrlib.status import show_status
88
128
show_status(b, show_unchanged=all, show_ids=show_ids,
210
252
takes_args = ['filename']
213
256
def run(self, filename):
214
print Branch.open_containing(filename).relpath(filename)
257
branch, relpath = Branch.open_containing(filename)
218
261
class cmd_inventory(Command):
219
"""Show inventory of the current working copy or a revision."""
220
takes_options = ['revision', 'show-ids']
262
"""Show inventory of the current working copy or a revision.
264
It is possible to limit the output to a particular entry
265
type using the --kind option. For example; --kind file.
267
takes_options = ['revision', 'show-ids', 'kind']
222
def run(self, revision=None, show_ids=False):
223
b = Branch.open_containing('.')
270
def run(self, revision=None, show_ids=False, kind=None):
271
if kind and kind not in ['file', 'directory', 'symlink']:
272
raise BzrCommandError('invalid kind specified')
273
b = Branch.open_containing('.')[0]
224
274
if revision is None:
225
inv = b.read_working_inventory()
275
inv = b.working_tree().read_working_inventory()
227
277
if len(revision) > 1:
228
278
raise BzrCommandError('bzr inventory --revision takes'
307
357
print "%s => %s" % (rel_names[0], rel_names[1])
312
360
class cmd_pull(Command):
313
361
"""Pull any changes from another branch into the current one.
315
If the location is omitted, the last-used location will be used.
316
Both the revision history and the working directory will be
363
If there is no default location set, the first pull will set it. After
364
that, you can omit the location to use the default. To change the
365
default, use --remember.
319
367
This command only works on branches that have not diverged. Branches are
320
368
considered diverged if both branches have had commits without first
321
369
pulling from the other.
323
371
If branches have diverged, you can use 'bzr merge' to pull the text changes
324
from one into the other.
372
from one into the other. Once one branch has merged, the other should
373
be able to pull it again.
375
If you want to forget your local changes and just update your branch to
376
match the remote one, use --overwrite.
378
takes_options = ['remember', 'overwrite', 'verbose']
326
379
takes_args = ['location?']
328
def run(self, location=None):
381
def run(self, location=None, remember=False, overwrite=False, verbose=False):
329
382
from bzrlib.merge import merge
331
383
from shutil import rmtree
334
br_to = Branch.open_containing('.')
386
br_to = Branch.open_containing('.')[0]
335
387
stored_loc = br_to.get_parent()
336
388
if location is None:
337
389
if stored_loc is None:
338
390
raise BzrCommandError("No pull location known or specified.")
340
print "Using last location: %s" % stored_loc
341
location = stored_loc
342
cache_root = tempfile.mkdtemp()
343
from bzrlib.errors import DivergedBranches
344
br_from = Branch.open_containing(location)
345
location = br_from.base
346
old_revno = br_to.revno()
348
from bzrlib.errors import DivergedBranches
349
br_from = Branch.open(location)
350
br_from.setup_caching(cache_root)
351
location = br_from.base
352
old_revno = br_to.revno()
354
br_to.update_revisions(br_from)
355
except DivergedBranches:
356
raise BzrCommandError("These branches have diverged."
359
merge(('.', -1), ('.', old_revno), check_clean=False)
360
if location != stored_loc:
361
br_to.set_parent(location)
392
print "Using saved location: %s" % stored_loc
393
location = stored_loc
394
br_from = Branch.open(location)
396
old_rh = br_to.revision_history()
397
br_to.working_tree().pull(br_from, overwrite)
398
except DivergedBranches:
399
raise BzrCommandError("These branches have diverged."
401
if br_to.get_parent() is None or remember:
402
br_to.set_parent(location)
405
new_rh = br_to.revision_history()
408
from bzrlib.log import show_changed_revisions
409
show_changed_revisions(br_to, old_rh, new_rh)
412
class cmd_push(Command):
413
"""Push this branch into another branch.
415
The remote branch will not have its working tree populated because this
416
is both expensive, and may not be supported on the remote file system.
418
Some smart servers or protocols *may* put the working tree in place.
420
If there is no default push location set, the first push will set it.
421
After that, you can omit the location to use the default. To change the
422
default, use --remember.
424
This command only works on branches that have not diverged. Branches are
425
considered diverged if the branch being pushed to is not an older version
428
If branches have diverged, you can use 'bzr push --overwrite' to replace
429
the other branch completely.
431
If you want to ensure you have the different changes in the other branch,
432
do a merge (see bzr help merge) from the other branch, and commit that
433
before doing a 'push --overwrite'.
435
takes_options = ['remember', 'overwrite',
436
Option('create-prefix',
437
help='Create the path leading up to the branch '
438
'if it does not already exist')]
439
takes_args = ['location?']
441
def run(self, location=None, remember=False, overwrite=False,
442
create_prefix=False, verbose=False):
444
from shutil import rmtree
445
from bzrlib.transport import get_transport
447
br_from = Branch.open_containing('.')[0]
448
stored_loc = br_from.get_push_location()
450
if stored_loc is None:
451
raise BzrCommandError("No push location known or specified.")
453
print "Using saved location: %s" % stored_loc
454
location = stored_loc
456
br_to = Branch.open(location)
457
except NotBranchError:
459
transport = get_transport(location).clone('..')
460
if not create_prefix:
462
transport.mkdir(transport.relpath(location))
464
raise BzrCommandError("Parent directory of %s "
465
"does not exist." % location)
467
current = transport.base
468
needed = [(transport, transport.relpath(location))]
471
transport, relpath = needed[-1]
472
transport.mkdir(relpath)
475
new_transport = transport.clone('..')
476
needed.append((new_transport,
477
new_transport.relpath(transport.base)))
478
if new_transport.base == transport.base:
479
raise BzrCommandError("Could not creeate "
483
br_to = Branch.initialize(location)
485
old_rh = br_to.revision_history()
486
br_to.pull(br_from, overwrite)
487
except DivergedBranches:
488
raise BzrCommandError("These branches have diverged."
489
" Try a merge then push with overwrite.")
490
if br_from.get_push_location() is None or remember:
491
br_from.set_push_location(location)
494
new_rh = br_to.revision_history()
497
from bzrlib.log import show_changed_revisions
498
show_changed_revisions(br_to, old_rh, new_rh)
367
500
class cmd_branch(Command):
368
501
"""Create a new copy of a branch.
427
561
copy_branch(br_from, to_location, revision_id, basis_branch)
428
562
except bzrlib.errors.NoSuchRevision:
429
563
rmtree(to_location)
430
msg = "The branch %s has no revision %d." % (from_location, revision[0])
564
msg = "The branch %s has no revision %s." % (from_location, revision[0])
431
565
raise BzrCommandError(msg)
432
566
except bzrlib.errors.UnlistableBranch:
433
568
msg = "The branch %s cannot be used as a --basis"
569
raise BzrCommandError(msg)
571
branch = Branch.open(to_location)
572
name = StringIO(name)
573
branch.put_controlfile('branch-name', name)
438
578
class cmd_renames(Command):
439
579
"""Show list of renamed files.
441
TODO: Option to show renames between two historical versions.
443
TODO: Only show renames under dir, rather than in the whole branch.
581
# TODO: Option to show renames between two historical versions.
583
# TODO: Only show renames under dir, rather than in the whole branch.
445
584
takes_args = ['dir?']
447
587
def run(self, dir='.'):
448
b = Branch.open_containing(dir)
588
b = Branch.open_containing(dir)[0]
449
589
old_inv = b.basis_tree().inventory
450
new_inv = b.read_working_inventory()
590
new_inv = b.working_tree().read_working_inventory()
452
592
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
565
713
If files are listed, only the changes in those files are listed.
566
714
Otherwise, all changes for the tree are listed.
568
TODO: Allow diff across branches.
570
TODO: Option to use external diff command; could be GNU diff, wdiff,
573
TODO: Python difflib is not exactly the same as unidiff; should
574
either fix it up or prefer to use an external diff.
576
TODO: If a directory is given, diff everything under that.
578
TODO: Selected-file diff is inefficient and doesn't show you
581
TODO: This probably handles non-Unix newlines poorly.
721
# TODO: Allow diff across branches.
722
# TODO: Option to use external diff command; could be GNU diff, wdiff,
723
# or a graphical diff.
725
# TODO: Python difflib is not exactly the same as unidiff; should
726
# either fix it up or prefer to use an external diff.
728
# TODO: If a directory is given, diff everything under that.
730
# TODO: Selected-file diff is inefficient and doesn't show you
733
# TODO: This probably handles non-Unix newlines poorly.
589
735
takes_args = ['file*']
590
736
takes_options = ['revision', 'diff-options']
591
737
aliases = ['di', 'dif']
593
740
def run(self, revision=None, file_list=None, diff_options=None):
594
741
from bzrlib.diff import show_diff
597
b = Branch.open_containing(file_list[0])
598
file_list = [b.relpath(f) for f in file_list]
599
if file_list == ['']:
600
# just pointing to top-of-tree
603
b = Branch.open_containing('.')
743
b, file_list = inner_branch_files(file_list)
745
except FileInWrongBranch:
746
if len(file_list) != 2:
747
raise BzrCommandError("Files are in different branches")
749
b, file1 = Branch.open_containing(file_list[0])
750
b2, file2 = Branch.open_containing(file_list[1])
751
if file1 != "" or file2 != "":
752
raise BzrCommandError("Files are in different branches")
605
754
if revision is not None:
756
raise BzrCommandError("Can't specify -r with two branches")
606
757
if len(revision) == 1:
607
show_diff(b, revision[0], specific_files=file_list,
608
external_diff_options=diff_options)
758
return show_diff(b, revision[0], specific_files=file_list,
759
external_diff_options=diff_options)
609
760
elif len(revision) == 2:
610
show_diff(b, revision[0], specific_files=file_list,
611
external_diff_options=diff_options,
612
revision2=revision[1])
761
return show_diff(b, revision[0], specific_files=file_list,
762
external_diff_options=diff_options,
763
revision2=revision[1])
614
765
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
616
show_diff(b, None, specific_files=file_list,
617
external_diff_options=diff_options)
767
return show_diff(b, None, specific_files=file_list,
768
external_diff_options=diff_options, b2=b2)
622
771
class cmd_deleted(Command):
623
772
"""List files deleted in the working tree.
625
TODO: Show files deleted since a previous revision, or between two revisions.
774
# TODO: Show files deleted since a previous revision, or
775
# between two revisions.
776
# TODO: Much more efficient way to do this: read in new
777
# directories with readdir, rather than stating each one. Same
778
# level of effort but possibly much less IO. (Or possibly not,
779
# if the directories are very large...)
627
781
def run(self, show_ids=False):
628
b = Branch.open_containing('.')
782
b = Branch.open_containing('.')[0]
629
783
old = b.basis_tree()
630
784
new = b.working_tree()
632
## TODO: Much more efficient way to do this: read in new
633
## directories with readdir, rather than stating each one. Same
634
## level of effort but possibly much less IO. (Or possibly not,
635
## if the directories are very large...)
637
785
for path, ie in old.inventory.iter_entries():
638
786
if not new.has_id(ie.file_id):
680
830
The root is the nearest enclosing directory with a .bzr control
682
832
takes_args = ['filename?']
683
834
def run(self, filename=None):
684
835
"""Print the branch root."""
685
b = Branch.open_containing(filename)
836
b = Branch.open_containing(filename)[0]
689
840
class cmd_log(Command):
690
841
"""Show log of this branch.
692
To request a range of logs, you can use the command -r begin:end
693
-r revision requests a specific revision, -r :end or -r begin: are
843
To request a range of logs, you can use the command -r begin..end
844
-r revision requests a specific revision, -r ..end or -r begin.. are
696
--message allows you to give a regular expression, which will be evaluated
697
so that only matching entries will be displayed.
699
TODO: Make --revision support uuid: and hash: [future tag:] notation.
848
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
703
850
takes_args = ['filename?']
704
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
705
'long', 'message', 'short',]
851
takes_options = [Option('forward',
852
help='show from oldest to newest'),
853
'timezone', 'verbose',
854
'show-ids', 'revision',
855
Option('line', help='format with one line per revision'),
858
help='show revisions whose message matches this regexp',
860
Option('short', help='use moderately short format'),
707
863
def run(self, filename=None, timezone='original',
776
939
A more user-friendly interface is "bzr log FILE"."""
778
941
takes_args = ["filename"]
779
943
def run(self, filename):
780
b = Branch.open_containing(filename)
781
inv = b.read_working_inventory()
782
file_id = inv.path2id(b.relpath(filename))
944
b, relpath = Branch.open_containing(filename)[0]
945
inv = b.working_tree().read_working_inventory()
946
file_id = inv.path2id(relpath)
783
947
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
784
948
print "%6d %s" % (revno, what)
787
951
class cmd_ls(Command):
788
952
"""List files in a tree.
790
TODO: Take a revision or remote path and list that tree instead.
954
# TODO: Take a revision or remote path and list that tree instead.
793
def run(self, revision=None, verbose=False):
794
b = Branch.open_containing('.')
956
takes_options = ['verbose', 'revision',
957
Option('non-recursive',
958
help='don\'t recurse into sub-directories'),
960
help='Print all paths from the root of the branch.'),
961
Option('unknown', help='Print unknown files'),
962
Option('versioned', help='Print versioned files'),
963
Option('ignored', help='Print ignored files'),
965
Option('null', help='Null separate the files'),
968
def run(self, revision=None, verbose=False,
969
non_recursive=False, from_root=False,
970
unknown=False, versioned=False, ignored=False,
974
raise BzrCommandError('Cannot set both --verbose and --null')
975
all = not (unknown or versioned or ignored)
977
selection = {'I':ignored, '?':unknown, 'V':versioned}
979
b, relpath = Branch.open_containing('.')
795
984
if revision == None:
796
985
tree = b.working_tree()
798
tree = b.revision_tree(revision.in_history(b).rev_id)
987
tree = b.revision_tree(revision[0].in_history(b).rev_id)
799
988
for fp, fc, kind, fid, entry in tree.list_files():
801
kindch = entry.kind_character()
802
print '%-8s %s%s' % (fc, fp, kindch)
989
if fp.startswith(relpath):
990
fp = fp[len(relpath):]
991
if non_recursive and '/' in fp:
993
if not all and not selection[fc]:
996
kindch = entry.kind_character()
997
print '%-8s %s%s' % (fc, fp, kindch)
1000
sys.stdout.write('\0')
808
1007
class cmd_unknowns(Command):
809
1008
"""List unknown files."""
811
1011
from bzrlib.osutils import quotefn
812
for f in Branch.open_containing('.').unknowns():
1012
for f in Branch.open_containing('.')[0].unknowns():
813
1013
print quotefn(f)
983
1189
A selected-file commit may fail in some cases where the committed
984
1190
tree would be invalid, such as trying to commit a file in a
985
1191
newly-added directory that is not itself committed.
987
TODO: Run hooks on tree to-be-committed, and after commit.
989
TODO: Strict commit that fails if there are unknown or deleted files.
1193
# TODO: Run hooks on tree to-be-committed, and after commit.
1195
# TODO: Strict commit that fails if there are deleted files.
1196
# (what does "deleted files" mean ??)
1198
# TODO: Give better message for -s, --summary, used by tla people
1200
# XXX: verbose currently does nothing
991
1202
takes_args = ['selected*']
992
takes_options = ['message', 'file', 'verbose', 'unchanged']
1203
takes_options = ['message', 'verbose',
1205
help='commit even if nothing has changed'),
1206
Option('file', type=str,
1208
help='file containing commit message'),
1210
help="refuse to commit if there are unknown "
1211
"files in the working tree."),
993
1213
aliases = ['ci', 'checkin']
995
# TODO: Give better message for -s, --summary, used by tla people
997
# XXX: verbose currently does nothing
999
1215
def run(self, message=None, file=None, verbose=True, selected_list=None,
1001
from bzrlib.errors import PointlessCommit
1216
unchanged=False, strict=False):
1217
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1002
1219
from bzrlib.msgeditor import edit_commit_message
1003
1220
from bzrlib.status import show_status
1004
1221
from cStringIO import StringIO
1006
b = Branch.open_containing('.')
1008
selected_list = [b.relpath(s) for s in selected_list]
1010
if not message and not file:
1223
b, selected_list = branch_files(selected_list)
1224
if message is None and not file:
1011
1225
catcher = StringIO()
1012
1226
show_status(b, specific_files=selected_list,
1013
1227
to_file=catcher)
1014
1228
message = edit_commit_message(catcher.getvalue())
1016
1230
if message is None:
1017
1231
raise BzrCommandError("please specify a commit message"
1018
1232
" with either --message or --file")
1024
1238
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1241
raise BzrCommandError("empty commit message specified")
1028
specific_files=selected_list,
1029
allow_pointless=unchanged)
1244
b.working_tree().commit(message, specific_files=selected_list,
1245
allow_pointless=unchanged, strict=strict)
1030
1246
except PointlessCommit:
1031
1247
# FIXME: This should really happen before the file is read in;
1032
1248
# perhaps prepare the commit; get the message; then actually commit
1033
1249
raise BzrCommandError("no changes to commit",
1034
1250
["use --unchanged to commit anyhow"])
1251
except ConflictsInTree:
1252
raise BzrCommandError("Conflicts detected in working tree. "
1253
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1254
except StrictCommitFailed:
1255
raise BzrCommandError("Commit refused because there are unknown "
1256
"files in the working tree.")
1037
1259
class cmd_check(Command):
1088
1310
"""Show bzr user id."""
1089
1311
takes_options = ['email']
1091
1314
def run(self, email=False):
1093
b = bzrlib.branch.Branch.open_containing('.')
1316
b = bzrlib.branch.Branch.open_containing('.')[0]
1317
config = bzrlib.config.BranchConfig(b)
1094
1318
except NotBranchError:
1319
config = bzrlib.config.GlobalConfig()
1098
print bzrlib.osutils.user_email(b)
1100
print bzrlib.osutils.username(b)
1322
print config.user_email()
1324
print config.username()
1326
class cmd_nick(Command):
1328
Print or set the branch nickname.
1329
If unset, the tree root directory name is used as the nickname
1330
To print the current nickname, execute with no argument.
1332
takes_args = ['nickname?']
1333
def run(self, nickname=None):
1334
branch = Branch.open_containing('.')[0]
1335
if nickname is None:
1336
self.printme(branch)
1338
branch.nick = nickname
1341
def printme(self, branch):
1103
1344
class cmd_selftest(Command):
1104
"""Run internal test suite"""
1345
"""Run internal test suite.
1347
This creates temporary test directories in the working directory,
1348
but not existing data is affected. These directories are deleted
1349
if the tests pass, or left behind to help in debugging if they
1350
fail and --keep-output is specified.
1352
If arguments are given, they are regular expressions that say
1353
which tests should run.
1355
# TODO: --list should give a list of all available tests
1106
takes_options = ['verbose', 'pattern']
1107
def run(self, verbose=False, pattern=".*"):
1357
takes_args = ['testspecs*']
1358
takes_options = ['verbose',
1359
Option('one', help='stop when one test fails'),
1360
Option('keep-output',
1361
help='keep output directories when tests fail')
1364
def run(self, testspecs_list=None, verbose=False, one=False,
1108
1366
import bzrlib.ui
1109
1367
from bzrlib.selftest import selftest
1110
1368
# we don't want progress meters from the tests to go to the
1141
1406
class cmd_version(Command):
1142
1407
"""Show version of bzr."""
1146
1412
class cmd_rocks(Command):
1147
1413
"""Statement of optimism."""
1150
1417
print "it sure does!"
1153
1420
class cmd_find_merge_base(Command):
1154
1421
"""Find and print a base revision for merging two branches.
1156
TODO: Options to specify revisions on either side, as if
1157
merging only part of the history.
1423
# TODO: Options to specify revisions on either side, as if
1424
# merging only part of the history.
1159
1425
takes_args = ['branch', 'other']
1162
1429
def run(self, branch, other):
1163
1430
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1165
branch1 = Branch.open_containing(branch)
1166
branch2 = Branch.open_containing(other)
1432
branch1 = Branch.open_containing(branch)[0]
1433
branch2 = Branch.open_containing(other)[0]
1168
1435
history_1 = branch1.revision_history()
1169
1436
history_2 = branch2.revision_history()
1218
1485
--force is given.
1220
1487
takes_args = ['branch?']
1221
takes_options = ['revision', 'force', 'merge-type']
1488
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1489
Option('show-base', help="Show base revision text in "
1223
def run(self, branch='.', revision=None, force=False,
1492
def run(self, branch=None, revision=None, force=False, merge_type=None,
1493
show_base=False, reprocess=False):
1225
1494
from bzrlib.merge import merge
1226
1495
from bzrlib.merge_core import ApplyMerge3
1227
1496
if merge_type is None:
1228
1497
merge_type = ApplyMerge3
1499
branch = Branch.open_containing('.')[0].get_parent()
1501
raise BzrCommandError("No merge location known or specified.")
1503
print "Using saved location: %s" % branch
1230
1504
if revision is None or len(revision) < 1:
1231
1505
base = [None, None]
1232
1506
other = [branch, -1]
1234
1508
if len(revision) == 1:
1235
1509
base = [None, None]
1236
other = [branch, revision[0].in_history(branch).revno]
1510
other_branch = Branch.open_containing(branch)[0]
1511
revno = revision[0].in_history(other_branch).revno
1512
other = [branch, revno]
1238
1514
assert len(revision) == 2
1239
1515
if None in revision:
1240
1516
raise BzrCommandError(
1241
1517
"Merge doesn't permit that revision specifier.")
1242
b = Branch.open(branch)
1518
b = Branch.open_containing(branch)[0]
1244
1520
base = [branch, revision[0].in_history(b).revno]
1245
1521
other = [branch, revision[1].in_history(b).revno]
1248
merge(other, base, check_clean=(not force), merge_type=merge_type)
1524
conflict_count = merge(other, base, check_clean=(not force),
1525
merge_type=merge_type, reprocess=reprocess,
1526
show_base=show_base)
1527
if conflict_count != 0:
1249
1531
except bzrlib.errors.AmbiguousBase, e:
1250
1532
m = ("sorry, bzr can't determine the right merge base yet\n"
1251
1533
"candidates are:\n "
1541
class cmd_remerge(Command):
1544
takes_args = ['file*']
1545
takes_options = ['merge-type', 'reprocess',
1546
Option('show-base', help="Show base revision text in "
1549
def run(self, file_list=None, merge_type=None, show_base=False,
1551
from bzrlib.merge import merge_inner, transform_tree
1552
from bzrlib.merge_core import ApplyMerge3
1553
if merge_type is None:
1554
merge_type = ApplyMerge3
1555
b, file_list = branch_files(file_list)
1558
pending_merges = b.working_tree().pending_merges()
1559
if len(pending_merges) != 1:
1560
raise BzrCommandError("Sorry, remerge only works after normal"
1561
+ " merges. Not cherrypicking or"
1563
this_tree = b.working_tree()
1564
base_revision = common_ancestor(b.last_revision(),
1565
pending_merges[0], b)
1566
base_tree = b.revision_tree(base_revision)
1567
other_tree = b.revision_tree(pending_merges[0])
1568
interesting_ids = None
1569
if file_list is not None:
1570
interesting_ids = set()
1571
for filename in file_list:
1572
file_id = this_tree.path2id(filename)
1573
interesting_ids.add(file_id)
1574
if this_tree.kind(file_id) != "directory":
1577
for name, ie in this_tree.inventory.iter_entries(file_id):
1578
interesting_ids.add(ie.file_id)
1579
transform_tree(this_tree, b.basis_tree(), interesting_ids)
1580
if file_list is None:
1581
restore_files = list(this_tree.iter_conflicts())
1583
restore_files = file_list
1584
for filename in restore_files:
1586
restore(this_tree.abspath(filename))
1587
except NotConflicted:
1589
conflicts = merge_inner(b, other_tree, base_tree,
1590
interesting_ids = interesting_ids,
1591
other_rev_id=pending_merges[0],
1592
merge_type=merge_type,
1593
show_base=show_base,
1594
reprocess=reprocess)
1259
1602
class cmd_revert(Command):
1260
1603
"""Reverse all changes since the last commit.
1268
1611
aliases = ['merge-revert']
1270
1613
def run(self, revision=None, no_backup=False, file_list=None):
1271
from bzrlib.merge import merge
1614
from bzrlib.merge import merge_inner
1272
1615
from bzrlib.commands import parse_spec
1274
1616
if file_list is not None:
1275
1617
if len(file_list) == 0:
1276
1618
raise BzrCommandError("No files specified")
1277
1621
if revision is None:
1623
b = Branch.open_containing('.')[0]
1624
rev_id = b.last_revision()
1279
1625
elif len(revision) != 1:
1280
1626
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1282
b = Branch.open_containing('.')
1283
revno = revision[0].in_history(b).revno
1284
merge(('.', revno), parse_spec('.'),
1287
backup_files=not no_backup,
1288
file_list=file_list)
1290
Branch.open_containing('.').set_pending_merges([])
1628
b, file_list = branch_files(file_list)
1629
rev_id = revision[0].in_history(b).rev_id
1630
b.working_tree().revert(file_list, b.revision_tree(rev_id),
1293
1634
class cmd_assert_fail(Command):
1395
1747
print '\t', d.split('\n')[0]
1750
class cmd_testament(Command):
1751
"""Show testament (signing-form) of a revision."""
1752
takes_options = ['revision', 'long']
1753
takes_args = ['branch?']
1755
def run(self, branch='.', revision=None, long=False):
1756
from bzrlib.testament import Testament
1757
b = Branch.open_containing(branch)[0]
1760
if revision is None:
1761
rev_id = b.last_revision()
1763
rev_id = revision[0].in_history(b).rev_id
1764
t = Testament.from_revision(b, rev_id)
1766
sys.stdout.writelines(t.as_text_lines())
1768
sys.stdout.write(t.as_short_text())
1773
class cmd_annotate(Command):
1774
"""Show the origin of each line in a file.
1776
This prints out the given file with an annotation on the left side
1777
indicating which revision, author and date introduced the change.
1779
If the origin is the same for a run of consecutive lines, it is
1780
shown only at the top, unless the --all option is given.
1782
# TODO: annotate directories; showing when each file was last changed
1783
# TODO: annotate a previous version of a file
1784
# TODO: if the working copy is modified, show annotations on that
1785
# with new uncommitted lines marked
1786
aliases = ['blame', 'praise']
1787
takes_args = ['filename']
1788
takes_options = [Option('all', help='show annotations on all lines'),
1789
Option('long', help='show date in annotations'),
1793
def run(self, filename, all=False, long=False):
1794
from bzrlib.annotate import annotate_file
1795
b, relpath = Branch.open_containing(filename)
1798
tree = WorkingTree(b.base, b)
1799
tree = b.revision_tree(b.last_revision())
1800
file_id = tree.inventory.path2id(relpath)
1801
file_version = tree.inventory[file_id].revision
1802
annotate_file(b, file_version, file_id, long, all, sys.stdout)
1807
class cmd_re_sign(Command):
1808
"""Create a digital signature for an existing revision."""
1809
# TODO be able to replace existing ones.
1811
hidden = True # is this right ?
1812
takes_args = ['revision_id?']
1813
takes_options = ['revision']
1815
def run(self, revision_id=None, revision=None):
1816
import bzrlib.config as config
1817
import bzrlib.gpg as gpg
1818
if revision_id is not None and revision is not None:
1819
raise BzrCommandError('You can only supply one of revision_id or --revision')
1820
if revision_id is None and revision is None:
1821
raise BzrCommandError('You must supply either --revision or a revision_id')
1822
b = Branch.open_containing('.')[0]
1823
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1824
if revision_id is not None:
1825
b.sign_revision(revision_id, gpg_strategy)
1826
elif revision is not None:
1827
if len(revision) == 1:
1828
revno, rev_id = revision[0].in_history(b)
1829
b.sign_revision(rev_id, gpg_strategy)
1830
elif len(revision) == 2:
1831
# are they both on rh- if so we can walk between them
1832
# might be nice to have a range helper for arbitrary
1833
# revision paths. hmm.
1834
from_revno, from_revid = revision[0].in_history(b)
1835
to_revno, to_revid = revision[1].in_history(b)
1836
if to_revid is None:
1837
to_revno = b.revno()
1838
if from_revno is None or to_revno is None:
1839
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1840
for revno in range(from_revno, to_revno + 1):
1841
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1843
raise BzrCommandError('Please supply either one revision, or a range.')
1846
# these get imported and then picked up by the scan for cmd_*
1847
# TODO: Some more consistent way to split command definitions across files;
1848
# we do need to load at least some information about them to know of
1850
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore