133 |
133 |
134 |
134 |
135 def _findtool(ui, tool): |
135 def _findtool(ui, tool): |
136 if tool in internals: |
136 if tool in internals: |
137 return tool |
137 return tool |
138 cmd = _toolstr(ui, tool, "executable", tool) |
138 cmd = _toolstr(ui, tool, b"executable", tool) |
139 if cmd.startswith('python:'): |
139 if cmd.startswith(b'python:'): |
140 return cmd |
140 return cmd |
141 return findexternaltool(ui, tool) |
141 return findexternaltool(ui, tool) |
142 |
142 |
143 |
143 |
144 def _quotetoolpath(cmd): |
144 def _quotetoolpath(cmd): |
145 if cmd.startswith('python:'): |
145 if cmd.startswith(b'python:'): |
146 return cmd |
146 return cmd |
147 return procutil.shellquote(cmd) |
147 return procutil.shellquote(cmd) |
148 |
148 |
149 |
149 |
150 def findexternaltool(ui, tool): |
150 def findexternaltool(ui, tool): |
151 for kn in ("regkey", "regkeyalt"): |
151 for kn in (b"regkey", b"regkeyalt"): |
152 k = _toolstr(ui, tool, kn) |
152 k = _toolstr(ui, tool, kn) |
153 if not k: |
153 if not k: |
154 continue |
154 continue |
155 p = util.lookupreg(k, _toolstr(ui, tool, "regname")) |
155 p = util.lookupreg(k, _toolstr(ui, tool, b"regname")) |
156 if p: |
156 if p: |
157 p = procutil.findexe(p + _toolstr(ui, tool, "regappend", "")) |
157 p = procutil.findexe(p + _toolstr(ui, tool, b"regappend", b"")) |
158 if p: |
158 if p: |
159 return p |
159 return p |
160 exe = _toolstr(ui, tool, "executable", tool) |
160 exe = _toolstr(ui, tool, b"executable", tool) |
161 return procutil.findexe(util.expandpath(exe)) |
161 return procutil.findexe(util.expandpath(exe)) |
162 |
162 |
163 |
163 |
164 def _picktool(repo, ui, path, binary, symlink, changedelete): |
164 def _picktool(repo, ui, path, binary, symlink, changedelete): |
165 strictcheck = ui.configbool('merge', 'strict-capability-check') |
165 strictcheck = ui.configbool(b'merge', b'strict-capability-check') |
166 |
166 |
167 def hascapability(tool, capability, strict=False): |
167 def hascapability(tool, capability, strict=False): |
168 if tool in internals: |
168 if tool in internals: |
169 return strict and internals[tool].capabilities.get(capability) |
169 return strict and internals[tool].capabilities.get(capability) |
170 return _toolbool(ui, tool, capability) |
170 return _toolbool(ui, tool, capability) |
173 return tool in internals and internals[tool].mergetype == nomerge |
173 return tool in internals and internals[tool].mergetype == nomerge |
174 |
174 |
175 def check(tool, pat, symlink, binary, changedelete): |
175 def check(tool, pat, symlink, binary, changedelete): |
176 tmsg = tool |
176 tmsg = tool |
177 if pat: |
177 if pat: |
178 tmsg = _("%s (for pattern %s)") % (tool, pat) |
178 tmsg = _(b"%s (for pattern %s)") % (tool, pat) |
179 if not _findtool(ui, tool): |
179 if not _findtool(ui, tool): |
180 if pat: # explicitly requested tool deserves a warning |
180 if pat: # explicitly requested tool deserves a warning |
181 ui.warn(_("couldn't find merge tool %s\n") % tmsg) |
181 ui.warn(_(b"couldn't find merge tool %s\n") % tmsg) |
182 else: # configured but non-existing tools are more silent |
182 else: # configured but non-existing tools are more silent |
183 ui.note(_("couldn't find merge tool %s\n") % tmsg) |
183 ui.note(_(b"couldn't find merge tool %s\n") % tmsg) |
184 elif symlink and not hascapability(tool, "symlink", strictcheck): |
184 elif symlink and not hascapability(tool, b"symlink", strictcheck): |
185 ui.warn(_("tool %s can't handle symlinks\n") % tmsg) |
185 ui.warn(_(b"tool %s can't handle symlinks\n") % tmsg) |
186 elif binary and not hascapability(tool, "binary", strictcheck): |
186 elif binary and not hascapability(tool, b"binary", strictcheck): |
187 ui.warn(_("tool %s can't handle binary\n") % tmsg) |
187 ui.warn(_(b"tool %s can't handle binary\n") % tmsg) |
188 elif changedelete and not supportscd(tool): |
188 elif changedelete and not supportscd(tool): |
189 # the nomerge tools are the only tools that support change/delete |
189 # the nomerge tools are the only tools that support change/delete |
190 # conflicts |
190 # conflicts |
191 pass |
191 pass |
192 elif not procutil.gui() and _toolbool(ui, tool, "gui"): |
192 elif not procutil.gui() and _toolbool(ui, tool, b"gui"): |
193 ui.warn(_("tool %s requires a GUI\n") % tmsg) |
193 ui.warn(_(b"tool %s requires a GUI\n") % tmsg) |
194 else: |
194 else: |
195 return True |
195 return True |
196 return False |
196 return False |
197 |
197 |
198 # internal config: ui.forcemerge |
198 # internal config: ui.forcemerge |
199 # forcemerge comes from command line arguments, highest priority |
199 # forcemerge comes from command line arguments, highest priority |
200 force = ui.config('ui', 'forcemerge') |
200 force = ui.config(b'ui', b'forcemerge') |
201 if force: |
201 if force: |
202 toolpath = _findtool(ui, force) |
202 toolpath = _findtool(ui, force) |
203 if changedelete and not supportscd(toolpath): |
203 if changedelete and not supportscd(toolpath): |
204 return ":prompt", None |
204 return b":prompt", None |
205 else: |
205 else: |
206 if toolpath: |
206 if toolpath: |
207 return (force, _quotetoolpath(toolpath)) |
207 return (force, _quotetoolpath(toolpath)) |
208 else: |
208 else: |
209 # mimic HGMERGE if given tool not found |
209 # mimic HGMERGE if given tool not found |
210 return (force, force) |
210 return (force, force) |
211 |
211 |
212 # HGMERGE takes next precedence |
212 # HGMERGE takes next precedence |
213 hgmerge = encoding.environ.get("HGMERGE") |
213 hgmerge = encoding.environ.get(b"HGMERGE") |
214 if hgmerge: |
214 if hgmerge: |
215 if changedelete and not supportscd(hgmerge): |
215 if changedelete and not supportscd(hgmerge): |
216 return ":prompt", None |
216 return b":prompt", None |
217 else: |
217 else: |
218 return (hgmerge, hgmerge) |
218 return (hgmerge, hgmerge) |
219 |
219 |
220 # then patterns |
220 # then patterns |
221 |
221 |
222 # whether binary capability should be checked strictly |
222 # whether binary capability should be checked strictly |
223 binarycap = binary and strictcheck |
223 binarycap = binary and strictcheck |
224 |
224 |
225 for pat, tool in ui.configitems("merge-patterns"): |
225 for pat, tool in ui.configitems(b"merge-patterns"): |
226 mf = match.match(repo.root, '', [pat]) |
226 mf = match.match(repo.root, b'', [pat]) |
227 if mf(path) and check(tool, pat, symlink, binarycap, changedelete): |
227 if mf(path) and check(tool, pat, symlink, binarycap, changedelete): |
228 if binary and not hascapability(tool, "binary", strict=True): |
228 if binary and not hascapability(tool, b"binary", strict=True): |
229 ui.warn( |
229 ui.warn( |
230 _( |
230 _( |
231 "warning: check merge-patterns configurations," |
231 b"warning: check merge-patterns configurations," |
232 " if %r for binary file %r is unintentional\n" |
232 b" if %r for binary file %r is unintentional\n" |
233 "(see 'hg help merge-tools'" |
233 b"(see 'hg help merge-tools'" |
234 " for binary files capability)\n" |
234 b" for binary files capability)\n" |
235 ) |
235 ) |
236 % (pycompat.bytestr(tool), pycompat.bytestr(path)) |
236 % (pycompat.bytestr(tool), pycompat.bytestr(path)) |
237 ) |
237 ) |
238 toolpath = _findtool(ui, tool) |
238 toolpath = _findtool(ui, tool) |
239 return (tool, _quotetoolpath(toolpath)) |
239 return (tool, _quotetoolpath(toolpath)) |
240 |
240 |
241 # then merge tools |
241 # then merge tools |
242 tools = {} |
242 tools = {} |
243 disabled = set() |
243 disabled = set() |
244 for k, v in ui.configitems("merge-tools"): |
244 for k, v in ui.configitems(b"merge-tools"): |
245 t = k.split('.')[0] |
245 t = k.split(b'.')[0] |
246 if t not in tools: |
246 if t not in tools: |
247 tools[t] = int(_toolstr(ui, t, "priority")) |
247 tools[t] = int(_toolstr(ui, t, b"priority")) |
248 if _toolbool(ui, t, "disabled"): |
248 if _toolbool(ui, t, b"disabled"): |
249 disabled.add(t) |
249 disabled.add(t) |
250 names = tools.keys() |
250 names = tools.keys() |
251 tools = sorted( |
251 tools = sorted( |
252 [(-p, tool) for tool, p in tools.items() if tool not in disabled] |
252 [(-p, tool) for tool, p in tools.items() if tool not in disabled] |
253 ) |
253 ) |
254 uimerge = ui.config("ui", "merge") |
254 uimerge = ui.config(b"ui", b"merge") |
255 if uimerge: |
255 if uimerge: |
256 # external tools defined in uimerge won't be able to handle |
256 # external tools defined in uimerge won't be able to handle |
257 # change/delete conflicts |
257 # change/delete conflicts |
258 if check(uimerge, path, symlink, binary, changedelete): |
258 if check(uimerge, path, symlink, binary, changedelete): |
259 if uimerge not in names and not changedelete: |
259 if uimerge not in names and not changedelete: |
260 return (uimerge, uimerge) |
260 return (uimerge, uimerge) |
261 tools.insert(0, (None, uimerge)) # highest priority |
261 tools.insert(0, (None, uimerge)) # highest priority |
262 tools.append((None, "hgmerge")) # the old default, if found |
262 tools.append((None, b"hgmerge")) # the old default, if found |
263 for p, t in tools: |
263 for p, t in tools: |
264 if check(t, None, symlink, binary, changedelete): |
264 if check(t, None, symlink, binary, changedelete): |
265 toolpath = _findtool(ui, t) |
265 toolpath = _findtool(ui, t) |
266 return (t, _quotetoolpath(toolpath)) |
266 return (t, _quotetoolpath(toolpath)) |
267 |
267 |
268 # internal merge or prompt as last resort |
268 # internal merge or prompt as last resort |
269 if symlink or binary or changedelete: |
269 if symlink or binary or changedelete: |
270 if not changedelete and len(tools): |
270 if not changedelete and len(tools): |
271 # any tool is rejected by capability for symlink or binary |
271 # any tool is rejected by capability for symlink or binary |
272 ui.warn(_("no tool found to merge %s\n") % path) |
272 ui.warn(_(b"no tool found to merge %s\n") % path) |
273 return ":prompt", None |
273 return b":prompt", None |
274 return ":merge", None |
274 return b":merge", None |
275 |
275 |
276 |
276 |
277 def _eoltype(data): |
277 def _eoltype(data): |
278 "Guess the EOL type of a file" |
278 b"Guess the EOL type of a file" |
279 if '\0' in data: # binary |
279 if b'\0' in data: # binary |
280 return None |
280 return None |
281 if '\r\n' in data: # Windows |
281 if b'\r\n' in data: # Windows |
282 return '\r\n' |
282 return b'\r\n' |
283 if '\r' in data: # Old Mac |
283 if b'\r' in data: # Old Mac |
284 return '\r' |
284 return b'\r' |
285 if '\n' in data: # UNIX |
285 if b'\n' in data: # UNIX |
286 return '\n' |
286 return b'\n' |
287 return None # unknown |
287 return None # unknown |
288 |
288 |
289 |
289 |
290 def _matcheol(file, back): |
290 def _matcheol(file, back): |
291 "Convert EOL markers in a file to match origfile" |
291 b"Convert EOL markers in a file to match origfile" |
292 tostyle = _eoltype(back.data()) # No repo.wread filters? |
292 tostyle = _eoltype(back.data()) # No repo.wread filters? |
293 if tostyle: |
293 if tostyle: |
294 data = util.readfile(file) |
294 data = util.readfile(file) |
295 style = _eoltype(data) |
295 style = _eoltype(data) |
296 if style: |
296 if style: |
297 newdata = data.replace(style, tostyle) |
297 newdata = data.replace(style, tostyle) |
298 if newdata != data: |
298 if newdata != data: |
299 util.writefile(file, newdata) |
299 util.writefile(file, newdata) |
300 |
300 |
301 |
301 |
302 @internaltool('prompt', nomerge) |
302 @internaltool(b'prompt', nomerge) |
303 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
303 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
304 """Asks the user which of the local `p1()` or the other `p2()` version to |
304 """Asks the user which of the local `p1()` or the other `p2()` version to |
305 keep as the merged version.""" |
305 keep as the merged version.""" |
306 ui = repo.ui |
306 ui = repo.ui |
307 fd = fcd.path() |
307 fd = fcd.path() |
309 |
309 |
310 # Avoid prompting during an in-memory merge since it doesn't support merge |
310 # Avoid prompting during an in-memory merge since it doesn't support merge |
311 # conflicts. |
311 # conflicts. |
312 if fcd.changectx().isinmemory(): |
312 if fcd.changectx().isinmemory(): |
313 raise error.InMemoryMergeConflictsError( |
313 raise error.InMemoryMergeConflictsError( |
314 'in-memory merge does not ' 'support file conflicts' |
314 b'in-memory merge does not ' b'support file conflicts' |
315 ) |
315 ) |
316 |
316 |
317 prompts = partextras(labels) |
317 prompts = partextras(labels) |
318 prompts['fd'] = uipathfn(fd) |
318 prompts[b'fd'] = uipathfn(fd) |
319 try: |
319 try: |
320 if fco.isabsent(): |
320 if fco.isabsent(): |
321 index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2) |
321 index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2) |
322 choice = ['local', 'other', 'unresolved'][index] |
322 choice = [b'local', b'other', b'unresolved'][index] |
323 elif fcd.isabsent(): |
323 elif fcd.isabsent(): |
324 index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2) |
324 index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2) |
325 choice = ['other', 'local', 'unresolved'][index] |
325 choice = [b'other', b'local', b'unresolved'][index] |
326 else: |
326 else: |
327 # IMPORTANT: keep the last line of this prompt ("What do you want to |
327 # IMPORTANT: keep the last line of this prompt ("What do you want to |
328 # do?") very short, see comment next to _localchangedotherdeletedmsg |
328 # do?") very short, see comment next to _localchangedotherdeletedmsg |
329 # at the top of the file for details. |
329 # at the top of the file for details. |
330 index = ui.promptchoice( |
330 index = ui.promptchoice( |
331 _( |
331 _( |
332 "file '%(fd)s' needs to be resolved.\n" |
332 b"file '%(fd)s' needs to be resolved.\n" |
333 "You can keep (l)ocal%(l)s, take (o)ther%(o)s, or leave " |
333 b"You can keep (l)ocal%(l)s, take (o)ther%(o)s, or leave " |
334 "(u)nresolved.\n" |
334 b"(u)nresolved.\n" |
335 "What do you want to do?" |
335 b"What do you want to do?" |
336 "$$ &Local $$ &Other $$ &Unresolved" |
336 b"$$ &Local $$ &Other $$ &Unresolved" |
337 ) |
337 ) |
338 % prompts, |
338 % prompts, |
339 2, |
339 2, |
340 ) |
340 ) |
341 choice = ['local', 'other', 'unresolved'][index] |
341 choice = [b'local', b'other', b'unresolved'][index] |
342 |
342 |
343 if choice == 'other': |
343 if choice == b'other': |
344 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
344 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
345 elif choice == 'local': |
345 elif choice == b'local': |
346 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
346 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
347 elif choice == 'unresolved': |
347 elif choice == b'unresolved': |
348 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
348 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
349 except error.ResponseExpected: |
349 except error.ResponseExpected: |
350 ui.write("\n") |
350 ui.write(b"\n") |
351 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
351 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels) |
352 |
352 |
353 |
353 |
354 @internaltool('local', nomerge) |
354 @internaltool(b'local', nomerge) |
355 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
355 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
356 """Uses the local `p1()` version of files as the merged version.""" |
356 """Uses the local `p1()` version of files as the merged version.""" |
357 return 0, fcd.isabsent() |
357 return 0, fcd.isabsent() |
358 |
358 |
359 |
359 |
360 @internaltool('other', nomerge) |
360 @internaltool(b'other', nomerge) |
361 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
361 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): |
362 """Uses the other `p2()` version of files as the merged version.""" |
362 """Uses the other `p2()` version of files as the merged version.""" |
363 if fco.isabsent(): |
363 if fco.isabsent(): |
364 # local changed, remote deleted -- 'deleted' picked |
364 # local changed, remote deleted -- 'deleted' picked |
365 _underlyingfctxifabsent(fcd).remove() |
365 _underlyingfctxifabsent(fcd).remove() |
399 return 1 |
399 return 1 |
400 unused, unused, unused, back = files |
400 unused, unused, unused, back = files |
401 |
401 |
402 ui = repo.ui |
402 ui = repo.ui |
403 |
403 |
404 validkeep = ['keep', 'keep-merge3'] |
404 validkeep = [b'keep', b'keep-merge3'] |
405 |
405 |
406 # do we attempt to simplemerge first? |
406 # do we attempt to simplemerge first? |
407 try: |
407 try: |
408 premerge = _toolbool(ui, tool, "premerge", not binary) |
408 premerge = _toolbool(ui, tool, b"premerge", not binary) |
409 except error.ConfigError: |
409 except error.ConfigError: |
410 premerge = _toolstr(ui, tool, "premerge", "").lower() |
410 premerge = _toolstr(ui, tool, b"premerge", b"").lower() |
411 if premerge not in validkeep: |
411 if premerge not in validkeep: |
412 _valid = ', '.join(["'" + v + "'" for v in validkeep]) |
412 _valid = b', '.join([b"'" + v + b"'" for v in validkeep]) |
413 raise error.ConfigError( |
413 raise error.ConfigError( |
414 _("%s.premerge not valid " "('%s' is neither boolean nor %s)") |
414 _(b"%s.premerge not valid " b"('%s' is neither boolean nor %s)") |
415 % (tool, premerge, _valid) |
415 % (tool, premerge, _valid) |
416 ) |
416 ) |
417 |
417 |
418 if premerge: |
418 if premerge: |
419 if premerge == 'keep-merge3': |
419 if premerge == b'keep-merge3': |
420 if not labels: |
420 if not labels: |
421 labels = _defaultconflictlabels |
421 labels = _defaultconflictlabels |
422 if len(labels) < 3: |
422 if len(labels) < 3: |
423 labels.append('base') |
423 labels.append(b'base') |
424 r = simplemerge.simplemerge(ui, fcd, fca, fco, quiet=True, label=labels) |
424 r = simplemerge.simplemerge(ui, fcd, fca, fco, quiet=True, label=labels) |
425 if not r: |
425 if not r: |
426 ui.debug(" premerge successful\n") |
426 ui.debug(b" premerge successful\n") |
427 return 0 |
427 return 0 |
428 if premerge not in validkeep: |
428 if premerge not in validkeep: |
429 # restore from backup and try again |
429 # restore from backup and try again |
430 _restorebackup(fcd, back) |
430 _restorebackup(fcd, back) |
431 return 1 # continue merging |
431 return 1 # continue merging |
463 r = simplemerge.simplemerge(ui, fcd, fca, fco, label=labels, mode=mode) |
463 r = simplemerge.simplemerge(ui, fcd, fca, fco, label=labels, mode=mode) |
464 return True, r, False |
464 return True, r, False |
465 |
465 |
466 |
466 |
467 @internaltool( |
467 @internaltool( |
468 'union', |
468 b'union', |
469 fullmerge, |
469 fullmerge, |
470 _( |
470 _( |
471 "warning: conflicts while merging %s! " |
471 b"warning: conflicts while merging %s! " |
472 "(edit, then use 'hg resolve --mark')\n" |
472 b"(edit, then use 'hg resolve --mark')\n" |
473 ), |
473 ), |
474 precheck=_mergecheck, |
474 precheck=_mergecheck, |
475 ) |
475 ) |
476 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
476 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
477 """ |
477 """ |
478 Uses the internal non-interactive simple merge algorithm for merging |
478 Uses the internal non-interactive simple merge algorithm for merging |
479 files. It will use both left and right sides for conflict regions. |
479 files. It will use both left and right sides for conflict regions. |
480 No markers are inserted.""" |
480 No markers are inserted.""" |
481 return _merge( |
481 return _merge( |
482 repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, 'union' |
482 repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, b'union' |
483 ) |
483 ) |
484 |
484 |
485 |
485 |
486 @internaltool( |
486 @internaltool( |
487 'merge', |
487 b'merge', |
488 fullmerge, |
488 fullmerge, |
489 _( |
489 _( |
490 "warning: conflicts while merging %s! " |
490 b"warning: conflicts while merging %s! " |
491 "(edit, then use 'hg resolve --mark')\n" |
491 b"(edit, then use 'hg resolve --mark')\n" |
492 ), |
492 ), |
493 precheck=_mergecheck, |
493 precheck=_mergecheck, |
494 ) |
494 ) |
495 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
495 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
496 """ |
496 """ |
497 Uses the internal non-interactive simple merge algorithm for merging |
497 Uses the internal non-interactive simple merge algorithm for merging |
498 files. It will fail if there are any conflicts and leave markers in |
498 files. It will fail if there are any conflicts and leave markers in |
499 the partially merged file. Markers will have two sections, one for each side |
499 the partially merged file. Markers will have two sections, one for each side |
500 of merge.""" |
500 of merge.""" |
501 return _merge( |
501 return _merge( |
502 repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, 'merge' |
502 repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, b'merge' |
503 ) |
503 ) |
504 |
504 |
505 |
505 |
506 @internaltool( |
506 @internaltool( |
507 'merge3', |
507 b'merge3', |
508 fullmerge, |
508 fullmerge, |
509 _( |
509 _( |
510 "warning: conflicts while merging %s! " |
510 b"warning: conflicts while merging %s! " |
511 "(edit, then use 'hg resolve --mark')\n" |
511 b"(edit, then use 'hg resolve --mark')\n" |
512 ), |
512 ), |
513 precheck=_mergecheck, |
513 precheck=_mergecheck, |
514 ) |
514 ) |
515 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
515 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
516 """ |
516 """ |
545 repo.ui, fcd, fca, fco, label=labels, localorother=localorother |
545 repo.ui, fcd, fca, fco, label=labels, localorother=localorother |
546 ) |
546 ) |
547 return True, r |
547 return True, r |
548 |
548 |
549 |
549 |
550 @internaltool('merge-local', mergeonly, precheck=_mergecheck) |
550 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck) |
551 def _imergelocal(*args, **kwargs): |
551 def _imergelocal(*args, **kwargs): |
552 """ |
552 """ |
553 Like :merge, but resolve all conflicts non-interactively in favor |
553 Like :merge, but resolve all conflicts non-interactively in favor |
554 of the local `p1()` changes.""" |
554 of the local `p1()` changes.""" |
555 success, status = _imergeauto(localorother='local', *args, **kwargs) |
555 success, status = _imergeauto(localorother=b'local', *args, **kwargs) |
556 return success, status, False |
556 return success, status, False |
557 |
557 |
558 |
558 |
559 @internaltool('merge-other', mergeonly, precheck=_mergecheck) |
559 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck) |
560 def _imergeother(*args, **kwargs): |
560 def _imergeother(*args, **kwargs): |
561 """ |
561 """ |
562 Like :merge, but resolve all conflicts non-interactively in favor |
562 Like :merge, but resolve all conflicts non-interactively in favor |
563 of the other `p2()` changes.""" |
563 of the other `p2()` changes.""" |
564 success, status = _imergeauto(localorother='other', *args, **kwargs) |
564 success, status = _imergeauto(localorother=b'other', *args, **kwargs) |
565 return success, status, False |
565 return success, status, False |
566 |
566 |
567 |
567 |
568 @internaltool( |
568 @internaltool( |
569 'tagmerge', |
569 b'tagmerge', |
570 mergeonly, |
570 mergeonly, |
571 _( |
571 _( |
572 "automatic tag merging of %s failed! " |
572 b"automatic tag merging of %s failed! " |
573 "(use 'hg resolve --tool :merge' or another merge " |
573 b"(use 'hg resolve --tool :merge' or another merge " |
574 "tool of your choice)\n" |
574 b"tool of your choice)\n" |
575 ), |
575 ), |
576 ) |
576 ) |
577 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
577 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
578 """ |
578 """ |
579 Uses the internal tag merge algorithm (experimental). |
579 Uses the internal tag merge algorithm (experimental). |
580 """ |
580 """ |
581 success, status = tagmerge.merge(repo, fcd, fco, fca) |
581 success, status = tagmerge.merge(repo, fcd, fco, fca) |
582 return success, status, False |
582 return success, status, False |
583 |
583 |
584 |
584 |
585 @internaltool('dump', fullmerge, binary=True, symlink=True) |
585 @internaltool(b'dump', fullmerge, binary=True, symlink=True) |
586 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
586 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
587 """ |
587 """ |
588 Creates three versions of the files to merge, containing the |
588 Creates three versions of the files to merge, containing the |
589 contents of local, other and base. These files can then be used to |
589 contents of local, other and base. These files can then be used to |
590 perform a merge manually. If the file to be merged is named |
590 perform a merge manually. If the file to be merged is named |
629 # raises the question of what to do if the user only partially resolves the |
629 # raises the question of what to do if the user only partially resolves the |
630 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/ |
630 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/ |
631 # directory and tell the user how to get it is my best idea, but it's |
631 # directory and tell the user how to get it is my best idea, but it's |
632 # clunky.) |
632 # clunky.) |
633 raise error.InMemoryMergeConflictsError( |
633 raise error.InMemoryMergeConflictsError( |
634 'in-memory merge does not support ' 'external merge tools' |
634 b'in-memory merge does not support ' b'external merge tools' |
635 ) |
635 ) |
636 |
636 |
637 |
637 |
638 def _describemerge(ui, repo, mynode, fcl, fcb, fco, env, toolpath, args): |
638 def _describemerge(ui, repo, mynode, fcl, fcb, fco, env, toolpath, args): |
639 tmpl = ui.config('ui', 'pre-merge-tool-output-template') |
639 tmpl = ui.config(b'ui', b'pre-merge-tool-output-template') |
640 if not tmpl: |
640 if not tmpl: |
641 return |
641 return |
642 |
642 |
643 mappingdict = templateutil.mappingdict |
643 mappingdict = templateutil.mappingdict |
644 props = { |
644 props = { |
645 'ctx': fcl.changectx(), |
645 b'ctx': fcl.changectx(), |
646 'node': hex(mynode), |
646 b'node': hex(mynode), |
647 'path': fcl.path(), |
647 b'path': fcl.path(), |
648 'local': mappingdict( |
648 b'local': mappingdict( |
649 { |
649 { |
650 'ctx': fcl.changectx(), |
650 b'ctx': fcl.changectx(), |
651 'fctx': fcl, |
651 b'fctx': fcl, |
652 'node': hex(mynode), |
652 b'node': hex(mynode), |
653 'name': _('local'), |
653 b'name': _(b'local'), |
654 'islink': 'l' in fcl.flags(), |
654 b'islink': b'l' in fcl.flags(), |
655 'label': env['HG_MY_LABEL'], |
655 b'label': env[b'HG_MY_LABEL'], |
656 } |
656 } |
657 ), |
657 ), |
658 'base': mappingdict( |
658 b'base': mappingdict( |
659 { |
659 { |
660 'ctx': fcb.changectx(), |
660 b'ctx': fcb.changectx(), |
661 'fctx': fcb, |
661 b'fctx': fcb, |
662 'name': _('base'), |
662 b'name': _(b'base'), |
663 'islink': 'l' in fcb.flags(), |
663 b'islink': b'l' in fcb.flags(), |
664 'label': env['HG_BASE_LABEL'], |
664 b'label': env[b'HG_BASE_LABEL'], |
665 } |
665 } |
666 ), |
666 ), |
667 'other': mappingdict( |
667 b'other': mappingdict( |
668 { |
668 { |
669 'ctx': fco.changectx(), |
669 b'ctx': fco.changectx(), |
670 'fctx': fco, |
670 b'fctx': fco, |
671 'name': _('other'), |
671 b'name': _(b'other'), |
672 'islink': 'l' in fco.flags(), |
672 b'islink': b'l' in fco.flags(), |
673 'label': env['HG_OTHER_LABEL'], |
673 b'label': env[b'HG_OTHER_LABEL'], |
674 } |
674 } |
675 ), |
675 ), |
676 'toolpath': toolpath, |
676 b'toolpath': toolpath, |
677 'toolargs': args, |
677 b'toolargs': args, |
678 } |
678 } |
679 |
679 |
680 # TODO: make all of this something that can be specified on a per-tool basis |
680 # TODO: make all of this something that can be specified on a per-tool basis |
681 tmpl = templater.unquotestring(tmpl) |
681 tmpl = templater.unquotestring(tmpl) |
682 |
682 |
692 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
692 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): |
693 tool, toolpath, binary, symlink, scriptfn = toolconf |
693 tool, toolpath, binary, symlink, scriptfn = toolconf |
694 uipathfn = scmutil.getuipathfn(repo) |
694 uipathfn = scmutil.getuipathfn(repo) |
695 if fcd.isabsent() or fco.isabsent(): |
695 if fcd.isabsent() or fco.isabsent(): |
696 repo.ui.warn( |
696 repo.ui.warn( |
697 _('warning: %s cannot merge change/delete conflict ' 'for %s\n') |
697 _(b'warning: %s cannot merge change/delete conflict ' b'for %s\n') |
698 % (tool, uipathfn(fcd.path())) |
698 % (tool, uipathfn(fcd.path())) |
699 ) |
699 ) |
700 return False, 1, None |
700 return False, 1, None |
701 unused, unused, unused, back = files |
701 unused, unused, unused, back = files |
702 localpath = _workingpath(repo, fcd) |
702 localpath = _workingpath(repo, fcd) |
703 args = _toolstr(repo.ui, tool, "args") |
703 args = _toolstr(repo.ui, tool, b"args") |
704 |
704 |
705 with _maketempfiles( |
705 with _maketempfiles( |
706 repo, fco, fca, repo.wvfs.join(back.path()), "$output" in args |
706 repo, fco, fca, repo.wvfs.join(back.path()), b"$output" in args |
707 ) as temppaths: |
707 ) as temppaths: |
708 basepath, otherpath, localoutputpath = temppaths |
708 basepath, otherpath, localoutputpath = temppaths |
709 outpath = "" |
709 outpath = b"" |
710 mylabel, otherlabel = labels[:2] |
710 mylabel, otherlabel = labels[:2] |
711 if len(labels) >= 3: |
711 if len(labels) >= 3: |
712 baselabel = labels[2] |
712 baselabel = labels[2] |
713 else: |
713 else: |
714 baselabel = 'base' |
714 baselabel = b'base' |
715 env = { |
715 env = { |
716 'HG_FILE': fcd.path(), |
716 b'HG_FILE': fcd.path(), |
717 'HG_MY_NODE': short(mynode), |
717 b'HG_MY_NODE': short(mynode), |
718 'HG_OTHER_NODE': short(fco.changectx().node()), |
718 b'HG_OTHER_NODE': short(fco.changectx().node()), |
719 'HG_BASE_NODE': short(fca.changectx().node()), |
719 b'HG_BASE_NODE': short(fca.changectx().node()), |
720 'HG_MY_ISLINK': 'l' in fcd.flags(), |
720 b'HG_MY_ISLINK': b'l' in fcd.flags(), |
721 'HG_OTHER_ISLINK': 'l' in fco.flags(), |
721 b'HG_OTHER_ISLINK': b'l' in fco.flags(), |
722 'HG_BASE_ISLINK': 'l' in fca.flags(), |
722 b'HG_BASE_ISLINK': b'l' in fca.flags(), |
723 'HG_MY_LABEL': mylabel, |
723 b'HG_MY_LABEL': mylabel, |
724 'HG_OTHER_LABEL': otherlabel, |
724 b'HG_OTHER_LABEL': otherlabel, |
725 'HG_BASE_LABEL': baselabel, |
725 b'HG_BASE_LABEL': baselabel, |
726 } |
726 } |
727 ui = repo.ui |
727 ui = repo.ui |
728 |
728 |
729 if "$output" in args: |
729 if b"$output" in args: |
730 # read input from backup, write to original |
730 # read input from backup, write to original |
731 outpath = localpath |
731 outpath = localpath |
732 localpath = localoutputpath |
732 localpath = localoutputpath |
733 replace = { |
733 replace = { |
734 'local': localpath, |
734 b'local': localpath, |
735 'base': basepath, |
735 b'base': basepath, |
736 'other': otherpath, |
736 b'other': otherpath, |
737 'output': outpath, |
737 b'output': outpath, |
738 'labellocal': mylabel, |
738 b'labellocal': mylabel, |
739 'labelother': otherlabel, |
739 b'labelother': otherlabel, |
740 'labelbase': baselabel, |
740 b'labelbase': baselabel, |
741 } |
741 } |
742 args = util.interpolate( |
742 args = util.interpolate( |
743 br'\$', |
743 br'\$', |
744 replace, |
744 replace, |
745 args, |
745 args, |
746 lambda s: procutil.shellquote(util.localpath(s)), |
746 lambda s: procutil.shellquote(util.localpath(s)), |
747 ) |
747 ) |
748 if _toolbool(ui, tool, "gui"): |
748 if _toolbool(ui, tool, b"gui"): |
749 repo.ui.status( |
749 repo.ui.status( |
750 _('running merge tool %s for file %s\n') |
750 _(b'running merge tool %s for file %s\n') |
751 % (tool, uipathfn(fcd.path())) |
751 % (tool, uipathfn(fcd.path())) |
752 ) |
752 ) |
753 if scriptfn is None: |
753 if scriptfn is None: |
754 cmd = toolpath + ' ' + args |
754 cmd = toolpath + b' ' + args |
755 repo.ui.debug('launching merge tool: %s\n' % cmd) |
755 repo.ui.debug(b'launching merge tool: %s\n' % cmd) |
756 _describemerge(ui, repo, mynode, fcd, fca, fco, env, toolpath, args) |
756 _describemerge(ui, repo, mynode, fcd, fca, fco, env, toolpath, args) |
757 r = ui.system( |
757 r = ui.system( |
758 cmd, cwd=repo.root, environ=env, blockedtag='mergetool' |
758 cmd, cwd=repo.root, environ=env, blockedtag=b'mergetool' |
759 ) |
759 ) |
760 else: |
760 else: |
761 repo.ui.debug( |
761 repo.ui.debug( |
762 'launching python merge script: %s:%s\n' % (toolpath, scriptfn) |
762 b'launching python merge script: %s:%s\n' % (toolpath, scriptfn) |
763 ) |
763 ) |
764 r = 0 |
764 r = 0 |
765 try: |
765 try: |
766 # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil |
766 # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil |
767 from . import extensions |
767 from . import extensions |
768 |
768 |
769 mod = extensions.loadpath(toolpath, 'hgmerge.%s' % tool) |
769 mod = extensions.loadpath(toolpath, b'hgmerge.%s' % tool) |
770 except Exception: |
770 except Exception: |
771 raise error.Abort( |
771 raise error.Abort( |
772 _("loading python merge script failed: %s") % toolpath |
772 _(b"loading python merge script failed: %s") % toolpath |
773 ) |
773 ) |
774 mergefn = getattr(mod, scriptfn, None) |
774 mergefn = getattr(mod, scriptfn, None) |
775 if mergefn is None: |
775 if mergefn is None: |
776 raise error.Abort( |
776 raise error.Abort( |
777 _("%s does not have function: %s") % (toolpath, scriptfn) |
777 _(b"%s does not have function: %s") % (toolpath, scriptfn) |
778 ) |
778 ) |
779 argslist = procutil.shellsplit(args) |
779 argslist = procutil.shellsplit(args) |
780 # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil |
780 # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil |
781 from . import hook |
781 from . import hook |
782 |
782 |
783 ret, raised = hook.pythonhook( |
783 ret, raised = hook.pythonhook( |
784 ui, repo, "merge", toolpath, mergefn, {'args': argslist}, True |
784 ui, repo, b"merge", toolpath, mergefn, {b'args': argslist}, True |
785 ) |
785 ) |
786 if raised: |
786 if raised: |
787 r = 1 |
787 r = 1 |
788 repo.ui.debug('merge tool returned: %d\n' % r) |
788 repo.ui.debug(b'merge tool returned: %d\n' % r) |
789 return True, r, False |
789 return True, r, False |
790 |
790 |
791 |
791 |
792 def _formatconflictmarker(ctx, template, label, pad): |
792 def _formatconflictmarker(ctx, template, label, pad): |
793 """Applies the given template to the ctx, prefixed by the label. |
793 """Applies the given template to the ctx, prefixed by the label. |
917 """Writes out `fco` and `fca` as temporary files, and (if uselocalpath) |
917 """Writes out `fco` and `fca` as temporary files, and (if uselocalpath) |
918 copies `localpath` to another temporary file, so an external merge tool may |
918 copies `localpath` to another temporary file, so an external merge tool may |
919 use them. |
919 use them. |
920 """ |
920 """ |
921 tmproot = None |
921 tmproot = None |
922 tmprootprefix = repo.ui.config('experimental', 'mergetempdirprefix') |
922 tmprootprefix = repo.ui.config(b'experimental', b'mergetempdirprefix') |
923 if tmprootprefix: |
923 if tmprootprefix: |
924 tmproot = pycompat.mkdtemp(prefix=tmprootprefix) |
924 tmproot = pycompat.mkdtemp(prefix=tmprootprefix) |
925 |
925 |
926 def maketempfrompath(prefix, path): |
926 def maketempfrompath(prefix, path): |
927 fullbase, ext = os.path.splitext(path) |
927 fullbase, ext = os.path.splitext(path) |
928 pre = "%s~%s" % (os.path.basename(fullbase), prefix) |
928 pre = b"%s~%s" % (os.path.basename(fullbase), prefix) |
929 if tmproot: |
929 if tmproot: |
930 name = os.path.join(tmproot, pre) |
930 name = os.path.join(tmproot, pre) |
931 if ext: |
931 if ext: |
932 name += ext |
932 name += ext |
933 f = open(name, r"wb") |
933 f = open(name, r"wb") |
934 else: |
934 else: |
935 fd, name = pycompat.mkstemp(prefix=pre + '.', suffix=ext) |
935 fd, name = pycompat.mkstemp(prefix=pre + b'.', suffix=ext) |
936 f = os.fdopen(fd, r"wb") |
936 f = os.fdopen(fd, r"wb") |
937 return f, name |
937 return f, name |
938 |
938 |
939 def tempfromcontext(prefix, ctx): |
939 def tempfromcontext(prefix, ctx): |
940 f, name = maketempfrompath(prefix, ctx.path()) |
940 f, name = maketempfrompath(prefix, ctx.path()) |
941 data = repo.wwritedata(ctx.path(), ctx.data()) |
941 data = repo.wwritedata(ctx.path(), ctx.data()) |
942 f.write(data) |
942 f.write(data) |
943 f.close() |
943 f.close() |
944 return name |
944 return name |
945 |
945 |
946 b = tempfromcontext("base", fca) |
946 b = tempfromcontext(b"base", fca) |
947 c = tempfromcontext("other", fco) |
947 c = tempfromcontext(b"other", fco) |
948 d = localpath |
948 d = localpath |
949 if uselocalpath: |
949 if uselocalpath: |
950 # We start off with this being the backup filename, so remove the .orig |
950 # We start off with this being the backup filename, so remove the .orig |
951 # to make syntax-highlighting more likely. |
951 # to make syntax-highlighting more likely. |
952 if d.endswith('.orig'): |
952 if d.endswith(b'.orig'): |
953 d, _ = os.path.splitext(d) |
953 d, _ = os.path.splitext(d) |
954 f, d = maketempfrompath("local", d) |
954 f, d = maketempfrompath(b"local", d) |
955 with open(localpath, 'rb') as src: |
955 with open(localpath, b'rb') as src: |
956 f.write(src.read()) |
956 f.write(src.read()) |
957 f.close() |
957 f.close() |
958 |
958 |
959 try: |
959 try: |
960 yield b, c, d |
960 yield b, c, d |
989 ui = repo.ui |
989 ui = repo.ui |
990 fd = fcd.path() |
990 fd = fcd.path() |
991 uipathfn = scmutil.getuipathfn(repo) |
991 uipathfn = scmutil.getuipathfn(repo) |
992 fduipath = uipathfn(fd) |
992 fduipath = uipathfn(fd) |
993 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() |
993 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() |
994 symlink = 'l' in fcd.flags() + fco.flags() |
994 symlink = b'l' in fcd.flags() + fco.flags() |
995 changedelete = fcd.isabsent() or fco.isabsent() |
995 changedelete = fcd.isabsent() or fco.isabsent() |
996 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete) |
996 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete) |
997 scriptfn = None |
997 scriptfn = None |
998 if tool in internals and tool.startswith('internal:'): |
998 if tool in internals and tool.startswith(b'internal:'): |
999 # normalize to new-style names (':merge' etc) |
999 # normalize to new-style names (':merge' etc) |
1000 tool = tool[len('internal') :] |
1000 tool = tool[len(b'internal') :] |
1001 if toolpath and toolpath.startswith('python:'): |
1001 if toolpath and toolpath.startswith(b'python:'): |
1002 invalidsyntax = False |
1002 invalidsyntax = False |
1003 if toolpath.count(':') >= 2: |
1003 if toolpath.count(b':') >= 2: |
1004 script, scriptfn = toolpath[7:].rsplit(':', 1) |
1004 script, scriptfn = toolpath[7:].rsplit(b':', 1) |
1005 if not scriptfn: |
1005 if not scriptfn: |
1006 invalidsyntax = True |
1006 invalidsyntax = True |
1007 # missing :callable can lead to spliting on windows drive letter |
1007 # missing :callable can lead to spliting on windows drive letter |
1008 if '\\' in scriptfn or '/' in scriptfn: |
1008 if b'\\' in scriptfn or b'/' in scriptfn: |
1009 invalidsyntax = True |
1009 invalidsyntax = True |
1010 else: |
1010 else: |
1011 invalidsyntax = True |
1011 invalidsyntax = True |
1012 if invalidsyntax: |
1012 if invalidsyntax: |
1013 raise error.Abort(_("invalid 'python:' syntax: %s") % toolpath) |
1013 raise error.Abort(_(b"invalid 'python:' syntax: %s") % toolpath) |
1014 toolpath = script |
1014 toolpath = script |
1015 ui.debug( |
1015 ui.debug( |
1016 "picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n" |
1016 b"picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n" |
1017 % ( |
1017 % ( |
1018 tool, |
1018 tool, |
1019 fduipath, |
1019 fduipath, |
1020 pycompat.bytestr(binary), |
1020 pycompat.bytestr(binary), |
1021 pycompat.bytestr(symlink), |
1021 pycompat.bytestr(symlink), |
1046 return True, r, deleted |
1046 return True, r, deleted |
1047 |
1047 |
1048 if premerge: |
1048 if premerge: |
1049 if orig != fco.path(): |
1049 if orig != fco.path(): |
1050 ui.status( |
1050 ui.status( |
1051 _("merging %s and %s to %s\n") |
1051 _(b"merging %s and %s to %s\n") |
1052 % (uipathfn(orig), uipathfn(fco.path()), fduipath) |
1052 % (uipathfn(orig), uipathfn(fco.path()), fduipath) |
1053 ) |
1053 ) |
1054 else: |
1054 else: |
1055 ui.status(_("merging %s\n") % fduipath) |
1055 ui.status(_(b"merging %s\n") % fduipath) |
1056 |
1056 |
1057 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) |
1057 ui.debug(b"my %s other %s ancestor %s\n" % (fcd, fco, fca)) |
1058 |
1058 |
1059 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca, toolconf): |
1059 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca, toolconf): |
1060 if onfailure: |
1060 if onfailure: |
1061 if wctx.isinmemory(): |
1061 if wctx.isinmemory(): |
1062 raise error.InMemoryMergeConflictsError( |
1062 raise error.InMemoryMergeConflictsError( |
1063 'in-memory merge does ' 'not support merge ' 'conflicts' |
1063 b'in-memory merge does ' b'not support merge ' b'conflicts' |
1064 ) |
1064 ) |
1065 ui.warn(onfailure % fduipath) |
1065 ui.warn(onfailure % fduipath) |
1066 return True, 1, False |
1066 return True, 1, False |
1067 |
1067 |
1068 back = _makebackup(repo, ui, wctx, fcd, premerge) |
1068 back = _makebackup(repo, ui, wctx, fcd, premerge) |
1069 files = (None, None, None, back) |
1069 files = (None, None, None, back) |
1070 r = 1 |
1070 r = 1 |
1071 try: |
1071 try: |
1072 internalmarkerstyle = ui.config('ui', 'mergemarkers') |
1072 internalmarkerstyle = ui.config(b'ui', b'mergemarkers') |
1073 if isexternal: |
1073 if isexternal: |
1074 markerstyle = _toolstr(ui, tool, 'mergemarkers') |
1074 markerstyle = _toolstr(ui, tool, b'mergemarkers') |
1075 else: |
1075 else: |
1076 markerstyle = internalmarkerstyle |
1076 markerstyle = internalmarkerstyle |
1077 |
1077 |
1078 if not labels: |
1078 if not labels: |
1079 labels = _defaultconflictlabels |
1079 labels = _defaultconflictlabels |
1080 formattedlabels = labels |
1080 formattedlabels = labels |
1081 if markerstyle != 'basic': |
1081 if markerstyle != b'basic': |
1082 formattedlabels = _formatlabels( |
1082 formattedlabels = _formatlabels( |
1083 repo, fcd, fco, fca, labels, tool=tool |
1083 repo, fcd, fco, fca, labels, tool=tool |
1084 ) |
1084 ) |
1085 |
1085 |
1086 if premerge and mergetype == fullmerge: |
1086 if premerge and mergetype == fullmerge: |
1135 if not r and back is not None: |
1137 if not r and back is not None: |
1136 back.remove() |
1138 back.remove() |
1137 |
1139 |
1138 |
1140 |
1139 def _haltmerge(): |
1141 def _haltmerge(): |
1140 msg = _('merge halted after failed merge (see hg resolve)') |
1142 msg = _(b'merge halted after failed merge (see hg resolve)') |
1141 raise error.InterventionRequired(msg) |
1143 raise error.InterventionRequired(msg) |
1142 |
1144 |
1143 |
1145 |
1144 def _onfilemergefailure(ui): |
1146 def _onfilemergefailure(ui): |
1145 action = ui.config('merge', 'on-failure') |
1147 action = ui.config(b'merge', b'on-failure') |
1146 if action == 'prompt': |
1148 if action == b'prompt': |
1147 msg = _('continue merge operation (yn)?' '$$ &Yes $$ &No') |
1149 msg = _(b'continue merge operation (yn)?' b'$$ &Yes $$ &No') |
1148 if ui.promptchoice(msg, 0) == 1: |
1150 if ui.promptchoice(msg, 0) == 1: |
1149 _haltmerge() |
1151 _haltmerge() |
1150 if action == 'halt': |
1152 if action == b'halt': |
1151 _haltmerge() |
1153 _haltmerge() |
1152 # default action is 'continue', in which case we neither prompt nor halt |
1154 # default action is 'continue', in which case we neither prompt nor halt |
1153 |
1155 |
1154 |
1156 |
1155 def hasconflictmarkers(data): |
1157 def hasconflictmarkers(data): |
1156 return bool( |
1158 return bool( |
1157 re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", data, re.MULTILINE) |
1159 re.search(b"^(<<<<<<< .*|=======|>>>>>>> .*)$", data, re.MULTILINE) |
1158 ) |
1160 ) |
1159 |
1161 |
1160 |
1162 |
1161 def _check(repo, r, ui, tool, fcd, files): |
1163 def _check(repo, r, ui, tool, fcd, files): |
1162 fd = fcd.path() |
1164 fd = fcd.path() |
1163 uipathfn = scmutil.getuipathfn(repo) |
1165 uipathfn = scmutil.getuipathfn(repo) |
1164 unused, unused, unused, back = files |
1166 unused, unused, unused, back = files |
1165 |
1167 |
1166 if not r and ( |
1168 if not r and ( |
1167 _toolbool(ui, tool, "checkconflicts") |
1169 _toolbool(ui, tool, b"checkconflicts") |
1168 or 'conflicts' in _toollist(ui, tool, "check") |
1170 or b'conflicts' in _toollist(ui, tool, b"check") |
1169 ): |
1171 ): |
1170 if hasconflictmarkers(fcd.data()): |
1172 if hasconflictmarkers(fcd.data()): |
1171 r = 1 |
1173 r = 1 |
1172 |
1174 |
1173 checked = False |
1175 checked = False |
1174 if 'prompt' in _toollist(ui, tool, "check"): |
1176 if b'prompt' in _toollist(ui, tool, b"check"): |
1175 checked = True |
1177 checked = True |
1176 if ui.promptchoice( |
1178 if ui.promptchoice( |
1177 _("was merge of '%s' successful (yn)?" "$$ &Yes $$ &No") |
1179 _(b"was merge of '%s' successful (yn)?" b"$$ &Yes $$ &No") |
1178 % uipathfn(fd), |
1180 % uipathfn(fd), |
1179 1, |
1181 1, |
1180 ): |
1182 ): |
1181 r = 1 |
1183 r = 1 |
1182 |
1184 |
1183 if ( |
1185 if ( |
1184 not r |
1186 not r |
1185 and not checked |
1187 and not checked |
1186 and ( |
1188 and ( |
1187 _toolbool(ui, tool, "checkchanged") |
1189 _toolbool(ui, tool, b"checkchanged") |
1188 or 'changed' in _toollist(ui, tool, "check") |
1190 or b'changed' in _toollist(ui, tool, b"check") |
1189 ) |
1191 ) |
1190 ): |
1192 ): |
1191 if back is not None and not fcd.cmp(back): |
1193 if back is not None and not fcd.cmp(back): |
1192 if ui.promptchoice( |
1194 if ui.promptchoice( |
1193 _( |
1195 _( |
1194 " output file %s appears unchanged\n" |
1196 b" output file %s appears unchanged\n" |
1195 "was merge successful (yn)?" |
1197 b"was merge successful (yn)?" |
1196 "$$ &Yes $$ &No" |
1198 b"$$ &Yes $$ &No" |
1197 ) |
1199 ) |
1198 % uipathfn(fd), |
1200 % uipathfn(fd), |
1199 1, |
1201 1, |
1200 ): |
1202 ): |
1201 r = 1 |
1203 r = 1 |
1202 |
1204 |
1203 if back is not None and _toolbool(ui, tool, "fixeol"): |
1205 if back is not None and _toolbool(ui, tool, b"fixeol"): |
1204 _matcheol(_workingpath(repo, fcd), back) |
1206 _matcheol(_workingpath(repo, fcd), back) |
1205 |
1207 |
1206 return r |
1208 return r |
1207 |
1209 |
1208 |
1210 |
1224 |
1226 |
1225 def loadinternalmerge(ui, extname, registrarobj): |
1227 def loadinternalmerge(ui, extname, registrarobj): |
1226 """Load internal merge tool from specified registrarobj |
1228 """Load internal merge tool from specified registrarobj |
1227 """ |
1229 """ |
1228 for name, func in registrarobj._table.iteritems(): |
1230 for name, func in registrarobj._table.iteritems(): |
1229 fullname = ':' + name |
1231 fullname = b':' + name |
1230 internals[fullname] = func |
1232 internals[fullname] = func |
1231 internals['internal:' + name] = func |
1233 internals[b'internal:' + name] = func |
1232 internalsdoc[fullname] = func |
1234 internalsdoc[fullname] = func |
1233 |
1235 |
1234 capabilities = sorted([k for k, v in func.capabilities.items() if v]) |
1236 capabilities = sorted([k for k, v in func.capabilities.items() if v]) |
1235 if capabilities: |
1237 if capabilities: |
1236 capdesc = " (actual capabilities: %s)" % ', '.join(capabilities) |
1238 capdesc = b" (actual capabilities: %s)" % b', '.join( |
1237 func.__doc__ = func.__doc__ + pycompat.sysstr("\n\n%s" % capdesc) |
1239 capabilities |
|
1240 ) |
|
1241 func.__doc__ = func.__doc__ + pycompat.sysstr(b"\n\n%s" % capdesc) |
1238 |
1242 |
1239 # to put i18n comments into hg.pot for automatically generated texts |
1243 # to put i18n comments into hg.pot for automatically generated texts |
1240 |
1244 |
1241 # i18n: "binary" and "symlink" are keywords |
1245 # i18n: "binary" and "symlink" are keywords |
1242 # i18n: this text is added automatically |
1246 # i18n: this text is added automatically |
1243 _(" (actual capabilities: binary, symlink)") |
1247 _(b" (actual capabilities: binary, symlink)") |
1244 # i18n: "binary" is keyword |
1248 # i18n: "binary" is keyword |
1245 # i18n: this text is added automatically |
1249 # i18n: this text is added automatically |
1246 _(" (actual capabilities: binary)") |
1250 _(b" (actual capabilities: binary)") |
1247 # i18n: "symlink" is keyword |
1251 # i18n: "symlink" is keyword |
1248 # i18n: this text is added automatically |
1252 # i18n: this text is added automatically |
1249 _(" (actual capabilities: symlink)") |
1253 _(b" (actual capabilities: symlink)") |
1250 |
1254 |
1251 |
1255 |
1252 # load built-in merge tools explicitly to setup internalsdoc |
1256 # load built-in merge tools explicitly to setup internalsdoc |
1253 loadinternalmerge(None, None, internaltool) |
1257 loadinternalmerge(None, None, internaltool) |
1254 |
1258 |