19
19
from stat import S_ISREG
21
from bzrlib import bzrdir, errors
21
22
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
22
23
ReusingTransform, NotVersionedError, CantMoveRoot,
23
ExistingLimbo, ImmortalLimbo)
24
ExistingLimbo, ImmortalLimbo, NoFinalPath)
24
25
from bzrlib.inventory import InventoryEntry
25
26
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
27
28
from bzrlib.progress import DummyProgress, ProgressPhase
28
29
from bzrlib.trace import mutter, warning
30
from bzrlib import tree
32
import bzrlib.urlutils as urlutils
32
35
ROOT_PARENT = "root-parent"
211
219
def canonical_path(self, path):
212
220
"""Get the canonical tree-relative path"""
213
221
# don't follow final symlinks
214
dirname, basename = os.path.split(self._tree.abspath(path))
215
dirname = os.path.realpath(dirname)
216
return self._tree.relpath(pathjoin(dirname, basename))
222
abs = self._tree.abspath(path)
223
if abs in self._relpaths:
224
return self._relpaths[abs]
225
dirname, basename = os.path.split(abs)
226
if dirname not in self._realpaths:
227
self._realpaths[dirname] = os.path.realpath(dirname)
228
dirname = self._realpaths[dirname]
229
abs = pathjoin(dirname, basename)
230
if dirname in self._relpaths:
231
relpath = pathjoin(self._relpaths[dirname], basename)
232
relpath = relpath.rstrip('/\\')
234
relpath = self._tree.relpath(abs)
235
self._relpaths[abs] = relpath
218
238
def trans_id_tree_path(self, path):
219
239
"""Determine (and maybe set) the transaction ID for a tree path."""
902
945
file_ids.sort(key=tree.id2path)
905
949
def build_tree(tree, wt):
906
"""Create working tree for a branch, using a Transaction."""
950
"""Create working tree for a branch, using a TreeTransform.
952
This function should be used on empty trees, having a tree root at most.
953
(see merge and revert functionality for working with existing trees)
955
Existing files are handled like so:
957
- Existing bzrdirs take precedence over creating new items. They are
958
created as '%s.diverted' % name.
959
- Otherwise, if the content on disk matches the content we are building,
960
it is silently replaced.
961
- Otherwise, conflict resolution will move the old file to 'oldname.moved'.
963
if len(wt.inventory) > 1: # more than just a root
964
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
907
965
file_trans_id = {}
908
966
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
967
pp = ProgressPhase("Build phase", 2, top_pb)
968
if tree.inventory.root is not None:
969
wt.set_root_id(tree.inventory.root.file_id)
910
970
tt = TreeTransform(wt)
913
file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
914
file_ids = topology_sorted_ids(tree)
974
file_trans_id[wt.get_root_id()] = \
975
tt.trans_id_tree_file_id(wt.get_root_id())
915
976
pb = bzrlib.ui.ui_factory.nested_progress_bar()
917
for num, file_id in enumerate(file_ids):
918
pb.update("Building tree", num, len(file_ids))
919
entry = tree.inventory[file_id]
978
for num, (tree_path, entry) in \
979
enumerate(tree.inventory.iter_entries_by_dir()):
980
pb.update("Building tree", num, len(tree.inventory))
920
981
if entry.parent_id is None:
984
file_id = entry.file_id
985
target_path = wt.abspath(tree_path)
987
kind = file_kind(target_path)
991
if kind == "directory":
993
bzrdir.BzrDir.open(target_path)
994
except errors.NotBranchError:
998
if (file_id not in divert and
999
_content_match(tree, entry, file_id, kind,
1001
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1002
if kind == 'directory':
922
1004
if entry.parent_id not in file_trans_id:
923
1005
raise repr(entry.parent_id)
924
1006
parent_id = file_trans_id[entry.parent_id]
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1007
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1010
new_trans_id = file_trans_id[file_id]
1011
old_parent = tt.trans_id_tree_path(tree_path)
1012
_reparent_children(tt, old_parent, new_trans_id)
1016
divert_trans = set(file_trans_id[f] for f in divert)
1017
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1018
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1019
conflicts = cook_conflicts(raw_conflicts, tt)
1020
for conflict in conflicts:
1023
wt.add_conflicts(conflicts)
1024
except errors.UnsupportedOperation:
933
1029
top_pb.finished()
1032
def _reparent_children(tt, old_parent, new_parent):
1033
for child in tt.iter_tree_children(old_parent):
1034
tt.adjust_path(tt.final_name(child), new_parent, child)
1037
def _content_match(tree, entry, file_id, kind, target_path):
1038
if entry.kind != kind:
1040
if entry.kind == "directory":
1042
if entry.kind == "file":
1043
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1045
elif entry.kind == "symlink":
1046
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1051
def resolve_checkout(tt, conflicts, divert):
1052
new_conflicts = set()
1053
for c_type, conflict in ((c[0], c) for c in conflicts):
1054
# Anything but a 'duplicate' would indicate programmer error
1055
assert c_type == 'duplicate', c_type
1056
# Now figure out which is new and which is old
1057
if tt.new_contents(conflict[1]):
1058
new_file = conflict[1]
1059
old_file = conflict[2]
1061
new_file = conflict[2]
1062
old_file = conflict[1]
1064
# We should only get here if the conflict wasn't completely
1066
final_parent = tt.final_parent(old_file)
1067
if new_file in divert:
1068
new_name = tt.final_name(old_file)+'.diverted'
1069
tt.adjust_path(new_name, final_parent, new_file)
1070
new_conflicts.add((c_type, 'Diverted to',
1071
new_file, old_file))
1073
new_name = tt.final_name(old_file)+'.moved'
1074
tt.adjust_path(new_name, final_parent, old_file)
1075
new_conflicts.add((c_type, 'Moved existing file to',
1076
old_file, new_file))
1077
return new_conflicts
935
1080
def new_by_entry(tt, entry, parent_id, tree):
936
1081
"""Create a new file according to its inventory entry"""
937
1082
name = entry.name
1110
1238
pp.next_phase()
1111
1239
wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1112
1240
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1114
1243
for id_num, file_id in enumerate(wt_interesting):
1244
if (working_tree.inventory.is_root(file_id) and
1245
len(target_tree.inventory) == 0):
1115
1247
child_pb.update("New file check", id_num+1,
1116
1248
len(sorted_interesting))
1117
1249
if file_id not in target_tree:
1118
1250
trans_id = tt.trans_id_tree_file_id(file_id)
1119
1251
tt.unversion_file(trans_id)
1120
if file_id in merge_modified:
1253
file_kind = working_tree.kind(file_id)
1256
delete_merge_modified = (file_id in merge_modified)
1257
if file_kind != 'file' and file_kind is not None:
1258
keep_contents = False
1260
if basis_tree is None:
1261
basis_tree = working_tree.basis_tree()
1262
wt_sha1 = working_tree.get_file_sha1(file_id)
1263
if (file_id in merge_modified and
1264
merge_modified[file_id] == wt_sha1):
1265
keep_contents = False
1266
elif (file_id in basis_tree and
1267
basis_tree.get_file_sha1(file_id) == wt_sha1):
1268
keep_contents = False
1270
keep_contents = True
1271
if not keep_contents:
1121
1272
tt.delete_contents(trans_id)
1273
if delete_merge_modified:
1122
1274
del merge_modified[file_id]
1124
1276
child_pb.finished()
1188
1342
trans_id = conflict[1]
1190
1344
tt.cancel_deletion(trans_id)
1191
new_conflicts.add((c_type, 'Not deleting', trans_id))
1345
new_conflicts.add(('deleting parent', 'Not deleting',
1192
1347
except KeyError:
1193
1348
tt.create_directory(trans_id)
1194
new_conflicts.add((c_type, 'Created directory.', trans_id))
1349
new_conflicts.add((c_type, 'Created directory', trans_id))
1195
1350
elif c_type == 'unversioned parent':
1196
1351
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1197
1352
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1198
1353
return new_conflicts
1200
1356
def cook_conflicts(raw_conflicts, tt):
1201
1357
"""Generate a list of cooked conflicts, sorted by file path"""
1203
if conflict.path is not None:
1204
return conflict.path, conflict.typestring
1205
elif getattr(conflict, "conflict_path", None) is not None:
1206
return conflict.conflict_path, conflict.typestring
1208
return None, conflict.typestring
1358
from bzrlib.conflicts import Conflict
1359
conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
1360
return sorted(conflict_iter, key=Conflict.sort_key)
1210
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1212
1363
def iter_cook_conflicts(raw_conflicts, tt):
1213
1364
from bzrlib.conflicts import Conflict