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