44
42
from breezy import (
45
43
conflicts as _mod_conflicts,
48
44
filters as _mod_filters,
51
46
revision as _mod_revision,
51
from breezy.bzr import (
59
from .controldir import (
61
ControlComponentFormatRegistry,
62
ControlComponentFormat,
61
69
from .i18n import gettext
62
70
from . import mutabletree
71
from .symbol_versioning import deprecated_method, deprecated_in
63
72
from .trace import mutter, note
66
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
69
75
class SettingFileIdUnsupported(errors.BzrError):
71
77
_fmt = "This format does not support setting file ids."
293
299
return WorkingTree.open(path, _unsupported=True)
296
def find_trees(location):
297
def list_current(transport):
298
return [d for d in transport.list_dir('')
299
if not controldir.is_control_filename(d)]
301
def evaluate(controldir):
303
tree = controldir.open_workingtree()
304
except errors.NoWorkingTree:
308
t = transport.get_transport(location)
309
iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
310
list_current=list_current)
311
return [tr for tr in iterator if tr is not None]
313
301
def __repr__(self):
314
302
return "<%s of %s>" % (self.__class__.__name__,
315
303
getattr(self, 'basedir', None))
362
350
def has_filename(self, filename):
363
351
return osutils.lexists(self.abspath(filename))
365
def get_file(self, path, file_id=None, filtered=True):
366
return self.get_file_with_stat(path, file_id, filtered=filtered)[0]
353
def get_file(self, path, filtered=True):
354
return self.get_file_with_stat(path, filtered=filtered)[0]
368
def get_file_with_stat(self, path, file_id=None, filtered=True,
356
def get_file_with_stat(self, path, filtered=True,
369
357
_fstat=osutils.fstat):
370
358
"""See Tree.get_file_with_stat."""
371
359
abspath = self.abspath(path)
378
366
stat_value = _fstat(file_obj.fileno())
379
367
if filtered and self.supports_content_filtering():
380
368
filters = self._content_filter_stack(path)
381
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
370
file_obj, size = _mod_filters.filtered_input_file(
372
stat_value = _mod_filters.FilteredStat(
373
stat_value, st_size=size)
382
374
return (file_obj, stat_value)
384
def get_file_text(self, path, file_id=None, filtered=True):
385
with self.get_file(path, file_id, filtered=filtered) as my_file:
376
def get_file_text(self, path, filtered=True):
377
with self.get_file(path, filtered=filtered) as my_file:
386
378
return my_file.read()
388
def get_file_lines(self, path, file_id=None, filtered=True):
380
def get_file_lines(self, path, filtered=True):
389
381
"""See Tree.get_file_lines()"""
390
with self.get_file(path, file_id, filtered=filtered) as file:
382
with self.get_file(path, filtered=filtered) as file:
391
383
return file.readlines()
393
385
def get_parent_ids(self):
719
707
self.add(path, file_id, 'directory')
722
def get_symlink_target(self, path, file_id=None):
710
def get_symlink_target(self, path):
723
711
abspath = self.abspath(path)
724
target = osutils.readlink(abspath)
713
return osutils.readlink(abspath)
715
if getattr(e, 'errno', None) == errno.ENOENT:
716
raise errors.NoSuchFile(path)
727
719
def subsume(self, other_tree):
728
720
raise NotImplementedError(self.subsume)
730
def _setup_directory_is_tree_reference(self):
731
if self._branch.repository._format.supports_tree_reference:
732
self._directory_is_tree_reference = \
733
self._directory_may_be_tree_reference
735
self._directory_is_tree_reference = \
736
self._directory_is_never_tree_reference
738
def _directory_is_never_tree_reference(self, relpath):
741
def _directory_may_be_tree_reference(self, relpath):
742
# as a special case, if a directory contains control files then
743
# it's a tree reference, except that the root of the tree is not
744
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
745
# TODO: We could ask all the control formats whether they
746
# recognize this directory, but at the moment there's no cheap api
747
# to do that. Since we probably can only nest bzr checkouts and
748
# they always use this name it's ok for now. -- mbp 20060306
750
# FIXME: There is an unhandled case here of a subdirectory
751
# containing .bzr but not a branch; that will probably blow up
752
# when you try to commit it. It might happen if there is a
753
# checkout in a subdirectory. This can be avoided by not adding
756
def extract(self, path, file_id=None, format=None):
722
def _directory_is_tree_reference(self, relpath):
723
raise NotImplementedError(self._directory_is_tree_reference)
725
def extract(self, path, format=None):
757
726
"""Extract a subtree from this tree.
759
728
A new branch will be created, relative to the path for this tree.
764
733
"""Write the in memory meta data to disk."""
765
734
raise NotImplementedError(self.flush)
767
def kind(self, relpath, file_id=None):
736
def kind(self, relpath):
768
737
return osutils.file_kind(self.abspath(relpath))
770
def list_files(self, include_root=False, from_dir=None, recursive=True):
739
def list_files(self, include_root=False, from_dir=None, recursive=True,
740
recurse_nested=False):
771
741
"""List all files as (path, class, kind, id, entry).
773
743
Lists, but does not descend into unversioned directories.
854
824
def pull(self, source, overwrite=False, stop_revision=None,
855
825
change_reporter=None, possible_transports=None, local=False,
826
show_base=False, tag_selector=None):
857
827
with self.lock_write(), source.lock_read():
858
828
old_revision_info = self.branch.last_revision_info()
859
829
basis_tree = self.basis_tree()
860
830
count = self.branch.pull(source, overwrite, stop_revision,
861
831
possible_transports=possible_transports,
832
local=local, tag_selector=tag_selector)
863
833
new_revision_info = self.branch.last_revision_info()
864
834
if new_revision_info != old_revision_info:
865
835
repository = self.branch.repository
1034
1004
def revert(self, filenames=None, old_tree=None, backups=True,
1035
1005
pb=None, report_changes=False):
1036
1006
from .conflicts import resolve
1037
with self.lock_tree_write():
1007
with contextlib.ExitStack() as exit_stack:
1008
exit_stack.enter_context(self.lock_tree_write())
1038
1009
if old_tree is None:
1039
1010
basis_tree = self.basis_tree()
1040
basis_tree.lock_read()
1011
exit_stack.enter_context(basis_tree.lock_read())
1041
1012
old_tree = basis_tree
1043
1014
basis_tree = None
1045
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1047
if filenames is None and len(self.get_parent_ids()) > 1:
1049
last_revision = self.last_revision()
1050
if last_revision != _mod_revision.NULL_REVISION:
1051
if basis_tree is None:
1052
basis_tree = self.basis_tree()
1053
basis_tree.lock_read()
1054
parent_trees.append((last_revision, basis_tree))
1055
self.set_parent_trees(parent_trees)
1058
resolve(self, filenames, ignore_misses=True, recursive=True)
1060
if basis_tree is not None:
1015
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1017
if filenames is None and len(self.get_parent_ids()) > 1:
1019
last_revision = self.last_revision()
1020
if last_revision != _mod_revision.NULL_REVISION:
1021
if basis_tree is None:
1022
basis_tree = self.basis_tree()
1023
exit_stack.enter_context(basis_tree.lock_read())
1024
parent_trees.append((last_revision, basis_tree))
1025
self.set_parent_trees(parent_trees)
1028
resolve(self, filenames, ignore_misses=True, recursive=True)
1062
1029
return conflicts
1064
1031
def store_uncommitted(self):
1144
1108
returned (old tip of the branch or None). _marker is used
1147
if self.branch.get_bound_location() is not None:
1149
update_branch = (old_tip is self._marker)
1151
self.lock_tree_write()
1152
update_branch = False
1155
old_tip = self.branch.update(possible_transports)
1157
if old_tip is self._marker:
1159
return self._update_tree(old_tip, change_reporter, revision, show_base)
1163
def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1165
"""Update a tree to the master branch.
1167
:param old_tip: if supplied, the previous tip revision the branch,
1168
before it was changed to the master branch's tip.
1170
# here if old_tip is not None, it is the old tip of the branch before
1171
# it was updated from the master branch. This should become a pending
1172
# merge in the working tree to preserve the user existing work. we
1173
# cant set that until we update the working trees last revision to be
1174
# one from the new branch, because it will just get absorbed by the
1175
# parent de-duplication logic.
1177
# We MUST save it even if an error occurs, because otherwise the users
1178
# local work is unreferenced and will appear to have been lost.
1180
with self.lock_tree_write():
1183
last_rev = self.get_parent_ids()[0]
1185
last_rev = _mod_revision.NULL_REVISION
1186
if revision is None:
1187
revision = self.branch.last_revision()
1189
old_tip = old_tip or _mod_revision.NULL_REVISION
1191
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1192
# the branch we are bound to was updated
1193
# merge those changes in first
1194
base_tree = self.basis_tree()
1195
other_tree = self.branch.repository.revision_tree(old_tip)
1196
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1197
base_tree, this_tree=self,
1198
change_reporter=change_reporter,
1199
show_base=show_base)
1201
self.add_parent_tree((old_tip, other_tree))
1202
note(gettext('Rerun update after fixing the conflicts.'))
1205
if last_rev != _mod_revision.ensure_null(revision):
1206
# the working tree is up to date with the branch
1207
# we can merge the specified revision from master
1208
to_tree = self.branch.repository.revision_tree(revision)
1209
to_root_id = to_tree.get_root_id()
1211
basis = self.basis_tree()
1212
with basis.lock_read():
1213
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1214
self.set_root_id(to_root_id)
1217
# determine the branch point
1218
graph = self.branch.repository.get_graph()
1219
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1221
base_tree = self.branch.repository.revision_tree(base_rev_id)
1223
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1225
change_reporter=change_reporter,
1226
show_base=show_base)
1227
self.set_last_revision(revision)
1228
# TODO - dedup parents list with things merged by pull ?
1229
# reuse the tree we've updated to to set the basis:
1230
parent_trees = [(revision, to_tree)]
1231
merges = self.get_parent_ids()[1:]
1232
# Ideally we ask the tree for the trees here, that way the working
1233
# tree can decide whether to give us the entire tree or give us a
1234
# lazy initialised tree. dirstate for instance will have the trees
1235
# in ram already, whereas a last-revision + basis-inventory tree
1236
# will not, but also does not need them when setting parents.
1237
for parent in merges:
1238
parent_trees.append(
1239
(parent, self.branch.repository.revision_tree(parent)))
1240
if not _mod_revision.is_null(old_tip):
1241
parent_trees.append(
1242
(old_tip, self.branch.repository.revision_tree(old_tip)))
1243
self.set_parent_trees(parent_trees)
1244
last_rev = parent_trees[0][0]
1111
raise NotImplementedError(self.update)
1247
1113
def set_conflicts(self, arg):
1248
1114
raise errors.UnsupportedOperation(self.set_conflicts, self)
1277
1144
into files that have text conflicts. The corresponding .THIS .BASE and
1278
1145
.OTHER files are deleted, as per 'resolve'.
1280
:return: a tuple of ConflictLists: (un_resolved, resolved).
1147
:return: a tuple of lists: (un_resolved, resolved).
1282
1149
with self.lock_tree_write():
1283
un_resolved = _mod_conflicts.ConflictList()
1284
resolved = _mod_conflicts.ConflictList()
1285
conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
1286
1152
for conflict in self.conflicts():
1287
path = self.id2path(conflict.file_id)
1288
if (conflict.typestring != 'text conflict' or
1289
self.kind(path) != 'file'):
1154
conflict.action_auto(self)
1155
except NotImplementedError:
1290
1156
un_resolved.append(conflict)
1292
with open(self.abspath(path), 'rb') as my_file:
1293
for line in my_file:
1294
if conflict_re.search(line):
1295
un_resolved.append(conflict)
1298
resolved.append(conflict)
1299
resolved.remove_files(self)
1158
conflict.cleanup(self)
1159
resolved.append(conflict)
1300
1160
self.set_conflicts(un_resolved)
1301
1161
return un_resolved, resolved
1370
1230
with self.lock_read():
1371
1231
return next(self.get_canonical_paths([path]))
1374
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1233
def reference_parent(self, path, branch=None, possible_transports=None):
1234
raise errors.UnsupportedOperation(self.reference_parent, self)
1236
def get_reference_info(self, path, branch=None):
1237
raise errors.UnsupportedOperation(self.get_reference_info, self)
1239
def set_reference_info(self, tree_path, branch_location):
1240
raise errors.UnsupportedOperation(self.set_reference_info, self)
1243
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
1375
1244
"""Registry for working tree formats."""
1377
1246
def __init__(self, other_registry=None):