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
assert 2 > len(wt.inventory)
907
964
file_trans_id = {}
908
965
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
966
pp = ProgressPhase("Build phase", 2, top_pb)
967
if tree.inventory.root is not None:
968
wt.set_root_id(tree.inventory.root.file_id)
910
969
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)
973
file_trans_id[wt.get_root_id()] = \
974
tt.trans_id_tree_file_id(wt.get_root_id())
915
975
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]
977
for num, (tree_path, entry) in \
978
enumerate(tree.inventory.iter_entries_by_dir()):
979
pb.update("Building tree", num, len(tree.inventory))
920
980
if entry.parent_id is None:
983
file_id = entry.file_id
984
target_path = wt.abspath(tree_path)
986
kind = file_kind(target_path)
990
if kind == "directory":
992
bzrdir.BzrDir.open(target_path)
993
except errors.NotBranchError:
997
if (file_id not in divert and
998
_content_match(tree, entry, file_id, kind,
1000
tt.delete_contents(tt.trans_id_tree_path(tree_path))
1001
if kind == 'directory':
922
1003
if entry.parent_id not in file_trans_id:
923
1004
raise repr(entry.parent_id)
924
1005
parent_id = file_trans_id[entry.parent_id]
925
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1006
file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1009
new_trans_id = file_trans_id[file_id]
1010
old_parent = tt.trans_id_tree_path(tree_path)
1011
_reparent_children(tt, old_parent, new_trans_id)
1015
divert_trans = set(file_trans_id[f] for f in divert)
1016
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1017
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1018
conflicts = cook_conflicts(raw_conflicts, tt)
1019
for conflict in conflicts:
1022
wt.add_conflicts(conflicts)
1023
except errors.UnsupportedOperation:
933
1028
top_pb.finished()
1031
def _reparent_children(tt, old_parent, new_parent):
1032
for child in tt.iter_tree_children(old_parent):
1033
tt.adjust_path(tt.final_name(child), new_parent, child)
1036
def _content_match(tree, entry, file_id, kind, target_path):
1037
if entry.kind != kind:
1039
if entry.kind == "directory":
1041
if entry.kind == "file":
1042
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1044
elif entry.kind == "symlink":
1045
if tree.get_symlink_target(file_id) == os.readlink(target_path):
1050
def resolve_checkout(tt, conflicts, divert):
1051
new_conflicts = set()
1052
for c_type, conflict in ((c[0], c) for c in conflicts):
1053
# Anything but a 'duplicate' would indicate programmer error
1054
assert c_type == 'duplicate', c_type
1055
# Now figure out which is new and which is old
1056
if tt.new_contents(conflict[1]):
1057
new_file = conflict[1]
1058
old_file = conflict[2]
1060
new_file = conflict[2]
1061
old_file = conflict[1]
1063
# We should only get here if the conflict wasn't completely
1065
final_parent = tt.final_parent(old_file)
1066
if new_file in divert:
1067
new_name = tt.final_name(old_file)+'.diverted'
1068
tt.adjust_path(new_name, final_parent, new_file)
1069
new_conflicts.add((c_type, 'Diverted to',
1070
new_file, old_file))
1072
new_name = tt.final_name(old_file)+'.moved'
1073
tt.adjust_path(new_name, final_parent, old_file)
1074
new_conflicts.add((c_type, 'Moved existing file to',
1075
old_file, new_file))
1076
return new_conflicts
935
1079
def new_by_entry(tt, entry, parent_id, tree):
936
1080
"""Create a new file according to its inventory entry"""
937
1081
name = entry.name
1110
1237
pp.next_phase()
1111
1238
wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1112
1239
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1114
1242
for id_num, file_id in enumerate(wt_interesting):
1243
if (working_tree.inventory.is_root(file_id) and
1244
len(target_tree.inventory) == 0):
1115
1246
child_pb.update("New file check", id_num+1,
1116
1247
len(sorted_interesting))
1117
1248
if file_id not in target_tree:
1118
1249
trans_id = tt.trans_id_tree_file_id(file_id)
1119
1250
tt.unversion_file(trans_id)
1120
if file_id in merge_modified:
1252
file_kind = working_tree.kind(file_id)
1255
delete_merge_modified = (file_id in merge_modified)
1256
if file_kind != 'file' and file_kind is not None:
1257
keep_contents = False
1259
if basis_tree is None:
1260
basis_tree = working_tree.basis_tree()
1261
wt_sha1 = working_tree.get_file_sha1(file_id)
1262
if (file_id in merge_modified and
1263
merge_modified[file_id] == wt_sha1):
1264
keep_contents = False
1265
elif (file_id in basis_tree and
1266
basis_tree.get_file_sha1(file_id) == wt_sha1):
1267
keep_contents = False
1269
keep_contents = True
1270
if not keep_contents:
1121
1271
tt.delete_contents(trans_id)
1272
if delete_merge_modified:
1122
1273
del merge_modified[file_id]
1124
1275
child_pb.finished()
1188
1341
trans_id = conflict[1]
1190
1343
tt.cancel_deletion(trans_id)
1191
new_conflicts.add((c_type, 'Not deleting', trans_id))
1344
new_conflicts.add(('deleting parent', 'Not deleting',
1192
1346
except KeyError:
1193
1347
tt.create_directory(trans_id)
1194
new_conflicts.add((c_type, 'Created directory.', trans_id))
1348
new_conflicts.add((c_type, 'Created directory', trans_id))
1195
1349
elif c_type == 'unversioned parent':
1196
1350
tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1197
1351
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1198
1352
return new_conflicts
1200
1355
def cook_conflicts(raw_conflicts, tt):
1201
1356
"""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
1357
from bzrlib.conflicts import Conflict
1358
conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
1359
return sorted(conflict_iter, key=Conflict.sort_key)
1210
return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1212
1362
def iter_cook_conflicts(raw_conflicts, tt):
1213
1363
from bzrlib.conflicts import Conflict