78
78
ROOT_PARENT = "root-parent"
80
81
def unique_add(map, key, value):
82
83
raise DuplicateKey(key=key)
87
87
class _TransformResults(object):
88
88
def __init__(self, modified_paths, rename_count):
89
89
object.__init__(self)
259
259
self.unversion_file(old_new_root)
260
260
# if, at this stage, root still has an old file_id, zap it so we can
261
261
# stick a new one in.
262
if (self.tree_file_id(self._new_root) is not None and
263
self._new_root not in self._removed_id):
262
if (self.tree_file_id(self._new_root) is not None
263
and self._new_root not in self._removed_id):
264
264
self.unversion_file(self._new_root)
265
265
if file_id is not None:
266
266
self.version_file(file_id, self._new_root)
513
513
items = list(viewitems(self._new_parent))
514
514
items.extend((t, self.final_parent(t))
515
for t in list(self._tree_id_paths))
515
for t in list(self._tree_id_paths))
516
516
for trans_id, parent_id in items:
517
517
if parent_id not in by_parent:
518
518
by_parent[parent_id] = set()
643
643
for child_id in children:
644
644
if self.final_file_id(child_id) is not None:
645
645
conflicts.append(('unversioned parent', parent_id))
649
649
def _improper_versioning(self):
687
687
if trans_id not in self._removed_contents:
688
688
conflicts.append(('overwrite', trans_id,
689
self.final_name(trans_id)))
689
self.final_name(trans_id)))
692
692
def _duplicate_entries(self, by_parent):
903
903
if from_versioned:
904
904
# get data from working tree if versioned
905
905
from_entry = next(self._tree.iter_entries_by_dir(
906
specific_files=[from_path]))[1]
906
specific_files=[from_path]))[1]
907
907
from_name = from_entry.name
908
908
from_parent = from_entry.parent_id
990
990
if from_kind != to_kind:
992
992
elif to_kind in ('file', 'symlink') and (
993
to_trans_id != from_trans_id or
994
to_trans_id in self._new_contents):
993
to_trans_id != from_trans_id
994
or to_trans_id in self._new_contents):
996
if (not modified and from_versioned == to_versioned and
997
from_parent==to_parent and from_name == to_name and
998
from_executable == to_executable):
996
if (not modified and from_versioned == to_versioned
997
and from_parent == to_parent and from_name == to_name
998
and from_executable == to_executable):
1000
1000
results.append((file_id, (from_path, to_path), modified,
1001
(from_versioned, to_versioned),
1002
(from_parent, to_parent),
1003
(from_name, to_name),
1004
(from_kind, to_kind),
1005
(from_executable, to_executable)))
1001
(from_versioned, to_versioned),
1002
(from_parent, to_parent),
1003
(from_name, to_name),
1004
(from_kind, to_kind),
1005
(from_executable, to_executable)))
1007
1007
def path_key(t):
1146
1146
if not isinstance(content, bytes):
1147
1147
content = content.encode('utf-8')
1148
1148
yield serializer.bytes_record(
1149
content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
1149
content, ((trans_id.encode('utf-8'), kind.encode('ascii')),))
1151
1151
def deserialize(self, records):
1152
1152
"""Deserialize a stored TreeTransform.
1162
1162
self._new_parent = {k.decode('utf-8'): v.decode('utf-8')
1163
1163
for k, v in viewitems(attribs[b'_new_parent'])}
1164
1164
self._new_executability = {k.decode('utf-8'): bool(v)
1165
for k, v in viewitems(attribs[b'_new_executability'])}
1165
for k, v in viewitems(attribs[b'_new_executability'])}
1166
1166
self._new_id = {k.decode('utf-8'): v
1167
1167
for k, v in viewitems(attribs[b'_new_id'])}
1168
1168
self._r_new_id = {v: k for k, v in viewitems(self._new_id)}
1281
1281
previous_parent = self._new_parent.get(trans_id)
1282
1282
previous_name = self._new_name.get(trans_id)
1283
1283
TreeTransformBase.adjust_path(self, name, parent, trans_id)
1284
if (trans_id in self._limbo_files and
1285
trans_id not in self._needs_rename):
1284
if (trans_id in self._limbo_files
1285
and trans_id not in self._needs_rename):
1286
1286
self._rename_in_limbo([trans_id])
1287
1287
if previous_parent != parent:
1288
1288
self._limbo_children[previous_parent].remove(trans_id)
1654
1655
children = os.listdir(self._tree.abspath(path))
1655
1656
except OSError as e:
1656
if not (osutils._is_error_enotdir(e)
1657
or e.errno in (errno.ENOENT, errno.ESRCH)):
1657
if not (osutils._is_error_enotdir(e) or
1658
e.errno in (errno.ENOENT, errno.ESRCH)):
1691
1692
# if it is already associated with this trans_id.
1692
1693
elif self._case_sensitive_target:
1693
1694
if (self._limbo_children_names[parent].get(filename)
1694
in (trans_id, None)):
1695
in (trans_id, None)):
1695
1696
use_direct_path = True
1697
1698
for l_filename, l_trans_id in viewitems(
1767
1768
with ui.ui_factory.nested_progress_bar() as child_pb:
1768
1769
for num, trans_id in enumerate(self._removed_id):
1769
1770
if (num % 10) == 0:
1770
child_pb.update(gettext('removing file'), num, total_entries)
1771
child_pb.update(gettext('removing file'),
1771
1773
if trans_id == self._new_root:
1772
1774
file_id = self._tree.get_root_id()
1791
1793
kind = self.final_kind(trans_id)
1792
1794
if kind is None:
1793
1795
kind = self._tree.stored_kind(
1794
self._tree.id2path(file_id), file_id)
1796
self._tree.id2path(file_id), file_id)
1795
1797
parent_trans_id = self.final_parent(trans_id)
1796
1798
parent_file_id = new_path_file_ids.get(parent_trans_id)
1797
1799
if parent_file_id is None:
1804
1806
None, self._new_reference_revision[trans_id])
1806
1808
new_entry = inventory.make_entry(kind,
1807
self.final_name(trans_id),
1808
parent_file_id, file_id)
1809
self.final_name(trans_id),
1810
parent_file_id, file_id)
1810
1812
old_path = self._tree.id2path(new_entry.file_id)
1811
1813
except errors.NoSuchId:
1837
1839
if trans_id in self._removed_contents:
1838
1840
delete_path = os.path.join(self._deletiondir, trans_id)
1839
1841
mover.pre_delete(full_path, delete_path)
1840
elif (trans_id in self._new_name
1841
or trans_id in self._new_parent):
1842
elif (trans_id in self._new_name or
1843
trans_id in self._new_parent):
1843
1845
mover.rename(full_path, self._limbo_name(trans_id))
1844
1846
except errors.TransformRenameFailed as e:
1864
1866
with ui.ui_factory.nested_progress_bar() as child_pb:
1865
1867
for num, (path, trans_id) in enumerate(new_paths):
1866
1868
if (num % 10) == 0:
1867
child_pb.update(gettext('adding file'), num, len(new_paths))
1869
child_pb.update(gettext('adding file'),
1870
num, len(new_paths))
1868
1871
full_path = self._tree.abspath(path)
1869
1872
if trans_id in self._needs_rename:
1878
1881
# TODO: if trans_id in self._observed_sha1s, we should
1879
1882
# re-stat the final target, since ctime will be
1880
1883
# updated by the change.
1881
if (trans_id in self._new_contents or
1882
self.path_changed(trans_id)):
1884
if (trans_id in self._new_contents
1885
or self.path_changed(trans_id)):
1883
1886
if trans_id in self._new_contents:
1884
1887
modified_paths.append(full_path)
1885
1888
if trans_id in self._new_executability:
1964
1967
entry = next(self._tree.iter_entries_by_dir(
1965
specific_files=[path]))[1]
1968
specific_files=[path]))[1]
1966
1969
except StopIteration:
1968
1971
children = getattr(entry, 'children', {})
2011
2014
def _get_file_revision(self, path, file_id, vf, tree_revision):
2012
2015
parent_keys = [
2013
(file_id, t.get_file_revision(t.id2path(file_id), file_id))
2014
for t in self._iter_parent_trees()]
2016
(file_id, t.get_file_revision(t.id2path(file_id), file_id))
2017
for t in self._iter_parent_trees()]
2015
2018
vf.add_lines((file_id, tree_revision), parent_keys,
2016
2019
self.get_file_lines(path, file_id))
2017
2020
repo = self._get_repository()
2072
2075
if file_id in self._transform._r_new_id:
2074
2077
elif file_id in {self._transform.tree_file_id(trans_id) for
2075
trans_id in self._transform._removed_id}:
2078
trans_id in self._transform._removed_id}:
2078
2081
return fallback_check(file_id)
2149
2152
file_id = self._transform.final_file_id(trans_id)
2150
2153
if file_id is None:
2152
if (specific_files is not None and
2153
self._final_paths.get_path(trans_id) not in specific_files):
2155
if (specific_files is not None
2156
and self._final_paths.get_path(trans_id) not in specific_files):
2155
2158
kind = self._transform.final_kind(trans_id)
2156
2159
if kind is None:
2195
2198
ordered_ids = self._list_files_by_dir()
2196
2199
for entry, trans_id in self._make_inv_entries(ordered_ids,
2198
2201
yield self._final_paths.get_path(trans_id), entry
2200
2203
def _iter_entries_for_dir(self, dir_path):
2231
2234
if from_dir is None and include_root is True:
2232
2235
root_entry = inventory.make_entry('directory', '',
2233
ROOT_PARENT, self.get_root_id())
2236
ROOT_PARENT, self.get_root_id())
2234
2237
yield '', 'V', 'directory', root_entry.file_id, root_entry
2235
2238
entries = self._iter_entries_for_dir(from_dir or '')
2236
2239
for path, entry in entries:
2259
2262
raise errors.NoSuchFile(path)
2260
2263
if not self._content_change(file_id):
2261
2264
return self._transform._tree.get_file_mtime(
2262
self._transform._tree.id2path(file_id), file_id)
2265
self._transform._tree.id2path(file_id), file_id)
2263
2266
trans_id = self._path2trans_id(path)
2264
2267
return self._stat_limbo_file(trans_id).st_mtime
2358
2361
return kind, size, executable, link_or_sha1
2360
2363
def iter_changes(self, from_tree, include_unchanged=False,
2361
specific_files=None, pb=None, extra_trees=None,
2362
require_versioned=True, want_unversioned=False):
2364
specific_files=None, pb=None, extra_trees=None,
2365
require_versioned=True, want_unversioned=False):
2363
2366
"""See InterTree.iter_changes.
2365
2368
This has a fast path that is only used when the from_tree matches
2366
2369
the transform tree, and no fancy options are supplied.
2368
if (from_tree is not self._transform._tree or include_unchanged or
2369
specific_files or want_unversioned):
2371
if (from_tree is not self._transform._tree or include_unchanged
2372
or specific_files or want_unversioned):
2370
2373
return tree.InterTree(from_tree, self).iter_changes(
2371
2374
include_unchanged=include_unchanged,
2372
2375
specific_files=specific_files,
2406
2409
get_old = (kind[0] == 'file' and versioned[0])
2408
2411
old_annotation = self._transform._tree.annotate_iter(
2409
path, file_id=file_id, default_revision=default_revision)
2412
path, file_id=file_id, default_revision=default_revision)
2411
2414
old_annotation = []
2412
2415
if changes is None:
2447
2450
path_from_root = self._final_paths.get_path(child_id)
2448
2451
basename = self._transform.final_name(child_id)
2449
2452
file_id = self._transform.final_file_id(child_id)
2450
kind = self._transform.final_kind(child_id)
2453
kind = self._transform.final_kind(child_id)
2451
2454
if kind is not None:
2452
2455
versioned_kind = kind
2454
2457
kind = 'unknown'
2455
2458
versioned_kind = self._transform._tree.stored_kind(
2456
self._transform._tree.id2path(file_id),
2459
self._transform._tree.id2path(file_id),
2458
2461
if versioned_kind == 'directory':
2459
2462
subdirs.append(child_id)
2460
2463
children.append((path_from_root, basename, kind, None,
2601
2604
for dir, files in wt.walkdirs():
2602
2605
existing_files.update(f[0] for f in files)
2603
2606
for num, (tree_path, entry) in \
2604
enumerate(tree.iter_entries_by_dir()):
2605
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2607
enumerate(tree.iter_entries_by_dir()):
2608
pb.update(gettext("Building tree"), num
2609
- len(deferred_contents), total)
2606
2610
if entry.parent_id is None:
2608
2612
reparent = False
2621
2625
divert.add(file_id)
2622
if (file_id not in divert and
2623
_content_match(tree, entry, tree_path, file_id, kind,
2626
if (file_id not in divert
2627
and _content_match(tree, entry, tree_path, file_id, kind,
2625
2629
tt.delete_contents(tt.trans_id_tree_path(tree_path))
2626
2630
if kind == 'directory':
2627
2631
reparent = True
2635
2639
executable = tree.is_executable(tree_path, file_id)
2637
2641
tt.set_executability(executable, trans_id)
2638
trans_data = (trans_id, file_id, tree_path, entry.text_sha1)
2642
trans_data = (trans_id, file_id,
2643
tree_path, entry.text_sha1)
2639
2644
deferred_contents.append((tree_path, trans_data))
2641
2646
file_trans_id[file_id] = new_by_entry(
2642
tree_path, tt, entry, parent_id, tree)
2647
tree_path, tt, entry, parent_id, tree)
2644
2649
new_trans_id = file_trans_id[file_id]
2645
2650
old_parent = tt.trans_id_tree_path(tree_path)
2649
2654
accelerator_tree, hardlink)
2650
2655
pp.next_phase()
2651
2656
divert_trans = set(file_trans_id[f] for f in divert)
2652
resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
2658
def resolver(t, c): return resolve_checkout(t, c, divert_trans)
2653
2659
raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
2654
2660
if len(raw_conflicts) > 0:
2655
2661
precomputed_delta = None
2688
2694
accelerator_path = unchanged.get(tree_path)
2689
2695
if accelerator_path is None:
2690
2696
new_desired_files.append((tree_path,
2691
(trans_id, file_id, tree_path, text_sha1)))
2697
(trans_id, file_id, tree_path, text_sha1)))
2693
2699
pb.update(gettext('Adding file contents'), count + offset, total)
2700
2706
if wt.supports_content_filtering():
2701
2707
filters = wt._content_filter_stack(tree_path)
2702
2708
chunks = filtered_output_bytes(chunks, filters,
2703
ContentFilterContext(tree_path, tree))
2709
ContentFilterContext(tree_path, tree))
2704
2710
tt.create_file(chunks, trans_id, sha1=text_sha1)
2706
2712
offset += count
2709
2715
if wt.supports_content_filtering():
2710
2716
filters = wt._content_filter_stack(tree_path)
2711
2717
contents = filtered_output_bytes(contents, filters,
2712
ContentFilterContext(tree_path, tree))
2718
ContentFilterContext(tree_path, tree))
2713
2719
tt.create_file(contents, trans_id, sha1=text_sha1)
2714
2720
pb.update(gettext('Adding file contents'), count + offset, total)
2734
2740
if entry.kind == "file":
2735
2741
with open(target_path, 'rb') as f1, \
2736
tree.get_file(tree_path, file_id) as f2:
2742
tree.get_file(tree_path, file_id) as f2:
2737
2743
if osutils.compare_files(f1, f2):
2739
2745
elif entry.kind == "symlink":
2761
2767
final_parent = tt.final_parent(old_file)
2762
2768
if new_file in divert:
2763
new_name = tt.final_name(old_file)+'.diverted'
2769
new_name = tt.final_name(old_file) + '.diverted'
2764
2770
tt.adjust_path(new_name, final_parent, new_file)
2765
2771
new_conflicts.add((c_type, 'Diverted to',
2766
2772
new_file, old_file))
2768
new_name = tt.final_name(old_file)+'.moved'
2774
new_name = tt.final_name(old_file) + '.moved'
2769
2775
tt.adjust_path(new_name, final_parent, old_file)
2770
2776
new_conflicts.add((c_type, 'Moved existing file to',
2771
2777
old_file, new_file))
2780
2786
with tree.get_file(path, entry.file_id) as f:
2781
2787
executable = tree.is_executable(path, entry.file_id)
2782
2788
return tt.new_file(
2783
name, parent_id, osutils.file_iterator(f), entry.file_id,
2789
name, parent_id, osutils.file_iterator(f), entry.file_id,
2785
2791
elif kind in ('directory', 'tree-reference'):
2786
2792
trans_id = tt.new_directory(name, parent_id, entry.file_id)
2787
2793
if kind == 'tree-reference':
2797
2803
def create_from_tree(tt, trans_id, tree, path, file_id=None, chunks=None,
2798
filter_tree_path=None):
2804
filter_tree_path=None):
2799
2805
"""Create new file contents according to tree contents.
2801
2807
:param filter_tree_path: the tree path to use to lookup
2816
2822
if wt.supports_content_filtering() and filter_tree_path is not None:
2817
2823
filters = wt._content_filter_stack(filter_tree_path)
2818
2824
chunks = filtered_output_bytes(chunks, filters,
2819
ContentFilterContext(filter_tree_path, tree))
2825
ContentFilterContext(filter_tree_path, tree))
2820
2826
tt.create_file(chunks, trans_id)
2822
2828
if f is not None:
2868
2874
merge_modified, basis_tree)
2869
2875
with ui.ui_factory.nested_progress_bar() as child_pb:
2870
2876
raw_conflicts = resolve_conflicts(tt, child_pb,
2871
lambda t, c: conflict_pass(t, c, target_tree))
2877
lambda t, c: conflict_pass(t, c, target_tree))
2872
2878
conflicts = cook_conflicts(raw_conflicts, tt)
2873
2879
return conflicts, merge_modified
2882
2888
# optimizer to compare itself to a target, but no optimizer for the
2884
2890
change_list = working_tree.iter_changes(target_tree,
2885
specific_files=specific_files, pb=pb)
2891
specific_files=specific_files, pb=pb)
2886
2892
if not target_tree.is_versioned(u''):
2887
2893
skip_root = True
2891
2897
deferred_files = []
2892
2898
for id_num, (file_id, path, changed_content, versioned, parent, name,
2893
kind, executable) in enumerate(change_list):
2899
kind, executable) in enumerate(change_list):
2894
2900
target_path, wt_path = path
2895
2901
target_versioned, wt_versioned = versioned
2896
2902
target_parent, wt_parent = parent
2912
2918
if basis_tree is None:
2913
2919
basis_tree = working_tree.basis_tree()
2914
2920
basis_tree.lock_read()
2915
basis_path = find_previous_path(working_tree, basis_tree, wt_path)
2921
basis_path = find_previous_path(
2922
working_tree, basis_tree, wt_path)
2916
2923
if basis_path is None:
2917
2924
if target_kind is None and not target_versioned:
2918
2925
keep_content = True
2939
2946
tt.create_directory(trans_id)
2940
2947
if target_kind == 'tree-reference':
2941
2948
revision = target_tree.get_reference_revision(
2942
target_path, file_id)
2949
target_path, file_id)
2943
2950
tt.set_tree_reference(revision, trans_id)
2944
2951
elif target_kind == 'symlink':
2945
2952
tt.create_symlink(target_tree.get_symlink_target(
2946
target_path, file_id), trans_id)
2953
target_path, file_id), trans_id)
2947
2954
elif target_kind == 'file':
2948
deferred_files.append((target_path, (trans_id, mode_id, file_id)))
2955
deferred_files.append(
2956
(target_path, (trans_id, mode_id, file_id)))
2949
2957
if basis_tree is None:
2950
2958
basis_tree = working_tree.basis_tree()
2951
2959
basis_tree.lock_read()
2952
2960
new_sha1 = target_tree.get_file_sha1(target_path, file_id)
2953
basis_path = find_previous_path(target_tree, basis_tree, target_path)
2954
if (basis_path is not None and
2955
new_sha1 == basis_tree.get_file_sha1(basis_path, file_id)):
2961
basis_path = find_previous_path(
2962
target_tree, basis_tree, target_path)
2963
if (basis_path is not None
2964
and new_sha1 == basis_tree.get_file_sha1(basis_path, file_id)):
2956
2965
if file_id in merge_modified:
2957
2966
del merge_modified[file_id]
2967
2976
tt.version_file(file_id, trans_id)
2968
2977
if wt_versioned and not target_versioned:
2969
2978
tt.unversion_file(trans_id)
2970
if (target_name is not None and
2971
(wt_name != target_name or wt_parent != target_parent)):
2979
if (target_name is not None
2980
and (wt_name != target_name or wt_parent != target_parent)):
2972
2981
if target_name == '' and target_parent is None:
2973
2982
parent_trans = ROOT_PARENT
2981
2990
tt.set_executability(target_executable, trans_id)
2982
2991
if working_tree.supports_content_filtering():
2983
2992
for (trans_id, mode_id, file_id), bytes in (
2984
target_tree.iter_files_bytes(deferred_files)):
2993
target_tree.iter_files_bytes(deferred_files)):
2985
2994
# We're reverting a tree to the target tree so using the
2986
2995
# target tree to find the file path seems the best choice
2987
2996
# here IMO - Ian C 27/Oct/2009
2988
2997
filter_tree_path = target_tree.id2path(file_id)
2989
2998
filters = working_tree._content_filter_stack(filter_tree_path)
2990
2999
bytes = filtered_output_bytes(bytes, filters,
2991
ContentFilterContext(filter_tree_path, working_tree))
3000
ContentFilterContext(filter_tree_path, working_tree))
2992
3001
tt.create_file(bytes, trans_id, mode_id)
2994
3003
for (trans_id, mode_id, file_id), bytes in target_tree.iter_files_bytes(
2996
3005
tt.create_file(bytes, trans_id, mode_id)
2997
3006
tt.fixup_new_roots()
3008
3017
new_conflicts = set()
3009
3018
with ui.ui_factory.nested_progress_bar() as pb:
3010
3019
for n in range(10):
3011
pb.update(gettext('Resolution pass'), n+1, 10)
3020
pb.update(gettext('Resolution pass'), n + 1, 10)
3012
3021
conflicts = tt.find_conflicts()
3013
3022
if len(conflicts) == 0:
3014
3023
return new_conflicts
3107
3116
# special-case the other tree root (move its children instead)
3108
3117
if path_tree and path_tree.path2id('') == file_id:
3109
3118
# This is the root entry, skip it
3111
3120
tt.version_file(file_id, conflict[1])
3112
3121
new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
3113
3122
elif c_type == 'non-directory parent':
3116
3125
parent_name = tt.final_name(parent_id)
3117
3126
parent_file_id = tt.final_file_id(parent_id)
3118
3127
new_parent_id = tt.new_directory(parent_name + '.new',
3119
parent_parent, parent_file_id)
3128
parent_parent, parent_file_id)
3120
3129
_reparent_transform_children(tt, parent_id, new_parent_id)
3121
3130
if parent_file_id is not None:
3122
3131
tt.unversion_file(parent_id)
3213
3222
for (file_id, paths, changed_content, versioned, parent, name, kind,
3214
3223
executable) in target_tree.iter_changes(source_tree,
3215
include_unchanged=True):
3224
include_unchanged=True):
3216
3225
if changed_content:
3218
3227
if kind != ('file', 'file'):