166
693
are added. This search proceeds recursively into versioned
167
694
directories. If no names are given '.' is assumed.
169
Therefore simply saying 'bzr add' will version all files that
696
A warning will be printed when nested trees are encountered,
697
unless they are explicitly ignored.
699
Therefore simply saying 'brz add' will version all files that
170
700
are currently unknown.
172
702
Adding a file whose parent directory is not versioned will
173
703
implicitly add the parent, and so on up to the root. This means
174
you should never need to explictly add a directory, they'll just
704
you should never need to explicitly add a directory, they'll just
175
705
get added when you add a file in the directory.
707
--dry-run will show which files would be added, but not actually
710
--file-ids-from will try to use the file ids from the supplied path.
711
It looks up ids trying to find a matching parent directory with the
712
same filename, and then by pure path. This option is rarely needed
713
but can be useful when adding the same logical file into two
714
branches that will be merged later (without showing the two different
715
adds as a conflict). It is also useful when merging another project
716
into a subdirectory of this one.
718
Any files matching patterns in the ignore list will not be added
719
unless they are explicitly mentioned.
721
In recursive mode, files larger than the configuration option
722
add.maximum_file_size will be skipped. Named items are never skipped due
177
725
takes_args = ['file*']
178
takes_options = ['verbose', 'no-recurse']
180
def run(self, file_list, verbose=False, no_recurse=False):
181
# verbose currently has no effect
182
from bzrlib.add import smart_add, add_reporter_print
183
smart_add(file_list, not no_recurse, add_reporter_print)
728
help="Don't recursively add the contents of directories.",
731
help="Show what would be done, but don't actually do "
734
Option('file-ids-from',
736
help='Lookup file ids from this tree.'),
738
encoding_type = 'replace'
739
_see_also = ['remove', 'ignore']
741
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
744
tree, file_list = tree_files_for_add(file_list)
746
if file_ids_from is not None and not tree.supports_setting_file_ids():
748
gettext('Ignoring --file-ids-from, since the tree does not '
749
'support setting file ids.'))
753
if file_ids_from is not None:
755
base_tree, base_path = WorkingTree.open_containing(
757
except errors.NoWorkingTree:
758
base_branch, base_path = Branch.open_containing(
760
base_tree = base_branch.basis_tree()
762
action = breezy.add.AddFromBaseAction(
763
base_tree, base_path, to_file=self.outf,
764
should_print=(not is_quiet()))
766
action = breezy.add.AddWithSkipLargeAction(
767
to_file=self.outf, should_print=(not is_quiet()))
770
self.enter_context(base_tree.lock_read())
771
added, ignored = tree.smart_add(
772
file_list, not no_recurse, action=action, save=not dry_run)
776
for glob in sorted(ignored):
777
for path in ignored[glob]:
779
gettext("ignored {0} matching \"{1}\"\n").format(
187
783
class cmd_mkdir(Command):
188
"""Create a new versioned directory.
784
__doc__ = """Create a new versioned directory.
190
786
This is equivalent to creating the directory and then adding it.
192
789
takes_args = ['dir+']
194
def run(self, dir_list):
200
b = Branch.open_containing(d)
793
help='No error if existing, make parent directories as needed.',
797
encoding_type = 'replace'
800
def add_file_with_parents(cls, wt, relpath):
801
if wt.is_versioned(relpath):
803
cls.add_file_with_parents(wt, osutils.dirname(relpath))
807
def add_file_single(cls, wt, relpath):
810
def run(self, dir_list, parents=False):
812
add_file = self.add_file_with_parents
814
add_file = self.add_file_single
816
wt, relpath = WorkingTree.open_containing(dir)
821
if e.errno != errno.EEXIST:
825
add_file(wt, relpath)
827
self.outf.write(gettext('added %s\n') % dir)
205
830
class cmd_relpath(Command):
206
"""Show path of a file relative to root"""
831
__doc__ = """Show path of a file relative to root"""
207
833
takes_args = ['filename']
210
837
def run(self, filename):
211
print Branch.open_containing(filename).relpath(filename)
838
# TODO: jam 20050106 Can relpath return a munged path if
839
# sys.stdout encoding cannot represent it?
840
tree, relpath = WorkingTree.open_containing(filename)
841
self.outf.write(relpath)
842
self.outf.write('\n')
215
845
class cmd_inventory(Command):
216
"""Show inventory of the current working copy or a revision."""
217
takes_options = ['revision', 'show-ids']
219
def run(self, revision=None, show_ids=False):
220
b = Branch.open_containing('.')
222
inv = b.read_working_inventory()
224
if len(revision) > 1:
225
raise BzrCommandError('bzr inventory --revision takes'
226
' exactly one revision identifier')
227
inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
229
for path, entry in inv.entries():
846
__doc__ = """Show inventory of the current working copy or a revision.
848
It is possible to limit the output to a particular entry
849
type using the --kind option. For example: --kind file.
851
It is also possible to restrict the list of files to a specific
852
set. For example: brz inventory --show-ids this/file
860
Option('include-root',
861
help='Include the entry for the root of the tree, if any.'),
863
help='List entries of a particular kind: file, directory, '
867
takes_args = ['file*']
870
def run(self, revision=None, show_ids=False, kind=None, include_root=False,
872
if kind and kind not in ['file', 'directory', 'symlink']:
873
raise errors.CommandError(
874
gettext('invalid kind %r specified') % (kind,))
876
revision = _get_one_revision('inventory', revision)
877
work_tree, file_list = WorkingTree.open_containing_paths(file_list)
878
self.enter_context(work_tree.lock_read())
879
if revision is not None:
880
tree = revision.as_tree(work_tree.branch)
882
extra_trees = [work_tree]
883
self.enter_context(tree.lock_read())
888
self.enter_context(tree.lock_read())
889
if file_list is not None:
890
paths = tree.find_related_paths_across_trees(
891
file_list, extra_trees, require_versioned=True)
892
# find_ids_across_trees may include some paths that don't
894
entries = tree.iter_entries_by_dir(specific_files=paths)
896
entries = tree.iter_entries_by_dir()
898
for path, entry in sorted(entries):
899
if kind and kind != entry.kind:
901
if path == "" and not include_root:
231
print '%-50s %s' % (path, entry.file_id)
904
self.outf.write('%-50s %s\n' % (
905
path, entry.file_id.decode('utf-8')))
236
class cmd_move(Command):
237
"""Move files to a different directory.
242
The destination must be a versioned directory in the same branch.
907
self.outf.write(path)
908
self.outf.write('\n')
911
class cmd_cp(Command):
912
__doc__ = """Copy a file.
915
brz cp OLDNAME NEWNAME
917
brz cp SOURCE... DESTINATION
919
If the last argument is a versioned directory, all the other names
920
are copied into it. Otherwise, there must be exactly two arguments
921
and the file is copied to a new name.
923
Files cannot be copied between branches. Only files can be copied
244
takes_args = ['source$', 'dest']
245
def run(self, source_list, dest):
246
b = Branch.open_containing('.')
248
# TODO: glob expansion on windows?
249
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
252
class cmd_rename(Command):
253
"""Change the name of an entry.
256
bzr rename frob.c frobber.c
257
bzr rename src/frob.c lib/frob.c
259
It is an error if the destination name exists.
261
See also the 'move' command, which moves files into a different
262
directory without changing their name.
264
TODO: Some way to rename multiple files without invoking bzr for each
266
takes_args = ['from_name', 'to_name']
268
def run(self, from_name, to_name):
269
b = Branch.open_containing('.')
270
b.rename_one(b.relpath(from_name), b.relpath(to_name))
927
takes_args = ['names*']
930
encoding_type = 'replace'
932
def run(self, names_list):
933
if names_list is None:
935
if len(names_list) < 2:
936
raise errors.CommandError(gettext("missing file argument"))
937
tree, rel_names = WorkingTree.open_containing_paths(
938
names_list, canonicalize=False)
939
for file_name in rel_names[0:-1]:
941
raise errors.CommandError(
942
gettext("can not copy root of branch"))
943
self.enter_context(tree.lock_tree_write())
944
into_existing = osutils.isdir(names_list[-1])
945
if not into_existing:
947
(src, dst) = rel_names
949
raise errors.CommandError(
950
gettext('to copy multiple files the'
951
' destination must be a versioned'
956
(n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
957
for n in rel_names[:-1]]
959
for src, dst in pairs:
961
src_kind = tree.stored_kind(src)
962
except errors.NoSuchFile:
963
raise errors.CommandError(
964
gettext('Could not copy %s => %s: %s is not versioned.')
967
raise errors.CommandError(
968
gettext('Could not copy %s => %s . %s is not versioned\\.'
970
if src_kind == 'directory':
971
raise errors.CommandError(
972
gettext('Could not copy %s => %s . %s is a directory.'
974
dst_parent = osutils.split(dst)[0]
977
dst_parent_kind = tree.stored_kind(dst_parent)
978
except errors.NoSuchFile:
979
raise errors.CommandError(
980
gettext('Could not copy %s => %s: %s is not versioned.')
981
% (src, dst, dst_parent))
982
if dst_parent_kind != 'directory':
983
raise errors.CommandError(
984
gettext('Could not copy to %s: %s is not a directory.')
985
% (dst_parent, dst_parent))
987
tree.copy_one(src, dst)
274
990
class cmd_mv(Command):
275
"""Move or rename a file.
278
bzr mv OLDNAME NEWNAME
279
bzr mv SOURCE... DESTINATION
991
__doc__ = """Move or rename a file.
994
brz mv OLDNAME NEWNAME
996
brz mv SOURCE... DESTINATION
281
998
If the last argument is a versioned directory, all the other names
282
999
are moved into it. Otherwise, there must be exactly two arguments
283
and the file is changed to a new name, which must not already exist.
1000
and the file is changed to a new name.
1002
If OLDNAME does not exist on the filesystem but is versioned and
1003
NEWNAME does exist on the filesystem but is not versioned, mv
1004
assumes that the file has been manually moved and only updates
1005
its internal inventory to reflect that change.
1006
The same is valid when moving many SOURCE files to a DESTINATION.
285
1008
Files cannot be moved between branches.
287
1011
takes_args = ['names*']
288
def run(self, names_list):
1012
takes_options = [Option("after", help="Move only the brz identifier"
1013
" of the file, because the file has already been moved."),
1014
Option('auto', help='Automatically guess renames.'),
1016
'dry-run', help='Avoid making changes when guessing renames.'),
1018
aliases = ['move', 'rename']
1019
encoding_type = 'replace'
1021
def run(self, names_list, after=False, auto=False, dry_run=False):
1023
return self.run_auto(names_list, after, dry_run)
1025
raise errors.CommandError(gettext('--dry-run requires --auto.'))
1026
if names_list is None:
289
1028
if len(names_list) < 2:
290
raise BzrCommandError("missing file argument")
291
b = Branch.open_containing(names_list[0])
293
rel_names = [b.relpath(x) for x in names_list]
295
if os.path.isdir(names_list[-1]):
1029
raise errors.CommandError(gettext("missing file argument"))
1030
tree, rel_names = WorkingTree.open_containing_paths(
1031
names_list, canonicalize=False)
1032
for file_name in rel_names[0:-1]:
1034
raise errors.CommandError(
1035
gettext("can not move root of branch"))
1036
self.enter_context(tree.lock_tree_write())
1037
self._run(tree, names_list, rel_names, after)
1039
def run_auto(self, names_list, after, dry_run):
1040
if names_list is not None and len(names_list) > 1:
1041
raise errors.CommandError(
1042
gettext('Only one path may be specified to --auto.'))
1044
raise errors.CommandError(
1045
gettext('--after cannot be specified with --auto.'))
1046
work_tree, file_list = WorkingTree.open_containing_paths(
1047
names_list, default_directory='.')
1048
self.enter_context(work_tree.lock_tree_write())
1049
rename_map.RenameMap.guess_renames(
1050
work_tree.basis_tree(), work_tree, dry_run)
1052
def _run(self, tree, names_list, rel_names, after):
1053
into_existing = osutils.isdir(names_list[-1])
1054
if into_existing and len(names_list) == 2:
1056
# a. case-insensitive filesystem and change case of dir
1057
# b. move directory after the fact (if the source used to be
1058
# a directory, but now doesn't exist in the working tree
1059
# and the target is an existing directory, just rename it)
1060
if (not tree.case_sensitive
1061
and rel_names[0].lower() == rel_names[1].lower()):
1062
into_existing = False
1064
# 'fix' the case of a potential 'from'
1065
from_path = tree.get_canonical_path(rel_names[0])
1066
if (not osutils.lexists(names_list[0]) and
1067
tree.is_versioned(from_path) and
1068
tree.stored_kind(from_path) == "directory"):
1069
into_existing = False
296
1072
# move into existing directory
297
for pair in b.move(rel_names[:-1], rel_names[-1]):
298
print "%s => %s" % pair
1073
# All entries reference existing inventory items, so fix them up
1074
# for cicp file-systems.
1075
rel_names = list(tree.get_canonical_paths(rel_names))
1076
for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
1078
self.outf.write("%s => %s\n" % (src, dest))
300
1080
if len(names_list) != 2:
301
raise BzrCommandError('to mv multiple files the destination '
302
'must be a versioned directory')
303
b.rename_one(rel_names[0], rel_names[1])
304
print "%s => %s" % (rel_names[0], rel_names[1])
1081
raise errors.CommandError(gettext('to mv multiple files the'
1082
' destination must be a versioned'
1085
# for cicp file-systems: the src references an existing inventory
1087
src = tree.get_canonical_path(rel_names[0])
1088
# Find the canonical version of the destination: In all cases, the
1089
# parent of the target must be in the inventory, so we fetch the
1090
# canonical version from there (we do not always *use* the
1091
# canonicalized tail portion - we may be attempting to rename the
1093
canon_dest = tree.get_canonical_path(rel_names[1])
1094
dest_parent = osutils.dirname(canon_dest)
1095
spec_tail = osutils.basename(rel_names[1])
1096
# For a CICP file-system, we need to avoid creating 2 inventory
1097
# entries that differ only by case. So regardless of the case
1098
# we *want* to use (ie, specified by the user or the file-system),
1099
# we must always choose to use the case of any existing inventory
1100
# items. The only exception to this is when we are attempting a
1101
# case-only rename (ie, canonical versions of src and dest are
1103
dest_id = tree.path2id(canon_dest)
1104
if dest_id is None or tree.path2id(src) == dest_id:
1105
# No existing item we care about, so work out what case we
1106
# are actually going to use.
1108
# If 'after' is specified, the tail must refer to a file on disk.
1110
dest_parent_fq = osutils.pathjoin(
1111
tree.basedir, dest_parent)
1113
# pathjoin with an empty tail adds a slash, which breaks
1115
dest_parent_fq = tree.basedir
1117
dest_tail = osutils.canonical_relpath(
1119
osutils.pathjoin(dest_parent_fq, spec_tail))
1121
# not 'after', so case as specified is used
1122
dest_tail = spec_tail
1124
# Use the existing item so 'mv' fails with AlreadyVersioned.
1125
dest_tail = os.path.basename(canon_dest)
1126
dest = osutils.pathjoin(dest_parent, dest_tail)
1127
mutter("attempting to move %s => %s", src, dest)
1128
tree.rename_one(src, dest, after=after)
1130
self.outf.write("%s => %s\n" % (src, dest))
309
1133
class cmd_pull(Command):
310
"""Pull any changes from another branch into the current one.
312
If the location is omitted, the last-used location will be used.
313
Both the revision history and the working directory will be
1134
__doc__ = """Turn this branch into a mirror of another branch.
1136
By default, this command only works on branches that have not diverged.
1137
Branches are considered diverged if the destination branch's most recent
1138
commit is one that has not been merged (directly or indirectly) into the
1141
If branches have diverged, you can use 'brz merge' to integrate the changes
1142
from one into the other. Once one branch has merged, the other should
1143
be able to pull it again.
1145
If you want to replace your local changes and just want your branch to
1146
match the remote one, use pull --overwrite. This will work even if the two
1147
branches have diverged.
1149
If there is no default location set, the first pull will set it (use
1150
--no-remember to avoid setting it). After that, you can omit the
1151
location to use the default. To change the default, use --remember. The
1152
value will only be saved if the remote location can be accessed.
1154
The --verbose option will display the revisions pulled using the log_format
1155
configuration option. You can use a different format by overriding it with
1156
-Olog_format=<other_format>.
1158
Note: The location can be specified either in the form of a branch,
1159
or in the form of a path to a file containing a merge directive generated
1163
_see_also = ['push', 'update', 'status-flags', 'send']
1164
takes_options = ['remember', 'overwrite', 'revision',
1165
custom_help('verbose',
1166
help='Show logs of pulled revisions.'),
1167
custom_help('directory',
1168
help='Branch to pull into, '
1169
'rather than the one containing the working directory.'),
1171
help="Perform a local pull in a bound "
1172
"branch. Local pulls are not applied to "
1173
"the master branch."
1176
help="Show base revision text in conflicts."),
1177
Option('overwrite-tags',
1178
help="Overwrite tags only."),
1180
takes_args = ['location?']
1181
encoding_type = 'replace'
1183
def run(self, location=None, remember=None, overwrite=False,
1184
revision=None, verbose=False,
1185
directory=None, local=False,
1186
show_base=False, overwrite_tags=False):
1189
overwrite = ["history", "tags"]
1190
elif overwrite_tags:
1191
overwrite = ["tags"]
1194
# FIXME: too much stuff is in the command class
1197
if directory is None:
1200
tree_to = WorkingTree.open_containing(directory)[0]
1201
branch_to = tree_to.branch
1202
self.enter_context(tree_to.lock_write())
1203
except errors.NoWorkingTree:
1205
branch_to = Branch.open_containing(directory)[0]
1206
self.enter_context(branch_to.lock_write())
1208
warning(gettext("No working tree, ignoring --show-base"))
1210
if local and not branch_to.get_bound_location():
1211
raise errors.LocalRequiresBoundBranch()
1213
possible_transports = []
1214
if location is not None:
1216
mergeable = _mod_mergeable.read_mergeable_from_url(
1217
location, possible_transports=possible_transports)
1218
except errors.NotABundle:
1221
stored_loc = branch_to.get_parent()
1222
if location is None:
1223
if stored_loc is None:
1224
raise errors.CommandError(gettext("No pull location known or"
1227
display_url = urlutils.unescape_for_display(stored_loc,
1231
gettext("Using saved parent location: %s\n") % display_url)
1232
location = stored_loc
1234
revision = _get_one_revision('pull', revision)
1235
if mergeable is not None:
1236
if revision is not None:
1237
raise errors.CommandError(gettext(
1238
'Cannot use -r with merge directives or bundles'))
1239
mergeable.install_revisions(branch_to.repository)
1240
base_revision_id, revision_id, verified = \
1241
mergeable.get_merge_request(branch_to.repository)
1242
branch_from = branch_to
1244
branch_from = Branch.open(location,
1245
possible_transports=possible_transports)
1246
self.enter_context(branch_from.lock_read())
1247
# Remembers if asked explicitly or no previous location is set
1249
or (remember is None and branch_to.get_parent() is None)):
1250
# FIXME: This shouldn't be done before the pull
1251
# succeeds... -- vila 2012-01-02
1252
branch_to.set_parent(branch_from.base)
1254
if revision is not None:
1255
revision_id = revision.as_revision_id(branch_from)
1257
if tree_to is not None:
1258
view_info = _get_view_info_for_change_reporter(tree_to)
1259
change_reporter = delta._ChangeReporter(
1260
unversioned_filter=tree_to.is_ignored,
1261
view_info=view_info)
1262
result = tree_to.pull(
1263
branch_from, overwrite, revision_id, change_reporter,
1264
local=local, show_base=show_base)
1266
result = branch_to.pull(
1267
branch_from, overwrite, revision_id, local=local)
1269
result.report(self.outf)
1270
if verbose and result.old_revid != result.new_revid:
1271
log.show_branch_change(
1272
branch_to, self.outf, result.old_revno,
1274
if getattr(result, 'tag_conflicts', None):
1280
class cmd_push(Command):
1281
__doc__ = """Update a mirror of this branch.
1283
The target branch will not have its working tree populated because this
1284
is both expensive, and is not supported on remote file systems.
1286
Some smart servers or protocols *may* put the working tree in place in
316
1289
This command only works on branches that have not diverged. Branches are
317
considered diverged if both branches have had commits without first
318
pulling from the other.
320
If branches have diverged, you can use 'bzr merge' to pull the text changes
321
from one into the other.
1290
considered diverged if the destination branch's most recent commit is one
1291
that has not been merged (directly or indirectly) by the source branch.
1293
If branches have diverged, you can use 'brz push --overwrite' to replace
1294
the other branch completely, discarding its unmerged changes.
1296
If you want to ensure you have the different changes in the other branch,
1297
do a merge (see brz help merge) from the other branch, and commit that.
1298
After that you will be able to do a push without '--overwrite'.
1300
If there is no default push location set, the first push will set it (use
1301
--no-remember to avoid setting it). After that, you can omit the
1302
location to use the default. To change the default, use --remember. The
1303
value will only be saved if the remote location can be accessed.
1305
The --verbose option will display the revisions pushed using the log_format
1306
configuration option. You can use a different format by overriding it with
1307
-Olog_format=<other_format>.
1310
_see_also = ['pull', 'update', 'working-trees']
1311
takes_options = ['remember', 'overwrite', 'verbose', 'revision',
1312
Option('create-prefix',
1313
help='Create the path leading up to the branch '
1314
'if it does not already exist.'),
1315
custom_help('directory',
1316
help='Branch to push from, '
1317
'rather than the one containing the working directory.'),
1318
Option('use-existing-dir',
1319
help='By default push will fail if the target'
1320
' directory exists, but does not already'
1321
' have a control directory. This flag will'
1322
' allow push to proceed.'),
1324
help='Create a stacked branch that references the public location '
1325
'of the parent branch.'),
1326
Option('stacked-on',
1327
help='Create a stacked branch that refers to another branch '
1328
'for the commit history. Only the work not present in the '
1329
'referenced branch is included in the branch created.',
1332
help='Refuse to push if there are uncommitted changes in'
1333
' the working tree, --no-strict disables the check.'),
1335
help="Don't populate the working tree, even for protocols"
1336
" that support it."),
1337
Option('overwrite-tags',
1338
help="Overwrite tags only."),
1339
Option('lossy', help="Allow lossy push, i.e. dropping metadata "
1340
"that can't be represented in the target.")
323
1342
takes_args = ['location?']
325
def run(self, location=None):
326
from bzrlib.merge import merge
328
from shutil import rmtree
331
br_to = Branch.open_containing('.')
332
stored_loc = br_to.get_parent()
1343
encoding_type = 'replace'
1345
def run(self, location=None, remember=None, overwrite=False,
1346
create_prefix=False, verbose=False, revision=None,
1347
use_existing_dir=False, directory=None, stacked_on=None,
1348
stacked=False, strict=None, no_tree=False,
1349
overwrite_tags=False, lossy=False):
1350
from .location import location_to_url
1351
from .push import _show_push_branch
1354
overwrite = ["history", "tags"]
1355
elif overwrite_tags:
1356
overwrite = ["tags"]
1360
if directory is None:
1362
# Get the source branch
1364
_unused) = controldir.ControlDir.open_containing_tree_or_branch(directory)
1365
# Get the tip's revision_id
1366
revision = _get_one_revision('push', revision)
1367
if revision is not None:
1368
revision_id = revision.in_history(br_from).rev_id
1371
if tree is not None and revision_id is None:
1372
tree.check_changed_or_out_of_date(
1373
strict, 'push_strict',
1374
more_error='Use --no-strict to force the push.',
1375
more_warning='Uncommitted changes will not be pushed.')
1376
# Get the stacked_on branch, if any
1377
if stacked_on is not None:
1378
stacked_on = location_to_url(stacked_on, 'read')
1379
stacked_on = urlutils.normalize_url(stacked_on)
1381
parent_url = br_from.get_parent()
1383
parent = Branch.open(parent_url)
1384
stacked_on = parent.get_public_branch()
1386
# I considered excluding non-http url's here, thus forcing
1387
# 'public' branches only, but that only works for some
1388
# users, so it's best to just depend on the user spotting an
1389
# error by the feedback given to them. RBC 20080227.
1390
stacked_on = parent_url
1392
raise errors.CommandError(gettext(
1393
"Could not determine branch to refer to."))
1395
# Get the destination location
333
1396
if location is None:
1397
stored_loc = br_from.get_push_location()
334
1398
if stored_loc is None:
335
raise BzrCommandError("No pull location known or specified.")
1399
parent_loc = br_from.get_parent()
1401
raise errors.CommandError(gettext(
1402
"No push location known or specified. To push to the "
1403
"parent branch (at %s), use 'brz push :parent'." %
1404
urlutils.unescape_for_display(parent_loc,
1405
self.outf.encoding)))
1407
raise errors.CommandError(gettext(
1408
"No push location known or specified."))
337
print "Using last location: %s" % stored_loc
1410
display_url = urlutils.unescape_for_display(stored_loc,
1412
note(gettext("Using saved push location: %s") % display_url)
338
1413
location = stored_loc
339
cache_root = tempfile.mkdtemp()
340
from bzrlib.errors import DivergedBranches
341
br_from = Branch.open_containing(location)
342
location = br_from.base
343
old_revno = br_to.revno()
345
from bzrlib.errors import DivergedBranches
346
br_from = Branch.open(location)
347
br_from.setup_caching(cache_root)
348
location = br_from.base
349
old_revno = br_to.revno()
351
br_to.update_revisions(br_from)
352
except DivergedBranches:
353
raise BzrCommandError("These branches have diverged."
356
merge(('.', -1), ('.', old_revno), check_clean=False)
357
if location != stored_loc:
358
br_to.set_parent(location)
1415
_show_push_branch(br_from, revision_id, location, self.outf,
1416
verbose=verbose, overwrite=overwrite, remember=remember,
1417
stacked_on=stacked_on, create_prefix=create_prefix,
1418
use_existing_dir=use_existing_dir, no_tree=no_tree,
364
1422
class cmd_branch(Command):
365
"""Create a new copy of a branch.
1423
__doc__ = """Create a new branch that is a copy of an existing branch.
367
1425
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
368
1426
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
1427
If the FROM_LOCATION has no / or path separator embedded, the TO_LOCATION
1428
is derived from the FROM_LOCATION by stripping a leading scheme or drive
1429
identifier, if any. For example, "branch lp:foo-bar" will attempt to
370
1432
To retrieve the branch as of a particular revision, supply the --revision
371
1433
parameter, as in "branch foo/bar -r 5".
373
takes_args = ['from_location', 'to_location?']
374
takes_options = ['revision']
375
aliases = ['get', 'clone']
377
def run(self, from_location, to_location=None, revision=None):
378
from bzrlib.branch import copy_branch
381
from shutil import rmtree
382
cache_root = tempfile.mkdtemp()
386
elif len(revision) > 1:
387
raise BzrCommandError(
388
'bzr branch --revision takes exactly 1 revision value')
390
br_from = Branch.open(from_location)
392
if e.errno == errno.ENOENT:
393
raise BzrCommandError('Source location "%s" does not'
394
' exist.' % to_location)
397
br_from.setup_caching(cache_root)
398
if to_location is None:
399
to_location = os.path.basename(from_location.rstrip("/\\"))
401
os.mkdir(to_location)
403
if e.errno == errno.EEXIST:
404
raise BzrCommandError('Target directory "%s" already'
405
' exists.' % to_location)
406
if e.errno == errno.ENOENT:
407
raise BzrCommandError('Parent of "%s" does not exist.' %
412
copy_branch(br_from, to_location, revision[0])
413
except bzrlib.errors.NoSuchRevision:
415
msg = "The branch %s has no revision %d." % (from_location, revision[0])
416
raise BzrCommandError(msg)
1437
_see_also = ['checkout']
1438
takes_args = ['from_location', 'to_location?']
1439
takes_options = ['revision',
1441
'hardlink', help='Hard-link working tree files where possible.'),
1442
Option('files-from', type=str,
1443
help="Get file contents from this tree."),
1445
help="Create a branch without a working-tree."),
1447
help="Switch the checkout in the current directory "
1448
"to the new branch."),
1450
help='Create a stacked branch referring to the source branch. '
1451
'The new branch will depend on the availability of the source '
1452
'branch for all operations.'),
1453
Option('standalone',
1454
help='Do not use a shared repository, even if available.'),
1455
Option('use-existing-dir',
1456
help='By default branch will fail if the target'
1457
' directory exists, but does not already'
1458
' have a control directory. This flag will'
1459
' allow branch to proceed.'),
1461
help="Bind new branch to from location."),
1462
Option('no-recurse-nested',
1463
help='Do not recursively check out nested trees.'),
1464
Option('colocated-branch', short_name='b',
1465
type=str, help='Name of colocated branch to sprout.'),
1468
def run(self, from_location, to_location=None, revision=None,
1469
hardlink=False, stacked=False, standalone=False, no_tree=False,
1470
use_existing_dir=False, switch=False, bind=False,
1471
files_from=None, no_recurse_nested=False, colocated_branch=None):
1472
from breezy import switch as _mod_switch
1473
accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1474
from_location, name=colocated_branch)
1475
if no_recurse_nested:
1479
if not (hardlink or files_from):
1480
# accelerator_tree is usually slower because you have to read N
1481
# files (no readahead, lots of seeks, etc), but allow the user to
1482
# explicitly request it
1483
accelerator_tree = None
1484
if files_from is not None and files_from != from_location:
1485
accelerator_tree = WorkingTree.open(files_from)
1486
revision = _get_one_revision('branch', revision)
1487
self.enter_context(br_from.lock_read())
1488
if revision is not None:
1489
revision_id = revision.as_revision_id(br_from)
1491
# FIXME - wt.last_revision, fallback to branch, fall back to
1492
# None or perhaps NULL_REVISION to mean copy nothing
1494
revision_id = br_from.last_revision()
1495
if to_location is None:
1496
to_location = urlutils.derive_to_location(from_location)
1497
to_transport = transport.get_transport(to_location, purpose='write')
1499
to_transport.mkdir('.')
1500
except errors.FileExists:
1502
to_dir = controldir.ControlDir.open_from_transport(
1504
except errors.NotBranchError:
1505
if not use_existing_dir:
1506
raise errors.CommandError(gettext('Target directory "%s" '
1507
'already exists.') % to_location)
1512
to_dir.open_branch()
1513
except errors.NotBranchError:
1516
raise errors.AlreadyBranchError(to_location)
1517
except errors.NoSuchFile:
1518
raise errors.CommandError(gettext('Parent of "%s" does not exist.')
1524
# preserve whatever source format we have.
1525
to_dir = br_from.controldir.sprout(
1526
to_transport.base, revision_id,
1527
possible_transports=[to_transport],
1528
accelerator_tree=accelerator_tree, hardlink=hardlink,
1529
stacked=stacked, force_new_repo=standalone,
1530
create_tree_if_local=not no_tree, source_branch=br_from,
1532
branch = to_dir.open_branch(
1533
possible_transports=[
1534
br_from.controldir.root_transport, to_transport])
1535
except errors.NoSuchRevision:
1536
to_transport.delete_tree('.')
1537
msg = gettext("The branch {0} has no revision {1}.").format(
1538
from_location, revision)
1539
raise errors.CommandError(msg)
1542
to_repo = to_dir.open_repository()
1543
except errors.NoRepositoryPresent:
1544
to_repo = to_dir.create_repository()
1545
to_repo.fetch(br_from.repository, revision_id=revision_id)
1546
branch = br_from.sprout(
1547
to_dir, revision_id=revision_id)
1548
br_from.tags.merge_to(branch.tags)
1550
# If the source branch is stacked, the new branch may
1551
# be stacked whether we asked for that explicitly or not.
1552
# We therefore need a try/except here and not just 'if stacked:'
1554
note(gettext('Created new stacked branch referring to %s.') %
1555
branch.get_stacked_on_url())
1556
except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
1557
errors.UnstackableRepositoryFormat) as e:
1558
revno = branch.revno()
1559
if revno is not None:
1560
note(ngettext('Branched %d revision.',
1561
'Branched %d revisions.',
1562
branch.revno()) % revno)
1564
note(gettext('Created new branch.'))
1566
# Bind to the parent
1567
parent_branch = Branch.open(from_location)
1568
branch.bind(parent_branch)
1569
note(gettext('New branch bound to %s') % from_location)
1571
# Switch to the new branch
1572
wt, _ = WorkingTree.open_containing('.')
1573
_mod_switch.switch(wt.controldir, branch)
1574
note(gettext('Switched to branch: %s'),
1575
urlutils.unescape_for_display(branch.base, 'utf-8'))
1578
class cmd_branches(Command):
1579
__doc__ = """List the branches available at the current location.
1581
This command will print the names of all the branches at the current
1585
takes_args = ['location?']
1587
Option('recursive', short_name='R',
1588
help='Recursively scan for branches rather than '
1589
'just looking in the specified location.')]
1591
def run(self, location=".", recursive=False):
1593
t = transport.get_transport(location, purpose='read')
1594
if not t.listable():
1595
raise errors.CommandError(
1596
"Can't scan this type of location.")
1597
for b in controldir.ControlDir.find_branches(t):
1598
self.outf.write("%s\n" % urlutils.unescape_for_display(
1599
urlutils.relative_url(t.base, b.base),
1600
self.outf.encoding).rstrip("/"))
1602
dir = controldir.ControlDir.open_containing(location)[0]
1604
active_branch = dir.open_branch(name="")
1605
except errors.NotBranchError:
1606
active_branch = None
1608
for name, branch in iter_sibling_branches(dir):
1611
active = (active_branch is not None and
1612
active_branch.user_url == branch.user_url)
1613
names[name] = active
1614
# Only mention the current branch explicitly if it's not
1615
# one of the colocated branches
1616
if not any(names.values()) and active_branch is not None:
1617
self.outf.write("* %s\n" % gettext("(default)"))
1618
for name in sorted(names):
1619
active = names[name]
1624
self.outf.write("%s %s\n" % (prefix, name))
1627
class cmd_checkout(Command):
1628
__doc__ = """Create a new checkout of an existing branch.
1630
If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree
1631
for the branch found in '.'. This is useful if you have removed the working
1632
tree or if it was never created - i.e. if you pushed the branch to its
1633
current location using SFTP.
1635
If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION
1636
will be used. In other words, "checkout ../foo/bar" will attempt to create
1637
./bar. If the BRANCH_LOCATION has no / or path separator embedded, the
1638
TO_LOCATION is derived from the BRANCH_LOCATION by stripping a leading
1639
scheme or drive identifier, if any. For example, "checkout lp:foo-bar" will
1640
attempt to create ./foo-bar.
1642
To retrieve the branch as of a particular revision, supply the --revision
1643
parameter, as in "checkout foo/bar -r 5". Note that this will be
1644
immediately out of date [so you cannot commit] but it may be useful (i.e.
1645
to examine old code.)
1648
_see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
1649
takes_args = ['branch_location?', 'to_location?']
1650
takes_options = ['revision',
1651
Option('lightweight',
1652
help="Perform a lightweight checkout. Lightweight "
1653
"checkouts depend on access to the branch for "
1654
"every operation. Normal checkouts can perform "
1655
"common operations like diff and status without "
1656
"such access, and also support local commits."
1658
Option('files-from', type=str,
1659
help="Get file contents from this tree."),
1661
help='Hard-link working tree files where possible.'
1666
def run(self, branch_location=None, to_location=None, revision=None,
1667
lightweight=False, files_from=None, hardlink=False):
1668
if branch_location is None:
1669
branch_location = osutils.getcwd()
1670
to_location = branch_location
1671
accelerator_tree, source = controldir.ControlDir.open_tree_or_branch(
1673
if not (hardlink or files_from):
1674
# accelerator_tree is usually slower because you have to read N
1675
# files (no readahead, lots of seeks, etc), but allow the user to
1676
# explicitly request it
1677
accelerator_tree = None
1678
revision = _get_one_revision('checkout', revision)
1679
if files_from is not None and files_from != branch_location:
1680
accelerator_tree = WorkingTree.open(files_from)
1681
if revision is not None:
1682
revision_id = revision.as_revision_id(source)
1685
if to_location is None:
1686
to_location = urlutils.derive_to_location(branch_location)
1687
# if the source and to_location are the same,
1688
# and there is no working tree,
1689
# then reconstitute a branch
1690
if osutils.abspath(to_location) == osutils.abspath(branch_location):
1692
source.controldir.open_workingtree()
1693
except errors.NoWorkingTree:
1694
source.controldir.create_workingtree(revision_id)
1696
source.create_checkout(to_location, revision_id, lightweight,
1697
accelerator_tree, hardlink)
1700
class cmd_clone(Command):
1701
__doc__ = """Clone a control directory.
1704
takes_args = ['from_location', 'to_location?']
1705
takes_options = ['revision',
1706
Option('no-recurse-nested',
1707
help='Do not recursively check out nested trees.'),
1710
def run(self, from_location, to_location=None, revision=None, no_recurse_nested=False):
1711
accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1713
if no_recurse_nested:
1717
revision = _get_one_revision('branch', revision)
1718
self.enter_context(br_from.lock_read())
1719
if revision is not None:
1720
revision_id = revision.as_revision_id(br_from)
1722
# FIXME - wt.last_revision, fallback to branch, fall back to
1723
# None or perhaps NULL_REVISION to mean copy nothing
1725
revision_id = br_from.last_revision()
1726
if to_location is None:
1727
to_location = urlutils.derive_to_location(from_location)
1728
target_controldir = br_from.controldir.clone(to_location, revision_id=revision_id)
1729
note(gettext('Created new control directory.'))
421
1732
class cmd_renames(Command):
422
"""Show list of renamed files.
424
TODO: Option to show renames between two historical versions.
426
TODO: Only show renames under dir, rather than in the whole branch.
1733
__doc__ = """Show list of renamed files.
1735
# TODO: Option to show renames between two historical versions.
1737
# TODO: Only show renames under dir, rather than in the whole branch.
1738
_see_also = ['status']
428
1739
takes_args = ['dir?']
430
def run(self, dir='.'):
431
b = Branch.open_containing(dir)
432
old_inv = b.basis_tree().inventory
433
new_inv = b.read_working_inventory()
435
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
1742
def run(self, dir=u'.'):
1743
tree = WorkingTree.open_containing(dir)[0]
1744
self.enter_context(tree.lock_read())
1745
old_tree = tree.basis_tree()
1746
self.enter_context(old_tree.lock_read())
1748
iterator = tree.iter_changes(old_tree, include_unchanged=True)
1749
for change in iterator:
1750
if change.path[0] == change.path[1]:
1752
if None in change.path:
1754
renames.append(change.path)
437
1756
for old_name, new_name in renames:
438
print "%s => %s" % (old_name, new_name)
1757
self.outf.write("%s => %s\n" % (old_name, new_name))
1760
class cmd_update(Command):
1761
__doc__ = """Update a working tree to a new revision.
1763
This will perform a merge of the destination revision (the tip of the
1764
branch, or the specified revision) into the working tree, and then make
1765
that revision the basis revision for the working tree.
1767
You can use this to visit an older revision, or to update a working tree
1768
that is out of date from its branch.
1770
If there are any uncommitted changes in the tree, they will be carried
1771
across and remain as uncommitted changes after the update. To discard
1772
these changes, use 'brz revert'. The uncommitted changes may conflict
1773
with the changes brought in by the change in basis revision.
1775
If the tree's branch is bound to a master branch, brz will also update
1776
the branch from the master.
1778
You cannot update just a single file or directory, because each Breezy
1779
working tree has just a single basis revision. If you want to restore a
1780
file that has been removed locally, use 'brz revert' instead of 'brz
1781
update'. If you want to restore a file to its state in a previous
1782
revision, use 'brz revert' with a '-r' option, or use 'brz cat' to write
1783
out the old content of that file to a new location.
1785
The 'dir' argument, if given, must be the location of the root of a
1786
working tree to update. By default, the working tree that contains the
1787
current working directory is used.
1790
_see_also = ['pull', 'working-trees', 'status-flags']
1791
takes_args = ['dir?']
1792
takes_options = ['revision',
1794
help="Show base revision text in conflicts."),
1798
def run(self, dir=None, revision=None, show_base=None):
1799
if revision is not None and len(revision) != 1:
1800
raise errors.CommandError(gettext(
1801
"brz update --revision takes exactly one revision"))
1803
tree = WorkingTree.open_containing('.')[0]
1805
tree, relpath = WorkingTree.open_containing(dir)
1808
raise errors.CommandError(gettext(
1809
"brz update can only update a whole tree, "
1810
"not a file or subdirectory"))
1811
branch = tree.branch
1812
possible_transports = []
1813
master = branch.get_master_branch(
1814
possible_transports=possible_transports)
1815
if master is not None:
1816
branch_location = master.base
1817
self.enter_context(tree.lock_write())
1819
branch_location = tree.branch.base
1820
self.enter_context(tree.lock_tree_write())
1821
# get rid of the final '/' and be ready for display
1822
branch_location = urlutils.unescape_for_display(
1823
branch_location.rstrip('/'),
1825
existing_pending_merges = tree.get_parent_ids()[1:]
1829
# may need to fetch data into a heavyweight checkout
1830
# XXX: this may take some time, maybe we should display a
1832
old_tip = branch.update(possible_transports)
1833
if revision is not None:
1834
revision_id = revision[0].as_revision_id(branch)
1836
revision_id = branch.last_revision()
1837
if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1838
revno = branch.revision_id_to_dotted_revno(revision_id)
1839
note(gettext("Tree is up to date at revision {0} of branch {1}"
1840
).format('.'.join(map(str, revno)), branch_location))
1842
view_info = _get_view_info_for_change_reporter(tree)
1843
change_reporter = delta._ChangeReporter(
1844
unversioned_filter=tree.is_ignored,
1845
view_info=view_info)
1847
conflicts = tree.update(
1849
possible_transports=possible_transports,
1850
revision=revision_id,
1852
show_base=show_base)
1853
except errors.NoSuchRevision as e:
1854
raise errors.CommandError(gettext(
1855
"branch has no revision %s\n"
1856
"brz update --revision only works"
1857
" for a revision in the branch history")
1859
revno = tree.branch.revision_id_to_dotted_revno(
1860
_mod_revision.ensure_null(tree.last_revision()))
1861
note(gettext('Updated to revision {0} of branch {1}').format(
1862
'.'.join(map(str, revno)), branch_location))
1863
parent_ids = tree.get_parent_ids()
1864
if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
1865
note(gettext('Your local commits will now show as pending merges with '
1866
"'brz status', and can be committed with 'brz commit'."))
441
1873
class cmd_info(Command):
442
"""Show statistical information about a branch."""
443
takes_args = ['branch?']
445
def run(self, branch=None):
448
b = Branch.open_containing(branch)
1874
__doc__ = """Show information about a working tree, branch or repository.
1876
This command will show all known locations and formats associated to the
1877
tree, branch or repository.
1879
In verbose mode, statistical information is included with each report.
1880
To see extended statistic information, use a verbosity level of 2 or
1881
higher by specifying the verbose option multiple times, e.g. -vv.
1883
Branches and working trees will also report any missing revisions.
1887
Display information on the format and related locations:
1891
Display the above together with extended format information and
1892
basic statistics (like the number of files in the working tree and
1893
number of revisions in the branch and repository):
1897
Display the above together with number of committers to the branch:
1901
_see_also = ['revno', 'working-trees', 'repositories']
1902
takes_args = ['location?']
1903
takes_options = ['verbose']
1904
encoding_type = 'replace'
1907
def run(self, location=None, verbose=False):
1909
noise_level = get_verbosity_level()
1912
from .info import show_bzrdir_info
1913
show_bzrdir_info(controldir.ControlDir.open_containing(location)[0],
1914
verbose=noise_level, outfile=self.outf)
452
1917
class cmd_remove(Command):
453
"""Make a file unversioned.
455
This makes bzr stop tracking changes to a versioned file. It does
456
not delete the working copy.
458
takes_args = ['file+']
459
takes_options = ['verbose']
461
def run(self, file_list, verbose=False):
462
b = Branch.open_containing(file_list[0])
463
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
466
class cmd_file_id(Command):
467
"""Print file_id of a particular file or directory.
469
The file_id is assigned when the file is first added and remains the
470
same through all revisions where the file exists, even when it is
474
takes_args = ['filename']
475
def run(self, filename):
476
b = Branch.open_containing(filename)
477
i = b.inventory.path2id(b.relpath(filename))
479
raise BzrError("%r is not a versioned file" % filename)
484
class cmd_file_path(Command):
485
"""Print path of file_ids to a file or directory.
487
This prints one line for each directory down to the target,
488
starting at the branch root."""
490
takes_args = ['filename']
491
def run(self, filename):
492
b = Branch.open_containing(filename)
494
fid = inv.path2id(b.relpath(filename))
496
raise BzrError("%r is not a versioned file" % filename)
497
for fip in inv.get_idpath(fid):
1918
__doc__ = """Remove files or directories.
1920
This makes Breezy stop tracking changes to the specified files. Breezy will
1921
delete them if they can easily be recovered using revert otherwise they
1922
will be backed up (adding an extension of the form .~#~). If no options or
1923
parameters are given Breezy will scan for files that are being tracked by
1924
Breezy but missing in your tree and stop tracking them for you.
1926
takes_args = ['file*']
1927
takes_options = ['verbose',
1929
'new', help='Only remove files that have never been committed.'),
1930
RegistryOption.from_kwargs('file-deletion-strategy',
1931
'The file deletion mode to be used.',
1932
title='Deletion Strategy', value_switches=True, enum_switch=False,
1933
safe='Backup changed files (default).',
1934
keep='Delete from brz but leave the working copy.',
1935
no_backup='Don\'t backup changed files.'),
1937
aliases = ['rm', 'del']
1938
encoding_type = 'replace'
1940
def run(self, file_list, verbose=False, new=False,
1941
file_deletion_strategy='safe'):
1943
tree, file_list = WorkingTree.open_containing_paths(file_list)
1945
if file_list is not None:
1946
file_list = [f for f in file_list]
1948
self.enter_context(tree.lock_write())
1949
# Heuristics should probably all move into tree.remove_smart or
1952
added = tree.changes_from(tree.basis_tree(),
1953
specific_files=file_list).added
1954
file_list = sorted([f.path[1] for f in added], reverse=True)
1955
if len(file_list) == 0:
1956
raise errors.CommandError(gettext('No matching files.'))
1957
elif file_list is None:
1958
# missing files show up in iter_changes(basis) as
1959
# versioned-with-no-kind.
1961
for change in tree.iter_changes(tree.basis_tree()):
1962
# Find paths in the working tree that have no kind:
1963
if change.path[1] is not None and change.kind[1] is None:
1964
missing.append(change.path[1])
1965
file_list = sorted(missing, reverse=True)
1966
file_deletion_strategy = 'keep'
1967
tree.remove(file_list, verbose=verbose, to_file=self.outf,
1968
keep_files=file_deletion_strategy == 'keep',
1969
force=(file_deletion_strategy == 'no-backup'))
1972
class cmd_reconcile(Command):
1973
__doc__ = """Reconcile brz metadata in a branch.
1975
This can correct data mismatches that may have been caused by
1976
previous ghost operations or brz upgrades. You should only
1977
need to run this command if 'brz check' or a brz developer
1978
advises you to run it.
1980
If a second branch is provided, cross-branch reconciliation is
1981
also attempted, which will check that data like the tree root
1982
id which was not present in very early brz versions is represented
1983
correctly in both branches.
1985
At the same time it is run it may recompress data resulting in
1986
a potential saving in disk space or performance gain.
1988
The branch *MUST* be on a listable system such as local disk or sftp.
1991
_see_also = ['check']
1992
takes_args = ['branch?']
1994
Option('canonicalize-chks',
1995
help='Make sure CHKs are in canonical form (repairs '
2000
def run(self, branch=".", canonicalize_chks=False):
2001
from .reconcile import reconcile
2002
dir = controldir.ControlDir.open(branch)
2003
reconcile(dir, canonicalize_chks=canonicalize_chks)
501
2006
class cmd_revision_history(Command):
502
"""Display list of revision ids on this branch."""
505
for patchid in Branch.open_containing('.').revision_history():
509
class cmd_directories(Command):
510
"""Display list of versioned directories in this branch."""
512
for name, ie in Branch.open_containing('.').read_working_inventory().directories():
2007
__doc__ = """Display the list of revision ids on a branch."""
2010
takes_args = ['location?']
2015
def run(self, location="."):
2016
branch = Branch.open_containing(location)[0]
2017
self.enter_context(branch.lock_read())
2018
graph = branch.repository.get_graph()
2019
history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
2020
[_mod_revision.NULL_REVISION]))
2021
for revid in reversed(history):
2022
self.outf.write(revid)
2023
self.outf.write('\n')
2026
class cmd_ancestry(Command):
2027
__doc__ = """List all revisions merged into this branch."""
2029
_see_also = ['log', 'revision-history']
2030
takes_args = ['location?']
2035
def run(self, location="."):
2037
wt = WorkingTree.open_containing(location)[0]
2038
except errors.NoWorkingTree:
2039
b = Branch.open(location)
2040
last_revision = b.last_revision()
2043
last_revision = wt.last_revision()
2045
self.enter_context(b.repository.lock_read())
2046
graph = b.repository.get_graph()
2047
revisions = [revid for revid, parents in
2048
graph.iter_ancestry([last_revision])]
2049
for revision_id in reversed(revisions):
2050
if _mod_revision.is_null(revision_id):
2052
self.outf.write(revision_id.decode('utf-8') + '\n')
519
2055
class cmd_init(Command):
520
"""Make a directory into a versioned branch.
2056
__doc__ = """Make a directory into a versioned branch.
522
2058
Use this to create an empty branch, or before importing an
523
2059
existing project.
525
Recipe for importing a tree of files:
2061
If there is a repository in a parent directory of the location, then
2062
the history of the branch will be stored in the repository. Otherwise
2063
init creates a standalone branch which carries its own history
2064
in the .bzr directory.
2066
If there is already a branch at the location but it has no working tree,
2067
the tree can be populated with 'brz checkout'.
2069
Recipe for importing a tree of files::
530
bzr commit -m 'imported project'
533
from bzrlib.branch import Branch
534
Branch.initialize('.')
2075
brz commit -m "imported project"
2078
_see_also = ['init-shared-repository', 'branch', 'checkout']
2079
takes_args = ['location?']
2081
Option('create-prefix',
2082
help='Create the path leading up to the branch '
2083
'if it does not already exist.'),
2084
RegistryOption('format',
2085
help='Specify a format for this branch. '
2086
'See "help formats" for a full list.',
2087
lazy_registry=('breezy.controldir', 'format_registry'),
2088
converter=lambda name: controldir.format_registry.make_controldir(
2090
value_switches=True,
2091
title="Branch format",
2093
Option('append-revisions-only',
2094
help='Never change revnos or the existing log.'
2095
' Append revisions to it only.'),
2097
'Create a branch without a working tree.')
2100
def run(self, location=None, format=None, append_revisions_only=False,
2101
create_prefix=False, no_tree=False):
2103
format = controldir.format_registry.make_controldir('default')
2104
if location is None:
2107
to_transport = transport.get_transport(location, purpose='write')
2109
# The path has to exist to initialize a
2110
# branch inside of it.
2111
# Just using os.mkdir, since I don't
2112
# believe that we want to create a bunch of
2113
# locations if the user supplies an extended path
2115
to_transport.ensure_base()
2116
except errors.NoSuchFile:
2117
if not create_prefix:
2118
raise errors.CommandError(gettext("Parent directory of %s"
2120
"\nYou may supply --create-prefix to create all"
2121
" leading parent directories.")
2123
to_transport.create_prefix()
2126
a_controldir = controldir.ControlDir.open_from_transport(
2128
except errors.NotBranchError:
2129
# really a NotBzrDir error...
2130
create_branch = controldir.ControlDir.create_branch_convenience
2132
force_new_tree = False
2134
force_new_tree = None
2135
branch = create_branch(to_transport.base, format=format,
2136
possible_transports=[to_transport],
2137
force_new_tree=force_new_tree)
2138
a_controldir = branch.controldir
2140
from .transport.local import LocalTransport
2141
if a_controldir.has_branch():
2142
if (isinstance(to_transport, LocalTransport)
2143
and not a_controldir.has_workingtree()):
2144
raise errors.BranchExistsWithoutWorkingTree(location)
2145
raise errors.AlreadyBranchError(location)
2146
branch = a_controldir.create_branch()
2147
if not no_tree and not a_controldir.has_workingtree():
2148
a_controldir.create_workingtree()
2149
if append_revisions_only:
2151
branch.set_append_revisions_only(True)
2152
except errors.UpgradeRequired:
2153
raise errors.CommandError(gettext('This branch format cannot be set'
2154
' to append-revisions-only. Try --default.'))
2156
from .info import describe_layout, describe_format
2158
tree = a_controldir.open_workingtree(recommend_upgrade=False)
2159
except (errors.NoWorkingTree, errors.NotLocalUrl):
2161
repository = branch.repository
2162
layout = describe_layout(repository, branch, tree).lower()
2163
format = describe_format(a_controldir, repository, branch, tree)
2164
self.outf.write(gettext("Created a {0} (format: {1})\n").format(
2166
if repository.is_shared():
2167
# XXX: maybe this can be refactored into transport.path_or_url()
2168
url = repository.controldir.root_transport.external_url()
2170
url = urlutils.local_path_from_url(url)
2171
except urlutils.InvalidURL:
2173
self.outf.write(gettext("Using shared repository: %s\n") % url)
2176
class cmd_init_shared_repository(Command):
2177
__doc__ = """Create a shared repository for branches to share storage space.
2179
New branches created under the repository directory will store their
2180
revisions in the repository, not in the branch directory. For branches
2181
with shared history, this reduces the amount of storage needed and
2182
speeds up the creation of new branches.
2184
If the --no-trees option is given then the branches in the repository
2185
will not have working trees by default. They will still exist as
2186
directories on disk, but they will not have separate copies of the
2187
files at a certain revision. This can be useful for repositories that
2188
store branches which are interacted with through checkouts or remote
2189
branches, such as on a server.
2192
Create a shared repository holding just branches::
2194
brz init-shared-repo --no-trees repo
2197
Make a lightweight checkout elsewhere::
2199
brz checkout --lightweight repo/trunk trunk-checkout
2204
_see_also = ['init', 'branch', 'checkout', 'repositories']
2205
takes_args = ["location"]
2206
takes_options = [RegistryOption('format',
2207
help='Specify a format for this repository. See'
2208
' "brz help formats" for details.',
2210
'breezy.controldir', 'format_registry'),
2211
converter=lambda name: controldir.format_registry.make_controldir(
2213
value_switches=True, title='Repository format'),
2215
help='Branches in the repository will default to'
2216
' not having a working tree.'),
2218
aliases = ["init-shared-repo", "init-repo"]
2220
def run(self, location, format=None, no_trees=False):
2222
format = controldir.format_registry.make_controldir('default')
2224
if location is None:
2227
to_transport = transport.get_transport(location, purpose='write')
2229
if format.fixed_components:
2230
repo_format_name = None
2232
repo_format_name = format.repository_format.get_format_string()
2234
(repo, newdir, require_stacking, repository_policy) = (
2235
format.initialize_on_transport_ex(to_transport,
2236
create_prefix=True, make_working_trees=not no_trees,
2237
shared_repo=True, force_new_repo=True,
2238
use_existing_dir=True,
2239
repo_format_name=repo_format_name))
2241
from .info import show_bzrdir_info
2242
show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
537
2245
class cmd_diff(Command):
538
"""Show differences in working tree.
540
If files are listed, only the changes in those files are listed.
541
Otherwise, all changes for the tree are listed.
543
TODO: Allow diff across branches.
545
TODO: Option to use external diff command; could be GNU diff, wdiff,
548
TODO: Python difflib is not exactly the same as unidiff; should
549
either fix it up or prefer to use an external diff.
551
TODO: If a directory is given, diff everything under that.
553
TODO: Selected-file diff is inefficient and doesn't show you
556
TODO: This probably handles non-Unix newlines poorly.
2246
__doc__ = """Show differences in the working tree, between revisions or branches.
2248
If no arguments are given, all changes for the current tree are listed.
2249
If files are given, only the changes in those files are listed.
2250
Remote and multiple branches can be compared by using the --old and
2251
--new options. If not provided, the default for both is derived from
2252
the first argument, if any, or the current tree if no arguments are
2255
"brz diff -p1" is equivalent to "brz diff --prefix old/:new/", and
2256
produces patches suitable for "patch -p1".
2258
Note that when using the -r argument with a range of revisions, the
2259
differences are computed between the two specified revisions. That
2260
is, the command does not show the changes introduced by the first
2261
revision in the range. This differs from the interpretation of
2262
revision ranges used by "brz log" which includes the first revision
2267
2 - unrepresentable changes
2272
Shows the difference in the working tree versus the last commit::
2276
Difference between the working tree and revision 1::
2280
Difference between revision 3 and revision 1::
2284
Difference between revision 3 and revision 1 for branch xxx::
2288
The changes introduced by revision 2 (equivalent to -r1..2)::
2292
To see the changes introduced by revision X::
2296
Note that in the case of a merge, the -c option shows the changes
2297
compared to the left hand parent. To see the changes against
2298
another parent, use::
2300
brz diff -r<chosen_parent>..X
2302
The changes between the current revision and the previous revision
2303
(equivalent to -c-1 and -r-2..-1)
2307
Show just the differences for file NEWS::
2311
Show the differences in working tree xxx for file NEWS::
2315
Show the differences from branch xxx to this working tree:
2319
Show the differences between two branches for file NEWS::
2321
brz diff --old xxx --new yyy NEWS
2323
Same as 'brz diff' but prefix paths with old/ and new/::
2325
brz diff --prefix old/:new/
2327
Show the differences using a custom diff program with options::
2329
brz diff --using /usr/bin/diff --diff-options -wu
2331
_see_also = ['status']
564
2332
takes_args = ['file*']
565
takes_options = ['revision', 'diff-options']
2334
Option('diff-options', type=str,
2335
help='Pass these options to the external diff program.'),
2336
Option('prefix', type=str,
2338
help='Set prefixes added to old and new filenames, as '
2339
'two values separated by a colon. (eg "old/:new/").'),
2341
help='Branch/tree to compare from.',
2345
help='Branch/tree to compare to.',
2351
help='Use this command to compare files.',
2354
RegistryOption('format',
2356
help='Diff format to use.',
2357
lazy_registry=('breezy.diff', 'format_registry'),
2358
title='Diff format'),
2360
help='How many lines of context to show.',
2363
RegistryOption.from_kwargs(
2365
help='Color mode to use.',
2366
title='Color Mode', value_switches=False, enum_switch=True,
2367
never='Never colorize output.',
2368
auto='Only colorize output if terminal supports it and STDOUT is a'
2370
always='Always colorize output (default).'),
2373
help=('Warn if trailing whitespace or spurious changes have been'
566
2377
aliases = ['di', 'dif']
568
def run(self, revision=None, file_list=None, diff_options=None):
569
from bzrlib.diff import show_diff
572
b = Branch.open_containing(file_list[0])
573
file_list = [b.relpath(f) for f in file_list]
574
if file_list == ['']:
575
# just pointing to top-of-tree
2378
encoding_type = 'exact'
2381
def run(self, revision=None, file_list=None, diff_options=None,
2382
prefix=None, old=None, new=None, using=None, format=None,
2383
context=None, color='never'):
2384
from .diff import (get_trees_and_branches_to_diff_locked,
2391
elif prefix == u'1' or prefix is None:
2394
elif u':' in prefix:
2395
old_label, new_label = prefix.split(u":")
578
b = Branch.open_containing('.')
580
if revision is not None:
581
if len(revision) == 1:
582
show_diff(b, revision[0], specific_files=file_list,
583
external_diff_options=diff_options)
584
elif len(revision) == 2:
585
show_diff(b, revision[0], specific_files=file_list,
586
external_diff_options=diff_options,
587
revision2=revision[1])
2397
raise errors.CommandError(gettext(
2398
'--prefix expects two values separated by a colon'
2399
' (eg "old/:new/")'))
2401
if revision and len(revision) > 2:
2402
raise errors.CommandError(gettext('brz diff --revision takes exactly'
2403
' one or two revision specifiers'))
2405
if using is not None and format is not None:
2406
raise errors.CommandError(gettext(
2407
'{0} and {1} are mutually exclusive').format(
2408
'--using', '--format'))
2410
(old_tree, new_tree,
2411
old_branch, new_branch,
2412
specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
2413
file_list, revision, old, new, self._exit_stack, apply_view=True)
2414
# GNU diff on Windows uses ANSI encoding for filenames
2415
path_encoding = osutils.get_diff_header_encoding()
2418
from .terminal import has_ansi_colors
2419
if has_ansi_colors():
589
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
591
show_diff(b, None, specific_files=file_list,
592
external_diff_options=diff_options)
2423
if 'always' == color:
2424
from .colordiff import DiffWriter
2425
outf = DiffWriter(outf)
2426
return show_diff_trees(old_tree, new_tree, outf,
2427
specific_files=specific_files,
2428
external_diff_options=diff_options,
2429
old_label=old_label, new_label=new_label,
2430
extra_trees=extra_trees,
2431
path_encoding=path_encoding,
2432
using=using, context=context,
597
2436
class cmd_deleted(Command):
598
"""List files deleted in the working tree.
600
TODO: Show files deleted since a previous revision, or between two revisions.
2437
__doc__ = """List files deleted in the working tree.
602
def run(self, show_ids=False):
603
b = Branch.open_containing('.')
605
new = b.working_tree()
607
## TODO: Much more efficient way to do this: read in new
608
## directories with readdir, rather than stating each one. Same
609
## level of effort but possibly much less IO. (Or possibly not,
610
## if the directories are very large...)
612
for path, ie in old.inventory.iter_entries():
613
if not new.has_id(ie.file_id):
615
print '%-50s %s' % (path, ie.file_id)
2439
# TODO: Show files deleted since a previous revision, or
2440
# between two revisions.
2441
# TODO: Much more efficient way to do this: read in new
2442
# directories with readdir, rather than stating each one. Same
2443
# level of effort but possibly much less IO. (Or possibly not,
2444
# if the directories are very large...)
2445
_see_also = ['status', 'ls']
2446
takes_options = ['directory', 'show-ids']
2449
def run(self, show_ids=False, directory=u'.'):
2450
tree = WorkingTree.open_containing(directory)[0]
2451
self.enter_context(tree.lock_read())
2452
old = tree.basis_tree()
2453
self.enter_context(old.lock_read())
2454
delta = tree.changes_from(old)
2455
for change in delta.removed:
2456
self.outf.write(change.path[0])
2458
self.outf.write(' ')
2459
self.outf.write(change.file_id)
2460
self.outf.write('\n')
620
2463
class cmd_modified(Command):
621
"""List files modified in working tree."""
2464
__doc__ = """List files modified in working tree.
624
from bzrlib.delta import compare_trees
626
b = Branch.open_containing('.')
627
td = compare_trees(b.basis_tree(), b.working_tree())
629
for path, id, kind in td.modified:
2468
_see_also = ['status', 'ls']
2469
takes_options = ['directory', 'null']
2472
def run(self, null=False, directory=u'.'):
2473
tree = WorkingTree.open_containing(directory)[0]
2474
self.enter_context(tree.lock_read())
2475
td = tree.changes_from(tree.basis_tree())
2477
for change in td.modified:
2479
self.outf.write(change.path[1] + '\0')
2481
self.outf.write(osutils.quotefn(change.path[1]) + '\n')
634
2484
class cmd_added(Command):
635
"""List files added in working tree."""
2485
__doc__ = """List files added in working tree.
638
b = Branch.open_containing('.')
639
wt = b.working_tree()
640
basis_inv = b.basis_tree().inventory
643
if file_id in basis_inv:
645
path = inv.id2path(file_id)
646
if not os.access(b.abspath(path), os.F_OK):
2489
_see_also = ['status', 'ls']
2490
takes_options = ['directory', 'null']
2493
def run(self, null=False, directory=u'.'):
2494
wt = WorkingTree.open_containing(directory)[0]
2495
self.enter_context(wt.lock_read())
2496
basis = wt.basis_tree()
2497
self.enter_context(basis.lock_read())
2498
for path in wt.all_versioned_paths():
2499
if basis.has_filename(path):
2503
if not os.access(osutils.pathjoin(wt.basedir, path), os.F_OK):
2506
self.outf.write(path + '\0')
2508
self.outf.write(osutils.quotefn(path) + '\n')
652
2511
class cmd_root(Command):
653
"""Show the tree root directory.
2512
__doc__ = """Show the tree root directory.
655
The root is the nearest enclosing directory with a .bzr control
2514
The root is the nearest enclosing directory with a control
657
2517
takes_args = ['filename?']
658
2520
def run(self, filename=None):
659
2521
"""Print the branch root."""
660
b = Branch.open_containing(filename)
2522
tree = WorkingTree.open_containing(filename)[0]
2523
self.outf.write(tree.basedir + '\n')
2526
def _parse_limit(limitstring):
2528
return int(limitstring)
2530
msg = gettext("The limit argument must be an integer.")
2531
raise errors.CommandError(msg)
2534
def _parse_levels(s):
2538
msg = gettext("The levels argument must be an integer.")
2539
raise errors.CommandError(msg)
664
2542
class cmd_log(Command):
665
"""Show log of this branch.
667
To request a range of logs, you can use the command -r begin:end
668
-r revision requests a specific revision, -r :end or -r begin: are
671
--message allows you to give a regular expression, which will be evaluated
672
so that only matching entries will be displayed.
674
TODO: Make --revision support uuid: and hash: [future tag:] notation.
2543
__doc__ = """Show historical log for a branch or subset of a branch.
2545
log is brz's default tool for exploring the history of a branch.
2546
The branch to use is taken from the first parameter. If no parameters
2547
are given, the branch containing the working directory is logged.
2548
Here are some simple examples::
2550
brz log log the current branch
2551
brz log foo.py log a file in its branch
2552
brz log http://server/branch log a branch on a server
2554
The filtering, ordering and information shown for each revision can
2555
be controlled as explained below. By default, all revisions are
2556
shown sorted (topologically) so that newer revisions appear before
2557
older ones and descendants always appear before ancestors. If displayed,
2558
merged revisions are shown indented under the revision in which they
2563
The log format controls how information about each revision is
2564
displayed. The standard log formats are called ``long``, ``short``
2565
and ``line``. The default is long. See ``brz help log-formats``
2566
for more details on log formats.
2568
The following options can be used to control what information is
2571
-l N display a maximum of N revisions
2572
-n N display N levels of revisions (0 for all, 1 for collapsed)
2573
-v display a status summary (delta) for each revision
2574
-p display a diff (patch) for each revision
2575
--show-ids display revision-ids (and file-ids), not just revnos
2577
Note that the default number of levels to display is a function of the
2578
log format. If the -n option is not used, the standard log formats show
2579
just the top level (mainline).
2581
Status summaries are shown using status flags like A, M, etc. To see
2582
the changes explained using words like ``added`` and ``modified``
2583
instead, use the -vv option.
2587
To display revisions from oldest to newest, use the --forward option.
2588
In most cases, using this option will have little impact on the total
2589
time taken to produce a log, though --forward does not incrementally
2590
display revisions like --reverse does when it can.
2592
:Revision filtering:
2594
The -r option can be used to specify what revision or range of revisions
2595
to filter against. The various forms are shown below::
2597
-rX display revision X
2598
-rX.. display revision X and later
2599
-r..Y display up to and including revision Y
2600
-rX..Y display from X to Y inclusive
2602
See ``brz help revisionspec`` for details on how to specify X and Y.
2603
Some common examples are given below::
2605
-r-1 show just the tip
2606
-r-10.. show the last 10 mainline revisions
2607
-rsubmit:.. show what's new on this branch
2608
-rancestor:path.. show changes since the common ancestor of this
2609
branch and the one at location path
2610
-rdate:yesterday.. show changes since yesterday
2612
When logging a range of revisions using -rX..Y, log starts at
2613
revision Y and searches back in history through the primary
2614
("left-hand") parents until it finds X. When logging just the
2615
top level (using -n1), an error is reported if X is not found
2616
along the way. If multi-level logging is used (-n0), X may be
2617
a nested merge revision and the log will be truncated accordingly.
2621
If parameters are given and the first one is not a branch, the log
2622
will be filtered to show only those revisions that changed the
2623
nominated files or directories.
2625
Filenames are interpreted within their historical context. To log a
2626
deleted file, specify a revision range so that the file existed at
2627
the end or start of the range.
2629
Historical context is also important when interpreting pathnames of
2630
renamed files/directories. Consider the following example:
2632
* revision 1: add tutorial.txt
2633
* revision 2: modify tutorial.txt
2634
* revision 3: rename tutorial.txt to guide.txt; add tutorial.txt
2638
* ``brz log guide.txt`` will log the file added in revision 1
2640
* ``brz log tutorial.txt`` will log the new file added in revision 3
2642
* ``brz log -r2 -p tutorial.txt`` will show the changes made to
2643
the original file in revision 2.
2645
* ``brz log -r2 -p guide.txt`` will display an error message as there
2646
was no file called guide.txt in revision 2.
2648
Renames are always followed by log. By design, there is no need to
2649
explicitly ask for this (and no way to stop logging a file back
2650
until it was last renamed).
2654
The --match option can be used for finding revisions that match a
2655
regular expression in a commit message, committer, author or bug.
2656
Specifying the option several times will match any of the supplied
2657
expressions. --match-author, --match-bugs, --match-committer and
2658
--match-message can be used to only match a specific field.
2662
GUI tools and IDEs are often better at exploring history than command
2663
line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2664
bzr-explorer shell, or the Loggerhead web interface. See the Bazaar
2665
Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2666
<http://wiki.bazaar.canonical.com/IDEIntegration>.
2668
You may find it useful to add the aliases below to ``breezy.conf``::
2672
top = log -l10 --line
2675
``brz tip`` will then show the latest revision while ``brz top``
2676
will show the last 10 mainline revisions. To see the details of a
2677
particular revision X, ``brz show -rX``.
2679
If you are interested in looking deeper into a particular merge X,
2680
use ``brz log -n0 -rX``.
2682
``brz log -v`` on a branch with lots of history is currently
2683
very slow. A fix for this issue is currently under development.
2684
With or without that fix, it is recommended that a revision range
2685
be given when using the -v option.
2687
brz has a generic full-text matching plugin, brz-search, that can be
2688
used to find revisions matching user names, commit messages, etc.
2689
Among other features, this plugin can find all revisions containing
2690
a list of words but not others.
2692
When exploring non-mainline history on large projects with deep
2693
history, the performance of log can be greatly improved by installing
2694
the historycache plugin. This plugin buffers historical information
2695
trading disk space for faster speed.
2697
takes_args = ['file*']
2698
_see_also = ['log-formats', 'revisionspec']
2701
help='Show from oldest to newest.'),
2703
custom_help('verbose',
2704
help='Show files changed in each revision.'),
2708
type=breezy.option._parse_revision_str,
2710
help='Show just the specified revision.'
2711
' See also "help revisionspec".'),
2713
RegistryOption('authors',
2714
'What names to list as authors - first, all or committer.',
2717
'breezy.log', 'author_list_registry'),
2721
help='Number of levels to display - 0 for all, 1 for flat.',
2723
type=_parse_levels),
2725
help='Show revisions whose message matches this '
2726
'regular expression.',
2731
help='Limit the output to the first N revisions.',
2736
help='Show changes made in each revision as a patch.'),
2737
Option('include-merged',
2738
help='Show merged revisions like --levels 0 does.'),
2739
Option('include-merges', hidden=True,
2740
help='Historical alias for --include-merged.'),
2741
Option('omit-merges',
2742
help='Do not report commits with more than one parent.'),
2743
Option('exclude-common-ancestry',
2744
help='Display only the revisions that are not part'
2745
' of both ancestries (require -rX..Y).'
2747
Option('signatures',
2748
help='Show digital signature validity.'),
2751
help='Show revisions whose properties match this '
2754
ListOption('match-message',
2755
help='Show revisions whose message matches this '
2758
ListOption('match-committer',
2759
help='Show revisions whose committer matches this '
2762
ListOption('match-author',
2763
help='Show revisions whose authors match this '
2766
ListOption('match-bugs',
2767
help='Show revisions whose bugs match this '
2771
encoding_type = 'replace'
678
takes_args = ['filename?']
679
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
680
'long', 'message', 'short',]
682
def run(self, filename=None, timezone='original',
2774
def run(self, file_list=None, timezone='original',
690
from bzrlib.log import log_formatter, show_log
2785
include_merged=None,
2787
exclude_common_ancestry=False,
2791
match_committer=None,
2798
make_log_request_dict,
2799
_get_info_for_log_files,
693
2801
direction = (forward and 'forward') or 'reverse'
696
b = Branch.open_containing(filename)
697
fp = b.relpath(filename)
699
file_id = b.read_working_inventory().path2id(fp)
701
file_id = None # points to branch root
703
b = Branch.open_containing('.')
709
elif len(revision) == 1:
710
rev1 = rev2 = revision[0].in_history(b).revno
711
elif len(revision) == 2:
712
rev1 = revision[0].in_history(b).revno
713
rev2 = revision[1].in_history(b).revno
715
raise BzrCommandError('bzr log --revision takes one or two values.')
722
mutter('encoding log as %r' % bzrlib.user_encoding)
724
# use 'replace' so that we don't abort if trying to write out
725
# in e.g. the default C locale.
726
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
732
lf = log_formatter(log_format,
735
show_timezone=timezone)
2802
if include_merged is None:
2803
include_merged = False
2804
if (exclude_common_ancestry
2805
and (revision is None or len(revision) != 2)):
2806
raise errors.CommandError(gettext(
2807
'--exclude-common-ancestry requires -r with two revisions'))
2812
raise errors.CommandError(gettext(
2813
'{0} and {1} are mutually exclusive').format(
2814
'--levels', '--include-merged'))
2816
if change is not None:
2818
raise errors.RangeInChangeOption()
2819
if revision is not None:
2820
raise errors.CommandError(gettext(
2821
'{0} and {1} are mutually exclusive').format(
2822
'--revision', '--change'))
2827
filter_by_dir = False
2829
# find the file ids to log and check for directory filtering
2830
b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2831
revision, file_list, self._exit_stack)
2832
for relpath, kind in file_info_list:
2834
raise errors.CommandError(gettext(
2835
"Path unknown at end or start of revision range: %s") %
2837
# If the relpath is the top of the tree, we log everything
2842
files.append(relpath)
2843
filter_by_dir = filter_by_dir or (
2844
kind in ['directory', 'tree-reference'])
2847
# FIXME ? log the current subdir only RBC 20060203
2848
if revision is not None \
2849
and len(revision) > 0 and revision[0].get_branch():
2850
location = revision[0].get_branch()
2853
dir, relpath = controldir.ControlDir.open_containing(location)
2854
b = dir.open_branch()
2855
self.enter_context(b.lock_read())
2856
rev1, rev2 = _get_revision_range(revision, b, self.name())
2858
if b.get_config_stack().get('validate_signatures_in_log'):
2862
if not gpg.GPGStrategy.verify_signatures_available():
2863
raise errors.GpgmeNotInstalled(None)
2865
# Decide on the type of delta & diff filtering to use
2866
# TODO: add an --all-files option to make this configurable & consistent
2874
diff_type = 'partial'
2878
# Build the log formatter
2879
if log_format is None:
2880
log_format = log.log_formatter_registry.get_default(b)
2881
# Make a non-encoding output to include the diffs - bug 328007
2882
unencoded_output = ui.ui_factory.make_output_stream(
2883
encoding_type='exact')
2884
lf = log_format(show_ids=show_ids, to_file=self.outf,
2885
to_exact_file=unencoded_output,
2886
show_timezone=timezone,
2887
delta_format=get_verbosity_level(),
2889
show_advice=levels is None,
2890
author_list_handler=authors)
2892
# Choose the algorithm for doing the logging. It's annoying
2893
# having multiple code paths like this but necessary until
2894
# the underlying repository format is faster at generating
2895
# deltas or can provide everything we need from the indices.
2896
# The default algorithm - match-using-deltas - works for
2897
# multiple files and directories and is faster for small
2898
# amounts of history (200 revisions say). However, it's too
2899
# slow for logging a single file in a repository with deep
2900
# history, i.e. > 10K revisions. In the spirit of "do no
2901
# evil when adding features", we continue to use the
2902
# original algorithm - per-file-graph - for the "single
2903
# file that isn't a directory without showing a delta" case.
2904
partial_history = revision and b.repository._format.supports_chks
2905
match_using_deltas = (len(files) != 1 or filter_by_dir
2906
or delta_type or partial_history)
2910
match_dict[''] = match
2912
match_dict['message'] = match_message
2914
match_dict['committer'] = match_committer
2916
match_dict['author'] = match_author
2918
match_dict['bugs'] = match_bugs
2920
# Build the LogRequest and execute it
2923
rqst = make_log_request_dict(
2924
direction=direction, specific_files=files,
2925
start_revision=rev1, end_revision=rev2, limit=limit,
2926
message_search=message, delta_type=delta_type,
2927
diff_type=diff_type, _match_using_deltas=match_using_deltas,
2928
exclude_common_ancestry=exclude_common_ancestry, match=match_dict,
2929
signature=signatures, omit_merges=omit_merges,
2931
Logger(b, rqst).show(lf)
2934
def _get_revision_range(revisionspec_list, branch, command_name):
2935
"""Take the input of a revision option and turn it into a revision range.
2937
It returns RevisionInfo objects which can be used to obtain the rev_id's
2938
of the desired revisions. It does some user input validations.
2940
if revisionspec_list is None:
2943
elif len(revisionspec_list) == 1:
2944
rev1 = rev2 = revisionspec_list[0].in_history(branch)
2945
elif len(revisionspec_list) == 2:
2946
start_spec = revisionspec_list[0]
2947
end_spec = revisionspec_list[1]
2948
if end_spec.get_branch() != start_spec.get_branch():
2949
# b is taken from revision[0].get_branch(), and
2950
# show_log will use its revision_history. Having
2951
# different branches will lead to weird behaviors.
2952
raise errors.CommandError(gettext(
2953
"brz %s doesn't accept two revisions in different"
2954
" branches.") % command_name)
2955
if start_spec.spec is None:
2956
# Avoid loading all the history.
2957
rev1 = RevisionInfo(branch, None, None)
2959
rev1 = start_spec.in_history(branch)
2960
# Avoid loading all of history when we know a missing
2961
# end of range means the last revision ...
2962
if end_spec.spec is None:
2963
last_revno, last_revision_id = branch.last_revision_info()
2964
rev2 = RevisionInfo(branch, last_revno, last_revision_id)
2966
rev2 = end_spec.in_history(branch)
2968
raise errors.CommandError(gettext(
2969
'brz %s --revision takes one or two values.') % command_name)
2973
def _revision_range_to_revid_range(revision_range):
2976
if revision_range[0] is not None:
2977
rev_id1 = revision_range[0].rev_id
2978
if revision_range[1] is not None:
2979
rev_id2 = revision_range[1].rev_id
2980
return rev_id1, rev_id2
2983
def get_log_format(long=False, short=False, line=False, default='long'):
2984
log_format = default
2988
log_format = 'short'
748
2994
class cmd_touching_revisions(Command):
749
"""Return revision-ids which affected a particular file.
751
A more user-friendly interface is "bzr log FILE"."""
2995
__doc__ = """Return revision-ids which affected a particular file.
2997
A more user-friendly interface is "brz log FILE".
753
3001
takes_args = ["filename"]
754
3004
def run(self, filename):
755
b = Branch.open_containing(filename)
756
inv = b.read_working_inventory()
757
file_id = inv.path2id(b.relpath(filename))
758
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
759
print "%6d %s" % (revno, what)
3005
tree, relpath = WorkingTree.open_containing(filename)
3006
with tree.lock_read():
3007
touching_revs = log.find_touching_revisions(
3008
tree.branch.repository, tree.branch.last_revision(), tree, relpath)
3009
for revno, revision_id, what in reversed(list(touching_revs)):
3010
self.outf.write("%6d %s\n" % (revno, what))
762
3013
class cmd_ls(Command):
763
"""List files in a tree.
765
TODO: Take a revision or remote path and list that tree instead.
3014
__doc__ = """List files in a tree.
768
def run(self, revision=None, verbose=False):
769
b = Branch.open_containing('.')
771
tree = b.working_tree()
3017
_see_also = ['status', 'cat']
3018
takes_args = ['path?']
3022
Option('recursive', short_name='R',
3023
help='Recurse into subdirectories.'),
3025
help='Print paths relative to the root of the branch.'),
3026
Option('unknown', short_name='u',
3027
help='Print unknown files.'),
3028
Option('versioned', help='Print versioned files.',
3030
Option('ignored', short_name='i',
3031
help='Print ignored files.'),
3032
Option('kind', short_name='k',
3033
help=('List entries of a particular kind: file, '
3034
'directory, symlink, tree-reference.'),
3042
def run(self, revision=None, verbose=False,
3043
recursive=False, from_root=False,
3044
unknown=False, versioned=False, ignored=False,
3045
null=False, kind=None, show_ids=False, path=None, directory=None):
3047
if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
3048
raise errors.CommandError(gettext('invalid kind specified'))
3050
if verbose and null:
3051
raise errors.CommandError(
3052
gettext('Cannot set both --verbose and --null'))
3053
all = not (unknown or versioned or ignored)
3055
selection = {'I': ignored, '?': unknown, 'V': versioned}
773
tree = b.revision_tree(revision.in_history(b).rev_id)
775
for fp, fc, kind, fid in tree.list_files():
3061
raise errors.CommandError(gettext('cannot specify both --from-root'
3064
tree, branch, relpath = \
3065
_open_directory_or_containing_tree_or_branch(fs_path, directory)
3067
# Calculate the prefix to use
3071
prefix = relpath + '/'
3072
elif fs_path != '.' and not fs_path.endswith('/'):
3073
prefix = fs_path + '/'
3075
if revision is not None or tree is None:
3076
tree = _get_one_revision_tree('ls', revision, branch=branch)
3079
if isinstance(tree, WorkingTree) and tree.supports_views():
3080
view_files = tree.views.lookup_view()
3083
view_str = views.view_display_str(view_files)
3084
note(gettext("Ignoring files outside view. View is %s") % view_str)
3086
self.enter_context(tree.lock_read())
3087
for fp, fc, fkind, entry in tree.list_files(
3088
include_root=False, from_dir=relpath, recursive=recursive):
3089
# Apply additional masking
3090
if not all and not selection[fc]:
3092
if kind is not None and fkind != kind:
3097
fullpath = osutils.pathjoin(relpath, fp)
3100
views.check_path_in_view(tree, fullpath)
3101
except views.FileOutsideView:
3106
fp = osutils.pathjoin(prefix, fp)
3107
kindch = entry.kind_character()
3108
outstring = fp + kindch
3109
ui.ui_factory.clear_term()
777
if kind == 'directory':
3111
outstring = '%-8s %s' % (fc, outstring)
3112
if show_ids and getattr(entry, 'file_id', None) is not None:
3113
outstring = "%-50s %s" % (outstring, entry.file_id.decode('utf-8'))
3114
self.outf.write(outstring + '\n')
3116
self.outf.write(fp + '\0')
3118
if getattr(entry, 'file_id', None) is not None:
3119
self.outf.write(entry.file_id.decode('utf-8'))
3120
self.outf.write('\0')
3124
if getattr(entry, 'file_id', None) is not None:
3125
my_id = entry.file_id.decode('utf-8')
3128
self.outf.write('%-50s %s\n' % (outstring, my_id))
784
print '%-8s %s%s' % (fc, fp, kindch)
3130
self.outf.write(outstring + '\n')
790
3133
class cmd_unknowns(Command):
791
"""List unknown files."""
793
from bzrlib.osutils import quotefn
794
for f in Branch.open_containing('.').unknowns():
3134
__doc__ = """List unknown files.
3139
takes_options = ['directory']
3142
def run(self, directory=u'.'):
3143
for f in WorkingTree.open_containing(directory)[0].unknowns():
3144
self.outf.write(osutils.quotefn(f) + '\n')
799
3147
class cmd_ignore(Command):
800
"""Ignore a command or pattern.
3148
__doc__ = """Ignore specified files or patterns.
3150
See ``brz help patterns`` for details on the syntax of patterns.
3152
If a .bzrignore file does not exist, the ignore command
3153
will create one and add the specified files or patterns to the newly
3154
created file. The ignore command will also automatically add the
3155
.bzrignore file to be versioned. Creating a .bzrignore file without
3156
the use of the ignore command will require an explicit add command.
802
3158
To remove patterns from the ignore list, edit the .bzrignore file.
804
If the pattern contains a slash, it is compared to the whole path
805
from the branch root. Otherwise, it is comapred to only the last
806
component of the path.
808
Ignore patterns are case-insensitive on case-insensitive systems.
810
Note: wildcards must be quoted from the shell on Unix.
813
bzr ignore ./Makefile
3159
After adding, editing or deleting that file either indirectly by
3160
using this command or directly by using an editor, be sure to commit
3163
Breezy also supports a global ignore file ~/.config/breezy/ignore. On
3164
Windows the global ignore file can be found in the application data
3166
C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
3167
Global ignores are not touched by this command. The global ignore file
3168
can be edited directly using an editor.
3170
Patterns prefixed with '!' are exceptions to ignore patterns and take
3171
precedence over regular ignores. Such exceptions are used to specify
3172
files that should be versioned which would otherwise be ignored.
3174
Patterns prefixed with '!!' act as regular ignore patterns, but have
3175
precedence over the '!' exception patterns.
3179
* Ignore patterns containing shell wildcards must be quoted from
3182
* Ignore patterns starting with "#" act as comments in the ignore file.
3183
To ignore patterns that begin with that character, use the "RE:" prefix.
3186
Ignore the top level Makefile::
3188
brz ignore ./Makefile
3190
Ignore .class files in all directories...::
3192
brz ignore "*.class"
3194
...but do not ignore "special.class"::
3196
brz ignore "!special.class"
3198
Ignore files whose name begins with the "#" character::
3202
Ignore .o files under the lib directory::
3204
brz ignore "lib/**/*.o"
3206
Ignore .o files under the lib directory::
3208
brz ignore "RE:lib/.*\\.o"
3210
Ignore everything but the "debian" toplevel directory::
3212
brz ignore "RE:(?!debian/).*"
3214
Ignore everything except the "local" toplevel directory,
3215
but always ignore autosave files ending in ~, even under local/::
3218
brz ignore "!./local"
816
takes_args = ['name_pattern']
818
def run(self, name_pattern):
819
from bzrlib.atomicfile import AtomicFile
822
b = Branch.open_containing('.')
823
ifn = b.abspath('.bzrignore')
825
if os.path.exists(ifn):
828
igns = f.read().decode('utf-8')
834
# TODO: If the file already uses crlf-style termination, maybe
835
# we should use that for the newly added lines?
837
if igns and igns[-1] != '\n':
839
igns += name_pattern + '\n'
842
f = AtomicFile(ifn, 'wt')
843
f.write(igns.encode('utf-8'))
848
inv = b.working_tree().inventory
849
if inv.path2id('.bzrignore'):
850
mutter('.bzrignore is already versioned')
852
mutter('need to make new .bzrignore file versioned')
853
b.add(['.bzrignore'])
3222
_see_also = ['status', 'ignored', 'patterns']
3223
takes_args = ['name_pattern*']
3224
takes_options = ['directory',
3225
Option('default-rules',
3226
help='Display the default ignore rules that brz uses.')
3229
def run(self, name_pattern_list=None, default_rules=None,
3231
from breezy import ignores
3232
if default_rules is not None:
3233
# dump the default rules and exit
3234
for pattern in ignores.USER_DEFAULTS:
3235
self.outf.write("%s\n" % pattern)
3237
if not name_pattern_list:
3238
raise errors.CommandError(gettext("ignore requires at least one "
3239
"NAME_PATTERN or --default-rules."))
3240
name_pattern_list = [globbing.normalize_pattern(p)
3241
for p in name_pattern_list]
3243
bad_patterns_count = 0
3244
for p in name_pattern_list:
3245
if not globbing.Globster.is_pattern_valid(p):
3246
bad_patterns_count += 1
3247
bad_patterns += ('\n %s' % p)
3249
msg = (ngettext('Invalid ignore pattern found. %s',
3250
'Invalid ignore patterns found. %s',
3251
bad_patterns_count) % bad_patterns)
3252
ui.ui_factory.show_error(msg)
3253
raise lazy_regex.InvalidPattern('')
3254
for name_pattern in name_pattern_list:
3255
if (name_pattern[0] == '/' or
3256
(len(name_pattern) > 1 and name_pattern[1] == ':')):
3257
raise errors.CommandError(gettext(
3258
"NAME_PATTERN should not be an absolute path"))
3259
tree, relpath = WorkingTree.open_containing(directory)
3260
ignores.tree_ignores_add_patterns(tree, name_pattern_list)
3261
ignored = globbing.Globster(name_pattern_list)
3263
self.enter_context(tree.lock_read())
3264
for filename, fc, fkind, entry in tree.list_files():
3265
id = getattr(entry, 'file_id', None)
3267
if ignored.match(filename):
3268
matches.append(filename)
3269
if len(matches) > 0:
3270
self.outf.write(gettext("Warning: the following files are version "
3271
"controlled and match your ignore pattern:\n%s"
3272
"\nThese files will continue to be version controlled"
3273
" unless you 'brz remove' them.\n") % ("\n".join(matches),))
857
3276
class cmd_ignored(Command):
858
"""List ignored files and the patterns that matched them.
860
See also: bzr ignore"""
862
tree = Branch.open_containing('.').working_tree()
863
for path, file_class, kind, file_id in tree.list_files():
3277
__doc__ = """List ignored files and the patterns that matched them.
3279
List all the ignored files and the ignore pattern that caused the file to
3282
Alternatively, to list just the files::
3287
encoding_type = 'replace'
3288
_see_also = ['ignore', 'ls']
3289
takes_options = ['directory']
3292
def run(self, directory=u'.'):
3293
tree = WorkingTree.open_containing(directory)[0]
3294
self.enter_context(tree.lock_read())
3295
for path, file_class, kind, entry in tree.list_files():
864
3296
if file_class != 'I':
866
## XXX: Slightly inefficient since this was already calculated
3298
# XXX: Slightly inefficient since this was already calculated
867
3299
pat = tree.is_ignored(path)
868
print '%-50s %s' % (path, pat)
3300
self.outf.write('%-50s %s\n' % (path, pat))
871
3303
class cmd_lookup_revision(Command):
872
"""Lookup the revision-id from a revision-number
3304
__doc__ = """Lookup the revision-id from a revision-number
875
bzr lookup-revision 33
3307
brz lookup-revision 33
878
3310
takes_args = ['revno']
880
def run(self, revno):
3311
takes_options = ['directory']
3314
def run(self, revno, directory=u'.'):
882
3316
revno = int(revno)
883
3317
except ValueError:
884
raise BzrCommandError("not a valid revision-number: %r" % revno)
886
print Branch.open_containing('.').get_rev_id(revno)
3318
raise errors.CommandError(gettext("not a valid revision-number: %r")
3320
revid = WorkingTree.open_containing(
3321
directory)[0].branch.get_rev_id(revno)
3322
self.outf.write("%s\n" % revid.decode('utf-8'))
889
3325
class cmd_export(Command):
890
"""Export past revision to destination directory.
3326
__doc__ = """Export current or past revision to a destination directory or archive.
892
3328
If no revision is specified this exports the last committed revision.
895
3331
given, try to find the format with the extension. If no extension
896
3332
is found exports to a directory (equivalent to --format=dir).
898
Root may be the top directory for tar, tgz and tbz2 formats. If none
899
is given, the top directory will be the root name of the file."""
900
# TODO: list known exporters
901
takes_args = ['dest']
902
takes_options = ['revision', 'format', 'root']
903
def run(self, dest, revision=None, format=None, root=None):
905
b = Branch.open_containing('.')
907
rev_id = b.last_patch()
909
if len(revision) != 1:
910
raise BzrError('bzr export --revision takes exactly 1 argument')
911
rev_id = revision[0].in_history(b).rev_id
912
t = b.revision_tree(rev_id)
913
root, ext = os.path.splitext(dest)
917
elif ext in (".gz", ".tgz"):
919
elif ext in (".bz2", ".tbz2"):
923
t.export(dest, format, root)
3334
If root is supplied, it will be used as the root directory inside
3335
container formats (tar, zip, etc). If it is not supplied it will default
3336
to the exported filename. The root option has no effect for 'dir' format.
3338
If branch is omitted then the branch containing the current working
3339
directory will be used.
3341
Note: Export of tree with non-ASCII filenames to zip is not supported.
3343
================= =========================
3344
Supported formats Autodetected by extension
3345
================= =========================
3348
tbz2 .tar.bz2, .tbz2
3351
================= =========================
3354
encoding_type = 'exact'
3355
takes_args = ['dest', 'branch_or_subdir?']
3356
takes_options = ['directory',
3358
help="Type of file to export to.",
3361
Option('filters', help='Apply content filters to export the '
3362
'convenient form.'),
3365
help="Name of the root directory inside the exported file."),
3366
Option('per-file-timestamps',
3367
help='Set modification time of files to that of the last '
3368
'revision in which it was changed.'),
3369
Option('uncommitted',
3370
help='Export the working tree contents rather than that of the '
3374
def run(self, dest, branch_or_subdir=None, revision=None, format=None,
3375
root=None, filters=False, per_file_timestamps=False, uncommitted=False,
3377
from .export import export, guess_format, get_root_name
3379
if branch_or_subdir is None:
3380
branch_or_subdir = directory
3382
(tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
3384
if tree is not None:
3385
self.enter_context(tree.lock_read())
3389
raise errors.CommandError(
3390
gettext("--uncommitted requires a working tree"))
3393
export_tree = _get_one_revision_tree(
3394
'export', revision, branch=b,
3398
format = guess_format(dest)
3401
root = get_root_name(dest)
3403
if not per_file_timestamps:
3404
force_mtime = time.time()
3409
from breezy.filter_tree import ContentFilterTree
3410
export_tree = ContentFilterTree(
3411
export_tree, export_tree._content_filter_stack)
3414
export(export_tree, dest, format, root, subdir,
3415
per_file_timestamps=per_file_timestamps)
3416
except errors.NoSuchExportFormat as e:
3417
raise errors.CommandError(
3418
gettext('Unsupported export format: %s') % e.format)
926
3421
class cmd_cat(Command):
927
"""Write a file's text from a previous revision."""
929
takes_options = ['revision']
3422
__doc__ = """Write the contents of a file as of a given revision to standard output.
3424
If no revision is nominated, the last revision is used.
3426
Note: Take care to redirect standard output when using this command on a
3431
takes_options = ['directory',
3432
Option('name-from-revision',
3433
help='The path name in the old tree.'),
3434
Option('filters', help='Apply content filters to display the '
3435
'convenience form.'),
930
3438
takes_args = ['filename']
932
def run(self, filename, revision=None):
934
raise BzrCommandError("bzr cat requires a revision number")
935
elif len(revision) != 1:
936
raise BzrCommandError("bzr cat --revision takes exactly one number")
937
b = Branch.open_containing('.')
938
b.print_file(b.relpath(filename), revision[0].in_history(b).revno)
3439
encoding_type = 'exact'
3442
def run(self, filename, revision=None, name_from_revision=False,
3443
filters=False, directory=None):
3444
if revision is not None and len(revision) != 1:
3445
raise errors.CommandError(gettext("brz cat --revision takes exactly"
3446
" one revision specifier"))
3447
tree, branch, relpath = \
3448
_open_directory_or_containing_tree_or_branch(filename, directory)
3449
self.enter_context(branch.lock_read())
3450
return self._run(tree, branch, relpath, filename, revision,
3451
name_from_revision, filters)
3453
def _run(self, tree, b, relpath, filename, revision, name_from_revision,
3457
tree = b.basis_tree()
3458
rev_tree = _get_one_revision_tree('cat', revision, branch=b)
3459
self.enter_context(rev_tree.lock_read())
3461
if name_from_revision:
3462
# Try in revision if requested
3463
if not rev_tree.is_versioned(relpath):
3464
raise errors.CommandError(gettext(
3465
"{0!r} is not present in revision {1}").format(
3466
filename, rev_tree.get_revision_id()))
3467
rev_tree_path = relpath
3470
rev_tree_path = _mod_tree.find_previous_path(
3471
tree, rev_tree, relpath)
3472
except errors.NoSuchFile:
3473
rev_tree_path = None
3475
if rev_tree_path is None:
3476
# Path didn't exist in working tree
3477
if not rev_tree.is_versioned(relpath):
3478
raise errors.CommandError(gettext(
3479
"{0!r} is not present in revision {1}").format(
3480
filename, rev_tree.get_revision_id()))
3482
# Fall back to the same path in the basis tree, if present.
3483
rev_tree_path = relpath
3486
from .filter_tree import ContentFilterTree
3487
filter_tree = ContentFilterTree(
3488
rev_tree, rev_tree._content_filter_stack)
3489
fileobj = filter_tree.get_file(rev_tree_path)
3491
fileobj = rev_tree.get_file(rev_tree_path)
3492
shutil.copyfileobj(fileobj, self.outf)
941
3496
class cmd_local_time_offset(Command):
942
"""Show the offset in seconds from GMT to local time."""
3497
__doc__ = """Show the offset in seconds from GMT to local time."""
945
print bzrlib.osutils.local_time_offset()
3502
self.outf.write("%s\n" % osutils.local_time_offset())
949
3505
class cmd_commit(Command):
950
"""Commit changes into a new revision.
952
If no arguments are given, the entire tree is committed.
954
If selected files are specified, only changes to those files are
955
committed. If a directory is specified then the directory and everything
956
within it is committed.
958
A selected-file commit may fail in some cases where the committed
959
tree would be invalid, such as trying to commit a file in a
960
newly-added directory that is not itself committed.
962
TODO: Run hooks on tree to-be-committed, and after commit.
964
TODO: Strict commit that fails if there are unknown or deleted files.
3506
__doc__ = """Commit changes into a new revision.
3508
An explanatory message needs to be given for each commit. This is
3509
often done by using the --message option (getting the message from the
3510
command line) or by using the --file option (getting the message from
3511
a file). If neither of these options is given, an editor is opened for
3512
the user to enter the message. To see the changed files in the
3513
boilerplate text loaded into the editor, use the --show-diff option.
3515
By default, the entire tree is committed and the person doing the
3516
commit is assumed to be the author. These defaults can be overridden
3521
If selected files are specified, only changes to those files are
3522
committed. If a directory is specified then the directory and
3523
everything within it is committed.
3525
When excludes are given, they take precedence over selected files.
3526
For example, to commit only changes within foo, but not changes
3529
brz commit foo -x foo/bar
3531
A selective commit after a merge is not yet supported.
3535
If the author of the change is not the same person as the committer,
3536
you can specify the author's name using the --author option. The
3537
name should be in the same format as a committer-id, e.g.
3538
"John Doe <jdoe@example.com>". If there is more than one author of
3539
the change you can specify the option multiple times, once for each
3544
A common mistake is to forget to add a new file or directory before
3545
running the commit command. The --strict option checks for unknown
3546
files and aborts the commit if any are found. More advanced pre-commit
3547
checks can be implemented by defining hooks. See ``brz help hooks``
3552
If you accidentally commit the wrong changes or make a spelling
3553
mistake in the commit message say, you can use the uncommit command
3554
to undo it. See ``brz help uncommit`` for details.
3556
Hooks can also be configured to run after a commit. This allows you
3557
to trigger updates to external systems like bug trackers. The --fixes
3558
option can be used to record the association between a revision and
3559
one or more bugs. See ``brz help bugs`` for details.
3562
_see_also = ['add', 'bugs', 'hooks', 'uncommit']
966
3563
takes_args = ['selected*']
967
takes_options = ['message', 'file', 'verbose', 'unchanged']
3566
'exclude', type=str, short_name='x',
3567
help="Do not consider changes made to a given path."),
3568
Option('message', type=str,
3570
help="Description of the new revision."),
3573
help='Commit even if nothing has changed.'),
3574
Option('file', type=str,
3577
help='Take commit message from this file.'),
3579
help="Refuse to commit if there are unknown "
3580
"files in the working tree."),
3581
Option('commit-time', type=str,
3582
help="Manually set a commit time using commit date "
3583
"format, e.g. '2009-10-10 08:00:00 +0100'."),
3586
help="Link to a related bug. (see \"brz help bugs\")."),
3589
help="Mark a bug as being fixed by this revision "
3590
"(see \"brz help bugs\")."),
3593
help="Set the author's name, if it's different "
3594
"from the committer."),
3596
help="Perform a local commit in a bound "
3597
"branch. Local commits are not pushed to "
3598
"the master branch until a normal commit "
3601
Option('show-diff', short_name='p',
3602
help='When no message is supplied, show the diff along'
3603
' with the status summary in the message editor.'),
3605
help='When committing to a foreign version control '
3606
'system do not push data that can not be natively '
968
3608
aliases = ['ci', 'checkin']
970
# TODO: Give better message for -s, --summary, used by tla people
972
def run(self, message=None, file=None, verbose=True, selected_list=None,
974
from bzrlib.errors import PointlessCommit
975
from bzrlib.msgeditor import edit_commit_message
976
from bzrlib.status import show_status
977
from cStringIO import StringIO
979
b = Branch.open_containing('.')
981
selected_list = [b.relpath(s) for s in selected_list]
983
if not message and not file:
985
show_status(b, specific_files=selected_list,
987
message = edit_commit_message(catcher.getvalue())
990
raise BzrCommandError("please specify a commit message"
991
" with either --message or --file")
992
elif message and file:
993
raise BzrCommandError("please specify either --message or --file")
997
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
3610
def _iter_bug_urls(self, bugs, branch, status):
3611
default_bugtracker = None
3612
# Configure the properties for bug fixing attributes.
3614
tokens = bug.split(':')
3615
if len(tokens) == 1:
3616
if default_bugtracker is None:
3617
branch_config = branch.get_config_stack()
3618
default_bugtracker = branch_config.get(
3620
if default_bugtracker is None:
3621
raise errors.CommandError(gettext(
3622
"No tracker specified for bug %s. Use the form "
3623
"'tracker:id' or specify a default bug tracker "
3624
"using the `bugtracker` option.\nSee "
3625
"\"brz help bugs\" for more information on this "
3626
"feature. Commit refused.") % bug)
3627
tag = default_bugtracker
3629
elif len(tokens) != 2:
3630
raise errors.CommandError(gettext(
3631
"Invalid bug %s. Must be in the form of 'tracker:id'. "
3632
"See \"brz help bugs\" for more information on this "
3633
"feature.\nCommit refused.") % bug)
3635
tag, bug_id = tokens
3637
yield bugtracker.get_bug_url(tag, branch, bug_id), status
3638
except bugtracker.UnknownBugTrackerAbbreviation:
3639
raise errors.CommandError(gettext(
3640
'Unrecognized bug %s. Commit refused.') % bug)
3641
except bugtracker.MalformedBugIdentifier as e:
3642
raise errors.CommandError(gettext(
3643
u"%s\nCommit refused.") % (e,))
3645
def run(self, message=None, file=None, verbose=False, selected_list=None,
3646
unchanged=False, strict=False, local=False, fixes=None, bugs=None,
3647
author=None, show_diff=False, exclude=None, commit_time=None,
3650
from .commit import (
3653
from .errors import (
3657
from .msgeditor import (
3658
edit_commit_message_encoded,
3659
generate_commit_message_template,
3660
make_commit_message_template_encoded,
3664
commit_stamp = offset = None
3665
if commit_time is not None:
3667
commit_stamp, offset = timestamp.parse_patch_date(commit_time)
3668
except ValueError as e:
3669
raise errors.CommandError(gettext(
3670
"Could not parse --commit-time: " + str(e)))
3674
tree, selected_list = WorkingTree.open_containing_paths(selected_list)
3675
if selected_list == ['']:
3676
# workaround - commit of root of tree should be exactly the same
3677
# as just default commit in that tree, and succeed even though
3678
# selected-file merge commit is not done yet
3685
bug_property = bugtracker.encode_fixes_bug_urls(
3687
self._iter_bug_urls(bugs, tree.branch, bugtracker.RELATED),
3688
self._iter_bug_urls(fixes, tree.branch, bugtracker.FIXED)))
3690
properties[u'bugs'] = bug_property
3692
if local and not tree.branch.get_bound_location():
3693
raise errors.LocalRequiresBoundBranch()
3695
if message is not None:
3697
file_exists = osutils.lexists(message)
3698
except UnicodeError:
3699
# The commit message contains unicode characters that can't be
3700
# represented in the filesystem encoding, so that can't be a
3705
'The commit message is a file name: "%(f)s".\n'
3706
'(use --file "%(f)s" to take commit message from that file)'
3708
ui.ui_factory.show_warning(warning_msg)
3710
message = message.replace('\r\n', '\n')
3711
message = message.replace('\r', '\n')
3713
raise errors.CommandError(gettext(
3714
"please specify either --message or --file"))
3716
def get_message(commit_obj):
3717
"""Callback to get commit message"""
3719
with open(file, 'rb') as f:
3720
my_message = f.read().decode(osutils.get_user_encoding())
3721
elif message is not None:
3722
my_message = message
3724
# No message supplied: make one up.
3725
# text is the status of the tree
3726
text = make_commit_message_template_encoded(tree,
3727
selected_list, diff=show_diff,
3728
output_encoding=osutils.get_user_encoding())
3729
# start_message is the template generated from hooks
3730
# XXX: Warning - looks like hooks return unicode,
3731
# make_commit_message_template_encoded returns user encoding.
3732
# We probably want to be using edit_commit_message instead to
3734
my_message = set_commit_message(commit_obj)
3735
if my_message is None:
3736
start_message = generate_commit_message_template(
3738
if start_message is not None:
3739
start_message = start_message.encode(
3740
osutils.get_user_encoding())
3741
my_message = edit_commit_message_encoded(text,
3742
start_message=start_message)
3743
if my_message is None:
3744
raise errors.CommandError(gettext("please specify a commit"
3745
" message with either --message or --file"))
3746
if my_message == "":
3747
raise errors.CommandError(gettext("Empty commit message specified."
3748
" Please specify a commit message with either"
3749
" --message or --file or leave a blank message"
3750
" with --message \"\"."))
3753
# The API permits a commit with a filter of [] to mean 'select nothing'
3754
# but the command line should not do that.
3755
if not selected_list:
3756
selected_list = None
1000
b.commit(message, verbose=verbose,
1001
specific_files=selected_list,
1002
allow_pointless=unchanged)
3758
tree.commit(message_callback=get_message,
3759
specific_files=selected_list,
3760
allow_pointless=unchanged, strict=strict, local=local,
3761
reporter=None, verbose=verbose, revprops=properties,
3762
authors=author, timestamp=commit_stamp,
3764
exclude=tree.safe_relpath_files(exclude),
1003
3766
except PointlessCommit:
1004
# FIXME: This should really happen before the file is read in;
1005
# perhaps prepare the commit; get the message; then actually commit
1006
raise BzrCommandError("no changes to commit",
1007
["use --unchanged to commit anyhow"])
3767
raise errors.CommandError(gettext("No changes to commit."
3768
" Please 'brz add' the files you want to commit, or use"
3769
" --unchanged to force an empty commit."))
3770
except ConflictsInTree:
3771
raise errors.CommandError(gettext('Conflicts detected in working '
3772
'tree. Use "brz conflicts" to list, "brz resolve FILE" to'
3774
except StrictCommitFailed:
3775
raise errors.CommandError(gettext("Commit refused because there are"
3776
" unknown files in the working tree."))
3777
except errors.BoundBranchOutOfDate as e:
3778
e.extra_help = (gettext("\n"
3779
'To commit to master branch, run update and then commit.\n'
3780
'You can also pass --local to commit to continue working '
1010
3785
class cmd_check(Command):
1011
"""Validate consistency of branch history.
1013
This command checks various invariants about the branch storage to
1014
detect data corruption or bzr bugs.
3786
__doc__ = """Validate working tree structure, branch consistency and repository history.
3788
This command checks various invariants about branch and repository storage
3789
to detect data corruption or brz bugs.
3791
The working tree and branch checks will only give output if a problem is
3792
detected. The output fields of the repository check are:
3795
This is just the number of revisions checked. It doesn't
3799
This is just the number of versionedfiles checked. It
3800
doesn't indicate a problem.
3802
unreferenced ancestors
3803
Texts that are ancestors of other texts, but
3804
are not properly referenced by the revision ancestry. This is a
3805
subtle problem that Breezy can work around.
3808
This is the total number of unique file contents
3809
seen in the checked revisions. It does not indicate a problem.
3812
This is the total number of repeated texts seen
3813
in the checked revisions. Texts can be repeated when their file
3814
entries are modified, but the file contents are not. It does not
3817
If no restrictions are specified, all data that is found at the given
3818
location will be checked.
3822
Check the tree and branch at 'foo'::
3824
brz check --tree --branch foo
3826
Check only the repository at 'bar'::
3828
brz check --repo bar
3830
Check everything at 'baz'::
1016
takes_args = ['dir?']
1018
def run(self, dir='.'):
1019
from bzrlib.check import check
1021
check(Branch.open_containing(dir))
1024
class cmd_scan_cache(Command):
1027
from bzrlib.hashcache import HashCache
1033
print '%6d stats' % c.stat_count
1034
print '%6d in hashcache' % len(c._cache)
1035
print '%6d files removed from cache' % c.removed_count
1036
print '%6d hashes updated' % c.update_count
1037
print '%6d files changed too recently to cache' % c.danger_count
3835
_see_also = ['reconcile']
3836
takes_args = ['path?']
3837
takes_options = ['verbose',
3838
Option('branch', help="Check the branch related to the"
3839
" current directory."),
3840
Option('repo', help="Check the repository related to the"
3841
" current directory."),
3842
Option('tree', help="Check the working tree related to"
3843
" the current directory.")]
3845
def run(self, path=None, verbose=False, branch=False, repo=False,
3847
from .check import check_dwim
3850
if not branch and not repo and not tree:
3851
branch = repo = tree = True
3852
check_dwim(path, verbose, do_branch=branch, do_repo=repo, do_tree=tree)
1044
3855
class cmd_upgrade(Command):
1045
"""Upgrade branch storage to current format.
1047
The check command or bzr developers may sometimes advise you to run
3856
__doc__ = """Upgrade a repository, branch or working tree to a newer format.
3858
When the default format has changed after a major new release of
3859
Bazaar/Breezy, you may be informed during certain operations that you
3860
should upgrade. Upgrading to a newer format may improve performance
3861
or make new features available. It may however limit interoperability
3862
with older repositories or with older versions of Bazaar or Breezy.
3864
If you wish to upgrade to a particular format rather than the
3865
current default, that can be specified using the --format option.
3866
As a consequence, you can use the upgrade command this way to
3867
"downgrade" to an earlier format, though some conversions are
3868
a one way process (e.g. changing from the 1.x default to the
3869
2.x default) so downgrading is not always possible.
3871
A backup.bzr.~#~ directory is created at the start of the conversion
3872
process (where # is a number). By default, this is left there on
3873
completion. If the conversion fails, delete the new .bzr directory
3874
and rename this one back in its place. Use the --clean option to ask
3875
for the backup.bzr directory to be removed on successful conversion.
3876
Alternatively, you can delete it by hand if everything looks good
3879
If the location given is a shared repository, dependent branches
3880
are also converted provided the repository converts successfully.
3881
If the conversion of a branch fails, remaining branches are still
3884
For more information on upgrades, see the Breezy Upgrade Guide,
3885
https://www.breezy-vcs.org/doc/en/upgrade-guide/.
1050
takes_args = ['dir?']
1052
def run(self, dir='.'):
1053
from bzrlib.upgrade import upgrade
1054
upgrade(Branch.open_containing(dir))
3888
_see_also = ['check', 'reconcile', 'formats']
3889
takes_args = ['url?']
3891
RegistryOption('format',
3892
help='Upgrade to a specific format. See "brz help'
3893
' formats" for details.',
3894
lazy_registry=('breezy.controldir', 'format_registry'),
3895
converter=lambda name: controldir.format_registry.make_controldir(
3897
value_switches=True, title='Branch format'),
3899
help='Remove the backup.bzr directory if successful.'),
3901
help="Show what would be done, but don't actually do anything."),
3904
def run(self, url='.', format=None, clean=False, dry_run=False):
3905
from .upgrade import upgrade
3906
exceptions = upgrade(url, format, clean_up=clean, dry_run=dry_run)
3908
if len(exceptions) == 1:
3909
# Compatibility with historical behavior
1058
3915
class cmd_whoami(Command):
1059
"""Show bzr user id."""
1060
takes_options = ['email']
1062
def run(self, email=False):
1064
b = bzrlib.branch.Branch.open_containing('.')
3916
__doc__ = """Show or set brz user id.
3919
Show the email of the current user::
3923
Set the current user::
3925
brz whoami "Frank Chu <fchu@example.com>"
3927
takes_options = ['directory',
3929
help='Display email address only.'),
3931
help='Set identity for the current branch instead of '
3934
takes_args = ['name?']
3935
encoding_type = 'replace'
3938
def run(self, email=False, branch=False, name=None, directory=None):
3940
if directory is None:
3941
# use branch if we're inside one; otherwise global config
3943
c = Branch.open_containing(u'.')[0].get_config_stack()
3944
except errors.NotBranchError:
3945
c = _mod_config.GlobalStack()
3947
c = Branch.open(directory).get_config_stack()
3948
identity = c.get('email')
3950
self.outf.write(_mod_config.extract_email_address(identity)
3953
self.outf.write(identity + '\n')
1069
print bzrlib.osutils.user_email(b)
1071
print bzrlib.osutils.username(b)
3957
raise errors.CommandError(gettext("--email can only be used to display existing "
3960
# display a warning if an email address isn't included in the given name.
3962
_mod_config.extract_email_address(name)
3963
except _mod_config.NoEmailInUsername:
3964
warning('"%s" does not seem to contain an email address. '
3965
'This is allowed, but not recommended.', name)
3967
# use global config unless --branch given
3969
if directory is None:
3970
c = Branch.open_containing(u'.')[0].get_config_stack()
3972
b = Branch.open(directory)
3973
self.enter_context(b.lock_write())
3974
c = b.get_config_stack()
3976
c = _mod_config.GlobalStack()
3977
c.set('email', name)
3980
class cmd_nick(Command):
3981
__doc__ = """Print or set the branch nickname.
3983
If unset, the colocated branch name is used for colocated branches, and
3984
the branch directory name is used for other branches. To print the
3985
current nickname, execute with no argument.
3987
Bound branches use the nickname of its master branch unless it is set
3991
_see_also = ['info']
3992
takes_args = ['nickname?']
3993
takes_options = ['directory']
3995
def run(self, nickname=None, directory=u'.'):
3996
branch = Branch.open_containing(directory)[0]
3997
if nickname is None:
3998
self.printme(branch)
4000
branch.nick = nickname
4003
def printme(self, branch):
4004
self.outf.write('%s\n' % branch.nick)
4007
class cmd_alias(Command):
4008
__doc__ = """Set/unset and display aliases.
4011
Show the current aliases::
4015
Show the alias specified for 'll'::
4019
Set an alias for 'll'::
4021
brz alias ll="log --line -r-10..-1"
4023
To remove an alias for 'll'::
4025
brz alias --remove ll
4028
takes_args = ['name?']
4030
Option('remove', help='Remove the alias.'),
4033
def run(self, name=None, remove=False):
4035
self.remove_alias(name)
4037
self.print_aliases()
4039
equal_pos = name.find('=')
4041
self.print_alias(name)
4043
self.set_alias(name[:equal_pos], name[equal_pos + 1:])
4045
def remove_alias(self, alias_name):
4046
if alias_name is None:
4047
raise errors.CommandError(gettext(
4048
'brz alias --remove expects an alias to remove.'))
4049
# If alias is not found, print something like:
4050
# unalias: foo: not found
4051
c = _mod_config.GlobalConfig()
4052
c.unset_alias(alias_name)
4055
def print_aliases(self):
4056
"""Print out the defined aliases in a similar format to bash."""
4057
aliases = _mod_config.GlobalConfig().get_aliases()
4058
for key, value in sorted(aliases.items()):
4059
self.outf.write('brz alias %s="%s"\n' % (key, value))
4062
def print_alias(self, alias_name):
4063
from .commands import get_alias
4064
alias = get_alias(alias_name)
4066
self.outf.write("brz alias: %s: not found\n" % alias_name)
4069
'brz alias %s="%s"\n' % (alias_name, ' '.join(alias)))
4071
def set_alias(self, alias_name, alias_command):
4072
"""Save the alias in the global config."""
4073
c = _mod_config.GlobalConfig()
4074
c.set_alias(alias_name, alias_command)
1074
4077
class cmd_selftest(Command):
1075
"""Run internal test suite"""
4078
__doc__ = """Run internal test suite.
4080
If arguments are given, they are regular expressions that say which tests
4081
should run. Tests matching any expression are run, and other tests are
4084
Alternatively if --first is given, matching tests are run first and then
4085
all other tests are run. This is useful if you have been working in a
4086
particular area, but want to make sure nothing else was broken.
4088
If --exclude is given, tests that match that regular expression are
4089
excluded, regardless of whether they match --first or not.
4091
To help catch accidential dependencies between tests, the --randomize
4092
option is useful. In most cases, the argument used is the word 'now'.
4093
Note that the seed used for the random number generator is displayed
4094
when this option is used. The seed can be explicitly passed as the
4095
argument to this option if required. This enables reproduction of the
4096
actual ordering used if and when an order sensitive problem is encountered.
4098
If --list-only is given, the tests that would be run are listed. This is
4099
useful when combined with --first, --exclude and/or --randomize to
4100
understand their impact. The test harness reports "Listed nn tests in ..."
4101
instead of "Ran nn tests in ..." when list mode is enabled.
4103
If the global option '--no-plugins' is given, plugins are not loaded
4104
before running the selftests. This has two effects: features provided or
4105
modified by plugins will not be tested, and tests provided by plugins will
4108
Tests that need working space on disk use a common temporary directory,
4109
typically inside $TMPDIR or /tmp.
4111
If you set BRZ_TEST_PDB=1 when running selftest, failing tests will drop
4112
into a pdb postmortem session.
4114
The --coverage=DIRNAME global option produces a report with covered code
4118
Run only tests relating to 'ignore'::
4122
Disable plugins and list tests as they're run::
4124
brz --no-plugins selftest -v
4126
# NB: this is used from the class without creating an instance, which is
4127
# why it does not have a self parameter.
4129
def get_transport_type(typestring):
4130
"""Parse and return a transport specifier."""
4131
if typestring == "sftp":
4132
from .tests import stub_sftp
4133
return stub_sftp.SFTPAbsoluteServer
4134
elif typestring == "memory":
4135
from .tests import test_server
4136
return memory.MemoryServer
4137
elif typestring == "fakenfs":
4138
from .tests import test_server
4139
return test_server.FakeNFSServer
4140
msg = "No known transport type %s. Supported types are: sftp\n" %\
4142
raise errors.CommandError(msg)
1077
takes_options = ['verbose', 'pattern']
1078
def run(self, verbose=False, pattern=".*"):
1080
from bzrlib.selftest import selftest
1081
# we don't want progress meters from the tests to go to the
1082
# real output; and we don't want log messages cluttering up
1084
save_ui = bzrlib.ui.ui_factory
1085
bzrlib.trace.info('running tests...')
1087
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1088
result = selftest(verbose=verbose, pattern=pattern)
1090
bzrlib.trace.info('tests passed')
1092
bzrlib.trace.info('tests failed')
1093
return int(not result)
4145
takes_args = ['testspecs*']
4146
takes_options = ['verbose',
4148
help='Stop when one test fails.',
4152
help='Use a different transport by default '
4153
'throughout the test suite.',
4154
type=get_transport_type),
4156
help='Run the benchmarks rather than selftests.',
4158
Option('lsprof-timed',
4159
help='Generate lsprof output for benchmarked'
4160
' sections of code.'),
4161
Option('lsprof-tests',
4162
help='Generate lsprof output for each test.'),
4164
help='Run all tests, but run specified tests first.',
4168
help='List the tests instead of running them.'),
4169
RegistryOption('parallel',
4170
help="Run the test suite in parallel.",
4172
'breezy.tests', 'parallel_registry'),
4173
value_switches=False,
4175
Option('randomize', type=str, argname="SEED",
4176
help='Randomize the order of tests using the given'
4177
' seed or "now" for the current time.'),
4178
ListOption('exclude', type=str, argname="PATTERN",
4180
help='Exclude tests that match this regular'
4183
help='Output test progress via subunit v1.'),
4185
help='Output test progress via subunit v2.'),
4186
Option('strict', help='Fail on missing dependencies or '
4188
Option('load-list', type=str, argname='TESTLISTFILE',
4189
help='Load a test id list from a text file.'),
4190
ListOption('debugflag', type=str, short_name='E',
4191
help='Turn on a selftest debug flag.'),
4192
ListOption('starting-with', type=str, argname='TESTID',
4193
param_name='starting_with', short_name='s',
4194
help='Load only the tests starting with TESTID.'),
4196
help="By default we disable fsync and fdatasync"
4197
" while running the test suite.")
4199
encoding_type = 'replace'
4202
Command.__init__(self)
4203
self.additional_selftest_args = {}
4205
def run(self, testspecs_list=None, verbose=False, one=False,
4206
transport=None, benchmark=None,
4208
first=False, list_only=False,
4209
randomize=None, exclude=None, strict=False,
4210
load_list=None, debugflag=None, starting_with=None, subunit1=False,
4211
subunit2=False, parallel=None, lsprof_tests=False, sync=False):
4213
# During selftest, disallow proxying, as it can cause severe
4214
# performance penalties and is only needed for thread
4215
# safety. The selftest command is assumed to not use threads
4216
# too heavily. The call should be as early as possible, as
4217
# error reporting for past duplicate imports won't have useful
4219
if sys.version_info[0] < 3:
4220
# TODO(pad.lv/1696545): Allow proxying on Python 3, since
4221
# disallowing it currently leads to failures in many places.
4222
lazy_import.disallow_proxying()
4226
except ImportError as e:
4227
raise errors.CommandError("tests not available. Install the "
4228
"breezy tests to run the breezy testsuite.")
4230
if testspecs_list is not None:
4231
pattern = '|'.join(testspecs_list)
4236
from .tests import SubUnitBzrRunnerv1
4238
raise errors.CommandError(gettext(
4239
"subunit not available. subunit needs to be installed "
4240
"to use --subunit."))
4241
self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
4242
# On Windows, disable automatic conversion of '\n' to '\r\n' in
4243
# stdout, which would corrupt the subunit stream.
4244
# FIXME: This has been fixed in subunit trunk (>0.0.5) so the
4245
# following code can be deleted when it's sufficiently deployed
4246
# -- vila/mgz 20100514
4247
if (sys.platform == "win32"
4248
and getattr(sys.stdout, 'fileno', None) is not None):
4250
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4253
from .tests import SubUnitBzrRunnerv2
4255
raise errors.CommandError(gettext(
4256
"subunit not available. subunit "
4257
"needs to be installed to use --subunit2."))
4258
self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv2
4261
self.additional_selftest_args.setdefault(
4262
'suite_decorators', []).append(parallel)
4264
raise errors.CommandError(gettext(
4265
"--benchmark is no longer supported from brz 2.2; "
4266
"use bzr-usertest instead"))
4267
test_suite_factory = None
4269
exclude_pattern = None
4271
exclude_pattern = '(' + '|'.join(exclude) + ')'
4273
self._disable_fsync()
4274
selftest_kwargs = {"verbose": verbose,
4276
"stop_on_failure": one,
4277
"transport": transport,
4278
"test_suite_factory": test_suite_factory,
4279
"lsprof_timed": lsprof_timed,
4280
"lsprof_tests": lsprof_tests,
4281
"matching_tests_first": first,
4282
"list_only": list_only,
4283
"random_seed": randomize,
4284
"exclude_pattern": exclude_pattern,
4286
"load_list": load_list,
4287
"debug_flags": debugflag,
4288
"starting_with": starting_with
4290
selftest_kwargs.update(self.additional_selftest_args)
4292
# Make deprecation warnings visible, unless -Werror is set
4293
cleanup = symbol_versioning.activate_deprecation_warnings(
4296
result = tests.selftest(**selftest_kwargs)
1095
bzrlib.ui.ui_factory = save_ui
1099
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1100
# is bzrlib itself in a branch?
1101
bzrrev = bzrlib.get_bzr_revision()
1103
print " (bzr checkout, revision %d {%s})" % bzrrev
1104
print bzrlib.__copyright__
1105
print "http://bazaar-ng.org/"
1107
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1108
print "you may use, modify and redistribute it under the terms of the GNU"
1109
print "General Public License version 2 or later."
4299
return int(not result)
4301
def _disable_fsync(self):
4302
"""Change the 'os' functionality to not synchronize."""
4303
self._orig_fsync = getattr(os, 'fsync', None)
4304
if self._orig_fsync is not None:
4305
os.fsync = lambda filedes: None
4306
self._orig_fdatasync = getattr(os, 'fdatasync', None)
4307
if self._orig_fdatasync is not None:
4308
os.fdatasync = lambda filedes: None
1112
4311
class cmd_version(Command):
1113
"""Show version of bzr."""
4312
__doc__ = """Show version of brz."""
4314
encoding_type = 'replace'
4316
Option("short", help="Print just the version number."),
4320
def run(self, short=False):
4321
from .version import show_version
4323
self.outf.write(breezy.version_string + '\n')
4325
show_version(to_file=self.outf)
1117
4328
class cmd_rocks(Command):
1118
"""Statement of optimism."""
4329
__doc__ = """Statement of optimism."""
1121
print "it sure does!"
4335
self.outf.write(gettext("It sure does!\n"))
1124
4338
class cmd_find_merge_base(Command):
1125
"""Find and print a base revision for merging two branches.
1127
TODO: Options to specify revisions on either side, as if
1128
merging only part of the history.
4339
__doc__ = """Find and print a base revision for merging two branches."""
4340
# TODO: Options to specify revisions on either side, as if
4341
# merging only part of the history.
1130
4342
takes_args = ['branch', 'other']
1133
4346
def run(self, branch, other):
1134
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1136
branch1 = Branch.open_containing(branch)
1137
branch2 = Branch.open_containing(other)
1139
history_1 = branch1.revision_history()
1140
history_2 = branch2.revision_history()
1142
last1 = branch1.last_patch()
1143
last2 = branch2.last_patch()
1145
source = MultipleRevisionSources(branch1, branch2)
1147
base_rev_id = common_ancestor(last1, last2, source)
1149
print 'merge base is revision %s' % base_rev_id
1153
if base_revno is None:
1154
raise bzrlib.errors.UnrelatedBranches()
1156
print ' r%-6d in %s' % (base_revno, branch)
1158
other_revno = branch2.revision_id_to_revno(base_revid)
1160
print ' r%-6d in %s' % (other_revno, other)
4347
from .revision import ensure_null
4349
branch1 = Branch.open_containing(branch)[0]
4350
branch2 = Branch.open_containing(other)[0]
4351
self.enter_context(branch1.lock_read())
4352
self.enter_context(branch2.lock_read())
4353
last1 = ensure_null(branch1.last_revision())
4354
last2 = ensure_null(branch2.last_revision())
4356
graph = branch1.repository.get_graph(branch2.repository)
4357
base_rev_id = graph.find_unique_lca(last1, last2)
4359
self.outf.write(gettext('merge base is revision %s\n') %
4360
base_rev_id.decode('utf-8'))
1164
4363
class cmd_merge(Command):
1165
"""Perform a three-way merge.
1167
The branch is the branch you will merge from. By default, it will
1168
merge the latest revision. If you specify a revision, that
1169
revision will be merged. If you specify two revisions, the first
1170
will be used as a BASE, and the second one as OTHER. Revision
1171
numbers are always relative to the specified branch.
1173
By default bzr will try to merge in all new work from the other
1174
branch, automatically determining an appropriate base. If this
1175
fails, you may need to give an explicit base.
1179
To merge the latest revision from bzr.dev
1180
bzr merge ../bzr.dev
1182
To merge changes up to and including revision 82 from bzr.dev
1183
bzr merge -r 82 ../bzr.dev
1185
To merge the changes introduced by 82, without previous changes:
1186
bzr merge -r 81..82 ../bzr.dev
4364
__doc__ = """Perform a three-way merge.
4366
The source of the merge can be specified either in the form of a branch,
4367
or in the form of a path to a file containing a merge directive generated
4368
with brz send. If neither is specified, the default is the upstream branch
4369
or the branch most recently merged using --remember. The source of the
4370
merge may also be specified in the form of a path to a file in another
4371
branch: in this case, only the modifications to that file are merged into
4372
the current working tree.
4374
When merging from a branch, by default brz will try to merge in all new
4375
work from the other branch, automatically determining an appropriate base
4376
revision. If this fails, you may need to give an explicit base.
4378
To pick a different ending revision, pass "--revision OTHER". brz will
4379
try to merge in all new work up to and including revision OTHER.
4381
If you specify two values, "--revision BASE..OTHER", only revisions BASE
4382
through OTHER, excluding BASE but including OTHER, will be merged. If this
4383
causes some revisions to be skipped, i.e. if the destination branch does
4384
not already contain revision BASE, such a merge is commonly referred to as
4385
a "cherrypick". Unlike a normal merge, Breezy does not currently track
4386
cherrypicks. The changes look like a normal commit, and the history of the
4387
changes from the other branch is not stored in the commit.
4389
Revision numbers are always relative to the source branch.
4391
Merge will do its best to combine the changes in two branches, but there
4392
are some kinds of problems only a human can fix. When it encounters those,
4393
it will mark a conflict. A conflict means that you need to fix something,
4394
before you can commit.
4396
Use brz resolve when you have fixed a problem. See also brz conflicts.
4398
If there is no default branch set, the first merge will set it (use
4399
--no-remember to avoid setting it). After that, you can omit the branch
4400
to use the default. To change the default, use --remember. The value will
4401
only be saved if the remote location can be accessed.
4403
The results of the merge are placed into the destination working
4404
directory, where they can be reviewed (with brz diff), tested, and then
4405
committed to record the result of the merge.
1188
4407
merge refuses to run if there are any uncommitted changes, unless
1191
takes_args = ['branch?']
1192
takes_options = ['revision', 'force', 'merge-type']
1194
def run(self, branch='.', revision=None, force=False,
1196
from bzrlib.merge import merge
1197
from bzrlib.merge_core import ApplyMerge3
1198
if merge_type is None:
1199
merge_type = ApplyMerge3
1201
if revision is None or len(revision) < 1:
1203
other = [branch, -1]
1205
if len(revision) == 1:
1207
other = [branch, revision[0].in_history(branch).revno]
1209
assert len(revision) == 2
1210
if None in revision:
1211
raise BzrCommandError(
1212
"Merge doesn't permit that revision specifier.")
1213
from bzrlib.branch import Branch
1214
b = Branch.open(branch)
1216
base = [branch, revision[0].in_history(b).revno]
1217
other = [branch, revision[1].in_history(b).revno]
1220
merge(other, base, check_clean=(not force), merge_type=merge_type)
1221
except bzrlib.errors.AmbiguousBase, e:
1222
m = ("sorry, bzr can't determine the right merge base yet\n"
1223
"candidates are:\n "
1224
+ "\n ".join(e.bases)
1226
"please specify an explicit base with -r,\n"
1227
"and (if you want) report this to the bzr developers\n")
4408
--force is given. If --force is given, then the changes from the source
4409
will be merged with the current working tree, including any uncommitted
4410
changes in the tree. The --force option can also be used to create a
4411
merge revision which has more than two parents.
4413
If one would like to merge changes from the working tree of the other
4414
branch without merging any committed revisions, the --uncommitted option
4417
To select only some changes to merge, use "merge -i", which will prompt
4418
you to apply each diff hunk and file change, similar to "shelve".
4421
To merge all new revisions from brz.dev::
4423
brz merge ../brz.dev
4425
To merge changes up to and including revision 82 from brz.dev::
4427
brz merge -r 82 ../brz.dev
4429
To merge the changes introduced by 82, without previous changes::
4431
brz merge -r 81..82 ../brz.dev
4433
To apply a merge directive contained in /tmp/merge::
4435
brz merge /tmp/merge
4437
To create a merge revision with three parents from two branches
4438
feature1a and feature1b:
4440
brz merge ../feature1a
4441
brz merge ../feature1b --force
4442
brz commit -m 'revision with three parents'
4445
encoding_type = 'exact'
4446
_see_also = ['update', 'remerge', 'status-flags', 'send']
4447
takes_args = ['location?']
4452
help='Merge even if the destination tree has uncommitted changes.'),
4456
Option('show-base', help="Show base revision text in "
4458
Option('uncommitted', help='Apply uncommitted changes'
4459
' from a working copy, instead of branch changes.'),
4460
Option('pull', help='If the destination is already'
4461
' completely merged into the source, pull from the'
4462
' source rather than merging. When this happens,'
4463
' you do not need to commit the result.'),
4464
custom_help('directory',
4465
help='Branch to merge into, '
4466
'rather than the one containing the working directory.'),
4467
Option('preview', help='Instead of merging, show a diff of the'
4469
Option('interactive', help='Select changes interactively.',
4473
def run(self, location=None, revision=None, force=False,
4474
merge_type=None, show_base=False, reprocess=None, remember=None,
4475
uncommitted=False, pull=False,
4480
if merge_type is None:
4481
merge_type = _mod_merge.Merge3Merger
4483
if directory is None:
4485
possible_transports = []
4487
allow_pending = True
4488
verified = 'inapplicable'
4490
tree = WorkingTree.open_containing(directory)[0]
4491
if tree.branch.last_revision() == _mod_revision.NULL_REVISION:
4492
raise errors.CommandError(gettext('Merging into empty branches not currently supported, '
4493
'https://bugs.launchpad.net/bzr/+bug/308562'))
4495
# die as quickly as possible if there are uncommitted changes
4497
if tree.has_changes():
4498
raise errors.UncommittedChanges(tree)
4500
view_info = _get_view_info_for_change_reporter(tree)
4501
change_reporter = delta._ChangeReporter(
4502
unversioned_filter=tree.is_ignored, view_info=view_info)
4503
pb = ui.ui_factory.nested_progress_bar()
4504
self.enter_context(pb)
4505
self.enter_context(tree.lock_write())
4506
if location is not None:
4508
mergeable = _mod_mergeable.read_mergeable_from_url(
4509
location, possible_transports=possible_transports)
4510
except errors.NotABundle:
4514
raise errors.CommandError(gettext('Cannot use --uncommitted'
4515
' with bundles or merge directives.'))
4517
if revision is not None:
4518
raise errors.CommandError(gettext(
4519
'Cannot use -r with merge directives or bundles'))
4520
merger, verified = _mod_merge.Merger.from_mergeable(tree,
4523
if merger is None and uncommitted:
4524
if revision is not None and len(revision) > 0:
4525
raise errors.CommandError(gettext('Cannot use --uncommitted and'
4526
' --revision at the same time.'))
4527
merger = self.get_merger_from_uncommitted(tree, location, None)
4528
allow_pending = False
4531
merger, allow_pending = self._get_merger_from_branch(tree,
4532
location, revision, remember, possible_transports, None)
4534
merger.merge_type = merge_type
4535
merger.reprocess = reprocess
4536
merger.show_base = show_base
4537
self.sanity_check_merger(merger)
4538
if (merger.base_rev_id == merger.other_rev_id and
4539
merger.other_rev_id is not None):
4540
# check if location is a nonexistent file (and not a branch) to
4541
# disambiguate the 'Nothing to do'
4542
if merger.interesting_files:
4543
if not merger.other_tree.has_filename(
4544
merger.interesting_files[0]):
4545
note(gettext("merger: ") + str(merger))
4546
raise errors.PathsDoNotExist([location])
4547
note(gettext('Nothing to do.'))
4549
if pull and not preview:
4550
if merger.interesting_files is not None:
4551
raise errors.CommandError(
4552
gettext('Cannot pull individual files'))
4553
if (merger.base_rev_id == tree.last_revision()):
4554
result = tree.pull(merger.other_branch, False,
4555
merger.other_rev_id)
4556
result.report(self.outf)
4558
if merger.this_basis is None:
4559
raise errors.CommandError(gettext(
4560
"This branch has no commits."
4561
" (perhaps you would prefer 'brz pull')"))
4563
return self._do_preview(merger)
4565
return self._do_interactive(merger)
4567
return self._do_merge(merger, change_reporter, allow_pending,
4570
def _get_preview(self, merger):
4571
tree_merger = merger.make_merger()
4572
tt = tree_merger.make_preview_transform()
4573
self.enter_context(tt)
4574
result_tree = tt.get_preview_tree()
4577
def _do_preview(self, merger):
4578
from .diff import show_diff_trees
4579
result_tree = self._get_preview(merger)
4580
path_encoding = osutils.get_diff_header_encoding()
4581
show_diff_trees(merger.this_tree, result_tree, self.outf,
4582
old_label='', new_label='',
4583
path_encoding=path_encoding)
4585
def _do_merge(self, merger, change_reporter, allow_pending, verified):
4586
merger.change_reporter = change_reporter
4587
conflict_count = merger.do_merge()
4589
merger.set_pending()
4590
if verified == 'failed':
4591
warning('Preview patch does not match changes')
4592
if conflict_count != 0:
4597
def _do_interactive(self, merger):
4598
"""Perform an interactive merge.
4600
This works by generating a preview tree of the merge, then using
4601
Shelver to selectively remove the differences between the working tree
4602
and the preview tree.
4604
from . import shelf_ui
4605
result_tree = self._get_preview(merger)
4606
writer = breezy.option.diff_writer_registry.get()
4607
shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
4608
reporter=shelf_ui.ApplyReporter(),
4609
diff_writer=writer(self.outf))
4615
def sanity_check_merger(self, merger):
4616
if (merger.show_base and
4617
merger.merge_type is not _mod_merge.Merge3Merger):
4618
raise errors.CommandError(gettext("Show-base is not supported for this"
4619
" merge type. %s") % merger.merge_type)
4620
if merger.reprocess is None:
4621
if merger.show_base:
4622
merger.reprocess = False
4624
# Use reprocess if the merger supports it
4625
merger.reprocess = merger.merge_type.supports_reprocess
4626
if merger.reprocess and not merger.merge_type.supports_reprocess:
4627
raise errors.CommandError(gettext("Conflict reduction is not supported"
4628
" for merge type %s.") %
4630
if merger.reprocess and merger.show_base:
4631
raise errors.CommandError(gettext("Cannot do conflict reduction and"
4634
if (merger.merge_type.requires_file_merge_plan and
4635
(not getattr(merger.this_tree, 'plan_file_merge', None) or
4636
not getattr(merger.other_tree, 'plan_file_merge', None) or
4637
(merger.base_tree is not None and
4638
not getattr(merger.base_tree, 'plan_file_merge', None)))):
4639
raise errors.CommandError(
4640
gettext('Plan file merge unsupported: '
4641
'Merge type incompatible with tree formats.'))
4643
def _get_merger_from_branch(self, tree, location, revision, remember,
4644
possible_transports, pb):
4645
"""Produce a merger from a location, assuming it refers to a branch."""
4646
# find the branch locations
4647
other_loc, user_location = self._select_branch_location(tree, location,
4649
if revision is not None and len(revision) == 2:
4650
base_loc, _unused = self._select_branch_location(tree,
4651
location, revision, 0)
4653
base_loc = other_loc
4655
other_branch, other_path = Branch.open_containing(other_loc,
4656
possible_transports)
4657
if base_loc == other_loc:
4658
base_branch = other_branch
4660
base_branch, base_path = Branch.open_containing(base_loc,
4661
possible_transports)
4662
# Find the revision ids
4663
other_revision_id = None
4664
base_revision_id = None
4665
if revision is not None:
4666
if len(revision) >= 1:
4667
other_revision_id = revision[-1].as_revision_id(other_branch)
4668
if len(revision) == 2:
4669
base_revision_id = revision[0].as_revision_id(base_branch)
4670
if other_revision_id is None:
4671
other_revision_id = _mod_revision.ensure_null(
4672
other_branch.last_revision())
4673
# Remember where we merge from. We need to remember if:
4674
# - user specify a location (and we don't merge from the parent
4676
# - user ask to remember or there is no previous location set to merge
4677
# from and user didn't ask to *not* remember
4678
if (user_location is not None
4680
(remember is None and
4681
tree.branch.get_submit_branch() is None)))):
4682
tree.branch.set_submit_branch(other_branch.base)
4683
# Merge tags (but don't set them in the master branch yet, the user
4684
# might revert this merge). Commit will propagate them.
4685
other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
4686
merger = _mod_merge.Merger.from_revision_ids(tree,
4687
other_revision_id, base_revision_id, other_branch, base_branch)
4688
if other_path != '':
4689
allow_pending = False
4690
merger.interesting_files = [other_path]
4692
allow_pending = True
4693
return merger, allow_pending
4695
def get_merger_from_uncommitted(self, tree, location, pb):
4696
"""Get a merger for uncommitted changes.
4698
:param tree: The tree the merger should apply to.
4699
:param location: The location containing uncommitted changes.
4700
:param pb: The progress bar to use for showing progress.
4702
location = self._select_branch_location(tree, location)[0]
4703
other_tree, other_path = WorkingTree.open_containing(location)
4704
merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
4705
if other_path != '':
4706
merger.interesting_files = [other_path]
4709
def _select_branch_location(self, tree, user_location, revision=None,
4711
"""Select a branch location, according to possible inputs.
4713
If provided, branches from ``revision`` are preferred. (Both
4714
``revision`` and ``index`` must be supplied.)
4716
Otherwise, the ``location`` parameter is used. If it is None, then the
4717
``submit`` or ``parent`` location is used, and a note is printed.
4719
:param tree: The working tree to select a branch for merging into
4720
:param location: The location entered by the user
4721
:param revision: The revision parameter to the command
4722
:param index: The index to use for the revision parameter. Negative
4723
indices are permitted.
4724
:return: (selected_location, user_location). The default location
4725
will be the user-entered location.
4727
if (revision is not None and index is not None
4728
and revision[index] is not None):
4729
branch = revision[index].get_branch()
4730
if branch is not None:
4731
return branch, branch
4732
if user_location is None:
4733
location = self._get_remembered(tree, 'Merging from')
4735
location = user_location
4736
return location, user_location
4738
def _get_remembered(self, tree, verb_string):
4739
"""Use tree.branch's parent if none was supplied.
4741
Report if the remembered location was used.
4743
stored_location = tree.branch.get_submit_branch()
4744
stored_location_type = "submit"
4745
if stored_location is None:
4746
stored_location = tree.branch.get_parent()
4747
stored_location_type = "parent"
4748
mutter("%s", stored_location)
4749
if stored_location is None:
4750
raise errors.CommandError(
4751
gettext("No location specified or remembered"))
4752
display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
4753
note(gettext("{0} remembered {1} location {2}").format(verb_string,
4754
stored_location_type, display_url))
4755
return stored_location
4758
class cmd_remerge(Command):
4759
__doc__ = """Redo a merge.
4761
Use this if you want to try a different merge technique while resolving
4762
conflicts. Some merge techniques are better than others, and remerge
4763
lets you try different ones on different files.
4765
The options for remerge have the same meaning and defaults as the ones for
4766
merge. The difference is that remerge can (only) be run when there is a
4767
pending merge, and it lets you specify particular files.
4770
Re-do the merge of all conflicted files, and show the base text in
4771
conflict regions, in addition to the usual THIS and OTHER texts::
4773
brz remerge --show-base
4775
Re-do the merge of "foobar", using the weave merge algorithm, with
4776
additional processing to reduce the size of conflict regions::
4778
brz remerge --merge-type weave --reprocess foobar
4780
takes_args = ['file*']
4785
help="Show base revision text in conflicts."),
4788
def run(self, file_list=None, merge_type=None, show_base=False,
4790
from .conflicts import restore
4791
if merge_type is None:
4792
merge_type = _mod_merge.Merge3Merger
4793
tree, file_list = WorkingTree.open_containing_paths(file_list)
4794
self.enter_context(tree.lock_write())
4795
parents = tree.get_parent_ids()
4796
if len(parents) != 2:
4797
raise errors.CommandError(
4798
gettext("Sorry, remerge only works after normal"
4799
" merges. Not cherrypicking or multi-merges."))
4800
interesting_files = None
4802
conflicts = tree.conflicts()
4803
if file_list is not None:
4804
interesting_files = set()
4805
for filename in file_list:
4806
if not tree.is_versioned(filename):
4807
raise errors.NotVersionedError(filename)
4808
interesting_files.add(filename)
4809
if tree.kind(filename) != "directory":
4812
for path, ie in tree.iter_entries_by_dir(
4813
specific_files=[filename]):
4814
interesting_files.add(path)
4815
new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4817
# Remerge only supports resolving contents conflicts
4818
allowed_conflicts = ('text conflict', 'contents conflict')
4819
restore_files = [c.path for c in conflicts
4820
if c.typestring in allowed_conflicts]
4821
_mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
4822
tree.set_conflicts(new_conflicts)
4823
if file_list is not None:
4824
restore_files = file_list
4825
for filename in restore_files:
4827
restore(tree.abspath(filename))
4828
except errors.NotConflicted:
4830
# Disable pending merges, because the file texts we are remerging
4831
# have not had those merges performed. If we use the wrong parents
4832
# list, we imply that the working tree text has seen and rejected
4833
# all the changes from the other tree, when in fact those changes
4834
# have not yet been seen.
4835
tree.set_parent_ids(parents[:1])
4837
merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
4838
merger.interesting_files = interesting_files
4839
merger.merge_type = merge_type
4840
merger.show_base = show_base
4841
merger.reprocess = reprocess
4842
conflicts = merger.do_merge()
4844
tree.set_parent_ids(parents)
1231
4851
class cmd_revert(Command):
1232
"""Reverse all changes since the last commit.
1234
Only versioned files are affected. Specify filenames to revert only
1235
those files. By default, any files that are changed will be backed up
1236
first. Backup files have a '~' appended to their name.
4853
Set files in the working tree back to the contents of a previous revision.
4855
Giving a list of files will revert only those files. Otherwise, all files
4856
will be reverted. If the revision is not specified with '--revision', the
4857
working tree basis revision is used. A revert operation affects only the
4858
working tree, not any revision history like the branch and repository or
4859
the working tree basis revision.
4861
To remove only some changes, without reverting to a prior version, use
4862
merge instead. For example, "merge . -r -2..-3" (don't forget the ".")
4863
will remove the changes introduced by the second last commit (-2), without
4864
affecting the changes introduced by the last commit (-1). To remove
4865
certain changes on a hunk-by-hunk basis, see the shelve command.
4866
To update the branch to a specific revision or the latest revision and
4867
update the working tree accordingly while preserving local changes, see the
4870
Uncommitted changes to files that are reverted will be discarded.
4871
However, by default, any files that have been manually changed will be
4872
backed up first. (Files changed only by merge are not backed up.) Backup
4873
files have '.~#~' appended to their name, where # is a number.
4875
When you provide files, you can use their current pathname or the pathname
4876
from the target revision. So you can use revert to "undelete" a file by
4877
name. If you name a directory, all the contents of that directory will be
4880
If you have newly added files since the target revision, they will be
4881
removed. If the files to be removed have been changed, backups will be
4882
created as above. Directories containing unknown files will not be
4885
The working tree contains a list of revisions that have been merged but
4886
not yet committed. These revisions will be included as additional parents
4887
of the next commit. Normally, using revert clears that list as well as
4888
reverting the files. If any files are specified, revert leaves the list
4889
of uncommitted merges alone and reverts only the files. Use ``brz revert
4890
.`` in the tree root to revert all files but keep the recorded merges,
4891
and ``brz revert --forget-merges`` to clear the pending merge list without
4892
reverting any files.
4894
Using "brz revert --forget-merges", it is possible to apply all of the
4895
changes from a branch in a single revision. To do this, perform the merge
4896
as desired. Then doing revert with the "--forget-merges" option will keep
4897
the content of the tree as it was, but it will clear the list of pending
4898
merges. The next commit will then contain all of the changes that are
4899
present in the other branch, but without any other parent revisions.
4900
Because this technique forgets where these changes originated, it may
4901
cause additional conflicts on later merges involving the same source and
1238
takes_options = ['revision', 'no-backup']
4905
_see_also = ['cat', 'export', 'merge', 'shelve']
4908
Option('no-backup', "Do not save backups of reverted files."),
4909
Option('forget-merges',
4910
'Remove pending merge marker, without changing any files.'),
1239
4912
takes_args = ['file*']
1240
aliases = ['merge-revert']
1242
def run(self, revision=None, no_backup=False, file_list=None):
1243
from bzrlib.merge import merge
1244
from bzrlib.branch import Branch
1245
from bzrlib.commands import parse_spec
1247
if file_list is not None:
1248
if len(file_list) == 0:
1249
raise BzrCommandError("No files specified")
1250
if revision is None:
1252
elif len(revision) != 1:
1253
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
4914
def run(self, revision=None, no_backup=False, file_list=None,
4915
forget_merges=None):
4916
tree, file_list = WorkingTree.open_containing_paths(file_list)
4917
self.enter_context(tree.lock_tree_write())
4919
tree.set_parent_ids(tree.get_parent_ids()[:1])
1255
b = Branch.open_containing('.')
1256
revno = revision[0].in_history(b).revno
1257
merge(('.', revno), parse_spec('.'),
1260
backup_files=not no_backup,
1261
file_list=file_list)
1263
Branch.open_containing('.').set_pending_merges([])
4921
self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4924
def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4925
rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4926
tree.revert(file_list, rev_tree, not no_backup, None,
4927
report_changes=True)
1266
4930
class cmd_assert_fail(Command):
1267
"""Test reporting of assertion failures"""
4931
__doc__ = """Test reporting of assertion failures"""
4932
# intended just for use in testing
1270
assert False, "always fails"
4937
raise AssertionError("always fails")
1273
4940
class cmd_help(Command):
1274
"""Show help on a command or other topic.
4941
__doc__ = """Show help on a command or other topic.
1276
For a list of all available commands, say 'bzr help commands'."""
1277
takes_options = ['long']
4944
_see_also = ['topics']
4946
Option('long', 'Show help on all commands.'),
1278
4948
takes_args = ['topic?']
4949
aliases = ['?', '--help', '-?', '-h']
1281
4952
def run(self, topic=None, long=False):
1283
4954
if topic is None and long:
1284
4955
topic = "commands"
4956
breezy.help.help(topic)
1288
4959
class cmd_shell_complete(Command):
1289
"""Show appropriate completions for context.
4960
__doc__ = """Show appropriate completions for context.
1291
For a list of all available commands, say 'bzr shell-complete'."""
4962
For a list of all available commands, say 'brz shell-complete'.
1292
4964
takes_args = ['context?']
1293
4965
aliases = ['s-c']
1296
4969
def run(self, context=None):
1297
import shellcomplete
4970
from . import shellcomplete
1298
4971
shellcomplete.shellcomplete(context)
1301
4974
class cmd_missing(Command):
1302
"""What is missing in this branch relative to other branch.
1304
takes_args = ['remote?']
1305
aliases = ['mis', 'miss']
1306
# We don't have to add quiet to the list, because
1307
# unknown options are parsed as booleans
1308
takes_options = ['verbose', 'quiet']
1310
def run(self, remote=None, verbose=False, quiet=False):
1311
from bzrlib.errors import BzrCommandError
1312
from bzrlib.missing import show_missing
1314
if verbose and quiet:
1315
raise BzrCommandError('Cannot pass both quiet and verbose')
1317
b = Branch.open_containing('.')
1318
parent = b.get_parent()
1321
raise BzrCommandError("No missing location known or specified.")
1324
print "Using last location: %s" % parent
1326
elif parent is None:
1327
# We only update parent if it did not exist, missing
1328
# should not change the parent
1329
b.set_parent(remote)
1330
br_remote = Branch.open_containing(remote)
1331
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
4975
__doc__ = """Show unmerged/unpulled revisions between two branches.
4977
OTHER_BRANCH may be local or remote.
4979
To filter on a range of revisions, you can use the command -r begin..end
4980
-r revision requests a specific revision, -r ..end or -r begin.. are
4984
1 - some missing revisions
4985
0 - no missing revisions
4989
Determine the missing revisions between this and the branch at the
4990
remembered pull location::
4994
Determine the missing revisions between this and another branch::
4996
brz missing http://server/branch
4998
Determine the missing revisions up to a specific revision on the other
5001
brz missing -r ..-10
5003
Determine the missing revisions up to a specific revision on this
5006
brz missing --my-revision ..-10
5009
_see_also = ['merge', 'pull']
5010
takes_args = ['other_branch?']
5013
Option('reverse', 'Reverse the order of revisions.'),
5015
'Display changes in the local branch only.'),
5016
Option('this', 'Same as --mine-only.'),
5017
Option('theirs-only',
5018
'Display changes in the remote branch only.'),
5019
Option('other', 'Same as --theirs-only.'),
5023
custom_help('revision',
5024
help='Filter on other branch revisions (inclusive). '
5025
'See "help revisionspec" for details.'),
5026
Option('my-revision',
5027
type=_parse_revision_str,
5028
help='Filter on local branch revisions (inclusive). '
5029
'See "help revisionspec" for details.'),
5030
Option('include-merged',
5031
'Show all revisions in addition to the mainline ones.'),
5032
Option('include-merges', hidden=True,
5033
help='Historical alias for --include-merged.'),
5035
encoding_type = 'replace'
5038
def run(self, other_branch=None, reverse=False, mine_only=False,
5040
log_format=None, long=False, short=False, line=False,
5041
show_ids=False, verbose=False, this=False, other=False,
5042
include_merged=None, revision=None, my_revision=None,
5044
from breezy.missing import find_unmerged, iter_log_revisions
5050
if include_merged is None:
5051
include_merged = False
5056
# TODO: We should probably check that we don't have mine-only and
5057
# theirs-only set, but it gets complicated because we also have
5058
# this and other which could be used.
5065
local_branch = Branch.open_containing(directory)[0]
5066
self.enter_context(local_branch.lock_read())
5068
parent = local_branch.get_parent()
5069
if other_branch is None:
5070
other_branch = parent
5071
if other_branch is None:
5072
raise errors.CommandError(gettext("No peer location known"
5074
display_url = urlutils.unescape_for_display(parent,
5076
message(gettext("Using saved parent location: {0}\n").format(
5079
remote_branch = Branch.open(other_branch)
5080
if remote_branch.base == local_branch.base:
5081
remote_branch = local_branch
5083
self.enter_context(remote_branch.lock_read())
5085
local_revid_range = _revision_range_to_revid_range(
5086
_get_revision_range(my_revision, local_branch,
5089
remote_revid_range = _revision_range_to_revid_range(
5090
_get_revision_range(revision,
5091
remote_branch, self.name()))
5093
local_extra, remote_extra = find_unmerged(
5094
local_branch, remote_branch, restrict,
5095
backward=not reverse,
5096
include_merged=include_merged,
5097
local_revid_range=local_revid_range,
5098
remote_revid_range=remote_revid_range)
5100
if log_format is None:
5101
registry = log.log_formatter_registry
5102
log_format = registry.get_default(local_branch)
5103
lf = log_format(to_file=self.outf,
5105
show_timezone='original')
5108
if local_extra and not theirs_only:
5109
message(ngettext("You have %d extra revision:\n",
5110
"You have %d extra revisions:\n",
5114
if local_branch.supports_tags():
5115
rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5116
for revision in iter_log_revisions(local_extra,
5117
local_branch.repository,
5120
lf.log_revision(revision)
5121
printed_local = True
5124
printed_local = False
5126
if remote_extra and not mine_only:
5127
if printed_local is True:
5129
message(ngettext("You are missing %d revision:\n",
5130
"You are missing %d revisions:\n",
5131
len(remote_extra)) %
5133
if remote_branch.supports_tags():
5134
rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5135
for revision in iter_log_revisions(remote_extra,
5136
remote_branch.repository,
5139
lf.log_revision(revision)
5142
if mine_only and not local_extra:
5143
# We checked local, and found nothing extra
5144
message(gettext('This branch has no new revisions.\n'))
5145
elif theirs_only and not remote_extra:
5146
# We checked remote, and found nothing extra
5147
message(gettext('Other branch has no new revisions.\n'))
5148
elif not (mine_only or theirs_only or local_extra or
5150
# We checked both branches, and neither one had extra
5152
message(gettext("Branches are up to date.\n"))
5154
if not status_code and parent is None and other_branch is not None:
5155
self.enter_context(local_branch.lock_write())
5156
# handle race conditions - a parent might be set while we run.
5157
if local_branch.get_parent() is None:
5158
local_branch.set_parent(remote_branch.base)
5162
class cmd_pack(Command):
5163
__doc__ = """Compress the data within a repository.
5165
This operation compresses the data within a bazaar repository. As
5166
bazaar supports automatic packing of repository, this operation is
5167
normally not required to be done manually.
5169
During the pack operation, bazaar takes a backup of existing repository
5170
data, i.e. pack files. This backup is eventually removed by bazaar
5171
automatically when it is safe to do so. To save disk space by removing
5172
the backed up pack files, the --clean-obsolete-packs option may be
5175
Warning: If you use --clean-obsolete-packs and your machine crashes
5176
during or immediately after repacking, you may be left with a state
5177
where the deletion has been written to disk but the new packs have not
5178
been. In this case the repository may be unusable.
5181
_see_also = ['repositories']
5182
takes_args = ['branch_or_repo?']
5184
Option('clean-obsolete-packs',
5185
'Delete obsolete packs to save disk space.'),
5188
def run(self, branch_or_repo='.', clean_obsolete_packs=False):
5189
dir = controldir.ControlDir.open_containing(branch_or_repo)[0]
5191
branch = dir.open_branch()
5192
repository = branch.repository
5193
except errors.NotBranchError:
5194
repository = dir.open_repository()
5195
repository.pack(clean_obsolete_packs=clean_obsolete_packs)
1334
5198
class cmd_plugins(Command):
1338
import bzrlib.plugin
1339
from inspect import getdoc
1340
for plugin in bzrlib.plugin.all_plugins:
1341
if hasattr(plugin, '__path__'):
1342
print plugin.__path__[0]
1343
elif hasattr(plugin, '__file__'):
1344
print plugin.__file__
1350
print '\t', d.split('\n')[0]
5199
__doc__ = """List the installed plugins.
5201
This command displays the list of installed plugins including
5202
version of plugin and a short description of each.
5204
--verbose shows the path where each plugin is located.
5206
A plugin is an external component for Breezy that extends the
5207
revision control system, by adding or replacing code in Breezy.
5208
Plugins can do a variety of things, including overriding commands,
5209
adding new commands, providing additional network transports and
5210
customizing log output.
5212
See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
5213
for further information on plugins including where to find them and how to
5214
install them. Instructions are also provided there on how to write new
5215
plugins using the Python programming language.
5217
takes_options = ['verbose']
5220
def run(self, verbose=False):
5221
from . import plugin
5222
# Don't give writelines a generator as some codecs don't like that
5223
self.outf.writelines(
5224
list(plugin.describe_plugins(show_paths=verbose)))
5227
class cmd_testament(Command):
5228
__doc__ = """Show testament (signing-form) of a revision."""
5231
Option('long', help='Produce long-format testament.'),
5233
help='Produce a strict-format testament.')]
5234
takes_args = ['branch?']
5235
encoding_type = 'exact'
5238
def run(self, branch=u'.', revision=None, long=False, strict=False):
5239
from .bzr.testament import Testament, StrictTestament
5241
testament_class = StrictTestament
5243
testament_class = Testament
5245
b = Branch.open_containing(branch)[0]
5247
b = Branch.open(branch)
5248
self.enter_context(b.lock_read())
5249
if revision is None:
5250
rev_id = b.last_revision()
5252
rev_id = revision[0].as_revision_id(b)
5253
t = testament_class.from_revision(b.repository, rev_id)
5255
self.outf.writelines(t.as_text_lines())
5257
self.outf.write(t.as_short_text())
5260
class cmd_annotate(Command):
5261
__doc__ = """Show the origin of each line in a file.
5263
This prints out the given file with an annotation on the left side
5264
indicating which revision, author and date introduced the change.
5266
If the origin is the same for a run of consecutive lines, it is
5267
shown only at the top, unless the --all option is given.
5269
# TODO: annotate directories; showing when each file was last changed
5270
# TODO: if the working copy is modified, show annotations on that
5271
# with new uncommitted lines marked
5272
aliases = ['ann', 'blame', 'praise']
5273
takes_args = ['filename']
5274
takes_options = [Option('all', help='Show annotations on all lines.'),
5275
Option('long', help='Show commit date in annotations.'),
5280
encoding_type = 'exact'
5283
def run(self, filename, all=False, long=False, revision=None,
5284
show_ids=False, directory=None):
5285
from .annotate import (
5288
wt, branch, relpath = \
5289
_open_directory_or_containing_tree_or_branch(filename, directory)
5291
self.enter_context(wt.lock_read())
5293
self.enter_context(branch.lock_read())
5294
tree = _get_one_revision_tree('annotate', revision, branch=branch)
5295
self.enter_context(tree.lock_read())
5296
if wt is not None and revision is None:
5297
if not wt.is_versioned(relpath):
5298
raise errors.NotVersionedError(relpath)
5299
# If there is a tree and we're not annotating historical
5300
# versions, annotate the working tree's content.
5301
annotate_file_tree(wt, relpath, self.outf, long, all,
5304
if not tree.is_versioned(relpath):
5305
raise errors.NotVersionedError(relpath)
5306
annotate_file_tree(tree, relpath, self.outf, long, all,
5307
show_ids=show_ids, branch=branch)
5310
class cmd_re_sign(Command):
5311
__doc__ = """Create a digital signature for an existing revision."""
5312
# TODO be able to replace existing ones.
5314
hidden = True # is this right ?
5315
takes_args = ['revision_id*']
5316
takes_options = ['directory', 'revision']
5318
def run(self, revision_id_list=None, revision=None, directory=u'.'):
5319
if revision_id_list is not None and revision is not None:
5320
raise errors.CommandError(
5321
gettext('You can only supply one of revision_id or --revision'))
5322
if revision_id_list is None and revision is None:
5323
raise errors.CommandError(
5324
gettext('You must supply either --revision or a revision_id'))
5325
b = WorkingTree.open_containing(directory)[0].branch
5326
self.enter_context(b.lock_write())
5327
return self._run(b, revision_id_list, revision)
5329
def _run(self, b, revision_id_list, revision):
5330
from .repository import WriteGroup
5331
gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
5332
if revision_id_list is not None:
5333
with WriteGroup(b.repository):
5334
for revision_id in revision_id_list:
5335
revision_id = cache_utf8.encode(revision_id)
5336
b.repository.sign_revision(revision_id, gpg_strategy)
5337
elif revision is not None:
5338
if len(revision) == 1:
5339
revno, rev_id = revision[0].in_history(b)
5340
with WriteGroup(b.repository):
5341
b.repository.sign_revision(rev_id, gpg_strategy)
5342
elif len(revision) == 2:
5343
# are they both on rh- if so we can walk between them
5344
# might be nice to have a range helper for arbitrary
5345
# revision paths. hmm.
5346
from_revno, from_revid = revision[0].in_history(b)
5347
to_revno, to_revid = revision[1].in_history(b)
5348
if to_revid is None:
5349
to_revno = b.revno()
5350
if from_revno is None or to_revno is None:
5351
raise errors.CommandError(
5352
gettext('Cannot sign a range of non-revision-history revisions'))
5353
with WriteGroup(b.repository):
5354
for revno in range(from_revno, to_revno + 1):
5355
b.repository.sign_revision(b.get_rev_id(revno),
5358
raise errors.CommandError(
5359
gettext('Please supply either one revision, or a range.'))
5362
class cmd_bind(Command):
5363
__doc__ = """Convert the current branch into a checkout of the supplied branch.
5364
If no branch is supplied, rebind to the last bound location.
5366
Once converted into a checkout, commits must succeed on the master branch
5367
before they will be applied to the local branch.
5369
Bound branches use the nickname of its master branch unless it is set
5370
locally, in which case binding will update the local nickname to be
5374
_see_also = ['checkouts', 'unbind']
5375
takes_args = ['location?']
5376
takes_options = ['directory']
5378
def run(self, location=None, directory=u'.'):
5379
b, relpath = Branch.open_containing(directory)
5380
if location is None:
5382
location = b.get_old_bound_location()
5383
except errors.UpgradeRequired:
5384
raise errors.CommandError(
5385
gettext('No location supplied. '
5386
'This format does not remember old locations.'))
5388
if location is None:
5389
if b.get_bound_location() is not None:
5390
raise errors.CommandError(
5391
gettext('Branch is already bound'))
5393
raise errors.CommandError(
5394
gettext('No location supplied'
5395
' and no previous location known'))
5396
b_other = Branch.open(location)
5399
except errors.DivergedBranches:
5400
raise errors.CommandError(
5401
gettext('These branches have diverged.'
5402
' Try merging, and then bind again.'))
5403
if b.get_config().has_explicit_nickname():
5404
b.nick = b_other.nick
5407
class cmd_unbind(Command):
5408
__doc__ = """Convert the current checkout into a regular branch.
5410
After unbinding, the local branch is considered independent and subsequent
5411
commits will be local only.
5414
_see_also = ['checkouts', 'bind']
5416
takes_options = ['directory']
5418
def run(self, directory=u'.'):
5419
b, relpath = Branch.open_containing(directory)
5421
raise errors.CommandError(gettext('Local branch is not bound'))
5424
class cmd_uncommit(Command):
5425
__doc__ = """Remove the last committed revision.
5427
--verbose will print out what is being removed.
5428
--dry-run will go through all the motions, but not actually
5431
If --revision is specified, uncommit revisions to leave the branch at the
5432
specified revision. For example, "brz uncommit -r 15" will leave the
5433
branch at revision 15.
5435
Uncommit leaves the working tree ready for a new commit. The only change
5436
it may make is to restore any pending merges that were present before
5440
# TODO: jam 20060108 Add an option to allow uncommit to remove
5441
# unreferenced information in 'branch-as-repository' branches.
5442
# TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
5443
# information in shared branches as well.
5444
_see_also = ['commit']
5445
takes_options = ['verbose', 'revision',
5446
Option('dry-run', help='Don\'t actually make changes.'),
5447
Option('force', help='Say yes to all questions.'),
5449
help='Keep tags that point to removed revisions.'),
5451
help="Only remove the commits from the local "
5452
"branch when in a checkout."
5455
takes_args = ['location?']
5457
encoding_type = 'replace'
5459
def run(self, location=None, dry_run=False, verbose=False,
5460
revision=None, force=False, local=False, keep_tags=False):
5461
if location is None:
5463
control, relpath = controldir.ControlDir.open_containing(location)
5465
tree = control.open_workingtree()
5467
except (errors.NoWorkingTree, errors.NotLocalUrl):
5469
b = control.open_branch()
5471
if tree is not None:
5472
self.enter_context(tree.lock_write())
5474
self.enter_context(b.lock_write())
5475
return self._run(b, tree, dry_run, verbose, revision, force,
5476
local, keep_tags, location)
5478
def _run(self, b, tree, dry_run, verbose, revision, force, local,
5479
keep_tags, location):
5480
from .log import log_formatter, show_log
5481
from .uncommit import uncommit
5483
last_revno, last_rev_id = b.last_revision_info()
5486
if revision is None:
5488
rev_id = last_rev_id
5490
# 'brz uncommit -r 10' actually means uncommit
5491
# so that the final tree is at revno 10.
5492
# but breezy.uncommit.uncommit() actually uncommits
5493
# the revisions that are supplied.
5494
# So we need to offset it by one
5495
revno = revision[0].in_history(b).revno + 1
5496
if revno <= last_revno:
5497
rev_id = b.get_rev_id(revno)
5499
if rev_id is None or _mod_revision.is_null(rev_id):
5500
self.outf.write(gettext('No revisions to uncommit.\n'))
5503
lf = log_formatter('short',
5505
show_timezone='original')
5510
direction='forward',
5511
start_revision=revno,
5512
end_revision=last_revno)
5515
self.outf.write(gettext('Dry-run, pretending to remove'
5516
' the above revisions.\n'))
5519
gettext('The above revision(s) will be removed.\n'))
5522
if not ui.ui_factory.confirm_action(
5523
gettext(u'Uncommit these revisions'),
5524
'breezy.builtins.uncommit',
5526
self.outf.write(gettext('Canceled\n'))
5529
mutter('Uncommitting from {%s} to {%s}',
5530
last_rev_id, rev_id)
5531
uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
5532
revno=revno, local=local, keep_tags=keep_tags)
5535
gettext('You can restore the old tip by running:\n'
5536
' brz pull -d %s %s -r revid:%s\n')
5537
% (location, location, last_rev_id.decode('utf-8')))
5540
gettext('You can restore the old tip by running:\n'
5541
' brz pull . -r revid:%s\n')
5542
% last_rev_id.decode('utf-8'))
5545
class cmd_break_lock(Command):
5546
__doc__ = """Break a dead lock.
5548
This command breaks a lock on a repository, branch, working directory or
5551
CAUTION: Locks should only be broken when you are sure that the process
5552
holding the lock has been stopped.
5554
You can get information on what locks are open via the 'brz info
5555
[location]' command.
5559
brz break-lock brz+ssh://example.com/brz/foo
5560
brz break-lock --conf ~/.config/breezy
5563
takes_args = ['location?']
5566
help='LOCATION is the directory where the config lock is.'),
5568
help='Do not ask for confirmation before breaking the lock.'),
5571
def run(self, location=None, config=False, force=False):
5572
if location is None:
5575
ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
5577
{'breezy.lockdir.break': True})
5579
conf = _mod_config.LockableConfig(file_name=location)
5582
control, relpath = controldir.ControlDir.open_containing(location)
5584
control.break_lock()
5585
except NotImplementedError:
5589
class cmd_wait_until_signalled(Command):
5590
__doc__ = """Test helper for test_start_and_stop_brz_subprocess_send_signal.
5592
This just prints a line to signal when it is ready, then blocks on stdin.
5598
self.outf.write("running\n")
5600
sys.stdin.readline()
5603
class cmd_serve(Command):
5604
__doc__ = """Run the brz server."""
5606
aliases = ['server']
5610
help='Serve on stdin/out for use from inetd or sshd.'),
5611
RegistryOption('protocol',
5612
help="Protocol to serve.",
5613
lazy_registry=('breezy.transport',
5614
'transport_server_registry'),
5615
value_switches=True),
5617
help='Listen for connections on nominated address.',
5620
help='Listen for connections on nominated port. Passing 0 as '
5621
'the port number will result in a dynamically allocated '
5622
'port. The default port depends on the protocol.',
5624
custom_help('directory',
5625
help='Serve contents of this directory.'),
5626
Option('allow-writes',
5627
help='By default the server is a readonly server. Supplying '
5628
'--allow-writes enables write access to the contents of '
5629
'the served directory and below. Note that ``brz serve`` '
5630
'does not perform authentication, so unless some form of '
5631
'external authentication is arranged supplying this '
5632
'option leads to global uncontrolled write access to your '
5635
Option('client-timeout', type=float,
5636
help='Override the default idle client timeout (5min).'),
5639
def run(self, listen=None, port=None, inet=False, directory=None,
5640
allow_writes=False, protocol=None, client_timeout=None):
5641
from . import location, transport
5642
if directory is None:
5643
directory = osutils.getcwd()
5644
if protocol is None:
5645
protocol = transport.transport_server_registry.get()
5646
url = location.location_to_url(directory)
5647
if not allow_writes:
5648
url = 'readonly+' + url
5649
t = transport.get_transport_from_url(url)
5650
protocol(t, listen, port, inet, client_timeout)
5653
class cmd_join(Command):
5654
__doc__ = """Combine a tree into its containing tree.
5656
This command requires the target tree to be in a rich-root format.
5658
The TREE argument should be an independent tree, inside another tree, but
5659
not part of it. (Such trees can be produced by "brz split", but also by
5660
running "brz branch" with the target inside a tree.)
5662
The result is a combined tree, with the subtree no longer an independent
5663
part. This is marked as a merge of the subtree into the containing tree,
5664
and all history is preserved.
5667
_see_also = ['split']
5668
takes_args = ['tree']
5670
Option('reference', help='Join by reference.', hidden=True),
5673
def run(self, tree, reference=False):
5674
from breezy.mutabletree import BadReferenceTarget
5675
sub_tree = WorkingTree.open(tree)
5676
parent_dir = osutils.dirname(sub_tree.basedir)
5677
containing_tree = WorkingTree.open_containing(parent_dir)[0]
5678
repo = containing_tree.branch.repository
5679
if not repo.supports_rich_root():
5680
raise errors.CommandError(gettext(
5681
"Can't join trees because %s doesn't support rich root data.\n"
5682
"You can use brz upgrade on the repository.")
5686
containing_tree.add_reference(sub_tree)
5687
except BadReferenceTarget as e:
5688
# XXX: Would be better to just raise a nicely printable
5689
# exception from the real origin. Also below. mbp 20070306
5690
raise errors.CommandError(
5691
gettext("Cannot join {0}. {1}").format(tree, e.reason))
5694
containing_tree.subsume(sub_tree)
5695
except errors.BadSubsumeSource as e:
5696
raise errors.CommandError(
5697
gettext("Cannot join {0}. {1}").format(tree, e.reason))
5700
class cmd_split(Command):
5701
__doc__ = """Split a subdirectory of a tree into a separate tree.
5703
This command will produce a target tree in a format that supports
5704
rich roots, like 'rich-root' or 'rich-root-pack'. These formats cannot be
5705
converted into earlier formats like 'dirstate-tags'.
5707
The TREE argument should be a subdirectory of a working tree. That
5708
subdirectory will be converted into an independent tree, with its own
5709
branch. Commits in the top-level tree will not apply to the new subtree.
5712
_see_also = ['join']
5713
takes_args = ['tree']
5715
def run(self, tree):
5716
containing_tree, subdir = WorkingTree.open_containing(tree)
5717
if not containing_tree.is_versioned(subdir):
5718
raise errors.NotVersionedError(subdir)
5720
containing_tree.extract(subdir)
5721
except errors.RootNotRich:
5722
raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
5725
class cmd_merge_directive(Command):
5726
__doc__ = """Generate a merge directive for auto-merge tools.
5728
A directive requests a merge to be performed, and also provides all the
5729
information necessary to do so. This means it must either include a
5730
revision bundle, or the location of a branch containing the desired
5733
A submit branch (the location to merge into) must be supplied the first
5734
time the command is issued. After it has been supplied once, it will
5735
be remembered as the default.
5737
A public branch is optional if a revision bundle is supplied, but required
5738
if --diff or --plain is specified. It will be remembered as the default
5739
after the first use.
5742
takes_args = ['submit_branch?', 'public_branch?']
5746
_see_also = ['send']
5750
RegistryOption.from_kwargs(
5752
'The type of patch to include in the directive.',
5754
value_switches=True,
5756
bundle='Bazaar revision bundle (default).',
5757
diff='Normal unified diff.',
5758
plain='No patch, just directive.'),
5759
Option('sign', help='GPG-sign the directive.'), 'revision',
5760
Option('mail-to', type=str,
5761
help='Instead of printing the directive, email to this '
5763
Option('message', type=str, short_name='m',
5764
help='Message to use when committing this merge.')
5767
encoding_type = 'exact'
5769
def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
5770
sign=False, revision=None, mail_to=None, message=None,
5772
from .revision import ensure_null, NULL_REVISION
5773
include_patch, include_bundle = {
5774
'plain': (False, False),
5775
'diff': (True, False),
5776
'bundle': (True, True),
5778
branch = Branch.open(directory)
5779
stored_submit_branch = branch.get_submit_branch()
5780
if submit_branch is None:
5781
submit_branch = stored_submit_branch
5783
if stored_submit_branch is None:
5784
branch.set_submit_branch(submit_branch)
5785
if submit_branch is None:
5786
submit_branch = branch.get_parent()
5787
if submit_branch is None:
5788
raise errors.CommandError(
5789
gettext('No submit branch specified or known'))
5791
stored_public_branch = branch.get_public_branch()
5792
if public_branch is None:
5793
public_branch = stored_public_branch
5794
elif stored_public_branch is None:
5795
# FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5796
branch.set_public_branch(public_branch)
5797
if not include_bundle and public_branch is None:
5798
raise errors.CommandError(
5799
gettext('No public branch specified or known'))
5800
base_revision_id = None
5801
if revision is not None:
5802
if len(revision) > 2:
5803
raise errors.CommandError(
5804
gettext('brz merge-directive takes '
5805
'at most two one revision identifiers'))
5806
revision_id = revision[-1].as_revision_id(branch)
5807
if len(revision) == 2:
5808
base_revision_id = revision[0].as_revision_id(branch)
5810
revision_id = branch.last_revision()
5811
revision_id = ensure_null(revision_id)
5812
if revision_id == NULL_REVISION:
5813
raise errors.CommandError(gettext('No revisions to bundle.'))
5814
directive = merge_directive.MergeDirective2.from_objects(
5815
branch.repository, revision_id, time.time(),
5816
osutils.local_time_offset(), submit_branch,
5817
public_branch=public_branch, include_patch=include_patch,
5818
include_bundle=include_bundle, message=message,
5819
base_revision_id=base_revision_id)
5822
self.outf.write(directive.to_signed(branch))
5824
self.outf.writelines(directive.to_lines())
5826
message = directive.to_email(mail_to, branch, sign)
5827
s = SMTPConnection(branch.get_config_stack())
5828
s.send_email(message)
5831
class cmd_send(Command):
5832
__doc__ = """Mail or create a merge-directive for submitting changes.
5834
A merge directive provides many things needed for requesting merges:
5836
* A machine-readable description of the merge to perform
5838
* An optional patch that is a preview of the changes requested
5840
* An optional bundle of revision data, so that the changes can be applied
5841
directly from the merge directive, without retrieving data from a
5844
`brz send` creates a compact data set that, when applied using brz
5845
merge, has the same effect as merging from the source branch.
5847
By default the merge directive is self-contained and can be applied to any
5848
branch containing submit_branch in its ancestory without needing access to
5851
If --no-bundle is specified, then Breezy doesn't send the contents of the
5852
revisions, but only a structured request to merge from the
5853
public_location. In that case the public_branch is needed and it must be
5854
up-to-date and accessible to the recipient. The public_branch is always
5855
included if known, so that people can check it later.
5857
The submit branch defaults to the parent of the source branch, but can be
5858
overridden. Both submit branch and public branch will be remembered in
5859
branch.conf the first time they are used for a particular branch. The
5860
source branch defaults to that containing the working directory, but can
5861
be changed using --from.
5863
Both the submit branch and the public branch follow the usual behavior with
5864
respect to --remember: If there is no default location set, the first send
5865
will set it (use --no-remember to avoid setting it). After that, you can
5866
omit the location to use the default. To change the default, use
5867
--remember. The value will only be saved if the location can be accessed.
5869
In order to calculate those changes, brz must analyse the submit branch.
5870
Therefore it is most efficient for the submit branch to be a local mirror.
5871
If a public location is known for the submit_branch, that location is used
5872
in the merge directive.
5874
The default behaviour is to send the merge directive by mail, unless -o is
5875
given, in which case it is sent to a file.
5877
Mail is sent using your preferred mail program. This should be transparent
5878
on Windows (it uses MAPI). On Unix, it requires the xdg-email utility.
5879
If the preferred client can't be found (or used), your editor will be used.
5881
To use a specific mail program, set the mail_client configuration option.
5882
Supported values for specific clients are "claws", "evolution", "kmail",
5883
"mail.app" (MacOS X's Mail.app), "mutt", and "thunderbird"; generic options
5884
are "default", "editor", "emacsclient", "mapi", and "xdg-email". Plugins
5885
may also add supported clients.
5887
If mail is being sent, a to address is required. This can be supplied
5888
either on the commandline, by setting the submit_to configuration
5889
option in the branch itself or the child_submit_to configuration option
5890
in the submit branch.
5892
The merge directives created by brz send may be applied using brz merge or
5893
brz pull by specifying a file containing a merge directive as the location.
5895
brz send makes extensive use of public locations to map local locations into
5896
URLs that can be used by other people. See `brz help configuration` to
5897
set them, and use `brz info` to display them.
5900
encoding_type = 'exact'
5902
_see_also = ['merge', 'pull']
5904
takes_args = ['submit_branch?', 'public_branch?']
5908
help='Do not include a bundle in the merge directive.'),
5909
Option('no-patch', help='Do not include a preview patch in the merge'
5912
help='Remember submit and public branch.'),
5914
help='Branch to generate the submission from, '
5915
'rather than the one containing the working directory.',
5918
Option('output', short_name='o',
5919
help='Write merge directive to this file or directory; '
5920
'use - for stdout.',
5923
help='Refuse to send if there are uncommitted changes in'
5924
' the working tree, --no-strict disables the check.'),
5925
Option('mail-to', help='Mail the request to this address.',
5929
Option('body', help='Body for the email.', type=str),
5930
RegistryOption('format',
5931
help='Use the specified output format.',
5932
lazy_registry=('breezy.send', 'format_registry')),
5935
def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5936
no_patch=False, revision=None, remember=None, output=None,
5937
format=None, mail_to=None, message=None, body=None,
5938
strict=None, **kwargs):
5939
from .send import send
5940
return send(submit_branch, revision, public_branch, remember,
5941
format, no_bundle, no_patch, output,
5942
kwargs.get('from', '.'), mail_to, message, body,
5947
class cmd_bundle_revisions(cmd_send):
5948
__doc__ = """Create a merge-directive for submitting changes.
5950
A merge directive provides many things needed for requesting merges:
5952
* A machine-readable description of the merge to perform
5954
* An optional patch that is a preview of the changes requested
5956
* An optional bundle of revision data, so that the changes can be applied
5957
directly from the merge directive, without retrieving data from a
5960
If --no-bundle is specified, then public_branch is needed (and must be
5961
up-to-date), so that the receiver can perform the merge using the
5962
public_branch. The public_branch is always included if known, so that
5963
people can check it later.
5965
The submit branch defaults to the parent, but can be overridden. Both
5966
submit branch and public branch will be remembered if supplied.
5968
If a public_branch is known for the submit_branch, that public submit
5969
branch is used in the merge instructions. This means that a local mirror
5970
can be used as your actual submit branch, once you have set public_branch
5976
help='Do not include a bundle in the merge directive.'),
5977
Option('no-patch', help='Do not include a preview patch in the merge'
5980
help='Remember submit and public branch.'),
5982
help='Branch to generate the submission from, '
5983
'rather than the one containing the working directory.',
5986
Option('output', short_name='o', help='Write directive to this file.',
5989
help='Refuse to bundle revisions if there are uncommitted'
5990
' changes in the working tree, --no-strict disables the check.'),
5992
RegistryOption('format',
5993
help='Use the specified output format.',
5994
lazy_registry=('breezy.send', 'format_registry')),
5996
aliases = ['bundle']
5998
_see_also = ['send', 'merge']
6002
def run(self, submit_branch=None, public_branch=None, no_bundle=False,
6003
no_patch=False, revision=None, remember=False, output=None,
6004
format=None, strict=None, **kwargs):
6007
from .send import send
6008
return send(submit_branch, revision, public_branch, remember,
6009
format, no_bundle, no_patch, output,
6010
kwargs.get('from', '.'), None, None, None,
6011
self.outf, strict=strict)
6014
class cmd_tag(Command):
6015
__doc__ = """Create, remove or modify a tag naming a revision.
6017
Tags give human-meaningful names to revisions. Commands that take a -r
6018
(--revision) option can be given -rtag:X, where X is any previously
6021
Tags are stored in the branch. Tags are copied from one branch to another
6022
along when you branch, push, pull or merge.
6024
It is an error to give a tag name that already exists unless you pass
6025
--force, in which case the tag is moved to point to the new revision.
6027
To rename a tag (change the name but keep it on the same revsion), run ``brz
6028
tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
6030
If no tag name is specified it will be determined through the
6031
'automatic_tag_name' hook. This can e.g. be used to automatically tag
6032
upstream releases by reading configure.ac. See ``brz help hooks`` for
6036
_see_also = ['commit', 'tags']
6037
takes_args = ['tag_name?']
6040
help='Delete this tag rather than placing it.',
6042
custom_help('directory',
6043
help='Branch in which to place the tag.'),
6045
help='Replace existing tags.',
6050
def run(self, tag_name=None,
6056
branch, relpath = Branch.open_containing(directory)
6057
self.enter_context(branch.lock_write())
6059
if tag_name is None:
6060
raise errors.CommandError(
6061
gettext("No tag specified to delete."))
6062
branch.tags.delete_tag(tag_name)
6063
note(gettext('Deleted tag %s.') % tag_name)
6066
if len(revision) != 1:
6067
raise errors.CommandError(gettext(
6068
"Tags can only be placed on a single revision, "
6070
revision_id = revision[0].as_revision_id(branch)
6072
revision_id = branch.last_revision()
6073
if tag_name is None:
6074
tag_name = branch.automatic_tag_name(revision_id)
6075
if tag_name is None:
6076
raise errors.CommandError(gettext(
6077
"Please specify a tag name."))
6079
existing_target = branch.tags.lookup_tag(tag_name)
6080
except errors.NoSuchTag:
6081
existing_target = None
6082
if not force and existing_target not in (None, revision_id):
6083
raise errors.TagAlreadyExists(tag_name)
6084
if existing_target == revision_id:
6085
note(gettext('Tag %s already exists for that revision.') % tag_name)
6087
branch.tags.set_tag(tag_name, revision_id)
6088
if existing_target is None:
6089
note(gettext('Created tag %s.') % tag_name)
6091
note(gettext('Updated tag %s.') % tag_name)
6094
class cmd_tags(Command):
6095
__doc__ = """List tags.
6097
This command shows a table of tag names and the revisions they reference.
6102
custom_help('directory',
6103
help='Branch whose tags should be displayed.'),
6104
RegistryOption('sort',
6105
'Sort tags by different criteria.', title='Sorting',
6106
lazy_registry=('breezy.tag', 'tag_sort_methods')
6113
def run(self, directory='.', sort=None, show_ids=False, revision=None):
6114
from .tag import tag_sort_methods
6115
branch, relpath = Branch.open_containing(directory)
6117
tags = list(branch.tags.get_tag_dict().items())
6121
self.enter_context(branch.lock_read())
6123
# Restrict to the specified range
6124
tags = self._tags_for_range(branch, revision)
6126
sort = tag_sort_methods.get()
6129
# [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
6130
for index, (tag, revid) in enumerate(tags):
6132
revno = branch.revision_id_to_dotted_revno(revid)
6133
if isinstance(revno, tuple):
6134
revno = '.'.join(map(str, revno))
6135
except (errors.NoSuchRevision,
6136
errors.GhostRevisionsHaveNoRevno,
6137
errors.UnsupportedOperation):
6138
# Bad tag data/merges can lead to tagged revisions
6139
# which are not in this branch. Fail gracefully ...
6141
tags[index] = (tag, revno)
6143
tags = [(tag, revid.decode('utf-8')) for (tag, revid) in tags]
6145
for tag, revspec in tags:
6146
self.outf.write('%-20s %s\n' % (tag, revspec))
6148
def _tags_for_range(self, branch, revision):
6149
rev1, rev2 = _get_revision_range(revision, branch, self.name())
6150
revid1, revid2 = rev1.rev_id, rev2.rev_id
6151
# _get_revision_range will always set revid2 if it's not specified.
6152
# If revid1 is None, it means we want to start from the branch
6153
# origin which is always a valid ancestor. If revid1 == revid2, the
6154
# ancestry check is useless.
6155
if revid1 and revid1 != revid2:
6156
# FIXME: We really want to use the same graph than
6157
# branch.iter_merge_sorted_revisions below, but this is not
6158
# easily available -- vila 2011-09-23
6159
if branch.repository.get_graph().is_ancestor(revid2, revid1):
6160
# We don't want to output anything in this case...
6162
# only show revisions between revid1 and revid2 (inclusive)
6163
tagged_revids = branch.tags.get_reverse_tag_dict()
6165
for r in branch.iter_merge_sorted_revisions(
6166
start_revision_id=revid2, stop_revision_id=revid1,
6167
stop_rule='include'):
6168
revid_tags = tagged_revids.get(r[0], None)
6170
found.extend([(tag, r[0]) for tag in revid_tags])
6174
class cmd_reconfigure(Command):
6175
__doc__ = """Reconfigure the type of a brz directory.
6177
A target configuration must be specified.
6179
For checkouts, the bind-to location will be auto-detected if not specified.
6180
The order of preference is
6181
1. For a lightweight checkout, the current bound location.
6182
2. For branches that used to be checkouts, the previously-bound location.
6183
3. The push location.
6184
4. The parent location.
6185
If none of these is available, --bind-to must be specified.
6188
_see_also = ['branches', 'checkouts', 'standalone-trees', 'working-trees']
6189
takes_args = ['location?']
6191
RegistryOption.from_kwargs(
6194
help='The relation between branch and tree.',
6195
value_switches=True, enum_switch=False,
6196
branch='Reconfigure to be an unbound branch with no working tree.',
6197
tree='Reconfigure to be an unbound branch with a working tree.',
6198
checkout='Reconfigure to be a bound branch with a working tree.',
6199
lightweight_checkout='Reconfigure to be a lightweight'
6200
' checkout (with no local history).',
6202
RegistryOption.from_kwargs(
6204
title='Repository type',
6205
help='Location fo the repository.',
6206
value_switches=True, enum_switch=False,
6207
standalone='Reconfigure to be a standalone branch '
6208
'(i.e. stop using shared repository).',
6209
use_shared='Reconfigure to use a shared repository.',
6211
RegistryOption.from_kwargs(
6213
title='Trees in Repository',
6214
help='Whether new branches in the repository have trees.',
6215
value_switches=True, enum_switch=False,
6216
with_trees='Reconfigure repository to create '
6217
'working trees on branches by default.',
6218
with_no_trees='Reconfigure repository to not create '
6219
'working trees on branches by default.'
6221
Option('bind-to', help='Branch to bind checkout to.', type=str),
6223
help='Perform reconfiguration even if local changes'
6225
Option('stacked-on',
6226
help='Reconfigure a branch to be stacked on another branch.',
6230
help='Reconfigure a branch to be unstacked. This '
6231
'may require copying substantial data into it.',
6235
def run(self, location=None, bind_to=None, force=False,
6236
tree_type=None, repository_type=None, repository_trees=None,
6237
stacked_on=None, unstacked=None):
6238
directory = controldir.ControlDir.open(location)
6239
if stacked_on and unstacked:
6240
raise errors.CommandError(
6241
gettext("Can't use both --stacked-on and --unstacked"))
6242
elif stacked_on is not None:
6243
reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
6245
reconfigure.ReconfigureUnstacked().apply(directory)
6246
# At the moment you can use --stacked-on and a different
6247
# reconfiguration shape at the same time; there seems no good reason
6249
if (tree_type is None and
6250
repository_type is None and
6251
repository_trees is None):
6252
if stacked_on or unstacked:
6255
raise errors.CommandError(gettext('No target configuration '
6257
reconfiguration = None
6258
if tree_type == 'branch':
6259
reconfiguration = reconfigure.Reconfigure.to_branch(directory)
6260
elif tree_type == 'tree':
6261
reconfiguration = reconfigure.Reconfigure.to_tree(directory)
6262
elif tree_type == 'checkout':
6263
reconfiguration = reconfigure.Reconfigure.to_checkout(
6265
elif tree_type == 'lightweight-checkout':
6266
reconfiguration = reconfigure.Reconfigure.to_lightweight_checkout(
6269
reconfiguration.apply(force)
6270
reconfiguration = None
6271
if repository_type == 'use-shared':
6272
reconfiguration = reconfigure.Reconfigure.to_use_shared(directory)
6273
elif repository_type == 'standalone':
6274
reconfiguration = reconfigure.Reconfigure.to_standalone(directory)
6276
reconfiguration.apply(force)
6277
reconfiguration = None
6278
if repository_trees == 'with-trees':
6279
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
6281
elif repository_trees == 'with-no-trees':
6282
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
6285
reconfiguration.apply(force)
6286
reconfiguration = None
6289
class cmd_switch(Command):
6290
__doc__ = """Set the branch of a checkout and update.
6292
For lightweight checkouts, this changes the branch being referenced.
6293
For heavyweight checkouts, this checks that there are no local commits
6294
versus the current bound branch, then it makes the local branch a mirror
6295
of the new location and binds to it.
6297
In both cases, the working tree is updated and uncommitted changes
6298
are merged. The user can commit or revert these as they desire.
6300
Pending merges need to be committed or reverted before using switch.
6302
The path to the branch to switch to can be specified relative to the parent
6303
directory of the current branch. For example, if you are currently in a
6304
checkout of /path/to/branch, specifying 'newbranch' will find a branch at
6307
Bound branches use the nickname of its master branch unless it is set
6308
locally, in which case switching will update the local nickname to be
6312
takes_args = ['to_location?']
6313
takes_options = ['directory',
6315
help='Switch even if local commits will be lost.'),
6317
Option('create-branch', short_name='b',
6318
help='Create the target branch from this one before'
6319
' switching to it.'),
6321
help='Store and restore uncommitted changes in the'
6325
def run(self, to_location=None, force=False, create_branch=False,
6326
revision=None, directory=u'.', store=False):
6327
from . import switch
6328
tree_location = directory
6329
revision = _get_one_revision('switch', revision)
6330
control_dir = controldir.ControlDir.open_containing(tree_location)[0]
6331
possible_transports = [control_dir.root_transport]
6332
if to_location is None:
6333
if revision is None:
6334
raise errors.CommandError(gettext('You must supply either a'
6335
' revision or a location'))
6336
to_location = tree_location
6338
branch = control_dir.open_branch(
6339
possible_transports=possible_transports)
6340
had_explicit_nick = branch.get_config().has_explicit_nickname()
6341
except errors.NotBranchError:
6343
had_explicit_nick = False
6345
possible_transports.append(branch.user_transport)
6348
raise errors.CommandError(
6349
gettext('cannot create branch without source branch'))
6350
to_location = lookup_new_sibling_branch(
6351
control_dir, to_location,
6352
possible_transports=possible_transports)
6353
if revision is not None:
6354
revision = revision.as_revision_id(branch)
6355
to_branch = branch.controldir.sprout(
6357
possible_transports=possible_transports,
6358
revision_id=revision,
6359
source_branch=branch).open_branch()
6362
to_branch = Branch.open(
6363
to_location, possible_transports=possible_transports)
6364
except errors.NotBranchError:
6365
to_branch = open_sibling_branch(
6366
control_dir, to_location,
6367
possible_transports=possible_transports)
6368
if revision is not None:
6369
revision = revision.as_revision_id(to_branch)
6370
possible_transports.append(to_branch.user_transport)
6372
switch.switch(control_dir, to_branch, force, revision_id=revision,
6373
store_uncommitted=store,
6374
possible_transports=possible_transports)
6375
except controldir.BranchReferenceLoop:
6376
raise errors.CommandError(
6377
gettext('switching would create a branch reference loop. '
6378
'Use the "bzr up" command to switch to a '
6379
'different revision.'))
6380
if had_explicit_nick:
6381
branch = control_dir.open_branch() # get the new branch!
6382
branch.nick = to_branch.nick
6384
if to_branch.controldir.control_url != control_dir.control_url:
6385
note(gettext('Switched to branch %s at %s'),
6386
to_branch.name, urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6388
note(gettext('Switched to branch %s'), to_branch.name)
6390
note(gettext('Switched to branch at %s'),
6391
urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6394
class cmd_view(Command):
6395
__doc__ = """Manage filtered views.
6397
Views provide a mask over the tree so that users can focus on
6398
a subset of a tree when doing their work. After creating a view,
6399
commands that support a list of files - status, diff, commit, etc -
6400
effectively have that list of files implicitly given each time.
6401
An explicit list of files can still be given but those files
6402
must be within the current view.
6404
In most cases, a view has a short life-span: it is created to make
6405
a selected change and is deleted once that change is committed.
6406
At other times, you may wish to create one or more named views
6407
and switch between them.
6409
To disable the current view without deleting it, you can switch to
6410
the pseudo view called ``off``. This can be useful when you need
6411
to see the whole tree for an operation or two (e.g. merge) but
6412
want to switch back to your view after that.
6415
To define the current view::
6417
brz view file1 dir1 ...
6419
To list the current view::
6423
To delete the current view::
6427
To disable the current view without deleting it::
6429
brz view --switch off
6431
To define a named view and switch to it::
6433
brz view --name view-name file1 dir1 ...
6435
To list a named view::
6437
brz view --name view-name
6439
To delete a named view::
6441
brz view --name view-name --delete
6443
To switch to a named view::
6445
brz view --switch view-name
6447
To list all views defined::
6451
To delete all views::
6453
brz view --delete --all
6457
takes_args = ['file*']
6460
help='Apply list or delete action to all views.',
6463
help='Delete the view.',
6466
help='Name of the view to define, list or delete.',
6470
help='Name of the view to switch to.',
6475
def run(self, file_list,
6481
tree, file_list = WorkingTree.open_containing_paths(file_list,
6483
current_view, view_dict = tree.views.get_view_info()
6488
raise errors.CommandError(gettext(
6489
"Both --delete and a file list specified"))
6491
raise errors.CommandError(gettext(
6492
"Both --delete and --switch specified"))
6494
tree.views.set_view_info(None, {})
6495
self.outf.write(gettext("Deleted all views.\n"))
6497
raise errors.CommandError(
6498
gettext("No current view to delete"))
6500
tree.views.delete_view(name)
6501
self.outf.write(gettext("Deleted '%s' view.\n") % name)
6504
raise errors.CommandError(gettext(
6505
"Both --switch and a file list specified"))
6507
raise errors.CommandError(gettext(
6508
"Both --switch and --all specified"))
6509
elif switch == 'off':
6510
if current_view is None:
6511
raise errors.CommandError(
6512
gettext("No current view to disable"))
6513
tree.views.set_view_info(None, view_dict)
6514
self.outf.write(gettext("Disabled '%s' view.\n") %
6517
tree.views.set_view_info(switch, view_dict)
6518
view_str = views.view_display_str(tree.views.lookup_view())
6520
gettext("Using '{0}' view: {1}\n").format(switch, view_str))
6523
self.outf.write(gettext('Views defined:\n'))
6524
for view in sorted(view_dict):
6525
if view == current_view:
6529
view_str = views.view_display_str(view_dict[view])
6530
self.outf.write('%s %-20s %s\n' % (active, view, view_str))
6532
self.outf.write(gettext('No views defined.\n'))
6535
# No name given and no current view set
6538
raise errors.CommandError(gettext(
6539
"Cannot change the 'off' pseudo view"))
6540
tree.views.set_view(name, sorted(file_list))
6541
view_str = views.view_display_str(tree.views.lookup_view())
6543
gettext("Using '{0}' view: {1}\n").format(name, view_str))
6547
# No name given and no current view set
6548
self.outf.write(gettext('No current view.\n'))
6550
view_str = views.view_display_str(tree.views.lookup_view(name))
6552
gettext("'{0}' view is: {1}\n").format(name, view_str))
6555
class cmd_hooks(Command):
6556
__doc__ = """Show hooks."""
6561
for hook_key in sorted(hooks.known_hooks.keys()):
6562
some_hooks = hooks.known_hooks_key_to_object(hook_key)
6563
self.outf.write("%s:\n" % type(some_hooks).__name__)
6564
for hook_name, hook_point in sorted(some_hooks.items()):
6565
self.outf.write(" %s:\n" % (hook_name,))
6566
found_hooks = list(hook_point)
6568
for hook in found_hooks:
6569
self.outf.write(" %s\n" %
6570
(some_hooks.get_hook_name(hook),))
6572
self.outf.write(gettext(" <no hooks installed>\n"))
6575
class cmd_remove_branch(Command):
6576
__doc__ = """Remove a branch.
6578
This will remove the branch from the specified location but
6579
will keep any working tree or repository in place.
6583
Remove the branch at repo/trunk::
6585
brz remove-branch repo/trunk
6589
takes_args = ["location?"]
6591
takes_options = ['directory',
6592
Option('force', help='Remove branch even if it is the active branch.')]
6594
aliases = ["rmbranch"]
6596
def run(self, directory=None, location=None, force=False):
6597
br = open_nearby_branch(near=directory, location=location)
6598
if not force and br.controldir.has_workingtree():
6600
active_branch = br.controldir.open_branch(name="")
6601
except errors.NotBranchError:
6602
active_branch = None
6603
if (active_branch is not None and
6604
br.control_url == active_branch.control_url):
6605
raise errors.CommandError(
6606
gettext("Branch is active. Use --force to remove it."))
6607
br.controldir.destroy_branch(br.name)
6610
class cmd_shelve(Command):
6611
__doc__ = """Temporarily set aside some changes from the current tree.
6613
Shelve allows you to temporarily put changes you've made "on the shelf",
6614
ie. out of the way, until a later time when you can bring them back from
6615
the shelf with the 'unshelve' command. The changes are stored alongside
6616
your working tree, and so they aren't propagated along with your branch nor
6617
will they survive its deletion.
6619
If shelve --list is specified, previously-shelved changes are listed.
6621
Shelve is intended to help separate several sets of changes that have
6622
been inappropriately mingled. If you just want to get rid of all changes
6623
and you don't need to restore them later, use revert. If you want to
6624
shelve all text changes at once, use shelve --all.
6626
If filenames are specified, only the changes to those files will be
6627
shelved. Other files will be left untouched.
6629
If a revision is specified, changes since that revision will be shelved.
6631
You can put multiple items on the shelf, and by default, 'unshelve' will
6632
restore the most recently shelved changes.
6634
For complicated changes, it is possible to edit the changes in a separate
6635
editor program to decide what the file remaining in the working copy
6636
should look like. To do this, add the configuration option
6638
change_editor = PROGRAM {new_path} {old_path}
6640
where {new_path} is replaced with the path of the new version of the
6641
file and {old_path} is replaced with the path of the old version of
6642
the file. The PROGRAM should save the new file with the desired
6643
contents of the file in the working tree.
6647
takes_args = ['file*']
6652
Option('all', help='Shelve all changes.'),
6654
RegistryOption('writer', 'Method to use for writing diffs.',
6655
breezy.option.diff_writer_registry,
6656
value_switches=True, enum_switch=False),
6658
Option('list', help='List shelved changes.'),
6660
help='Destroy removed changes instead of shelving them.'),
6662
_see_also = ['unshelve', 'configuration']
6664
def run(self, revision=None, all=False, file_list=None, message=None,
6665
writer=None, list=False, destroy=False, directory=None):
6667
return self.run_for_list(directory=directory)
6668
from .shelf_ui import Shelver
6670
writer = breezy.option.diff_writer_registry.get()
6672
shelver = Shelver.from_args(writer(self.outf), revision, all,
6673
file_list, message, destroy=destroy, directory=directory)
6678
except errors.UserAbort:
6681
def run_for_list(self, directory=None):
6682
if directory is None:
6684
tree = WorkingTree.open_containing(directory)[0]
6685
self.enter_context(tree.lock_read())
6686
manager = tree.get_shelf_manager()
6687
shelves = manager.active_shelves()
6688
if len(shelves) == 0:
6689
note(gettext('No shelved changes.'))
6691
for shelf_id in reversed(shelves):
6692
message = manager.get_metadata(shelf_id).get(b'message')
6694
message = '<no message>'
6695
self.outf.write('%3d: %s\n' % (shelf_id, message))
6699
class cmd_unshelve(Command):
6700
__doc__ = """Restore shelved changes.
6702
By default, the most recently shelved changes are restored. However if you
6703
specify a shelf by id those changes will be restored instead. This works
6704
best when the changes don't depend on each other.
6707
takes_args = ['shelf_id?']
6710
RegistryOption.from_kwargs(
6711
'action', help="The action to perform.",
6712
enum_switch=False, value_switches=True,
6713
apply="Apply changes and remove from the shelf.",
6714
dry_run="Show changes, but do not apply or remove them.",
6715
preview="Instead of unshelving the changes, show the diff that "
6716
"would result from unshelving.",
6717
delete_only="Delete changes without applying them.",
6718
keep="Apply changes but don't delete them.",
6721
_see_also = ['shelve']
6723
def run(self, shelf_id=None, action='apply', directory=u'.'):
6724
from .shelf_ui import Unshelver
6725
unshelver = Unshelver.from_args(shelf_id, action, directory=directory)
6729
unshelver.tree.unlock()
6732
class cmd_clean_tree(Command):
6733
__doc__ = """Remove unwanted files from working tree.
6735
By default, only unknown files, not ignored files, are deleted. Versioned
6736
files are never deleted.
6738
Another class is 'detritus', which includes files emitted by brz during
6739
normal operations and selftests. (The value of these files decreases with
6742
If no options are specified, unknown files are deleted. Otherwise, option
6743
flags are respected, and may be combined.
6745
To check what clean-tree will do, use --dry-run.
6747
takes_options = ['directory',
6748
Option('ignored', help='Delete all ignored files.'),
6749
Option('detritus', help='Delete conflict files, merge and revert'
6750
' backups, and failed selftest dirs.'),
6752
help='Delete files unknown to brz (default).'),
6753
Option('dry-run', help='Show files to delete instead of'
6755
Option('force', help='Do not prompt before deleting.')]
6757
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
6758
force=False, directory=u'.'):
6759
from .clean_tree import clean_tree
6760
if not (unknown or ignored or detritus):
6764
clean_tree(directory, unknown=unknown, ignored=ignored,
6765
detritus=detritus, dry_run=dry_run, no_prompt=force)
6768
class cmd_reference(Command):
6769
__doc__ = """list, view and set branch locations for nested trees.
6771
If no arguments are provided, lists the branch locations for nested trees.
6772
If one argument is provided, display the branch location for that tree.
6773
If two arguments are provided, set the branch location for that tree.
6778
takes_args = ['path?', 'location?']
6781
Option('force-unversioned',
6782
help='Set reference even if path is not versioned.'),
6785
def run(self, path=None, directory='.', location=None, force_unversioned=False):
6786
tree, branch, relpath = (
6787
controldir.ControlDir.open_containing_tree_or_branch(directory))
6789
tree = branch.basis_tree()
6791
with tree.lock_read():
6793
(path, tree.get_reference_info(path, branch))
6794
for path in tree.iter_references()]
6795
self._display_reference_info(tree, branch, info)
6797
if not tree.is_versioned(path) and not force_unversioned:
6798
raise errors.NotVersionedError(path)
6799
if location is None:
6800
info = [(path, tree.get_reference_info(path, branch))]
6801
self._display_reference_info(tree, branch, info)
6803
tree.set_reference_info(path, location)
6805
def _display_reference_info(self, tree, branch, info):
6807
for path, location in info:
6808
ref_list.append((path, location))
6809
for path, location in sorted(ref_list):
6810
self.outf.write('%s %s\n' % (path, location))
6813
class cmd_export_pot(Command):
6814
__doc__ = """Export command helps and error messages in po format."""
6817
takes_options = [Option('plugin',
6818
help='Export help text from named command '
6819
'(defaults to all built in commands).',
6821
Option('include-duplicates',
6822
help='Output multiple copies of the same msgid '
6823
'string if it appears more than once.'),
6826
def run(self, plugin=None, include_duplicates=False):
6827
from .export_pot import export_pot
6828
export_pot(self.outf, plugin, include_duplicates)
6831
class cmd_import(Command):
6832
__doc__ = """Import sources from a directory, tarball or zip file
6834
This command will import a directory, tarball or zip file into a bzr
6835
tree, replacing any versioned files already present. If a directory is
6836
specified, it is used as the target. If the directory does not exist, or
6837
is not versioned, it is created.
6839
Tarballs may be gzip or bzip2 compressed. This is autodetected.
6841
If the tarball or zip has a single root directory, that directory is
6842
stripped when extracting the tarball. This is not done for directories.
6845
takes_args = ['source', 'tree?']
6847
def run(self, source, tree=None):
6848
from .upstream_import import do_import
6849
do_import(source, tree)
6852
class cmd_link_tree(Command):
6853
__doc__ = """Hardlink matching files to another tree.
6855
Only files with identical content and execute bit will be linked.
6858
takes_args = ['location']
6860
def run(self, location):
6861
from .transform import link_tree
6862
target_tree = WorkingTree.open_containing(".")[0]
6863
source_tree = WorkingTree.open(location)
6864
with target_tree.lock_write(), source_tree.lock_read():
6865
link_tree(target_tree, source_tree)
6868
class cmd_fetch_ghosts(Command):
6869
__doc__ = """Attempt to retrieve ghosts from another branch.
6871
If the other branch is not supplied, the last-pulled branch is used.
6875
aliases = ['fetch-missing']
6876
takes_args = ['branch?']
6877
takes_options = [Option('no-fix', help="Skip additional synchonization.")]
6879
def run(self, branch=None, no_fix=False):
6880
from .fetch_ghosts import GhostFetcher
6881
installed, failed = GhostFetcher.from_cmdline(branch).run()
6882
if len(installed) > 0:
6883
self.outf.write("Installed:\n")
6884
for rev in installed:
6885
self.outf.write(rev.decode('utf-8') + "\n")
6887
self.outf.write("Still missing:\n")
6889
self.outf.write(rev.decode('utf-8') + "\n")
6890
if not no_fix and len(installed) > 0:
6891
cmd_reconcile().run(".")
6894
class cmd_grep(Command):
6895
"""Print lines matching PATTERN for specified files and revisions.
6897
This command searches the specified files and revisions for a given
6898
pattern. The pattern is specified as a Python regular expressions[1].
6900
If the file name is not specified, the revisions starting with the
6901
current directory are searched recursively. If the revision number is
6902
not specified, the working copy is searched. To search the last committed
6903
revision, use the '-r -1' or '-r last:1' option.
6905
Unversioned files are not searched unless explicitly specified on the
6906
command line. Unversioned directores are not searched.
6908
When searching a pattern, the output is shown in the 'filepath:string'
6909
format. If a revision is explicitly searched, the output is shown as
6910
'filepath~N:string', where N is the revision number.
6912
--include and --exclude options can be used to search only (or exclude
6913
from search) files with base name matches the specified Unix style GLOB
6914
pattern. The GLOB pattern an use *, ?, and [...] as wildcards, and \\
6915
to quote wildcard or backslash character literally. Note that the glob
6916
pattern is not a regular expression.
6918
[1] http://docs.python.org/library/re.html#regular-expression-syntax
6921
encoding_type = 'replace'
6922
takes_args = ['pattern', 'path*']
6926
Option('color', type=str, argname='when',
6927
help='Show match in color. WHEN is never, always or auto.'),
6928
Option('diff', short_name='p',
6929
help='Grep for pattern in changeset for each revision.'),
6930
ListOption('exclude', type=str, argname='glob', short_name='X',
6931
help="Skip files whose base name matches GLOB."),
6932
ListOption('include', type=str, argname='glob', short_name='I',
6933
help="Search only files whose base name matches GLOB."),
6934
Option('files-with-matches', short_name='l',
6935
help='Print only the name of each input file in '
6936
'which PATTERN is found.'),
6937
Option('files-without-match', short_name='L',
6938
help='Print only the name of each input file in '
6939
'which PATTERN is not found.'),
6940
Option('fixed-string', short_name='F',
6941
help='Interpret PATTERN is a single fixed string (not regex).'),
6943
help='Search for pattern starting from the root of the branch. '
6944
'(implies --recursive)'),
6945
Option('ignore-case', short_name='i',
6946
help='Ignore case distinctions while matching.'),
6948
help='Number of levels to display - 0 for all, 1 for collapsed '
6951
type=_parse_levels),
6952
Option('line-number', short_name='n',
6953
help='Show 1-based line number.'),
6954
Option('no-recursive',
6955
help="Don't recurse into subdirectories. (default is --recursive)"),
6956
Option('null', short_name='Z',
6957
help='Write an ASCII NUL (\\0) separator '
6958
'between output lines rather than a newline.'),
6962
def run(self, verbose=False, ignore_case=False, no_recursive=False,
6963
from_root=False, null=False, levels=None, line_number=False,
6964
path_list=None, revision=None, pattern=None, include=None,
6965
exclude=None, fixed_string=False, files_with_matches=False,
6966
files_without_match=False, color=None, diff=False):
6967
from breezy import _termcolor
6970
if path_list is None:
6974
raise errors.CommandError(
6975
'cannot specify both --from-root and PATH.')
6977
if files_with_matches and files_without_match:
6978
raise errors.CommandError(
6979
'cannot specify both '
6980
'-l/--files-with-matches and -L/--files-without-matches.')
6982
global_config = _mod_config.GlobalConfig()
6985
color = global_config.get_user_option('grep_color')
6990
if color not in ['always', 'never', 'auto']:
6991
raise errors.CommandError('Valid values for --color are '
6992
'"always", "never" or "auto".')
6998
if revision is not None or levels == 0:
6999
# print revision numbers as we may be showing multiple revisions
7006
if not ignore_case and grep.is_fixed_string(pattern):
7007
# if the pattern isalnum, implicitly use to -F for faster grep
7009
elif ignore_case and fixed_string:
7010
# GZ 2010-06-02: Fall back to regexp rather than lowercasing
7011
# pattern and text which will cause pain later
7012
fixed_string = False
7013
pattern = re.escape(pattern)
7016
re_flags = re.MULTILINE
7018
re_flags |= re.IGNORECASE
7020
if not fixed_string:
7021
patternc = grep.compile_pattern(
7022
pattern.encode(grep._user_encoding), re_flags)
7024
if color == 'always':
7026
elif color == 'never':
7028
elif color == 'auto':
7029
show_color = _termcolor.allow_color()
7031
opts = grep.GrepOptions()
7033
opts.verbose = verbose
7034
opts.ignore_case = ignore_case
7035
opts.no_recursive = no_recursive
7036
opts.from_root = from_root
7038
opts.levels = levels
7039
opts.line_number = line_number
7040
opts.path_list = path_list
7041
opts.revision = revision
7042
opts.pattern = pattern
7043
opts.include = include
7044
opts.exclude = exclude
7045
opts.fixed_string = fixed_string
7046
opts.files_with_matches = files_with_matches
7047
opts.files_without_match = files_without_match
7051
opts.eol_marker = eol_marker
7052
opts.print_revno = print_revno
7053
opts.patternc = patternc
7054
opts.recursive = not no_recursive
7055
opts.fixed_string = fixed_string
7056
opts.outf = self.outf
7057
opts.show_color = show_color
7061
# files_with_matches, files_without_match
7062
# levels(?), line_number, from_root
7064
# These are silently ignored.
7065
grep.grep_diff(opts)
7066
elif revision is None:
7067
grep.workingtree_grep(opts)
7069
grep.versioned_grep(opts)
7072
class cmd_patch(Command):
7073
"""Apply a named patch to the current tree.
7077
takes_args = ['filename?']
7078
takes_options = [Option('strip', type=int, short_name='p',
7079
help=("Strip the smallest prefix containing num "
7080
"leading slashes from filenames.")),
7081
Option('silent', help='Suppress chatter.')]
7083
def run(self, filename=None, strip=None, silent=False):
7084
from .patch import patch_tree
7085
wt = WorkingTree.open_containing('.')[0]
7089
if filename is None:
7090
my_file = getattr(sys.stdin, 'buffer', sys.stdin)
7092
my_file = open(filename, 'rb')
7093
patches = [my_file.read()]
7094
return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
7097
class cmd_resolve_location(Command):
7098
__doc__ = """Expand a location to a full URL.
7101
Look up a Launchpad URL.
7103
brz resolve-location lp:brz
7105
takes_args = ['location']
7108
def run(self, location):
7109
from .location import location_to_url
7110
url = location_to_url(location)
7111
display_url = urlutils.unescape_for_display(url, self.outf.encoding)
7112
self.outf.write('%s\n' % display_url)
7115
def _register_lazy_builtins():
7116
# register lazy builtins from other modules; called at startup and should
7117
# be only called once.
7118
for (name, aliases, module_name) in [
7119
('cmd_bisect', [], 'breezy.bisect'),
7120
('cmd_bundle_info', [], 'breezy.bzr.bundle.commands'),
7121
('cmd_config', [], 'breezy.config'),
7122
('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
7123
('cmd_file_id', [], 'breezy.bzr.debug_commands'),
7124
('cmd_file_path', [], 'breezy.bzr.debug_commands'),
7125
('cmd_version_info', [], 'breezy.cmd_version_info'),
7126
('cmd_resolve', ['resolved'], 'breezy.conflicts'),
7127
('cmd_conflicts', [], 'breezy.conflicts'),
7128
('cmd_ping', [], 'breezy.bzr.smart.ping'),
7129
('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
7130
('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
7131
('cmd_test_script', [], 'breezy.cmd_test_script'),
7133
builtin_command_registry.register_lazy(name, aliases, module_name)