1092.2.24
by Robert Collins
merge from martins newformat branch - brings in transport abstraction |
1 |
import os.path |
2 |
||
493
by Martin Pool
- Merge aaron's merge command |
3 |
import changeset |
4 |
from changeset import Inventory, apply_changeset, invert_dict |
|
1092.2.24
by Robert Collins
merge from martins newformat branch - brings in transport abstraction |
5 |
from bzrlib.osutils import backup_file, rename |
6 |
from bzrlib.merge3 import Merge3 |
|
7 |
import bzrlib |
|
1185.12.30
by Aaron Bentley
Moved all fooCreate into get_contents |
8 |
from changeset import get_contents |
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
9 |
|
10 |
class ApplyMerge3: |
|
11 |
"""Contents-change wrapper around merge3.Merge3"""
|
|
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
12 |
def __init__(self, file_id, base, other): |
13 |
self.file_id = file_id |
|
14 |
self.base = base |
|
15 |
self.other = other |
|
1185.12.31
by Aaron Bentley
Replaced FileCreate with TreeFileCreate, fixed revert with empty parents |
16 |
|
17 |
def is_creation(self): |
|
18 |
return False |
|
19 |
||
20 |
def is_deletion(self): |
|
21 |
return False |
|
22 |
||
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
23 |
def __eq__(self, other): |
24 |
if not isinstance(other, ApplyMerge3): |
|
25 |
return False |
|
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
26 |
return (self.base == other.base and |
27 |
self.other == other.other and self.file_id == other.file_id) |
|
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
28 |
|
29 |
def __ne__(self, other): |
|
30 |
return not (self == other) |
|
31 |
||
32 |
def apply(self, filename, conflict_handler, reverse=False): |
|
33 |
new_file = filename+".new" |
|
34 |
if not reverse: |
|
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
35 |
base = self.base |
36 |
other = self.other |
|
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
37 |
else: |
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
38 |
base = self.other |
39 |
other = self.base |
|
40 |
def get_lines(tree): |
|
41 |
if self.file_id not in tree: |
|
42 |
raise Exception("%s not in tree" % self.file_id) |
|
43 |
return () |
|
44 |
return tree.get_file(self.file_id).readlines() |
|
45 |
base_lines = get_lines(base) |
|
46 |
other_lines = get_lines(other) |
|
47 |
m3 = Merge3(base_lines, file(filename, "rb").readlines(), other_lines) |
|
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
48 |
|
49 |
new_conflicts = False |
|
50 |
output_file = file(new_file, "wb") |
|
51 |
start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE" |
|
52 |
for line in m3.merge_lines(name_a = "TREE", name_b = "MERGE-SOURCE", |
|
53 |
start_marker=start_marker): |
|
54 |
if line.startswith(start_marker): |
|
55 |
new_conflicts = True |
|
974.1.45
by aaron.bentley at utoronto
Shortened conflict markers to 7 characters, to please smerge |
56 |
output_file.write(line.replace(start_marker, '<' * 7)) |
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
57 |
else: |
58 |
output_file.write(line) |
|
59 |
output_file.close() |
|
60 |
if not new_conflicts: |
|
61 |
os.chmod(new_file, os.stat(filename).st_mode) |
|
1185.1.40
by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch. |
62 |
rename(new_file, filename) |
974.1.4
by Aaron Bentley
Implemented merge3 as the default text merge |
63 |
return
|
64 |
else: |
|
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
65 |
conflict_handler.merge_conflict(new_file, filename, base_lines, |
66 |
other_lines) |
|
493
by Martin Pool
- Merge aaron's merge command |
67 |
|
974.1.8
by Aaron Bentley
Added default backups for merge-revert |
68 |
|
69 |
class BackupBeforeChange: |
|
70 |
"""Contents-change wrapper to back up file first"""
|
|
71 |
def __init__(self, contents_change): |
|
72 |
self.contents_change = contents_change |
|
1185.12.31
by Aaron Bentley
Replaced FileCreate with TreeFileCreate, fixed revert with empty parents |
73 |
|
74 |
def is_creation(self): |
|
75 |
return self.contents_change.is_creation() |
|
76 |
||
77 |
def is_deletion(self): |
|
78 |
return self.contents_change.is_deletion() |
|
79 |
||
974.1.8
by Aaron Bentley
Added default backups for merge-revert |
80 |
def __eq__(self, other): |
81 |
if not isinstance(other, BackupBeforeChange): |
|
82 |
return False |
|
83 |
return (self.contents_change == other.contents_change) |
|
84 |
||
85 |
def __ne__(self, other): |
|
86 |
return not (self == other) |
|
87 |
||
88 |
def apply(self, filename, conflict_handler, reverse=False): |
|
89 |
backup_file(filename) |
|
90 |
self.contents_change.apply(filename, conflict_handler, reverse) |
|
91 |
||
92 |
||
493
by Martin Pool
- Merge aaron's merge command |
93 |
def invert_invent(inventory): |
94 |
invert_invent = {} |
|
974.1.19
by Aaron Bentley
Removed MergeTree.inventory |
95 |
for file_id in inventory: |
96 |
path = inventory.id2path(file_id) |
|
97 |
if path == '': |
|
98 |
path = './.' |
|
99 |
else: |
|
100 |
path = './' + path |
|
101 |
invert_invent[file_id] = path |
|
493
by Martin Pool
- Merge aaron's merge command |
102 |
return invert_invent |
103 |
||
104 |
||
105 |
def merge_flex(this, base, other, changeset_function, inventory_function, |
|
974.1.17
by Aaron Bentley
Switched to using a set of interesting file_ids instead of SourceFile attribute |
106 |
conflict_handler, merge_factory, interesting_ids): |
974.1.19
by Aaron Bentley
Removed MergeTree.inventory |
107 |
cset = changeset_function(base, other, interesting_ids) |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
108 |
new_cset = make_merge_changeset(cset, this, base, other, |
974.1.3
by Aaron Bentley
Added merge_factory parameter to merge_flex |
109 |
conflict_handler, merge_factory) |
1185.12.40
by abentley
Got even closer to standard Tree interface |
110 |
result = apply_changeset(new_cset, invert_invent(this.inventory), |
1185.12.38
by abentley
semi-broke merge |
111 |
this.basedir, conflict_handler, False) |
622
by Martin Pool
Updated merge patch from Aaron |
112 |
conflict_handler.finalize() |
113 |
return result |
|
493
by Martin Pool
- Merge aaron's merge command |
114 |
|
115 |
||
116 |
||
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
117 |
def make_merge_changeset(cset, this, base, other, |
974.1.3
by Aaron Bentley
Added merge_factory parameter to merge_flex |
118 |
conflict_handler, merge_factory): |
493
by Martin Pool
- Merge aaron's merge command |
119 |
new_cset = changeset.Changeset() |
120 |
||
121 |
for entry in cset.entries.itervalues(): |
|
122 |
if entry.is_boring(): |
|
123 |
new_cset.add_entry(entry) |
|
124 |
else: |
|
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
125 |
new_entry = make_merged_entry(entry, this, base, other, |
126 |
conflict_handler) |
|
909.1.4
by Aaron Bentley
Fixed conflict handling for missing merge targets |
127 |
new_contents = make_merged_contents(entry, this, base, other, |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
128 |
conflict_handler, |
974.1.3
by Aaron Bentley
Added merge_factory parameter to merge_flex |
129 |
merge_factory) |
850
by Martin Pool
- Merge merge updates from aaron |
130 |
new_entry.contents_change = new_contents |
131 |
new_entry.metadata_change = make_merged_metadata(entry, base, other) |
|
132 |
new_cset.add_entry(new_entry) |
|
133 |
||
493
by Martin Pool
- Merge aaron's merge command |
134 |
return new_cset |
135 |
||
974.1.22
by Aaron Bentley
Refactored code |
136 |
class ThreeWayConflict(Exception): |
137 |
def __init__(self, this, base, other): |
|
138 |
self.this = this |
|
139 |
self.base = base |
|
140 |
self.other = other |
|
141 |
msg = "Conflict merging %s %s and %s" % (this, base, other) |
|
142 |
Exception.__init__(self, msg) |
|
143 |
||
144 |
def threeway_select(this, base, other): |
|
145 |
"""Returns a value selected by the three-way algorithm.
|
|
146 |
Raises ThreewayConflict if the algorithm yields a conflict"""
|
|
147 |
if base == other: |
|
148 |
return this |
|
149 |
elif base == this: |
|
150 |
return other |
|
151 |
elif other == this: |
|
152 |
return this |
|
153 |
else: |
|
154 |
raise ThreeWayConflict(this, base, other) |
|
155 |
||
156 |
||
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
157 |
def make_merged_entry(entry, this, base, other, conflict_handler): |
909.1.2
by aaron.bentley at utoronto
Fixed rename handling in merge |
158 |
from bzrlib.trace import mutter |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
159 |
def entry_data(file_id, tree): |
160 |
assert hasattr(tree, "__contains__"), "%s" % tree |
|
1185.12.39
by abentley
Propogated has_or_had_id to Tree |
161 |
if not tree.has_or_had_id(file_id): |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
162 |
return (None, None, "") |
1185.12.40
by abentley
Got even closer to standard Tree interface |
163 |
entry = tree.inventory[file_id] |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
164 |
my_dir = tree.id2path(entry.parent_id) |
165 |
if my_dir is None: |
|
166 |
my_dir = "" |
|
167 |
return entry.name, entry.parent_id, my_dir |
|
168 |
this_name, this_parent, this_dir = entry_data(entry.id, this) |
|
169 |
base_name, base_parent, base_dir = entry_data(entry.id, base) |
|
170 |
other_name, other_parent, other_dir = entry_data(entry.id, other) |
|
909.1.2
by aaron.bentley at utoronto
Fixed rename handling in merge |
171 |
mutter("Dirs: this, base, other %r %r %r" % (this_dir, base_dir, other_dir)) |
172 |
mutter("Names: this, base, other %r %r %r" % (this_name, base_name, other_name)) |
|
974.1.22
by Aaron Bentley
Refactored code |
173 |
old_name = this_name |
174 |
try: |
|
175 |
new_name = threeway_select(this_name, base_name, other_name) |
|
176 |
except ThreeWayConflict: |
|
177 |
new_name = conflict_handler.rename_conflict(entry.id, this_name, |
|
178 |
base_name, other_name) |
|
179 |
||
180 |
old_parent = this_parent |
|
181 |
try: |
|
182 |
new_parent = threeway_select(this_parent, base_parent, other_parent) |
|
183 |
except ThreeWayConflict: |
|
184 |
new_parent = conflict_handler.move_conflict(entry.id, this_dir, |
|
185 |
base_dir, other_dir) |
|
186 |
def get_path(name, parent): |
|
974.1.47
by Aaron Bentley
Merged changes from the merge4 branch |
187 |
if name is not None: |
188 |
if name == "": |
|
189 |
assert parent is None |
|
190 |
return './.' |
|
974.1.22
by Aaron Bentley
Refactored code |
191 |
parent_dir = {this_parent: this_dir, other_parent: other_dir, |
192 |
base_parent: base_dir} |
|
193 |
directory = parent_dir[parent] |
|
194 |
return os.path.join(directory, name) |
|
195 |
else: |
|
974.1.47
by Aaron Bentley
Merged changes from the merge4 branch |
196 |
assert parent is None |
974.1.22
by Aaron Bentley
Refactored code |
197 |
return None |
198 |
||
199 |
old_path = get_path(old_name, old_parent) |
|
200 |
||
909.1.2
by aaron.bentley at utoronto
Fixed rename handling in merge |
201 |
new_entry = changeset.ChangesetEntry(entry.id, old_parent, old_path) |
974.1.22
by Aaron Bentley
Refactored code |
202 |
new_entry.new_path = get_path(new_name, new_parent) |
493
by Martin Pool
- Merge aaron's merge command |
203 |
new_entry.new_parent = new_parent |
909.1.2
by aaron.bentley at utoronto
Fixed rename handling in merge |
204 |
mutter(repr(new_entry)) |
850
by Martin Pool
- Merge merge updates from aaron |
205 |
return new_entry |
206 |
||
207 |
||
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
208 |
def make_merged_contents(entry, this, base, other, conflict_handler, |
974.1.3
by Aaron Bentley
Added merge_factory parameter to merge_flex |
209 |
merge_factory): |
850
by Martin Pool
- Merge merge updates from aaron |
210 |
contents = entry.contents_change |
211 |
if contents is None: |
|
212 |
return None |
|
1185.12.36
by abentley
Removed all remaining use of readonly_path |
213 |
if entry.id in this: |
214 |
this_path = this.id2abspath(entry.id) |
|
215 |
else: |
|
216 |
this_path = None |
|
974.1.3
by Aaron Bentley
Added merge_factory parameter to merge_flex |
217 |
def make_merge(): |
850
by Martin Pool
- Merge merge updates from aaron |
218 |
if this_path is None: |
974.1.20
by Aaron Bentley
Eliminated ThreeWayInventory |
219 |
return conflict_handler.missing_for_merge(entry.id, |
220 |
other.id2path(entry.id)) |
|
974.1.23
by Aaron Bentley
Avoided unnecessary temp files |
221 |
return merge_factory(entry.id, base, other) |
850
by Martin Pool
- Merge merge updates from aaron |
222 |
|
223 |
if isinstance(contents, changeset.ReplaceContents): |
|
224 |
if contents.old_contents is None and contents.new_contents is None: |
|
225 |
return None |
|
226 |
if contents.new_contents is None: |
|
1185.12.30
by Aaron Bentley
Moved all fooCreate into get_contents |
227 |
this_contents = get_contents(this, entry.id) |
1092.2.24
by Robert Collins
merge from martins newformat branch - brings in transport abstraction |
228 |
if this_path is not None and bzrlib.osutils.lexists(this_path): |
1185.10.8
by Aaron Bentley
Conflict handling where OTHER is deleted |
229 |
if this_contents != contents.old_contents: |
230 |
return conflict_handler.rem_contents_conflict(this_path, |
|
231 |
this_contents, contents.old_contents) |
|
850
by Martin Pool
- Merge merge updates from aaron |
232 |
return contents |
233 |
else: |
|
234 |
return None |
|
235 |
elif contents.old_contents is None: |
|
1092.2.24
by Robert Collins
merge from martins newformat branch - brings in transport abstraction |
236 |
if this_path is None or not bzrlib.osutils.lexists(this_path): |
850
by Martin Pool
- Merge merge updates from aaron |
237 |
return contents |
238 |
else: |
|
1185.12.30
by Aaron Bentley
Moved all fooCreate into get_contents |
239 |
this_contents = get_contents(this, entry.id) |
850
by Martin Pool
- Merge merge updates from aaron |
240 |
if this_contents == contents.new_contents: |
241 |
return None |
|
242 |
else: |
|
243 |
conflict_handler.new_contents_conflict(this_path, |
|
1185.12.36
by abentley
Removed all remaining use of readonly_path |
244 |
other_contents) |
1185.12.31
by Aaron Bentley
Replaced FileCreate with TreeFileCreate, fixed revert with empty parents |
245 |
elif isinstance(contents.old_contents, changeset.TreeFileCreate) and \ |
246 |
isinstance(contents.new_contents, changeset.TreeFileCreate): |
|
1448
by Robert Collins
revert symlinks correctly |
247 |
return make_merge() |
850
by Martin Pool
- Merge merge updates from aaron |
248 |
else: |
1185.12.33
by Aaron Bentley
Fixed symlink reverting |
249 |
this_contents = get_contents(this, entry.id) |
250 |
if this_contents == contents.old_contents: |
|
251 |
return contents |
|
252 |
elif this_contents == contents.new_contents: |
|
253 |
return None |
|
254 |
elif contents.old_contents == contents.new_contents: |
|
255 |
return None |
|
256 |
else: |
|
1185.12.34
by Aaron Bentley
Added symlink three-way tests |
257 |
conflict_handler.threeway_contents_conflict(this_path, |
258 |
this_contents, contents.old_contents, |
|
259 |
contents.new_contents) |
|
1185.12.33
by Aaron Bentley
Fixed symlink reverting |
260 |
|
850
by Martin Pool
- Merge merge updates from aaron |
261 |
|
262 |
def make_merged_metadata(entry, base, other): |
|
1398
by Robert Collins
integrate in Gustavos x-bit patch |
263 |
metadata = entry.metadata_change |
264 |
if metadata is None: |
|
265 |
return None |
|
1185.12.36
by abentley
Removed all remaining use of readonly_path |
266 |
assert isinstance(metadata, changeset.ChangeExecFlag) |
267 |
if metadata.new_exec_flag is None: |
|
268 |
return None |
|
269 |
elif metadata.old_exec_flag is None: |
|
270 |
return metadata |
|
271 |
else: |
|
272 |
return ExecFlagMerge(base, other, entry.id) |
|
493
by Martin Pool
- Merge aaron's merge command |
273 |
|
274 |
||
1434
by Robert Collins
merge Gustavos executable2 patch |
275 |
class ExecFlagMerge(object): |
1185.12.36
by abentley
Removed all remaining use of readonly_path |
276 |
def __init__(self, base_tree, other_tree, file_id): |
277 |
self.base_tree = base_tree |
|
278 |
self.other_tree = other_tree |
|
279 |
self.file_id = file_id |
|
493
by Martin Pool
- Merge aaron's merge command |
280 |
|
281 |
def apply(self, filename, conflict_handler, reverse=False): |
|
282 |
if not reverse: |
|
1185.12.36
by abentley
Removed all remaining use of readonly_path |
283 |
base = self.base_tree |
284 |
other = self.other_tree |
|
493
by Martin Pool
- Merge aaron's merge command |
285 |
else: |
1185.12.36
by abentley
Removed all remaining use of readonly_path |
286 |
base = self.other_tree |
287 |
other = self.base_tree |
|
288 |
base_exec_flag = base.is_executable(self.file_id) |
|
289 |
other_exec_flag = other.is_executable(self.file_id) |
|
1434
by Robert Collins
merge Gustavos executable2 patch |
290 |
this_mode = os.stat(filename).st_mode |
291 |
this_exec_flag = bool(this_mode & 0111) |
|
292 |
if (base_exec_flag != other_exec_flag and |
|
293 |
this_exec_flag != other_exec_flag): |
|
294 |
assert this_exec_flag == base_exec_flag |
|
295 |
current_mode = os.stat(filename).st_mode |
|
296 |
if other_exec_flag: |
|
297 |
umask = os.umask(0) |
|
298 |
os.umask(umask) |
|
299 |
to_mode = current_mode | (0100 & ~umask) |
|
300 |
# Enable x-bit for others only if they can read it.
|
|
301 |
if current_mode & 0004: |
|
302 |
to_mode |= 0001 & ~umask |
|
303 |
if current_mode & 0040: |
|
304 |
to_mode |= 0010 & ~umask |
|
305 |
else: |
|
306 |
to_mode = current_mode & ~0111 |
|
307 |
os.chmod(filename, to_mode) |
|
308 |