31
27
lazy_import.lazy_import(globals(), """
32
28
from bzrlib import (
42
38
revision as _mod_revision,
46
from bzrlib.i18n import gettext
48
from bzrlib.errors import (DuplicateKey, MalformedTransform,
43
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
49
44
ReusingTransform, CantMoveRoot,
50
45
ExistingLimbo, ImmortalLimbo, NoFinalPath,
51
46
UnableCreateSymlink)
52
47
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
48
from bzrlib.inventory import InventoryEntry
53
49
from bzrlib.osutils import (
60
57
supports_executable,
62
59
from bzrlib.progress import ProgressPhase
63
60
from bzrlib.symbol_versioning import (
65
from bzrlib.trace import mutter, warning
66
from bzrlib import tree
68
import bzrlib.urlutils as urlutils
70
71
ROOT_PARENT = "root-parent"
395
386
return sorted(FinalPaths(self).get_paths(new_ids))
397
388
def _inventory_altered(self):
398
"""Determine which trans_ids need new Inventory entries.
400
An new entry is needed when anything that would be reflected by an
401
inventory entry changes, including file name, file_id, parent file_id,
402
file kind, and the execute bit.
404
Some care is taken to return entries with real changes, not cases
405
where the value is deleted and then restored to its original value,
406
but some actually unchanged values may be returned.
408
:returns: A list of (path, trans_id) for all items requiring an
409
inventory change. Ordered by path.
412
# Find entries whose file_ids are new (or changed).
413
new_file_id = set(t for t in self._new_id
414
if self._new_id[t] != self.tree_file_id(t))
415
for id_set in [self._new_name, self._new_parent, new_file_id,
389
"""Get the trans_ids and paths of files needing new inv entries."""
391
for id_set in [self._new_name, self._new_parent, self._new_id,
416
392
self._new_executability]:
417
changed_ids.update(id_set)
418
# removing implies a kind change
393
new_ids.update(id_set)
419
394
changed_kind = set(self._removed_contents)
421
395
changed_kind.intersection_update(self._new_contents)
422
# Ignore entries that are already known to have changed.
423
changed_kind.difference_update(changed_ids)
424
# to keep only the truly changed ones
396
changed_kind.difference_update(new_ids)
425
397
changed_kind = (t for t in changed_kind
426
398
if self.tree_kind(t) != self.final_kind(t))
427
# all kind changes will alter the inventory
428
changed_ids.update(changed_kind)
429
# To find entries with changed parent_ids, find parents which existed,
430
# but changed file_id.
431
changed_file_id = set(t for t in new_file_id if t in self._removed_id)
432
# Now add all their children to the set.
433
for parent_trans_id in new_file_id:
434
changed_ids.update(self.iter_tree_children(parent_trans_id))
435
return sorted(FinalPaths(self).get_paths(changed_ids))
399
new_ids.update(changed_kind)
400
return sorted(FinalPaths(self).get_paths(new_ids))
437
402
def final_kind(self, trans_id):
438
403
"""Determine the final file kind, after any changes applied.
1299
1248
descendants.update(self._limbo_descendants(descendant))
1300
1249
return descendants
1302
def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1251
def create_file(self, contents, trans_id, mode_id=None):
1303
1252
"""Schedule creation of a new file.
1307
:param contents: an iterator of strings, all of which will be written
1308
to the target destination.
1309
:param trans_id: TreeTransform handle
1310
:param mode_id: If not None, force the mode of the target file to match
1311
the mode of the object referenced by mode_id.
1312
Otherwise, we will try to preserve mode bits of an existing file.
1313
:param sha1: If the sha1 of this content is already known, pass it in.
1314
We can use it to prevent future sha1 computations.
1256
Contents is an iterator of strings, all of which will be written
1257
to the target destination.
1259
New file takes the permissions of any existing file with that id,
1260
unless mode_id is specified.
1316
1262
name = self._limbo_name(trans_id)
1317
1263
f = open(name, 'wb')
1319
unique_add(self._new_contents, trans_id, 'file')
1266
unique_add(self._new_contents, trans_id, 'file')
1268
# Clean up the file, it never got registered so
1269
# TreeTransform.finalize() won't clean it up.
1320
1274
f.writelines(contents)
1323
1277
self._set_mtime(name)
1324
1278
self._set_mode(trans_id, mode_id, S_ISREG)
1325
# It is unfortunate we have to use lstat instead of fstat, but we just
1326
# used utime and chmod on the file, so we need the accurate final
1328
if sha1 is not None:
1329
self._observed_sha1s[trans_id] = (sha1, osutils.lstat(name))
1331
1280
def _read_file_chunks(self, trans_id):
1332
1281
cur_file = open(self._limbo_name(trans_id), 'rb')
1829
1777
tree_paths = list(self._tree_path_ids.iteritems())
1830
1778
tree_paths.sort(reverse=True)
1831
child_pb = ui.ui_factory.nested_progress_bar()
1779
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1833
for num, (path, trans_id) in enumerate(tree_paths):
1834
# do not attempt to move root into a subdirectory of itself.
1837
child_pb.update(gettext('removing file'), num, len(tree_paths))
1781
for num, data in enumerate(tree_paths):
1782
path, trans_id = data
1783
child_pb.update('removing file', num, len(tree_paths))
1838
1784
full_path = self._tree.abspath(path)
1839
1785
if trans_id in self._removed_contents:
1840
1786
delete_path = os.path.join(self._deletiondir, trans_id)
1882
1828
self.rename_count += 1
1883
# TODO: if trans_id in self._observed_sha1s, we should
1884
# re-stat the final target, since ctime will be
1885
# updated by the change.
1886
1829
if (trans_id in self._new_contents or
1887
1830
self.path_changed(trans_id)):
1888
1831
if trans_id in self._new_contents:
1889
1832
modified_paths.append(full_path)
1890
1833
if trans_id in self._new_executability:
1891
1834
self._set_executability(path, trans_id)
1892
if trans_id in self._observed_sha1s:
1893
o_sha1, o_st_val = self._observed_sha1s[trans_id]
1894
st = osutils.lstat(full_path)
1895
self._observed_sha1s[trans_id] = (o_sha1, st)
1897
1836
child_pb.finished()
1898
for path, trans_id in new_paths:
1899
# new_paths includes stuff like workingtree conflicts. Only the
1900
# stuff in new_contents actually comes from limbo.
1901
if trans_id in self._limbo_files:
1902
del self._limbo_files[trans_id]
1903
1837
self._new_contents.clear()
1904
1838
return modified_paths
1906
def _apply_observed_sha1s(self):
1907
"""After we have finished renaming everything, update observed sha1s
1909
This has to be done after self._tree.apply_inventory_delta, otherwise
1910
it doesn't know anything about the files we are updating. Also, we want
1911
to do this as late as possible, so that most entries end up cached.
1913
# TODO: this doesn't update the stat information for directories. So
1914
# the first 'bzr status' will still need to rewrite
1915
# .bzr/checkout/dirstate. However, we at least don't need to
1916
# re-read all of the files.
1917
# TODO: If the operation took a while, we could do a time.sleep(3) here
1918
# to allow the clock to tick over and ensure we won't have any
1919
# problems. (we could observe start time, and finish time, and if
1920
# it is less than eg 10% overhead, add a sleep call.)
1921
paths = FinalPaths(self)
1922
for trans_id, observed in self._observed_sha1s.iteritems():
1923
path = paths.get_path(trans_id)
1924
# We could get the file_id, but dirstate prefers to use the path
1925
# anyway, and it is 'cheaper' to determine.
1926
# file_id = self._new_id[trans_id]
1927
self._tree._observed_sha1(None, path, observed)
1930
1841
class TransformPreview(DiskTreeTransform):
1931
1842
"""A TreeTransform for generating preview trees.
2249
2160
def get_file_size(self, file_id):
2250
2161
"""See Tree.get_file_size"""
2251
trans_id = self._transform.trans_id_file_id(file_id)
2252
kind = self._transform.final_kind(trans_id)
2255
if trans_id in self._transform._new_contents:
2256
return self._stat_limbo_file(trans_id=trans_id).st_size
2257
2162
if self.kind(file_id) == 'file':
2258
2163
return self._transform._tree.get_file_size(file_id)
2262
def get_file_verifier(self, file_id, path=None, stat_value=None):
2263
trans_id = self._transform.trans_id_file_id(file_id)
2264
kind = self._transform._new_contents.get(trans_id)
2266
return self._transform._tree.get_file_verifier(file_id)
2268
fileobj = self.get_file(file_id)
2270
return ("SHA1", sha_file(fileobj))
2274
2167
def get_file_sha1(self, file_id, path=None, stat_value=None):
2275
2168
trans_id = self._transform.trans_id_file_id(file_id)
2276
2169
kind = self._transform._new_contents.get(trans_id)
2668
2552
unchanged = dict(unchanged)
2669
2553
new_desired_files = []
2671
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2555
for file_id, (trans_id, tree_path) in desired_files:
2672
2556
accelerator_path = unchanged.get(file_id)
2673
2557
if accelerator_path is None:
2674
new_desired_files.append((file_id,
2675
(trans_id, tree_path, text_sha1)))
2558
new_desired_files.append((file_id, (trans_id, tree_path)))
2677
pb.update(gettext('Adding file contents'), count + offset, total)
2560
pb.update('Adding file contents', count + offset, total)
2679
2562
tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2696
2579
offset += count
2697
for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
2580
for count, ((trans_id, tree_path), contents) in enumerate(
2698
2581
tree.iter_files_bytes(new_desired_files)):
2699
2582
if wt.supports_content_filtering():
2700
2583
filters = wt._content_filter_stack(tree_path)
2701
2584
contents = filtered_output_bytes(contents, filters,
2702
2585
ContentFilterContext(tree_path, tree))
2703
tt.create_file(contents, trans_id, sha1=text_sha1)
2704
pb.update(gettext('Adding file contents'), count + offset, total)
2586
tt.create_file(contents, trans_id)
2587
pb.update('Adding file contents', count + offset, total)
2707
2590
def _reparent_children(tt, old_parent, new_parent):
2839
2722
return new_name
2725
def _entry_changes(file_id, entry, working_tree):
2726
"""Determine in which ways the inventory entry has changed.
2728
Returns booleans: has_contents, content_mod, meta_mod
2729
has_contents means there are currently contents, but they differ
2730
contents_mod means contents need to be modified
2731
meta_mod means the metadata needs to be modified
2733
cur_entry = working_tree.inventory[file_id]
2735
working_kind = working_tree.kind(file_id)
2738
has_contents = False
2741
if has_contents is True:
2742
if entry.kind != working_kind:
2743
contents_mod, meta_mod = True, False
2745
cur_entry._read_tree_state(working_tree.id2path(file_id),
2747
contents_mod, meta_mod = entry.detect_changes(cur_entry)
2748
cur_entry._forget_tree_state()
2749
return has_contents, contents_mod, meta_mod
2842
2752
def revert(working_tree, target_tree, filenames, backups=False,
2843
2753
pb=None, change_reporter=None):
2844
2754
"""Revert a working tree's contents to those of a target tree."""
2905
2811
deferred_files = []
2906
2812
for id_num, (file_id, path, changed_content, versioned, parent, name,
2907
2813
kind, executable) in enumerate(change_list):
2908
target_path, wt_path = path
2909
target_versioned, wt_versioned = versioned
2910
target_parent, wt_parent = parent
2911
target_name, wt_name = name
2912
target_kind, wt_kind = kind
2913
target_executable, wt_executable = executable
2914
if skip_root and wt_parent is None:
2814
if skip_root and file_id[0] is not None and parent[0] is None:
2916
2816
trans_id = tt.trans_id_file_id(file_id)
2918
2818
if changed_content:
2919
2819
keep_content = False
2920
if wt_kind == 'file' and (backups or target_kind is None):
2820
if kind[0] == 'file' and (backups or kind[1] is None):
2921
2821
wt_sha1 = working_tree.get_file_sha1(file_id)
2922
2822
if merge_modified.get(file_id) != wt_sha1:
2923
2823
# acquire the basis tree lazily to prevent the
2926
2826
if basis_tree is None:
2927
2827
basis_tree = working_tree.basis_tree()
2928
2828
basis_tree.lock_read()
2929
if basis_tree.has_id(file_id):
2829
if file_id in basis_tree:
2930
2830
if wt_sha1 != basis_tree.get_file_sha1(file_id):
2931
2831
keep_content = True
2932
elif target_kind is None and not target_versioned:
2832
elif kind[1] is None and not versioned[1]:
2933
2833
keep_content = True
2934
if wt_kind is not None:
2834
if kind[0] is not None:
2935
2835
if not keep_content:
2936
2836
tt.delete_contents(trans_id)
2937
elif target_kind is not None:
2938
parent_trans_id = tt.trans_id_file_id(wt_parent)
2837
elif kind[1] is not None:
2838
parent_trans_id = tt.trans_id_file_id(parent[0])
2939
2839
backup_name = tt._available_backup_name(
2940
wt_name, parent_trans_id)
2840
name[0], parent_trans_id)
2941
2841
tt.adjust_path(backup_name, parent_trans_id, trans_id)
2942
new_trans_id = tt.create_path(wt_name, parent_trans_id)
2943
if wt_versioned and target_versioned:
2842
new_trans_id = tt.create_path(name[0], parent_trans_id)
2843
if versioned == (True, True):
2944
2844
tt.unversion_file(trans_id)
2945
2845
tt.version_file(file_id, new_trans_id)
2946
2846
# New contents should have the same unix perms as old
2948
2848
mode_id = trans_id
2949
2849
trans_id = new_trans_id
2950
if target_kind in ('directory', 'tree-reference'):
2850
if kind[1] in ('directory', 'tree-reference'):
2951
2851
tt.create_directory(trans_id)
2952
if target_kind == 'tree-reference':
2852
if kind[1] == 'tree-reference':
2953
2853
revision = target_tree.get_reference_revision(file_id,
2955
2855
tt.set_tree_reference(revision, trans_id)
2956
elif target_kind == 'symlink':
2856
elif kind[1] == 'symlink':
2957
2857
tt.create_symlink(target_tree.get_symlink_target(file_id),
2959
elif target_kind == 'file':
2859
elif kind[1] == 'file':
2960
2860
deferred_files.append((file_id, (trans_id, mode_id)))
2961
2861
if basis_tree is None:
2962
2862
basis_tree = working_tree.basis_tree()
2963
2863
basis_tree.lock_read()
2964
2864
new_sha1 = target_tree.get_file_sha1(file_id)
2965
if (basis_tree.has_id(file_id) and
2966
new_sha1 == basis_tree.get_file_sha1(file_id)):
2865
if (file_id in basis_tree and new_sha1 ==
2866
basis_tree.get_file_sha1(file_id)):
2967
2867
if file_id in merge_modified:
2968
2868
del merge_modified[file_id]
2970
2870
merge_modified[file_id] = new_sha1
2972
2872
# preserve the execute bit when backing up
2973
if keep_content and wt_executable == target_executable:
2974
tt.set_executability(target_executable, trans_id)
2975
elif target_kind is not None:
2976
raise AssertionError(target_kind)
2977
if not wt_versioned and target_versioned:
2873
if keep_content and executable[0] == executable[1]:
2874
tt.set_executability(executable[1], trans_id)
2875
elif kind[1] is not None:
2876
raise AssertionError(kind[1])
2877
if versioned == (False, True):
2978
2878
tt.version_file(file_id, trans_id)
2979
if wt_versioned and not target_versioned:
2879
if versioned == (True, False):
2980
2880
tt.unversion_file(trans_id)
2981
if (target_name is not None and
2982
(wt_name != target_name or wt_parent != target_parent)):
2983
if target_name == '' and target_parent is None:
2881
if (name[1] is not None and
2882
(name[0] != name[1] or parent[0] != parent[1])):
2883
if name[1] == '' and parent[1] is None:
2984
2884
parent_trans = ROOT_PARENT
2986
parent_trans = tt.trans_id_file_id(target_parent)
2987
if wt_parent is None and wt_versioned:
2988
tt.adjust_root_path(target_name, parent_trans)
2886
parent_trans = tt.trans_id_file_id(parent[1])
2887
if parent[0] is None and versioned[0]:
2888
tt.adjust_root_path(name[1], parent_trans)
2990
tt.adjust_path(target_name, parent_trans, trans_id)
2991
if wt_executable != target_executable and target_kind == "file":
2992
tt.set_executability(target_executable, trans_id)
2890
tt.adjust_path(name[1], parent_trans, trans_id)
2891
if executable[0] != executable[1] and kind[1] == "file":
2892
tt.set_executability(executable[1], trans_id)
2993
2893
if working_tree.supports_content_filtering():
2994
2894
for index, ((trans_id, mode_id), bytes) in enumerate(
2995
2895
target_tree.iter_files_bytes(deferred_files)):
3156
3056
modified_path = fp.get_path(conflict[2])
3157
3057
modified_id = tt.final_file_id(conflict[2])
3158
3058
if len(conflict) == 3:
3159
yield conflicts.Conflict.factory(
3160
c_type, action=action, path=modified_path, file_id=modified_id)
3059
yield Conflict.factory(c_type, action=action, path=modified_path,
3060
file_id=modified_id)
3163
3063
conflicting_path = fp.get_path(conflict[3])
3164
3064
conflicting_id = tt.final_file_id(conflict[3])
3165
yield conflicts.Conflict.factory(
3166
c_type, action=action, path=modified_path,
3167
file_id=modified_id,
3168
conflict_path=conflicting_path,
3169
conflict_file_id=conflicting_id)
3065
yield Conflict.factory(c_type, action=action, path=modified_path,
3066
file_id=modified_id,
3067
conflict_path=conflicting_path,
3068
conflict_file_id=conflicting_id)
3172
3071
class _FileMover(object):