258
258
self.unversion_file(old_new_root)
259
259
# if, at this stage, root still has an old file_id, zap it so we can
260
260
# stick a new one in.
261
if (self.tree_file_id(self._new_root) is not None and
262
self._new_root not in self._removed_id):
261
if (self.tree_file_id(self._new_root) is not None
262
and self._new_root not in self._removed_id):
263
263
self.unversion_file(self._new_root)
264
264
if file_id is not None:
265
265
self.version_file(file_id, self._new_root)
723
722
def _duplicate_ids(self):
724
723
"""Each inventory id may only be used once"""
726
all_ids = self._tree.all_file_ids()
727
except errors.UnsupportedOperation:
728
# it's okay for non-file-id trees to raise UnsupportedOperation.
726
730
removed_tree_ids = set((self.tree_file_id(trans_id) for trans_id in
727
731
self._removed_id))
728
all_ids = self._tree.all_file_ids()
729
732
active_tree_ids = all_ids.difference(removed_tree_ids)
730
733
for trans_id, file_id in viewitems(self._new_id):
731
734
if file_id in active_tree_ids:
992
995
if from_kind != to_kind:
994
997
elif to_kind in ('file', 'symlink') and (
995
to_trans_id != from_trans_id or
996
to_trans_id in self._new_contents):
998
to_trans_id != from_trans_id
999
or to_trans_id in self._new_contents):
998
if (not modified and from_versioned == to_versioned and
999
from_parent==to_parent and from_name == to_name and
1000
from_executable == to_executable):
1001
if (not modified and from_versioned == to_versioned
1002
and from_parent == to_parent and from_name == to_name
1003
and from_executable == to_executable):
1002
1005
results.append((file_id, (from_path, to_path), modified,
1003
(from_versioned, to_versioned),
1004
(from_parent, to_parent),
1005
(from_name, to_name),
1006
(from_kind, to_kind),
1007
(from_executable, to_executable)))
1006
(from_versioned, to_versioned),
1007
(from_parent, to_parent),
1008
(from_name, to_name),
1009
(from_kind, to_kind),
1010
(from_executable, to_executable)))
1009
1012
def path_key(t):
1148
1151
if not isinstance(content, bytes):
1149
1152
content = content.encode('utf-8')
1150
1153
yield serializer.bytes_record(
1151
content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
1154
content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
1153
1156
def deserialize(self, records):
1154
1157
"""Deserialize a stored TreeTransform.
1163
1166
for k, v in viewitems(attribs[b'_new_name'])}
1164
1167
self._new_parent = {k.decode('utf-8'): v.decode('utf-8')
1165
1168
for k, v in viewitems(attribs[b'_new_parent'])}
1166
self._new_executability = {k.decode('utf-8'): bool(v)
1169
self._new_executability = {
1170
k.decode('utf-8'): bool(v)
1167
1171
for k, v in viewitems(attribs[b'_new_executability'])}
1168
1172
self._new_id = {k.decode('utf-8'): v
1169
1173
for k, v in viewitems(attribs[b'_new_id'])}
1177
1181
self._tree_id_paths[trans_id] = path
1178
1182
self._removed_id = {trans_id.decode('utf-8')
1179
1183
for trans_id in attribs[b'_removed_id']}
1180
self._removed_contents = set(trans_id.decode('utf-8')
1181
for trans_id in attribs[b'_removed_contents'])
1182
self._non_present_ids = {k: v.decode('utf-8')
1183
for k, v in viewitems(attribs[b'_non_present_ids'])}
1184
self._removed_contents = set(
1185
trans_id.decode('utf-8')
1186
for trans_id in attribs[b'_removed_contents'])
1187
self._non_present_ids = {
1188
k: v.decode('utf-8')
1189
for k, v in viewitems(attribs[b'_non_present_ids'])}
1184
1190
for ((trans_id, kind),), content in records:
1185
1191
trans_id = trans_id.decode('utf-8')
1186
1192
kind = kind.decode('ascii')
1197
1203
class DiskTreeTransform(TreeTransformBase):
1198
1204
"""Tree transform storing its contents on disk."""
1200
def __init__(self, tree, limbodir, pb=None,
1201
case_sensitive=True):
1206
def __init__(self, tree, limbodir, pb=None, case_sensitive=True):
1202
1207
"""Constructor.
1203
1208
:param tree: The tree that will be transformed, but not necessarily
1204
1209
the output tree.
1282
1287
previous_parent = self._new_parent.get(trans_id)
1283
1288
previous_name = self._new_name.get(trans_id)
1284
1289
TreeTransformBase.adjust_path(self, name, parent, trans_id)
1285
if (trans_id in self._limbo_files and
1286
trans_id not in self._needs_rename):
1290
if (trans_id in self._limbo_files
1291
and trans_id not in self._needs_rename):
1287
1292
self._rename_in_limbo([trans_id])
1288
1293
if previous_parent != parent:
1289
1294
self._limbo_children[previous_parent].remove(trans_id)
1391
1396
target is a bytestring.
1392
1397
See also new_symlink.
1399
if osutils.supports_symlinks(self._limbodir):
1395
1400
os.symlink(target, self._limbo_name(trans_id))
1398
1403
path = FinalPaths(self).get_path(trans_id)
1399
1404
except KeyError:
1401
trace.warning('bzr: warning: Unable to create symlink "%s" on '
1402
'this platform.' % (path,))
1407
'bzr: warning: Unable to create symlink "%s" on '
1408
'this platform/filesystem.' % (path,))
1403
1409
# We add symlink to _new_contents even if they are unsupported
1404
1410
# and not created. These entries are subsequently used to avoid
1405
1411
# conflicts on platforms that don't support symlink
1842
1847
if trans_id in self._removed_contents:
1843
1848
delete_path = os.path.join(self._deletiondir, trans_id)
1844
1849
mover.pre_delete(full_path, delete_path)
1845
elif (trans_id in self._new_name
1846
or trans_id in self._new_parent):
1850
elif (trans_id in self._new_name or
1851
trans_id in self._new_parent):
1848
1853
mover.rename(full_path, self._limbo_name(trans_id))
1849
1854
except errors.TransformRenameFailed as e:
1865
1870
new_paths = self.new_paths(filesystem_only=True)
1866
1871
modified_paths = []
1867
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1869
1872
with ui.ui_factory.nested_progress_bar() as child_pb:
1870
1873
for num, (path, trans_id) in enumerate(new_paths):
1871
1874
if (num % 10) == 0:
1872
child_pb.update(gettext('adding file'), num, len(new_paths))
1875
child_pb.update(gettext('adding file'),
1876
num, len(new_paths))
1873
1877
full_path = self._tree.abspath(path)
1874
1878
if trans_id in self._needs_rename:
1883
1887
# TODO: if trans_id in self._observed_sha1s, we should
1884
1888
# re-stat the final target, since ctime will be
1885
1889
# updated by the change.
1886
if (trans_id in self._new_contents or
1887
self.path_changed(trans_id)):
1890
if (trans_id in self._new_contents
1891
or self.path_changed(trans_id)):
1888
1892
if trans_id in self._new_contents:
1889
1893
modified_paths.append(full_path)
1890
1894
if trans_id in self._new_executability:
1919
1923
paths = FinalPaths(self)
1920
1924
for trans_id, observed in viewitems(self._observed_sha1s):
1921
1925
path = paths.get_path(trans_id)
1922
# We could get the file_id, but dirstate prefers to use the path
1923
# anyway, and it is 'cheaper' to determine.
1924
# file_id = self._new_id[trans_id]
1925
self._tree._observed_sha1(None, path, observed)
1926
self._tree._observed_sha1(path, observed)
1928
1929
class TransformPreview(DiskTreeTransform):
2016
2017
def _get_file_revision(self, path, file_id, vf, tree_revision):
2017
2018
parent_keys = [
2018
(file_id, t.get_file_revision(t.id2path(file_id), file_id))
2019
for t in self._iter_parent_trees()]
2019
(file_id, t.get_file_revision(t.id2path(file_id)))
2020
for t in self._iter_parent_trees()]
2020
2021
vf.add_lines((file_id, tree_revision), parent_keys,
2021
self.get_file_lines(path, file_id))
2022
self.get_file_lines(path))
2022
2023
repo = self._get_repository()
2023
2024
base_vf = repo.texts
2024
2025
if base_vf not in vf.fallback_versionedfiles:
2071
2072
return tree_ids
2073
2074
def all_versioned_paths(self):
2074
return {self.id2path(fid) for fid in self.all_file_ids()}
2075
tree_paths = set(self._transform._tree.all_versioned_paths())
2077
tree_paths.difference_update(
2078
self._transform.trans_id_tree_path(t)
2079
for t in self._transform._removed_id)
2082
self._final_paths._determine_path(t)
2083
for t in self._transform._new_id)
2076
2087
def _has_id(self, file_id, fallback_check):
2077
2088
if file_id in self._transform._r_new_id:
2079
2090
elif file_id in {self._transform.tree_file_id(trans_id) for
2080
trans_id in self._transform._removed_id}:
2091
trans_id in self._transform._removed_id}:
2083
2094
return fallback_check(file_id)
2154
2165
file_id = self._transform.final_file_id(trans_id)
2155
2166
if file_id is None:
2157
if (specific_files is not None and
2158
self._final_paths.get_path(trans_id) not in specific_files):
2168
if (specific_files is not None
2169
and self._final_paths.get_path(trans_id) not in specific_files):
2160
2171
kind = self._transform.final_kind(trans_id)
2161
2172
if kind is None:
2162
2173
kind = self._transform._tree.stored_kind(
2163
self._transform._tree.id2path(file_id),
2174
self._transform._tree.id2path(file_id))
2165
2175
new_entry = inventory.make_entry(
2167
2177
self._transform.final_name(trans_id),
2231
2243
if not path.startswith(prefix):
2233
2245
path = path[len(prefix):]
2234
yield path, 'V', entry.kind, entry.file_id, entry
2246
yield path, 'V', entry.kind, entry
2236
2248
if from_dir is None and include_root is True:
2237
root_entry = inventory.make_entry('directory', '',
2238
ROOT_PARENT, self.get_root_id())
2239
yield '', 'V', 'directory', root_entry.file_id, root_entry
2249
root_entry = inventory.make_entry(
2250
'directory', '', ROOT_PARENT, self.get_root_id())
2251
yield '', 'V', 'directory', root_entry
2240
2252
entries = self._iter_entries_for_dir(from_dir or '')
2241
2253
for path, entry in entries:
2242
yield path, 'V', entry.kind, entry.file_id, entry
2254
yield path, 'V', entry.kind, entry
2244
def kind(self, path, file_id=None):
2256
def kind(self, path):
2245
2257
trans_id = self._path2trans_id(path)
2246
2258
if trans_id is None:
2247
2259
raise errors.NoSuchFile(path)
2248
2260
return self._transform.final_kind(trans_id)
2250
def stored_kind(self, path, file_id=None):
2262
def stored_kind(self, path):
2251
2263
trans_id = self._path2trans_id(path)
2252
2264
if trans_id is None:
2253
2265
raise errors.NoSuchFile(path)
2255
2267
return self._transform._new_contents[trans_id]
2256
2268
except KeyError:
2257
return self._transform._tree.stored_kind(path, file_id)
2269
return self._transform._tree.stored_kind(path)
2259
def get_file_mtime(self, path, file_id=None):
2271
def get_file_mtime(self, path):
2260
2272
"""See Tree.get_file_mtime"""
2262
file_id = self.path2id(path)
2273
file_id = self.path2id(path)
2263
2274
if file_id is None:
2264
2275
raise errors.NoSuchFile(path)
2265
2276
if not self._content_change(file_id):
2266
2277
return self._transform._tree.get_file_mtime(
2267
self._transform._tree.id2path(file_id), file_id)
2278
self._transform._tree.id2path(file_id))
2268
2279
trans_id = self._path2trans_id(path)
2269
2280
return self._stat_limbo_file(trans_id).st_mtime
2271
def get_file_size(self, path, file_id=None):
2282
def get_file_size(self, path):
2272
2283
"""See Tree.get_file_size"""
2273
2284
trans_id = self._path2trans_id(path)
2274
2285
if trans_id is None:
2279
2290
if trans_id in self._transform._new_contents:
2280
2291
return self._stat_limbo_file(trans_id).st_size
2281
if self.kind(path, file_id) == 'file':
2282
return self._transform._tree.get_file_size(path, file_id)
2292
if self.kind(path) == 'file':
2293
return self._transform._tree.get_file_size(path)
2286
def get_file_verifier(self, path, file_id=None, stat_value=None):
2297
def get_file_verifier(self, path, stat_value=None):
2287
2298
trans_id = self._path2trans_id(path)
2288
2299
if trans_id is None:
2289
2300
raise errors.NoSuchFile(path)
2290
2301
kind = self._transform._new_contents.get(trans_id)
2291
2302
if kind is None:
2292
return self._transform._tree.get_file_verifier(path, file_id)
2303
return self._transform._tree.get_file_verifier(path)
2293
2304
if kind == 'file':
2294
with self.get_file(path, file_id) as fileobj:
2305
with self.get_file(path) as fileobj:
2295
2306
return ("SHA1", sha_file(fileobj))
2297
def get_file_sha1(self, path, file_id=None, stat_value=None):
2308
def get_file_sha1(self, path, stat_value=None):
2298
2309
trans_id = self._path2trans_id(path)
2299
2310
if trans_id is None:
2300
2311
raise errors.NoSuchFile(path)
2301
2312
kind = self._transform._new_contents.get(trans_id)
2302
2313
if kind is None:
2303
return self._transform._tree.get_file_sha1(path, file_id)
2314
return self._transform._tree.get_file_sha1(path)
2304
2315
if kind == 'file':
2305
with self.get_file(path, file_id) as fileobj:
2316
with self.get_file(path) as fileobj:
2306
2317
return sha_file(fileobj)
2308
def is_executable(self, path, file_id=None):
2319
def is_executable(self, path):
2309
2320
trans_id = self._path2trans_id(path)
2310
2321
if trans_id is None:
2363
2374
return kind, size, executable, link_or_sha1
2365
2376
def iter_changes(self, from_tree, include_unchanged=False,
2366
specific_files=None, pb=None, extra_trees=None,
2367
require_versioned=True, want_unversioned=False):
2377
specific_files=None, pb=None, extra_trees=None,
2378
require_versioned=True, want_unversioned=False):
2368
2379
"""See InterTree.iter_changes.
2370
2381
This has a fast path that is only used when the from_tree matches
2371
2382
the transform tree, and no fancy options are supplied.
2373
if (from_tree is not self._transform._tree or include_unchanged or
2374
specific_files or want_unversioned):
2384
if (from_tree is not self._transform._tree or include_unchanged
2385
or specific_files or want_unversioned):
2375
2386
return tree.InterTree(from_tree, self).iter_changes(
2376
2387
include_unchanged=include_unchanged,
2377
2388
specific_files=specific_files,
2383
2394
raise ValueError('want_unversioned is not supported')
2384
2395
return self._transform.iter_changes()
2386
def get_file(self, path, file_id=None):
2397
def get_file(self, path):
2387
2398
"""See Tree.get_file"""
2389
file_id = self.path2id(path)
2399
file_id = self.path2id(path)
2390
2400
if not self._content_change(file_id):
2391
return self._transform._tree.get_file(path, file_id)
2401
return self._transform._tree.get_file(path)
2392
2402
trans_id = self._path2trans_id(path)
2393
2403
name = self._transform._limbo_name(trans_id)
2394
2404
return open(name, 'rb')
2396
def get_file_with_stat(self, path, file_id=None):
2397
return self.get_file(path, file_id), None
2406
def get_file_with_stat(self, path):
2407
return self.get_file(path), None
2399
def annotate_iter(self, path, file_id=None,
2409
def annotate_iter(self, path,
2400
2410
default_revision=_mod_revision.CURRENT_REVISION):
2402
file_id = self.path2id(path)
2411
file_id = self.path2id(path)
2403
2412
changes = self._iter_changes_cache.get(file_id)
2404
2413
if changes is None:
2426
2435
# It would be nice to be able to use the new Annotator based
2427
2436
# approach, as well.
2428
2437
return annotate.reannotate([old_annotation],
2429
self.get_file(path, file_id).readlines(),
2438
self.get_file(path).readlines(),
2430
2439
default_revision)
2432
def get_symlink_target(self, path, file_id=None):
2441
def get_symlink_target(self, path):
2433
2442
"""See Tree.get_symlink_target"""
2435
file_id = self.path2id(path)
2443
file_id = self.path2id(path)
2436
2444
if not self._content_change(file_id):
2437
2445
return self._transform._tree.get_symlink_target(path)
2438
2446
trans_id = self._path2trans_id(path)
2452
2460
path_from_root = self._final_paths.get_path(child_id)
2453
2461
basename = self._transform.final_name(child_id)
2454
2462
file_id = self._transform.final_file_id(child_id)
2455
kind = self._transform.final_kind(child_id)
2463
kind = self._transform.final_kind(child_id)
2456
2464
if kind is not None:
2457
2465
versioned_kind = kind
2459
2467
kind = 'unknown'
2460
2468
versioned_kind = self._transform._tree.stored_kind(
2461
self._transform._tree.id2path(file_id),
2469
self._transform._tree.id2path(file_id))
2463
2470
if versioned_kind == 'directory':
2464
2471
subdirs.append(child_id)
2465
2472
children.append((path_from_root, basename, kind, None,
2606
2613
for dir, files in wt.walkdirs():
2607
2614
existing_files.update(f[0] for f in files)
2608
2615
for num, (tree_path, entry) in \
2609
enumerate(tree.iter_entries_by_dir()):
2610
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2616
enumerate(tree.iter_entries_by_dir()):
2617
pb.update(gettext("Building tree"), num
2618
- len(deferred_contents), total)
2611
2619
if entry.parent_id is None:
2613
2621
reparent = False
2626
2634
divert.add(file_id)
2627
if (file_id not in divert and
2628
_content_match(tree, entry, tree_path, file_id, kind,
2635
if (file_id not in divert
2637
tree, entry, tree_path, file_id, kind,
2630
2639
tt.delete_contents(tt.trans_id_tree_path(tree_path))
2631
2640
if kind == 'directory':
2632
2641
reparent = True
2637
2646
trans_id = tt.create_path(entry.name, parent_id)
2638
2647
file_trans_id[file_id] = trans_id
2639
2648
tt.version_file(file_id, trans_id)
2640
executable = tree.is_executable(tree_path, file_id)
2649
executable = tree.is_executable(tree_path)
2642
2651
tt.set_executability(executable, trans_id)
2643
trans_data = (trans_id, file_id, tree_path, entry.text_sha1)
2652
trans_data = (trans_id, file_id,
2653
tree_path, entry.text_sha1)
2644
2654
deferred_contents.append((tree_path, trans_data))
2646
2656
file_trans_id[file_id] = new_by_entry(
2647
tree_path, tt, entry, parent_id, tree)
2657
tree_path, tt, entry, parent_id, tree)
2649
2659
new_trans_id = file_trans_id[file_id]
2650
2660
old_parent = tt.trans_id_tree_path(tree_path)
2654
2664
accelerator_tree, hardlink)
2655
2665
pp.next_phase()
2656
2666
divert_trans = set(file_trans_id[f] for f in divert)
2657
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
2669
return resolve_checkout(t, c, divert_trans)
2658
2670
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
2659
2671
if len(raw_conflicts) > 0:
2660
2672
precomputed_delta = None
2693
2705
accelerator_path = unchanged.get(tree_path)
2694
2706
if accelerator_path is None:
2695
2707
new_desired_files.append((tree_path,
2696
(trans_id, file_id, tree_path, text_sha1)))
2708
(trans_id, file_id, tree_path, text_sha1)))
2698
2710
pb.update(gettext('Adding file contents'), count + offset, total)
2700
2712
tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2703
with accelerator_tree.get_file(accelerator_path, file_id) as f:
2715
with accelerator_tree.get_file(accelerator_path) as f:
2704
2716
chunks = osutils.file_iterator(f)
2705
2717
if wt.supports_content_filtering():
2706
2718
filters = wt._content_filter_stack(tree_path)
2707
2719
chunks = filtered_output_bytes(chunks, filters,
2708
ContentFilterContext(tree_path, tree))
2720
ContentFilterContext(tree_path, tree))
2709
2721
tt.create_file(chunks, trans_id, sha1=text_sha1)
2711
2723
offset += count
2714
2726
if wt.supports_content_filtering():
2715
2727
filters = wt._content_filter_stack(tree_path)
2716
2728
contents = filtered_output_bytes(contents, filters,
2717
ContentFilterContext(tree_path, tree))
2729
ContentFilterContext(tree_path, tree))
2718
2730
tt.create_file(contents, trans_id, sha1=text_sha1)
2719
2731
pb.update(gettext('Adding file contents'), count + offset, total)
2739
2751
if entry.kind == "file":
2740
2752
with open(target_path, 'rb') as f1, \
2741
tree.get_file(tree_path, file_id) as f2:
2753
tree.get_file(tree_path) as f2:
2742
2754
if osutils.compare_files(f1, f2):
2744
2756
elif entry.kind == "symlink":
2745
if tree.get_symlink_target(tree_path, file_id) == os.readlink(target_path):
2757
if tree.get_symlink_target(tree_path) == os.readlink(target_path):
2766
2778
final_parent = tt.final_parent(old_file)
2767
2779
if new_file in divert:
2768
new_name = tt.final_name(old_file)+'.diverted'
2780
new_name = tt.final_name(old_file) + '.diverted'
2769
2781
tt.adjust_path(new_name, final_parent, new_file)
2770
2782
new_conflicts.add((c_type, 'Diverted to',
2771
2783
new_file, old_file))
2773
new_name = tt.final_name(old_file)+'.moved'
2785
new_name = tt.final_name(old_file) + '.moved'
2774
2786
tt.adjust_path(new_name, final_parent, old_file)
2775
2787
new_conflicts.add((c_type, 'Moved existing file to',
2776
2788
old_file, new_file))
2782
2794
name = entry.name
2783
2795
kind = entry.kind
2784
2796
if kind == 'file':
2785
with tree.get_file(path, entry.file_id) as f:
2786
executable = tree.is_executable(path, entry.file_id)
2797
with tree.get_file(path) as f:
2798
executable = tree.is_executable(path)
2787
2799
return tt.new_file(
2788
name, parent_id, osutils.file_iterator(f), entry.file_id,
2800
name, parent_id, osutils.file_iterator(f), entry.file_id,
2790
2802
elif kind in ('directory', 'tree-reference'):
2791
2803
trans_id = tt.new_directory(name, parent_id, entry.file_id)
2792
2804
if kind == 'tree-reference':
2793
2805
tt.set_tree_reference(entry.reference_revision, trans_id)
2794
2806
return trans_id
2795
2807
elif kind == 'symlink':
2796
target = tree.get_symlink_target(path, entry.file_id)
2808
target = tree.get_symlink_target(path)
2797
2809
return tt.new_symlink(name, parent_id, target, entry.file_id)
2799
2811
raise errors.BadFileKindError(name, kind)
2802
2814
def create_from_tree(tt, trans_id, tree, path, file_id=None, chunks=None,
2803
filter_tree_path=None):
2815
filter_tree_path=None):
2804
2816
"""Create new file contents according to tree contents.
2806
2818
:param filter_tree_path: the tree path to use to lookup
2807
2819
content filters to apply to the bytes output in the working tree.
2808
2820
This only applies if the working tree supports content filtering.
2810
kind = tree.kind(path, file_id)
2822
kind = tree.kind(path)
2811
2823
if kind == 'directory':
2812
2824
tt.create_directory(trans_id)
2813
2825
elif kind == "file":
2814
2826
if chunks is None:
2815
f = tree.get_file(path, file_id)
2827
f = tree.get_file(path)
2816
2828
chunks = osutils.file_iterator(f)
2821
2833
if wt.supports_content_filtering() and filter_tree_path is not None:
2822
2834
filters = wt._content_filter_stack(filter_tree_path)
2823
chunks = filtered_output_bytes(chunks, filters,
2835
chunks = filtered_output_bytes(
2824
2837
ContentFilterContext(filter_tree_path, tree))
2825
2838
tt.create_file(chunks, trans_id)
2827
2840
if f is not None:
2829
2842
elif kind == "symlink":
2830
tt.create_symlink(tree.get_symlink_target(path, file_id), trans_id)
2843
tt.create_symlink(tree.get_symlink_target(path), trans_id)
2832
2845
raise AssertionError('Unknown kind %r' % kind)
2872
2885
child_pb, filenames, backups,
2873
2886
merge_modified, basis_tree)
2874
2887
with ui.ui_factory.nested_progress_bar() as child_pb:
2875
raw_conflicts = resolve_conflicts(tt, child_pb,
2876
lambda t, c: conflict_pass(t, c, target_tree))
2888
raw_conflicts = resolve_conflicts(
2889
tt, child_pb, lambda t, c: conflict_pass(t, c, target_tree))
2877
2890
conflicts = cook_conflicts(raw_conflicts, tt)
2878
2891
return conflicts, merge_modified
2886
2899
# than the target changes relative to the working tree. Because WT4 has an
2887
2900
# optimizer to compare itself to a target, but no optimizer for the
2889
change_list = working_tree.iter_changes(target_tree,
2890
specific_files=specific_files, pb=pb)
2902
change_list = working_tree.iter_changes(
2903
target_tree, specific_files=specific_files, pb=pb)
2891
2904
if not target_tree.is_versioned(u''):
2892
2905
skip_root = True
2896
2909
deferred_files = []
2897
2910
for id_num, (file_id, path, changed_content, versioned, parent, name,
2898
kind, executable) in enumerate(change_list):
2911
kind, executable) in enumerate(change_list):
2899
2912
target_path, wt_path = path
2900
2913
target_versioned, wt_versioned = versioned
2901
2914
target_parent, wt_parent = parent
2917
2930
if basis_tree is None:
2918
2931
basis_tree = working_tree.basis_tree()
2919
2932
basis_tree.lock_read()
2920
basis_path = find_previous_path(working_tree, basis_tree, wt_path)
2933
basis_path = find_previous_path(
2934
working_tree, basis_tree, wt_path)
2921
2935
if basis_path is None:
2922
2936
if target_kind is None and not target_versioned:
2923
2937
keep_content = True
2925
if wt_sha1 != basis_tree.get_file_sha1(basis_path, file_id):
2939
if wt_sha1 != basis_tree.get_file_sha1(basis_path):
2926
2940
keep_content = True
2927
2941
if wt_kind is not None:
2928
2942
if not keep_content:
2944
2958
tt.create_directory(trans_id)
2945
2959
if target_kind == 'tree-reference':
2946
2960
revision = target_tree.get_reference_revision(
2947
target_path, file_id)
2948
2962
tt.set_tree_reference(revision, trans_id)
2949
2963
elif target_kind == 'symlink':
2950
2964
tt.create_symlink(target_tree.get_symlink_target(
2951
target_path, file_id), trans_id)
2965
target_path), trans_id)
2952
2966
elif target_kind == 'file':
2953
deferred_files.append((target_path, (trans_id, mode_id, file_id)))
2967
deferred_files.append(
2968
(target_path, (trans_id, mode_id, file_id)))
2954
2969
if basis_tree is None:
2955
2970
basis_tree = working_tree.basis_tree()
2956
2971
basis_tree.lock_read()
2957
new_sha1 = target_tree.get_file_sha1(target_path, file_id)
2972
new_sha1 = target_tree.get_file_sha1(target_path)
2958
2973
basis_path = find_previous_path(target_tree, basis_tree, target_path)
2959
2974
if (basis_path is not None and
2960
new_sha1 == basis_tree.get_file_sha1(basis_path, file_id)):
2975
new_sha1 == basis_tree.get_file_sha1(basis_path)):
2961
2976
if file_id in merge_modified:
2962
2977
del merge_modified[file_id]
2972
2987
tt.version_file(file_id, trans_id)
2973
2988
if wt_versioned and not target_versioned:
2974
2989
tt.unversion_file(trans_id)
2975
if (target_name is not None and
2976
(wt_name != target_name or wt_parent != target_parent)):
2990
if (target_name is not None
2991
and (wt_name != target_name or wt_parent != target_parent)):
2977
2992
if target_name == '' and target_parent is None:
2978
2993
parent_trans = ROOT_PARENT
2986
3001
tt.set_executability(target_executable, trans_id)
2987
3002
if working_tree.supports_content_filtering():
2988
3003
for (trans_id, mode_id, file_id), bytes in (
2989
target_tree.iter_files_bytes(deferred_files)):
3004
target_tree.iter_files_bytes(deferred_files)):
2990
3005
# We're reverting a tree to the target tree so using the
2991
3006
# target tree to find the file path seems the best choice
2992
3007
# here IMO - Ian C 27/Oct/2009
2993
3008
filter_tree_path = target_tree.id2path(file_id)
2994
3009
filters = working_tree._content_filter_stack(filter_tree_path)
2995
bytes = filtered_output_bytes(bytes, filters,
3010
bytes = filtered_output_bytes(
2996
3012
ContentFilterContext(filter_tree_path, working_tree))
2997
3013
tt.create_file(bytes, trans_id, mode_id)
2999
3015
for (trans_id, mode_id, file_id), bytes in target_tree.iter_files_bytes(
3001
3017
tt.create_file(bytes, trans_id, mode_id)
3002
3018
tt.fixup_new_roots()
3111
3127
file_id = tt.inactive_file_id(conflict[1])
3112
3128
# special-case the other tree root (move its children instead)
3113
3129
if path_tree and path_tree.path2id('') == file_id:
3114
# This is the root entry, skip it
3130
# This is the root entry, skip it
3116
3132
tt.version_file(file_id, conflict[1])
3117
3133
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
3118
3134
elif c_type == 'non-directory parent':
3121
3137
parent_name = tt.final_name(parent_id)
3122
3138
parent_file_id = tt.final_file_id(parent_id)
3123
3139
new_parent_id = tt.new_directory(parent_name + '.new',
3124
parent_parent, parent_file_id)
3140
parent_parent, parent_file_id)
3125
3141
_reparent_transform_children(tt, parent_id, new_parent_id)
3126
3142
if parent_file_id is not None:
3127
3143
tt.unversion_file(parent_id)
3195
3211
except OSError as e:
3196
3212
raise errors.TransformRenameFailed(to, from_, str(e), e.errno)
3197
3213
# after rollback, don't reuse _FileMover
3199
pending_deletions = None
3214
self.past_renames = None
3215
self.pending_deletions = None
3201
3217
def apply_deletions(self):
3202
3218
"""Apply all marked deletions"""
3203
3219
for path in self.pending_deletions:
3204
3220
delete_any(path)
3205
3221
# after apply_deletions, don't reuse _FileMover
3207
pending_deletions = None
3222
self.past_renames = None
3223
self.pending_deletions = None
3210
3226
def link_tree(target_tree, source_tree):