32
34
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
33
35
NotBranchError, DivergedBranches, NotConflicted,
34
36
NoSuchFile, NoWorkingTree, FileInWrongBranch,
35
NotVersionedError, BadBundle)
37
NotVersionedError, NotABundle)
36
38
from bzrlib.log import show_one_log
37
39
from bzrlib.merge import Merge3Merger
38
40
from bzrlib.option import Option
39
42
from bzrlib.progress import DummyProgress, ProgressPhase
40
43
from bzrlib.revision import common_ancestor
41
44
from bzrlib.revisionspec import RevisionSpec
43
46
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
44
47
from bzrlib.transport.local import LocalTransport
49
import bzrlib.urlutils as urlutils
46
50
from bzrlib.workingtree import WorkingTree
152
156
takes_args = ['file*']
153
157
takes_options = ['all', 'show-ids', 'revision']
154
158
aliases = ['st', 'stat']
160
encoding_type = 'replace'
157
163
def run(self, all=False, show_ids=False, file_list=None, revision=None):
158
tree, file_list = tree_files(file_list)
160
164
from bzrlib.status import show_tree_status
166
tree, file_list = tree_files(file_list)
161
168
show_tree_status(tree, show_unchanged=all, show_ids=show_ids,
162
specific_files=file_list, revision=revision)
169
specific_files=file_list, revision=revision,
165
173
class cmd_cat_revision(Command):
173
181
takes_args = ['revision_id?']
174
182
takes_options = ['revision']
183
# cat-revision is more for frontends so should be exact
177
187
def run(self, revision_id=None, revision=None):
181
191
if revision_id is None and revision is None:
182
192
raise BzrCommandError('You must supply either --revision or a revision_id')
183
193
b = WorkingTree.open_containing(u'.')[0].branch
195
# TODO: jam 20060112 should cat-revision always output utf-8?
184
196
if revision_id is not None:
185
sys.stdout.write(b.repository.get_revision_xml(revision_id))
197
self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
186
198
elif revision is not None:
187
199
for rev in revision:
189
201
raise BzrCommandError('You cannot specify a NULL revision.')
190
202
revno, rev_id = rev.in_history(b)
191
sys.stdout.write(b.repository.get_revision_xml(rev_id))
203
self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
194
206
class cmd_revno(Command):
195
207
"""Show current revision number.
197
This is equal to the number of revisions on this branch."""
209
This is equal to the number of revisions on this branch.
198
212
takes_args = ['location?']
200
215
def run(self, location=u'.'):
201
print Branch.open_containing(location)[0].revno()
216
self.outf.write(str(Branch.open_containing(location)[0].revno()))
217
self.outf.write('\n')
204
220
class cmd_revision_info(Command):
250
267
Adding a file whose parent directory is not versioned will
251
268
implicitly add the parent, and so on up to the root. This means
252
you should never need to explictly add a directory, they'll just
269
you should never need to explicitly add a directory, they'll just
253
270
get added when you add a file in the directory.
255
272
--dry-run will show which files would be added, but not actually
258
275
takes_args = ['file*']
259
276
takes_options = ['no-recurse', 'dry-run', 'verbose']
277
encoding_type = 'replace'
261
279
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
262
280
import bzrlib.add
266
# This is pointless, but I'd rather not raise an error
267
action = bzrlib.add.add_action_null
269
action = bzrlib.add.add_action_print
271
action = bzrlib.add.add_action_add
273
action = bzrlib.add.add_action_add_and_print
282
action = bzrlib.add.AddAction(to_file=self.outf,
283
should_print=(not is_quiet()))
275
285
added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
286
action=action, save=not dry_run)
277
287
if len(ignored) > 0:
279
289
for glob in sorted(ignored.keys()):
280
290
for path in ignored[glob]:
281
print "ignored %s matching \"%s\"" % (path, glob)
291
self.outf.write("ignored %s matching \"%s\"\n"
284
295
for glob, paths in ignored.items():
285
296
match_len += len(paths)
286
print "ignored %d file(s)." % match_len
287
print "If you wish to add some of these files, please add them"\
297
self.outf.write("ignored %d file(s).\n" % match_len)
298
self.outf.write("If you wish to add some of these files,"
299
" please add them by name.\n")
291
302
class cmd_mkdir(Command):
294
305
This is equivalent to creating the directory and then adding it.
296
308
takes_args = ['dir+']
309
encoding_type = 'replace'
298
311
def run(self, dir_list):
299
312
for d in dir_list:
301
314
wt, dd = WorkingTree.open_containing(d)
316
self.outf.write('added %s\n' % d)
306
319
class cmd_relpath(Command):
307
320
"""Show path of a file relative to root"""
308
322
takes_args = ['filename']
312
326
def run(self, filename):
327
# TODO: jam 20050106 Can relpath return a munged path if
328
# sys.stdout encoding cannot represent it?
313
329
tree, relpath = WorkingTree.open_containing(filename)
330
self.outf.write(relpath)
331
self.outf.write('\n')
317
334
class cmd_inventory(Command):
359
378
Files cannot be moved between branches.
361
381
takes_args = ['names*']
362
382
aliases = ['move', 'rename']
383
encoding_type = 'replace'
364
385
def run(self, names_list):
365
386
if len(names_list) < 2:
369
390
if os.path.isdir(names_list[-1]):
370
391
# move into existing directory
371
392
for pair in tree.move(rel_names[:-1], rel_names[-1]):
372
print "%s => %s" % pair
393
self.outf.write("%s => %s\n" % pair)
374
395
if len(names_list) != 2:
375
396
raise BzrCommandError('to mv multiple files the destination '
376
397
'must be a versioned directory')
377
398
tree.rename_one(rel_names[0], rel_names[1])
378
print "%s => %s" % (rel_names[0], rel_names[1])
399
self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
381
402
class cmd_pull(Command):
400
421
that, you can omit the location to use the default. To change the
401
422
default, use --remember.
403
425
takes_options = ['remember', 'overwrite', 'revision', 'verbose']
404
426
takes_args = ['location?']
427
encoding_type = 'replace'
406
429
def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
407
430
# FIXME: too much stuff is in the command class
410
433
branch_to = tree_to.branch
411
434
except NoWorkingTree:
413
branch_to = Branch.open_containing(u'.')[0]
436
branch_to = Branch.open_containing(u'.')[0]
414
437
stored_loc = branch_to.get_parent()
415
438
if location is None:
416
439
if stored_loc is None:
417
440
raise BzrCommandError("No pull location known or specified.")
419
print "Using saved location: %s" % stored_loc
442
display_url = urlutils.unescape_for_display(stored_loc,
444
self.outf.write("Using saved location: %s\n" % display_url)
420
445
location = stored_loc
447
branch_from = Branch.open(location)
422
449
if branch_to.get_parent() is None or remember:
423
branch_to.set_parent(location)
425
branch_from = Branch.open(location)
450
branch_to.set_parent(branch_from.base)
427
452
if revision is None:
443
468
if old_rh != new_rh:
444
469
# Something changed
445
470
from bzrlib.log import show_changed_revisions
446
show_changed_revisions(branch_to, old_rh, new_rh)
471
show_changed_revisions(branch_to, old_rh, new_rh,
449
475
class cmd_push(Command):
470
496
After that, you can omit the location to use the default. To change the
471
497
default, use --remember.
473
takes_options = ['remember', 'overwrite',
500
takes_options = ['remember', 'overwrite', 'verbose',
474
501
Option('create-prefix',
475
502
help='Create the path leading up to the branch '
476
503
'if it does not already exist')]
477
504
takes_args = ['location?']
505
encoding_type = 'replace'
479
507
def run(self, location=None, remember=False, overwrite=False,
480
508
create_prefix=False, verbose=False):
488
516
if stored_loc is None:
489
517
raise BzrCommandError("No push location known or specified.")
491
print "Using saved location: %s" % stored_loc
519
display_url = urlutils.unescape_for_display(stored_loc,
521
self.outf.write("Using saved location: %s" % display_url)
492
522
location = stored_loc
524
transport = get_transport(location)
525
location_url = transport.base
493
526
if br_from.get_push_location() is None or remember:
494
br_from.set_push_location(location)
527
br_from.set_push_location(location_url)
496
dir_to = bzrlib.bzrdir.BzrDir.open(location)
531
dir_to = bzrlib.bzrdir.BzrDir.open(location_url)
497
532
br_to = dir_to.open_branch()
498
533
except NotBranchError:
499
534
# create a branch.
500
transport = get_transport(location).clone('..')
535
transport = transport.clone('..')
501
536
if not create_prefix:
503
transport.mkdir(transport.relpath(location))
538
relurl = transport.relpath(location_url)
539
mutter('creating directory %s => %s', location_url, relurl)
540
transport.mkdir(relurl)
504
541
except NoSuchFile:
505
542
raise BzrCommandError("Parent directory of %s "
506
543
"does not exist." % location)
508
545
current = transport.base
509
needed = [(transport, transport.relpath(location))]
546
needed = [(transport, transport.relpath(location_url))]
512
549
transport, relpath = needed[-1]
519
556
if new_transport.base == transport.base:
520
557
raise BzrCommandError("Could not create "
522
dir_to = br_from.bzrdir.clone(location,
559
dir_to = br_from.bzrdir.clone(location_url,
523
560
revision_id=br_from.last_revision())
524
561
br_to = dir_to.open_branch()
525
562
count = len(br_to.revision_history())
546
583
if old_rh != new_rh:
547
584
# Something changed
548
585
from bzrlib.log import show_changed_revisions
549
show_changed_revisions(br_to, old_rh, new_rh)
586
show_changed_revisions(br_to, old_rh, new_rh,
552
590
class cmd_branch(Command):
567
605
aliases = ['get', 'clone']
569
607
def run(self, from_location, to_location=None, revision=None, basis=None):
608
from bzrlib.transport import get_transport
570
609
from bzrlib.osutils import rmtree
571
610
if revision is None:
572
611
revision = [None]
601
640
name = os.path.basename(to_location) + '\n'
642
to_transport = get_transport(to_location)
603
os.mkdir(to_location)
605
if e.errno == errno.EEXIST:
606
raise BzrCommandError('Target directory "%s" already'
607
' exists.' % to_location)
608
if e.errno == errno.ENOENT:
609
raise BzrCommandError('Parent of "%s" does not exist.' %
644
to_transport.mkdir('.')
645
except bzrlib.errors.FileExists:
646
raise BzrCommandError('Target directory "%s" already'
647
' exists.' % to_location)
648
except bzrlib.errors.NoSuchFile:
649
raise BzrCommandError('Parent of "%s" does not exist.' %
614
652
# preserve whatever source format we have.
615
dir = br_from.bzrdir.sprout(to_location, revision_id, basis_dir)
653
dir = br_from.bzrdir.sprout(to_transport.base,
654
revision_id, basis_dir)
616
655
branch = dir.open_branch()
617
656
except bzrlib.errors.NoSuchRevision:
657
to_transport.delete_tree('.')
619
658
msg = "The branch %s has no revision %s." % (from_location, revision[0])
620
659
raise BzrCommandError(msg)
621
660
except bzrlib.errors.UnlistableBranch:
735
773
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
737
775
for old_name, new_name in renames:
738
print "%s => %s" % (old_name, new_name)
776
self.outf.write("%s => %s\n" % (old_name, new_name))
741
779
class cmd_update(Command):
804
842
takes_args = ['file*']
805
843
takes_options = ['verbose', Option('new', help='remove newly-added files')]
845
encoding_type = 'replace'
808
847
def run(self, file_list, verbose=False, new=False):
809
848
tree, file_list = tree_files(file_list)
818
857
file_list = sorted([f[0] for f in added[0]], reverse=True)
819
858
if len(file_list) == 0:
820
859
raise BzrCommandError('No matching files.')
821
tree.remove(file_list, verbose=verbose)
860
tree.remove(file_list, verbose=verbose, to_file=self.outf)
824
863
class cmd_file_id(Command):
838
879
raise BzrError("%r is not a versioned file" % filename)
881
self.outf.write(i + '\n')
843
884
class cmd_file_path(Command):
844
885
"""Print path of file_ids to a file or directory.
846
887
This prints one line for each directory down to the target,
847
starting at the branch root."""
888
starting at the branch root.
849
892
takes_args = ['filename']
851
895
def run(self, filename):
852
896
tree, relpath = WorkingTree.open_containing(filename)
887
931
class cmd_revision_history(Command):
888
932
"""Display list of revision ids on this branch."""
892
937
branch = WorkingTree.open_containing(u'.')[0].branch
893
938
for patchid in branch.revision_history():
939
self.outf.write(patchid)
940
self.outf.write('\n')
897
943
class cmd_ancestry(Command):
898
944
"""List all revisions merged into this branch."""
902
949
tree = WorkingTree.open_containing(u'.')[0]
1037
1084
takes_args = ['file*']
1038
1085
takes_options = ['revision', 'diff-options', 'prefix']
1039
1086
aliases = ['di', 'dif']
1087
encoding_type = 'exact'
1041
1089
@display_command
1042
1090
def run(self, revision=None, file_list=None, diff_options=None,
1103
1151
# directories with readdir, rather than stating each one. Same
1104
1152
# level of effort but possibly much less IO. (Or possibly not,
1105
1153
# if the directories are very large...)
1154
takes_options = ['show-ids']
1106
1156
@display_command
1107
1157
def run(self, show_ids=False):
1108
1158
tree = WorkingTree.open_containing(u'.')[0]
1109
1159
old = tree.basis_tree()
1110
1160
for path, ie in old.inventory.iter_entries():
1111
1161
if not tree.has_id(ie.file_id):
1162
self.outf.write(path)
1113
print '%-50s %s' % (path, ie.file_id)
1164
self.outf.write(' ')
1165
self.outf.write(ie.file_id)
1166
self.outf.write('\n')
1118
1169
class cmd_modified(Command):
1126
1177
td = compare_trees(tree.basis_tree(), tree)
1128
1179
for path, id, kind, text_modified, meta_modified in td.modified:
1180
self.outf.write(path + '\n')
1133
1183
class cmd_added(Command):
1144
1194
path = inv.id2path(file_id)
1145
1195
if not os.access(bzrlib.osutils.abspath(path), os.F_OK):
1197
self.outf.write(path + '\n')
1151
1200
class cmd_root(Command):
1152
1201
"""Show the tree root directory.
1158
1207
def run(self, filename=None):
1159
1208
"""Print the branch root."""
1160
1209
tree = WorkingTree.open_containing(filename)[0]
1210
self.outf.write(tree.basedir + '\n')
1164
1213
class cmd_log(Command):
1206
1257
from bzrlib.log import log_formatter, show_log
1208
1258
assert message is None or isinstance(message, basestring), \
1209
1259
"invalid message argument %r" % message
1210
1260
direction = (forward and 'forward') or 'reverse'
1256
1306
if rev1 > rev2:
1257
1307
(rev2, rev1) = (rev1, rev2)
1259
mutter('encoding log as %r', bzrlib.user_encoding)
1261
# use 'replace' so that we don't abort if trying to write out
1262
# in e.g. the default C locale.
1263
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1265
1309
if (log_format == None):
1266
1310
default = bzrlib.config.BranchConfig(b).log_format()
1267
1311
log_format = get_log_format(long=long, short=short, line=line, default=default)
1269
1312
lf = log_formatter(log_format,
1270
1313
show_ids=show_ids,
1272
1315
show_timezone=timezone)
1295
1338
class cmd_touching_revisions(Command):
1296
1339
"""Return revision-ids which affected a particular file.
1298
A more user-friendly interface is "bzr log FILE"."""
1341
A more user-friendly interface is "bzr log FILE".
1300
1345
takes_args = ["filename"]
1301
1347
@display_command
1302
1348
def run(self, filename):
1303
1349
tree, relpath = WorkingTree.open_containing(filename)
1305
1351
inv = tree.read_working_inventory()
1306
1352
file_id = inv.path2id(relpath)
1307
1353
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1308
print "%6d %s" % (revno, what)
1354
self.outf.write("%6d %s\n" % (revno, what))
1311
1357
class cmd_ls(Command):
1359
1405
kindch = entry.kind_character()
1360
print '%-8s %s%s' % (fc, fp, kindch)
1406
self.outf.write('%-8s %s%s\n' % (fc, fp, kindch))
1362
sys.stdout.write(fp)
1363
sys.stdout.write('\0')
1408
self.outf.write(fp + '\0')
1411
self.outf.write(fp + '\n')
1369
1414
class cmd_unknowns(Command):
1595
1640
from bzrlib.msgeditor import edit_commit_message, \
1596
1641
make_commit_message_template
1597
1642
from tempfile import TemporaryFile
1600
1644
# TODO: Need a blackbox test for invoking the external editor; may be
1601
1645
# slightly problematic to run this cross-platform.
2021
2064
if e.errno not in (errno.ENOENT, errno.EISDIR):
2026
2069
if reader is not None:
2027
2070
conflicts = merge_bundle(reader, tree, not force, merge_type,
2034
stored_loc = tree.branch.get_parent()
2036
if stored_loc is None:
2037
raise BzrCommandError("No merge branch known or specified.")
2039
print "Using saved branch: %s" % stored_loc
2042
if tree.branch.get_parent() is None or remember:
2043
tree.branch.set_parent(branch)
2077
branch = self._get_remembered_parent(tree, branch, 'Merging from')
2045
2079
if revision is None or len(revision) < 1:
2046
2080
base = [None, None]
2057
2091
if None in revision:
2058
2092
raise BzrCommandError(
2059
2093
"Merge doesn't permit that revision specifier.")
2060
b, path = Branch.open_containing(branch)
2062
base = [branch, revision[0].in_history(b).revno]
2063
other = [branch, revision[1].in_history(b).revno]
2094
other_branch, path = Branch.open_containing(branch)
2096
base = [branch, revision[0].in_history(other_branch).revno]
2097
other = [branch, revision[1].in_history(other_branch).revno]
2099
if tree.branch.get_parent() is None or remember:
2100
tree.branch.set_parent(other_branch.base)
2065
2103
interesting_files = [path]
2088
2126
"and (if you want) report this to the bzr developers\n")
2129
# TODO: move up to common parent; this isn't merge-specific anymore.
2130
def _get_remembered_parent(self, tree, supplied_location, verb_string):
2131
"""Use tree.branch's parent if none was supplied.
2133
Report if the remembered location was used.
2135
if supplied_location is not None:
2136
return supplied_location
2137
stored_location = tree.branch.get_parent()
2138
mutter("%s", stored_location)
2139
if stored_location is None:
2140
raise BzrCommandError("No location specified or remembered")
2141
display_url = urlutils.unescape_for_display(stored_location, self.outf.encoding)
2142
self.outf.write("%s remembered location %s\n" % (verb_string, display_url))
2143
return stored_location
2092
2146
class cmd_remerge(Command):
2093
2147
"""Redo a merge.
2518
2572
# TODO: jam 20060108 Add an option to allow uncommit to remove
2519
# unreferenced information in 'branch-as-repostory' branches.
2573
# unreferenced information in 'branch-as-repository' branches.
2520
2574
# TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
2521
2575
# information in shared branches as well.
2522
2576
takes_options = ['verbose', 'revision',