617
617
'interesting_ids': self.interesting_ids,
618
618
'interesting_files': self.interesting_files,
619
619
'this_branch': self.this_branch,
620
'other_branch': self.other_branch,
620
621
'do_merge': False}
621
622
if self.merge_type.requires_base:
622
623
kwargs['base_tree'] = self.base_tree
725
726
interesting_ids=None, reprocess=False, show_base=False,
726
727
pb=None, pp=None, change_reporter=None,
727
728
interesting_files=None, do_merge=True,
728
cherrypick=False, lca_trees=None, this_branch=None):
729
cherrypick=False, lca_trees=None, this_branch=None,
729
731
"""Initialize the merger object and perform the merge.
731
733
:param working_tree: The working tree to apply the merge to
734
736
:param other_tree: The other tree to merge changes from
735
737
:param this_branch: The branch associated with this_tree. Defaults to
736
738
this_tree.branch if not supplied.
739
:param other_branch: The branch associated with other_tree, if any.
737
740
:param interesting_ids: The file_ids of files that should be
738
741
participate in the merge. May not be combined with
739
742
interesting_files.
761
764
this_branch = this_tree.branch
762
765
self.interesting_ids = interesting_ids
763
766
self.interesting_files = interesting_files
764
self.this_tree = working_tree
767
self.working_tree = working_tree
768
self.this_tree = this_tree
765
769
self.base_tree = base_tree
766
770
self.other_tree = other_tree
767
771
self.this_branch = this_branch
772
self.other_branch = other_branch
768
773
self._raw_conflicts = []
769
774
self.cooked_conflicts = []
770
775
self.reprocess = reprocess
787
792
def do_merge(self):
788
793
operation = cleanup.OperationWithCleanups(self._do_merge)
789
self.this_tree.lock_tree_write()
794
self.working_tree.lock_tree_write()
795
operation.add_cleanup(self.working_tree.unlock)
796
self.this_tree.lock_read()
790
797
operation.add_cleanup(self.this_tree.unlock)
791
798
self.base_tree.lock_read()
792
799
operation.add_cleanup(self.base_tree.unlock)
797
804
def _do_merge(self, operation):
798
self.tt = transform.TreeTransform(self.this_tree, None)
805
self.tt = transform.TreeTransform(self.working_tree, None)
799
806
operation.add_cleanup(self.tt.finalize)
800
807
self._compute_transform()
801
808
results = self.tt.apply(no_conflicts=True)
802
809
self.write_modified(results)
804
self.this_tree.add_conflicts(self.cooked_conflicts)
811
self.working_tree.add_conflicts(self.cooked_conflicts)
805
812
except errors.UnsupportedOperation:
934
941
walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
936
base_inventory = self.base_tree.inventory
937
this_inventory = self.this_tree.inventory
943
base_inventory = self.base_tree.root_inventory
944
this_inventory = self.this_tree.root_inventory
938
945
for path, file_id, other_ie, lca_values in walker.iter_all():
939
946
# Is this modified at all from any of the other trees?
940
947
if other_ie is None:
1074
@deprecated_method(deprecated_in((2, 4, 0)))
1076
if self.tt.final_kind(self.tt.root) is None:
1077
self.tt.cancel_deletion(self.tt.root)
1078
if self.tt.final_file_id(self.tt.root) is None:
1079
self.tt.version_file(self.tt.tree_file_id(self.tt.root),
1081
other_root_file_id = self.other_tree.get_root_id()
1082
if other_root_file_id is None:
1084
other_root = self.tt.trans_id_file_id(other_root_file_id)
1085
if other_root == self.tt.root:
1087
if self.this_tree.inventory.has_id(
1088
self.other_tree.inventory.root.file_id):
1089
# the other tree's root is a non-root in the current tree (as
1090
# when a previously unrelated branch is merged into another)
1092
if self.tt.final_kind(other_root) is not None:
1093
other_root_is_present = True
1095
# other_root doesn't have a physical representation. We still need
1096
# to move any references to the actual root of the tree.
1097
other_root_is_present = False
1098
# 'other_tree.inventory.root' is not present in this tree. We are
1099
# calling adjust_path for children which *want* to be present with a
1100
# correct place to go.
1101
for _, child in self.other_tree.inventory.root.children.iteritems():
1102
trans_id = self.tt.trans_id_file_id(child.file_id)
1103
if not other_root_is_present:
1104
if self.tt.final_kind(trans_id) is not None:
1105
# The item exist in the final tree and has a defined place
1108
# Move the item into the root
1110
final_name = self.tt.final_name(trans_id)
1111
except errors.NoFinalPath:
1112
# This file is not present anymore, ignore it.
1114
self.tt.adjust_path(final_name, self.tt.root, trans_id)
1115
if other_root_is_present:
1116
self.tt.cancel_creation(other_root)
1117
self.tt.cancel_versioning(other_root)
1119
1081
def write_modified(self, results):
1120
1082
modified_hashes = {}
1121
1083
for path in results.modified_paths:
1122
file_id = self.this_tree.path2id(self.this_tree.relpath(path))
1084
file_id = self.working_tree.path2id(self.working_tree.relpath(path))
1123
1085
if file_id is None:
1125
hash = self.this_tree.get_file_sha1(file_id)
1087
hash = self.working_tree.get_file_sha1(file_id)
1126
1088
if hash is None:
1128
1090
modified_hashes[file_id] = hash
1129
self.this_tree.set_merge_modified(modified_hashes)
1091
self.working_tree.set_merge_modified(modified_hashes)
1132
1094
def parent(entry, file_id):
1231
1193
# At this point, the lcas disagree, and the tip disagree
1232
1194
return 'conflict'
1235
@deprecated_method(deprecated_in((2, 2, 0)))
1236
def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1237
"""Do a three-way test on a scalar.
1238
Return "this", "other" or "conflict", depending whether a value wins.
1240
key_base = key(base_tree, file_id)
1241
key_other = key(other_tree, file_id)
1242
#if base == other, either they all agree, or only THIS has changed.
1243
if key_base == key_other:
1245
key_this = key(this_tree, file_id)
1246
# "Ambiguous clean merge"
1247
if key_this == key_other:
1249
elif key_this == key_base:
1254
1196
def merge_names(self, file_id):
1255
1197
def get_entry(tree):
1256
if tree.has_id(file_id):
1257
return tree.inventory[file_id]
1199
return tree.root_inventory[file_id]
1200
except errors.NoSuchId:
1260
1202
this_entry = get_entry(self.this_tree)
1261
1203
other_entry = get_entry(self.other_tree)
1934
1876
def _entries_to_incorporate(self):
1935
1877
"""Yields pairs of (inventory_entry, new_parent)."""
1936
other_inv = self.other_tree.inventory
1878
other_inv = self.other_tree.root_inventory
1937
1879
subdir_id = other_inv.path2id(self._source_subpath)
1938
1880
if subdir_id is None:
1939
1881
# XXX: The error would be clearer if it gave the URL of the source
1941
1883
raise PathNotInTree(self._source_subpath, "Source tree")
1942
1884
subdir = other_inv[subdir_id]
1943
1885
parent_in_target = osutils.dirname(self._target_subdir)
1944
target_id = self.this_tree.inventory.path2id(parent_in_target)
1886
target_id = self.this_tree.path2id(parent_in_target)
1945
1887
if target_id is None:
1946
1888
raise PathNotInTree(self._target_subdir, "Target tree")
1947
1889
name_in_target = osutils.basename(self._target_subdir)
1948
1890
merge_into_root = subdir.copy()
1949
1891
merge_into_root.name = name_in_target
1950
if self.this_tree.inventory.has_id(merge_into_root.file_id):
1892
if self.this_tree.has_id(merge_into_root.file_id):
1951
1893
# Give the root a new file-id.
1952
1894
# This can happen fairly easily if the directory we are
1953
1895
# incorporating is the root, and both trees have 'TREE_ROOT' as