##// END OF EJS Templates
simplemerge: take over formatting of label from `filemerge`...
Martin von Zweigbergk -
r49433:3c8cc987 default
parent child Browse files
Show More
@@ -1,1264 +1,1255 b''
1 # filemerge.py - file-level merge handling for Mercurial
1 # filemerge.py - file-level merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007, 2008 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2006, 2007, 2008 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import contextlib
10 import contextlib
11 import os
11 import os
12 import re
12 import re
13 import shutil
13 import shutil
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 short,
18 short,
19 )
19 )
20 from .pycompat import (
20 from .pycompat import (
21 getattr,
21 getattr,
22 open,
22 open,
23 )
23 )
24
24
25 from . import (
25 from . import (
26 encoding,
26 encoding,
27 error,
27 error,
28 formatter,
28 formatter,
29 match,
29 match,
30 pycompat,
30 pycompat,
31 registrar,
31 registrar,
32 scmutil,
32 scmutil,
33 simplemerge,
33 simplemerge,
34 tagmerge,
34 tagmerge,
35 templatekw,
35 templatekw,
36 templater,
36 templater,
37 templateutil,
37 templateutil,
38 util,
38 util,
39 )
39 )
40
40
41 from .utils import (
41 from .utils import (
42 procutil,
42 procutil,
43 stringutil,
44 )
43 )
45
44
46
45
47 def _toolstr(ui, tool, part, *args):
46 def _toolstr(ui, tool, part, *args):
48 return ui.config(b"merge-tools", tool + b"." + part, *args)
47 return ui.config(b"merge-tools", tool + b"." + part, *args)
49
48
50
49
51 def _toolbool(ui, tool, part, *args):
50 def _toolbool(ui, tool, part, *args):
52 return ui.configbool(b"merge-tools", tool + b"." + part, *args)
51 return ui.configbool(b"merge-tools", tool + b"." + part, *args)
53
52
54
53
55 def _toollist(ui, tool, part):
54 def _toollist(ui, tool, part):
56 return ui.configlist(b"merge-tools", tool + b"." + part)
55 return ui.configlist(b"merge-tools", tool + b"." + part)
57
56
58
57
59 internals = {}
58 internals = {}
60 # Merge tools to document.
59 # Merge tools to document.
61 internalsdoc = {}
60 internalsdoc = {}
62
61
63 internaltool = registrar.internalmerge()
62 internaltool = registrar.internalmerge()
64
63
65 # internal tool merge types
64 # internal tool merge types
66 nomerge = internaltool.nomerge
65 nomerge = internaltool.nomerge
67 mergeonly = internaltool.mergeonly # just the full merge, no premerge
66 mergeonly = internaltool.mergeonly # just the full merge, no premerge
68 fullmerge = internaltool.fullmerge # both premerge and merge
67 fullmerge = internaltool.fullmerge # both premerge and merge
69
68
70 # IMPORTANT: keep the last line of this prompt very short ("What do you want to
69 # IMPORTANT: keep the last line of this prompt very short ("What do you want to
71 # do?") because of issue6158, ideally to <40 English characters (to allow other
70 # do?") because of issue6158, ideally to <40 English characters (to allow other
72 # languages that may take more columns to still have a chance to fit in an
71 # languages that may take more columns to still have a chance to fit in an
73 # 80-column screen).
72 # 80-column screen).
74 _localchangedotherdeletedmsg = _(
73 _localchangedotherdeletedmsg = _(
75 b"file '%(fd)s' was deleted in other%(o)s but was modified in local%(l)s.\n"
74 b"file '%(fd)s' was deleted in other%(o)s but was modified in local%(l)s.\n"
76 b"You can use (c)hanged version, (d)elete, or leave (u)nresolved.\n"
75 b"You can use (c)hanged version, (d)elete, or leave (u)nresolved.\n"
77 b"What do you want to do?"
76 b"What do you want to do?"
78 b"$$ &Changed $$ &Delete $$ &Unresolved"
77 b"$$ &Changed $$ &Delete $$ &Unresolved"
79 )
78 )
80
79
81 _otherchangedlocaldeletedmsg = _(
80 _otherchangedlocaldeletedmsg = _(
82 b"file '%(fd)s' was deleted in local%(l)s but was modified in other%(o)s.\n"
81 b"file '%(fd)s' was deleted in local%(l)s but was modified in other%(o)s.\n"
83 b"You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.\n"
82 b"You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.\n"
84 b"What do you want to do?"
83 b"What do you want to do?"
85 b"$$ &Changed $$ &Deleted $$ &Unresolved"
84 b"$$ &Changed $$ &Deleted $$ &Unresolved"
86 )
85 )
87
86
88
87
89 class absentfilectx(object):
88 class absentfilectx(object):
90 """Represents a file that's ostensibly in a context but is actually not
89 """Represents a file that's ostensibly in a context but is actually not
91 present in it.
90 present in it.
92
91
93 This is here because it's very specific to the filemerge code for now --
92 This is here because it's very specific to the filemerge code for now --
94 other code is likely going to break with the values this returns."""
93 other code is likely going to break with the values this returns."""
95
94
96 def __init__(self, ctx, f):
95 def __init__(self, ctx, f):
97 self._ctx = ctx
96 self._ctx = ctx
98 self._f = f
97 self._f = f
99
98
100 def __bytes__(self):
99 def __bytes__(self):
101 return b'absent file %s@%s' % (self._f, self._ctx)
100 return b'absent file %s@%s' % (self._f, self._ctx)
102
101
103 def path(self):
102 def path(self):
104 return self._f
103 return self._f
105
104
106 def size(self):
105 def size(self):
107 return None
106 return None
108
107
109 def data(self):
108 def data(self):
110 return None
109 return None
111
110
112 def filenode(self):
111 def filenode(self):
113 return self._ctx.repo().nullid
112 return self._ctx.repo().nullid
114
113
115 _customcmp = True
114 _customcmp = True
116
115
117 def cmp(self, fctx):
116 def cmp(self, fctx):
118 """compare with other file context
117 """compare with other file context
119
118
120 returns True if different from fctx.
119 returns True if different from fctx.
121 """
120 """
122 return not (
121 return not (
123 fctx.isabsent()
122 fctx.isabsent()
124 and fctx.changectx() == self.changectx()
123 and fctx.changectx() == self.changectx()
125 and fctx.path() == self.path()
124 and fctx.path() == self.path()
126 )
125 )
127
126
128 def flags(self):
127 def flags(self):
129 return b''
128 return b''
130
129
131 def changectx(self):
130 def changectx(self):
132 return self._ctx
131 return self._ctx
133
132
134 def isbinary(self):
133 def isbinary(self):
135 return False
134 return False
136
135
137 def isabsent(self):
136 def isabsent(self):
138 return True
137 return True
139
138
140
139
141 def _findtool(ui, tool):
140 def _findtool(ui, tool):
142 if tool in internals:
141 if tool in internals:
143 return tool
142 return tool
144 cmd = _toolstr(ui, tool, b"executable", tool)
143 cmd = _toolstr(ui, tool, b"executable", tool)
145 if cmd.startswith(b'python:'):
144 if cmd.startswith(b'python:'):
146 return cmd
145 return cmd
147 return findexternaltool(ui, tool)
146 return findexternaltool(ui, tool)
148
147
149
148
150 def _quotetoolpath(cmd):
149 def _quotetoolpath(cmd):
151 if cmd.startswith(b'python:'):
150 if cmd.startswith(b'python:'):
152 return cmd
151 return cmd
153 return procutil.shellquote(cmd)
152 return procutil.shellquote(cmd)
154
153
155
154
156 def findexternaltool(ui, tool):
155 def findexternaltool(ui, tool):
157 for kn in (b"regkey", b"regkeyalt"):
156 for kn in (b"regkey", b"regkeyalt"):
158 k = _toolstr(ui, tool, kn)
157 k = _toolstr(ui, tool, kn)
159 if not k:
158 if not k:
160 continue
159 continue
161 p = util.lookupreg(k, _toolstr(ui, tool, b"regname"))
160 p = util.lookupreg(k, _toolstr(ui, tool, b"regname"))
162 if p:
161 if p:
163 p = procutil.findexe(p + _toolstr(ui, tool, b"regappend", b""))
162 p = procutil.findexe(p + _toolstr(ui, tool, b"regappend", b""))
164 if p:
163 if p:
165 return p
164 return p
166 exe = _toolstr(ui, tool, b"executable", tool)
165 exe = _toolstr(ui, tool, b"executable", tool)
167 return procutil.findexe(util.expandpath(exe))
166 return procutil.findexe(util.expandpath(exe))
168
167
169
168
170 def _picktool(repo, ui, path, binary, symlink, changedelete):
169 def _picktool(repo, ui, path, binary, symlink, changedelete):
171 strictcheck = ui.configbool(b'merge', b'strict-capability-check')
170 strictcheck = ui.configbool(b'merge', b'strict-capability-check')
172
171
173 def hascapability(tool, capability, strict=False):
172 def hascapability(tool, capability, strict=False):
174 if tool in internals:
173 if tool in internals:
175 return strict and internals[tool].capabilities.get(capability)
174 return strict and internals[tool].capabilities.get(capability)
176 return _toolbool(ui, tool, capability)
175 return _toolbool(ui, tool, capability)
177
176
178 def supportscd(tool):
177 def supportscd(tool):
179 return tool in internals and internals[tool].mergetype == nomerge
178 return tool in internals and internals[tool].mergetype == nomerge
180
179
181 def check(tool, pat, symlink, binary, changedelete):
180 def check(tool, pat, symlink, binary, changedelete):
182 tmsg = tool
181 tmsg = tool
183 if pat:
182 if pat:
184 tmsg = _(b"%s (for pattern %s)") % (tool, pat)
183 tmsg = _(b"%s (for pattern %s)") % (tool, pat)
185 if not _findtool(ui, tool):
184 if not _findtool(ui, tool):
186 if pat: # explicitly requested tool deserves a warning
185 if pat: # explicitly requested tool deserves a warning
187 ui.warn(_(b"couldn't find merge tool %s\n") % tmsg)
186 ui.warn(_(b"couldn't find merge tool %s\n") % tmsg)
188 else: # configured but non-existing tools are more silent
187 else: # configured but non-existing tools are more silent
189 ui.note(_(b"couldn't find merge tool %s\n") % tmsg)
188 ui.note(_(b"couldn't find merge tool %s\n") % tmsg)
190 elif symlink and not hascapability(tool, b"symlink", strictcheck):
189 elif symlink and not hascapability(tool, b"symlink", strictcheck):
191 ui.warn(_(b"tool %s can't handle symlinks\n") % tmsg)
190 ui.warn(_(b"tool %s can't handle symlinks\n") % tmsg)
192 elif binary and not hascapability(tool, b"binary", strictcheck):
191 elif binary and not hascapability(tool, b"binary", strictcheck):
193 ui.warn(_(b"tool %s can't handle binary\n") % tmsg)
192 ui.warn(_(b"tool %s can't handle binary\n") % tmsg)
194 elif changedelete and not supportscd(tool):
193 elif changedelete and not supportscd(tool):
195 # the nomerge tools are the only tools that support change/delete
194 # the nomerge tools are the only tools that support change/delete
196 # conflicts
195 # conflicts
197 pass
196 pass
198 elif not procutil.gui() and _toolbool(ui, tool, b"gui"):
197 elif not procutil.gui() and _toolbool(ui, tool, b"gui"):
199 ui.warn(_(b"tool %s requires a GUI\n") % tmsg)
198 ui.warn(_(b"tool %s requires a GUI\n") % tmsg)
200 else:
199 else:
201 return True
200 return True
202 return False
201 return False
203
202
204 # internal config: ui.forcemerge
203 # internal config: ui.forcemerge
205 # forcemerge comes from command line arguments, highest priority
204 # forcemerge comes from command line arguments, highest priority
206 force = ui.config(b'ui', b'forcemerge')
205 force = ui.config(b'ui', b'forcemerge')
207 if force:
206 if force:
208 toolpath = _findtool(ui, force)
207 toolpath = _findtool(ui, force)
209 if changedelete and not supportscd(toolpath):
208 if changedelete and not supportscd(toolpath):
210 return b":prompt", None
209 return b":prompt", None
211 else:
210 else:
212 if toolpath:
211 if toolpath:
213 return (force, _quotetoolpath(toolpath))
212 return (force, _quotetoolpath(toolpath))
214 else:
213 else:
215 # mimic HGMERGE if given tool not found
214 # mimic HGMERGE if given tool not found
216 return (force, force)
215 return (force, force)
217
216
218 # HGMERGE takes next precedence
217 # HGMERGE takes next precedence
219 hgmerge = encoding.environ.get(b"HGMERGE")
218 hgmerge = encoding.environ.get(b"HGMERGE")
220 if hgmerge:
219 if hgmerge:
221 if changedelete and not supportscd(hgmerge):
220 if changedelete and not supportscd(hgmerge):
222 return b":prompt", None
221 return b":prompt", None
223 else:
222 else:
224 return (hgmerge, hgmerge)
223 return (hgmerge, hgmerge)
225
224
226 # then patterns
225 # then patterns
227
226
228 # whether binary capability should be checked strictly
227 # whether binary capability should be checked strictly
229 binarycap = binary and strictcheck
228 binarycap = binary and strictcheck
230
229
231 for pat, tool in ui.configitems(b"merge-patterns"):
230 for pat, tool in ui.configitems(b"merge-patterns"):
232 mf = match.match(repo.root, b'', [pat])
231 mf = match.match(repo.root, b'', [pat])
233 if mf(path) and check(tool, pat, symlink, binarycap, changedelete):
232 if mf(path) and check(tool, pat, symlink, binarycap, changedelete):
234 if binary and not hascapability(tool, b"binary", strict=True):
233 if binary and not hascapability(tool, b"binary", strict=True):
235 ui.warn(
234 ui.warn(
236 _(
235 _(
237 b"warning: check merge-patterns configurations,"
236 b"warning: check merge-patterns configurations,"
238 b" if %r for binary file %r is unintentional\n"
237 b" if %r for binary file %r is unintentional\n"
239 b"(see 'hg help merge-tools'"
238 b"(see 'hg help merge-tools'"
240 b" for binary files capability)\n"
239 b" for binary files capability)\n"
241 )
240 )
242 % (pycompat.bytestr(tool), pycompat.bytestr(path))
241 % (pycompat.bytestr(tool), pycompat.bytestr(path))
243 )
242 )
244 toolpath = _findtool(ui, tool)
243 toolpath = _findtool(ui, tool)
245 return (tool, _quotetoolpath(toolpath))
244 return (tool, _quotetoolpath(toolpath))
246
245
247 # then merge tools
246 # then merge tools
248 tools = {}
247 tools = {}
249 disabled = set()
248 disabled = set()
250 for k, v in ui.configitems(b"merge-tools"):
249 for k, v in ui.configitems(b"merge-tools"):
251 t = k.split(b'.')[0]
250 t = k.split(b'.')[0]
252 if t not in tools:
251 if t not in tools:
253 tools[t] = int(_toolstr(ui, t, b"priority"))
252 tools[t] = int(_toolstr(ui, t, b"priority"))
254 if _toolbool(ui, t, b"disabled"):
253 if _toolbool(ui, t, b"disabled"):
255 disabled.add(t)
254 disabled.add(t)
256 names = tools.keys()
255 names = tools.keys()
257 tools = sorted(
256 tools = sorted(
258 [(-p, tool) for tool, p in tools.items() if tool not in disabled]
257 [(-p, tool) for tool, p in tools.items() if tool not in disabled]
259 )
258 )
260 uimerge = ui.config(b"ui", b"merge")
259 uimerge = ui.config(b"ui", b"merge")
261 if uimerge:
260 if uimerge:
262 # external tools defined in uimerge won't be able to handle
261 # external tools defined in uimerge won't be able to handle
263 # change/delete conflicts
262 # change/delete conflicts
264 if check(uimerge, path, symlink, binary, changedelete):
263 if check(uimerge, path, symlink, binary, changedelete):
265 if uimerge not in names and not changedelete:
264 if uimerge not in names and not changedelete:
266 return (uimerge, uimerge)
265 return (uimerge, uimerge)
267 tools.insert(0, (None, uimerge)) # highest priority
266 tools.insert(0, (None, uimerge)) # highest priority
268 tools.append((None, b"hgmerge")) # the old default, if found
267 tools.append((None, b"hgmerge")) # the old default, if found
269 for p, t in tools:
268 for p, t in tools:
270 if check(t, None, symlink, binary, changedelete):
269 if check(t, None, symlink, binary, changedelete):
271 toolpath = _findtool(ui, t)
270 toolpath = _findtool(ui, t)
272 return (t, _quotetoolpath(toolpath))
271 return (t, _quotetoolpath(toolpath))
273
272
274 # internal merge or prompt as last resort
273 # internal merge or prompt as last resort
275 if symlink or binary or changedelete:
274 if symlink or binary or changedelete:
276 if not changedelete and len(tools):
275 if not changedelete and len(tools):
277 # any tool is rejected by capability for symlink or binary
276 # any tool is rejected by capability for symlink or binary
278 ui.warn(_(b"no tool found to merge %s\n") % path)
277 ui.warn(_(b"no tool found to merge %s\n") % path)
279 return b":prompt", None
278 return b":prompt", None
280 return b":merge", None
279 return b":merge", None
281
280
282
281
283 def _eoltype(data):
282 def _eoltype(data):
284 """Guess the EOL type of a file"""
283 """Guess the EOL type of a file"""
285 if b'\0' in data: # binary
284 if b'\0' in data: # binary
286 return None
285 return None
287 if b'\r\n' in data: # Windows
286 if b'\r\n' in data: # Windows
288 return b'\r\n'
287 return b'\r\n'
289 if b'\r' in data: # Old Mac
288 if b'\r' in data: # Old Mac
290 return b'\r'
289 return b'\r'
291 if b'\n' in data: # UNIX
290 if b'\n' in data: # UNIX
292 return b'\n'
291 return b'\n'
293 return None # unknown
292 return None # unknown
294
293
295
294
296 def _matcheol(file, backup):
295 def _matcheol(file, backup):
297 """Convert EOL markers in a file to match origfile"""
296 """Convert EOL markers in a file to match origfile"""
298 tostyle = _eoltype(backup.data()) # No repo.wread filters?
297 tostyle = _eoltype(backup.data()) # No repo.wread filters?
299 if tostyle:
298 if tostyle:
300 data = util.readfile(file)
299 data = util.readfile(file)
301 style = _eoltype(data)
300 style = _eoltype(data)
302 if style:
301 if style:
303 newdata = data.replace(style, tostyle)
302 newdata = data.replace(style, tostyle)
304 if newdata != data:
303 if newdata != data:
305 util.writefile(file, newdata)
304 util.writefile(file, newdata)
306
305
307
306
308 @internaltool(b'prompt', nomerge)
307 @internaltool(b'prompt', nomerge)
309 def _iprompt(repo, mynode, local, other, base, toolconf):
308 def _iprompt(repo, mynode, local, other, base, toolconf):
310 """Asks the user which of the local `p1()` or the other `p2()` version to
309 """Asks the user which of the local `p1()` or the other `p2()` version to
311 keep as the merged version."""
310 keep as the merged version."""
312 ui = repo.ui
311 ui = repo.ui
313 fd = local.fctx.path()
312 fd = local.fctx.path()
314 uipathfn = scmutil.getuipathfn(repo)
313 uipathfn = scmutil.getuipathfn(repo)
315
314
316 # Avoid prompting during an in-memory merge since it doesn't support merge
315 # Avoid prompting during an in-memory merge since it doesn't support merge
317 # conflicts.
316 # conflicts.
318 if local.fctx.changectx().isinmemory():
317 if local.fctx.changectx().isinmemory():
319 raise error.InMemoryMergeConflictsError(
318 raise error.InMemoryMergeConflictsError(
320 b'in-memory merge does not support file conflicts'
319 b'in-memory merge does not support file conflicts'
321 )
320 )
322
321
323 prompts = partextras([local.label, other.label])
322 prompts = partextras([local.label, other.label])
324 prompts[b'fd'] = uipathfn(fd)
323 prompts[b'fd'] = uipathfn(fd)
325 try:
324 try:
326 if other.fctx.isabsent():
325 if other.fctx.isabsent():
327 index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2)
326 index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2)
328 choice = [b'local', b'other', b'unresolved'][index]
327 choice = [b'local', b'other', b'unresolved'][index]
329 elif local.fctx.isabsent():
328 elif local.fctx.isabsent():
330 index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2)
329 index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2)
331 choice = [b'other', b'local', b'unresolved'][index]
330 choice = [b'other', b'local', b'unresolved'][index]
332 else:
331 else:
333 # IMPORTANT: keep the last line of this prompt ("What do you want to
332 # IMPORTANT: keep the last line of this prompt ("What do you want to
334 # do?") very short, see comment next to _localchangedotherdeletedmsg
333 # do?") very short, see comment next to _localchangedotherdeletedmsg
335 # at the top of the file for details.
334 # at the top of the file for details.
336 index = ui.promptchoice(
335 index = ui.promptchoice(
337 _(
336 _(
338 b"file '%(fd)s' needs to be resolved.\n"
337 b"file '%(fd)s' needs to be resolved.\n"
339 b"You can keep (l)ocal%(l)s, take (o)ther%(o)s, or leave "
338 b"You can keep (l)ocal%(l)s, take (o)ther%(o)s, or leave "
340 b"(u)nresolved.\n"
339 b"(u)nresolved.\n"
341 b"What do you want to do?"
340 b"What do you want to do?"
342 b"$$ &Local $$ &Other $$ &Unresolved"
341 b"$$ &Local $$ &Other $$ &Unresolved"
343 )
342 )
344 % prompts,
343 % prompts,
345 2,
344 2,
346 )
345 )
347 choice = [b'local', b'other', b'unresolved'][index]
346 choice = [b'local', b'other', b'unresolved'][index]
348
347
349 if choice == b'other':
348 if choice == b'other':
350 return _iother(repo, mynode, local, other, base, toolconf)
349 return _iother(repo, mynode, local, other, base, toolconf)
351 elif choice == b'local':
350 elif choice == b'local':
352 return _ilocal(repo, mynode, local, other, base, toolconf)
351 return _ilocal(repo, mynode, local, other, base, toolconf)
353 elif choice == b'unresolved':
352 elif choice == b'unresolved':
354 return _ifail(repo, mynode, local, other, base, toolconf)
353 return _ifail(repo, mynode, local, other, base, toolconf)
355 except error.ResponseExpected:
354 except error.ResponseExpected:
356 ui.write(b"\n")
355 ui.write(b"\n")
357 return _ifail(repo, mynode, local, other, base, toolconf)
356 return _ifail(repo, mynode, local, other, base, toolconf)
358
357
359
358
360 @internaltool(b'local', nomerge)
359 @internaltool(b'local', nomerge)
361 def _ilocal(repo, mynode, local, other, base, toolconf):
360 def _ilocal(repo, mynode, local, other, base, toolconf):
362 """Uses the local `p1()` version of files as the merged version."""
361 """Uses the local `p1()` version of files as the merged version."""
363 return 0, local.fctx.isabsent()
362 return 0, local.fctx.isabsent()
364
363
365
364
366 @internaltool(b'other', nomerge)
365 @internaltool(b'other', nomerge)
367 def _iother(repo, mynode, local, other, base, toolconf):
366 def _iother(repo, mynode, local, other, base, toolconf):
368 """Uses the other `p2()` version of files as the merged version."""
367 """Uses the other `p2()` version of files as the merged version."""
369 if other.fctx.isabsent():
368 if other.fctx.isabsent():
370 # local changed, remote deleted -- 'deleted' picked
369 # local changed, remote deleted -- 'deleted' picked
371 _underlyingfctxifabsent(local.fctx).remove()
370 _underlyingfctxifabsent(local.fctx).remove()
372 deleted = True
371 deleted = True
373 else:
372 else:
374 _underlyingfctxifabsent(local.fctx).write(
373 _underlyingfctxifabsent(local.fctx).write(
375 other.fctx.data(), other.fctx.flags()
374 other.fctx.data(), other.fctx.flags()
376 )
375 )
377 deleted = False
376 deleted = False
378 return 0, deleted
377 return 0, deleted
379
378
380
379
381 @internaltool(b'fail', nomerge)
380 @internaltool(b'fail', nomerge)
382 def _ifail(repo, mynode, local, other, base, toolconf):
381 def _ifail(repo, mynode, local, other, base, toolconf):
383 """
382 """
384 Rather than attempting to merge files that were modified on both
383 Rather than attempting to merge files that were modified on both
385 branches, it marks them as unresolved. The resolve command must be
384 branches, it marks them as unresolved. The resolve command must be
386 used to resolve these conflicts."""
385 used to resolve these conflicts."""
387 # for change/delete conflicts write out the changed version, then fail
386 # for change/delete conflicts write out the changed version, then fail
388 if local.fctx.isabsent():
387 if local.fctx.isabsent():
389 _underlyingfctxifabsent(local.fctx).write(
388 _underlyingfctxifabsent(local.fctx).write(
390 other.fctx.data(), other.fctx.flags()
389 other.fctx.data(), other.fctx.flags()
391 )
390 )
392 return 1, False
391 return 1, False
393
392
394
393
395 def _underlyingfctxifabsent(filectx):
394 def _underlyingfctxifabsent(filectx):
396 """Sometimes when resolving, our fcd is actually an absentfilectx, but
395 """Sometimes when resolving, our fcd is actually an absentfilectx, but
397 we want to write to it (to do the resolve). This helper returns the
396 we want to write to it (to do the resolve). This helper returns the
398 underyling workingfilectx in that case.
397 underyling workingfilectx in that case.
399 """
398 """
400 if filectx.isabsent():
399 if filectx.isabsent():
401 return filectx.changectx()[filectx.path()]
400 return filectx.changectx()[filectx.path()]
402 else:
401 else:
403 return filectx
402 return filectx
404
403
405
404
406 def _premerge(repo, local, other, base, toolconf, backup):
405 def _premerge(repo, local, other, base, toolconf, backup):
407 tool, toolpath, binary, symlink, scriptfn = toolconf
406 tool, toolpath, binary, symlink, scriptfn = toolconf
408 if symlink or local.fctx.isabsent() or other.fctx.isabsent():
407 if symlink or local.fctx.isabsent() or other.fctx.isabsent():
409 return 1
408 return 1
410
409
411 ui = repo.ui
410 ui = repo.ui
412
411
413 validkeep = [b'keep', b'keep-merge3', b'keep-mergediff']
412 validkeep = [b'keep', b'keep-merge3', b'keep-mergediff']
414
413
415 # do we attempt to simplemerge first?
414 # do we attempt to simplemerge first?
416 try:
415 try:
417 premerge = _toolbool(ui, tool, b"premerge", not binary)
416 premerge = _toolbool(ui, tool, b"premerge", not binary)
418 except error.ConfigError:
417 except error.ConfigError:
419 premerge = _toolstr(ui, tool, b"premerge", b"").lower()
418 premerge = _toolstr(ui, tool, b"premerge", b"").lower()
420 if premerge not in validkeep:
419 if premerge not in validkeep:
421 _valid = b', '.join([b"'" + v + b"'" for v in validkeep])
420 _valid = b', '.join([b"'" + v + b"'" for v in validkeep])
422 raise error.ConfigError(
421 raise error.ConfigError(
423 _(b"%s.premerge not valid ('%s' is neither boolean nor %s)")
422 _(b"%s.premerge not valid ('%s' is neither boolean nor %s)")
424 % (tool, premerge, _valid)
423 % (tool, premerge, _valid)
425 )
424 )
426
425
427 if premerge:
426 if premerge:
428 mode = b'merge'
427 mode = b'merge'
429 if premerge == b'keep-mergediff':
428 if premerge == b'keep-mergediff':
430 mode = b'mergediff'
429 mode = b'mergediff'
431 elif premerge == b'keep-merge3':
430 elif premerge == b'keep-merge3':
432 mode = b'merge3'
431 mode = b'merge3'
433 r = simplemerge.simplemerge(
432 r = simplemerge.simplemerge(
434 ui, local, base, other, quiet=True, mode=mode
433 ui, local, base, other, quiet=True, mode=mode
435 )
434 )
436 if not r:
435 if not r:
437 ui.debug(b" premerge successful\n")
436 ui.debug(b" premerge successful\n")
438 return 0
437 return 0
439 if premerge not in validkeep:
438 if premerge not in validkeep:
440 # restore from backup and try again
439 # restore from backup and try again
441 _restorebackup(local.fctx, backup)
440 _restorebackup(local.fctx, backup)
442 return 1 # continue merging
441 return 1 # continue merging
443
442
444
443
445 def _mergecheck(repo, mynode, fcd, fco, fca, toolconf):
444 def _mergecheck(repo, mynode, fcd, fco, fca, toolconf):
446 tool, toolpath, binary, symlink, scriptfn = toolconf
445 tool, toolpath, binary, symlink, scriptfn = toolconf
447 uipathfn = scmutil.getuipathfn(repo)
446 uipathfn = scmutil.getuipathfn(repo)
448 if symlink:
447 if symlink:
449 repo.ui.warn(
448 repo.ui.warn(
450 _(b'warning: internal %s cannot merge symlinks for %s\n')
449 _(b'warning: internal %s cannot merge symlinks for %s\n')
451 % (tool, uipathfn(fcd.path()))
450 % (tool, uipathfn(fcd.path()))
452 )
451 )
453 return False
452 return False
454 if fcd.isabsent() or fco.isabsent():
453 if fcd.isabsent() or fco.isabsent():
455 repo.ui.warn(
454 repo.ui.warn(
456 _(
455 _(
457 b'warning: internal %s cannot merge change/delete '
456 b'warning: internal %s cannot merge change/delete '
458 b'conflict for %s\n'
457 b'conflict for %s\n'
459 )
458 )
460 % (tool, uipathfn(fcd.path()))
459 % (tool, uipathfn(fcd.path()))
461 )
460 )
462 return False
461 return False
463 return True
462 return True
464
463
465
464
466 def _merge(repo, local, other, base, mode):
465 def _merge(repo, local, other, base, mode):
467 """
466 """
468 Uses the internal non-interactive simple merge algorithm for merging
467 Uses the internal non-interactive simple merge algorithm for merging
469 files. It will fail if there are any conflicts and leave markers in
468 files. It will fail if there are any conflicts and leave markers in
470 the partially merged file. Markers will have two sections, one for each side
469 the partially merged file. Markers will have two sections, one for each side
471 of merge, unless mode equals 'union' which suppresses the markers."""
470 of merge, unless mode equals 'union' which suppresses the markers."""
472 ui = repo.ui
471 ui = repo.ui
473
472
474 r = simplemerge.simplemerge(ui, local, base, other, mode=mode)
473 r = simplemerge.simplemerge(ui, local, base, other, mode=mode)
475 return True, r, False
474 return True, r, False
476
475
477
476
478 @internaltool(
477 @internaltool(
479 b'union',
478 b'union',
480 fullmerge,
479 fullmerge,
481 _(
480 _(
482 b"warning: conflicts while merging %s! "
481 b"warning: conflicts while merging %s! "
483 b"(edit, then use 'hg resolve --mark')\n"
482 b"(edit, then use 'hg resolve --mark')\n"
484 ),
483 ),
485 precheck=_mergecheck,
484 precheck=_mergecheck,
486 )
485 )
487 def _iunion(repo, mynode, local, other, base, toolconf, backup):
486 def _iunion(repo, mynode, local, other, base, toolconf, backup):
488 """
487 """
489 Uses the internal non-interactive simple merge algorithm for merging
488 Uses the internal non-interactive simple merge algorithm for merging
490 files. It will use both left and right sides for conflict regions.
489 files. It will use both left and right sides for conflict regions.
491 No markers are inserted."""
490 No markers are inserted."""
492 return _merge(repo, local, other, base, b'union')
491 return _merge(repo, local, other, base, b'union')
493
492
494
493
495 @internaltool(
494 @internaltool(
496 b'merge',
495 b'merge',
497 fullmerge,
496 fullmerge,
498 _(
497 _(
499 b"warning: conflicts while merging %s! "
498 b"warning: conflicts while merging %s! "
500 b"(edit, then use 'hg resolve --mark')\n"
499 b"(edit, then use 'hg resolve --mark')\n"
501 ),
500 ),
502 precheck=_mergecheck,
501 precheck=_mergecheck,
503 )
502 )
504 def _imerge(repo, mynode, local, other, base, toolconf, backup):
503 def _imerge(repo, mynode, local, other, base, toolconf, backup):
505 """
504 """
506 Uses the internal non-interactive simple merge algorithm for merging
505 Uses the internal non-interactive simple merge algorithm for merging
507 files. It will fail if there are any conflicts and leave markers in
506 files. It will fail if there are any conflicts and leave markers in
508 the partially merged file. Markers will have two sections, one for each side
507 the partially merged file. Markers will have two sections, one for each side
509 of merge."""
508 of merge."""
510 return _merge(repo, local, other, base, b'merge')
509 return _merge(repo, local, other, base, b'merge')
511
510
512
511
513 @internaltool(
512 @internaltool(
514 b'merge3',
513 b'merge3',
515 fullmerge,
514 fullmerge,
516 _(
515 _(
517 b"warning: conflicts while merging %s! "
516 b"warning: conflicts while merging %s! "
518 b"(edit, then use 'hg resolve --mark')\n"
517 b"(edit, then use 'hg resolve --mark')\n"
519 ),
518 ),
520 precheck=_mergecheck,
519 precheck=_mergecheck,
521 )
520 )
522 def _imerge3(repo, mynode, local, other, base, toolconf, backup):
521 def _imerge3(repo, mynode, local, other, base, toolconf, backup):
523 """
522 """
524 Uses the internal non-interactive simple merge algorithm for merging
523 Uses the internal non-interactive simple merge algorithm for merging
525 files. It will fail if there are any conflicts and leave markers in
524 files. It will fail if there are any conflicts and leave markers in
526 the partially merged file. Marker will have three sections, one from each
525 the partially merged file. Marker will have three sections, one from each
527 side of the merge and one for the base content."""
526 side of the merge and one for the base content."""
528 return _merge(repo, local, other, base, b'merge3')
527 return _merge(repo, local, other, base, b'merge3')
529
528
530
529
531 @internaltool(
530 @internaltool(
532 b'merge3-lie-about-conflicts',
531 b'merge3-lie-about-conflicts',
533 fullmerge,
532 fullmerge,
534 b'',
533 b'',
535 precheck=_mergecheck,
534 precheck=_mergecheck,
536 )
535 )
537 def _imerge3alwaysgood(*args, **kwargs):
536 def _imerge3alwaysgood(*args, **kwargs):
538 # Like merge3, but record conflicts as resolved with markers in place.
537 # Like merge3, but record conflicts as resolved with markers in place.
539 #
538 #
540 # This is used for `diff.merge` to show the differences between
539 # This is used for `diff.merge` to show the differences between
541 # the auto-merge state and the committed merge state. It may be
540 # the auto-merge state and the committed merge state. It may be
542 # useful for other things.
541 # useful for other things.
543 b1, junk, b2 = _imerge3(*args, **kwargs)
542 b1, junk, b2 = _imerge3(*args, **kwargs)
544 # TODO is this right? I'm not sure what these return values mean,
543 # TODO is this right? I'm not sure what these return values mean,
545 # but as far as I can tell this will indicate to callers tha the
544 # but as far as I can tell this will indicate to callers tha the
546 # merge succeeded.
545 # merge succeeded.
547 return b1, False, b2
546 return b1, False, b2
548
547
549
548
550 @internaltool(
549 @internaltool(
551 b'mergediff',
550 b'mergediff',
552 fullmerge,
551 fullmerge,
553 _(
552 _(
554 b"warning: conflicts while merging %s! "
553 b"warning: conflicts while merging %s! "
555 b"(edit, then use 'hg resolve --mark')\n"
554 b"(edit, then use 'hg resolve --mark')\n"
556 ),
555 ),
557 precheck=_mergecheck,
556 precheck=_mergecheck,
558 )
557 )
559 def _imerge_diff(repo, mynode, local, other, base, toolconf, backup):
558 def _imerge_diff(repo, mynode, local, other, base, toolconf, backup):
560 """
559 """
561 Uses the internal non-interactive simple merge algorithm for merging
560 Uses the internal non-interactive simple merge algorithm for merging
562 files. It will fail if there are any conflicts and leave markers in
561 files. It will fail if there are any conflicts and leave markers in
563 the partially merged file. The marker will have two sections, one with the
562 the partially merged file. The marker will have two sections, one with the
564 content from one side of the merge, and one with a diff from the base
563 content from one side of the merge, and one with a diff from the base
565 content to the content on the other side. (experimental)"""
564 content to the content on the other side. (experimental)"""
566 return _merge(repo, local, other, base, b'mergediff')
565 return _merge(repo, local, other, base, b'mergediff')
567
566
568
567
569 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck)
568 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck)
570 def _imergelocal(repo, mynode, local, other, base, toolconf, backup):
569 def _imergelocal(repo, mynode, local, other, base, toolconf, backup):
571 """
570 """
572 Like :merge, but resolve all conflicts non-interactively in favor
571 Like :merge, but resolve all conflicts non-interactively in favor
573 of the local `p1()` changes."""
572 of the local `p1()` changes."""
574 return _merge(repo, local, other, base, b'local')
573 return _merge(repo, local, other, base, b'local')
575
574
576
575
577 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck)
576 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck)
578 def _imergeother(repo, mynode, local, other, base, toolconf, backup):
577 def _imergeother(repo, mynode, local, other, base, toolconf, backup):
579 """
578 """
580 Like :merge, but resolve all conflicts non-interactively in favor
579 Like :merge, but resolve all conflicts non-interactively in favor
581 of the other `p2()` changes."""
580 of the other `p2()` changes."""
582 return _merge(repo, local, other, base, b'other')
581 return _merge(repo, local, other, base, b'other')
583
582
584
583
585 @internaltool(
584 @internaltool(
586 b'tagmerge',
585 b'tagmerge',
587 mergeonly,
586 mergeonly,
588 _(
587 _(
589 b"automatic tag merging of %s failed! "
588 b"automatic tag merging of %s failed! "
590 b"(use 'hg resolve --tool :merge' or another merge "
589 b"(use 'hg resolve --tool :merge' or another merge "
591 b"tool of your choice)\n"
590 b"tool of your choice)\n"
592 ),
591 ),
593 )
592 )
594 def _itagmerge(repo, mynode, local, other, base, toolconf, backup):
593 def _itagmerge(repo, mynode, local, other, base, toolconf, backup):
595 """
594 """
596 Uses the internal tag merge algorithm (experimental).
595 Uses the internal tag merge algorithm (experimental).
597 """
596 """
598 success, status = tagmerge.merge(repo, local.fctx, other.fctx, base.fctx)
597 success, status = tagmerge.merge(repo, local.fctx, other.fctx, base.fctx)
599 return success, status, False
598 return success, status, False
600
599
601
600
602 @internaltool(b'dump', fullmerge, binary=True, symlink=True)
601 @internaltool(b'dump', fullmerge, binary=True, symlink=True)
603 def _idump(repo, mynode, local, other, base, toolconf, backup):
602 def _idump(repo, mynode, local, other, base, toolconf, backup):
604 """
603 """
605 Creates three versions of the files to merge, containing the
604 Creates three versions of the files to merge, containing the
606 contents of local, other and base. These files can then be used to
605 contents of local, other and base. These files can then be used to
607 perform a merge manually. If the file to be merged is named
606 perform a merge manually. If the file to be merged is named
608 ``a.txt``, these files will accordingly be named ``a.txt.local``,
607 ``a.txt``, these files will accordingly be named ``a.txt.local``,
609 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
608 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
610 same directory as ``a.txt``.
609 same directory as ``a.txt``.
611
610
612 This implies premerge. Therefore, files aren't dumped, if premerge
611 This implies premerge. Therefore, files aren't dumped, if premerge
613 runs successfully. Use :forcedump to forcibly write files out.
612 runs successfully. Use :forcedump to forcibly write files out.
614 """
613 """
615 a = _workingpath(repo, local.fctx)
614 a = _workingpath(repo, local.fctx)
616 fd = local.fctx.path()
615 fd = local.fctx.path()
617
616
618 from . import context
617 from . import context
619
618
620 if isinstance(local.fctx, context.overlayworkingfilectx):
619 if isinstance(local.fctx, context.overlayworkingfilectx):
621 raise error.InMemoryMergeConflictsError(
620 raise error.InMemoryMergeConflictsError(
622 b'in-memory merge does not support the :dump tool.'
621 b'in-memory merge does not support the :dump tool.'
623 )
622 )
624
623
625 util.writefile(a + b".local", local.fctx.decodeddata())
624 util.writefile(a + b".local", local.fctx.decodeddata())
626 repo.wwrite(fd + b".other", other.fctx.data(), other.fctx.flags())
625 repo.wwrite(fd + b".other", other.fctx.data(), other.fctx.flags())
627 repo.wwrite(fd + b".base", base.fctx.data(), base.fctx.flags())
626 repo.wwrite(fd + b".base", base.fctx.data(), base.fctx.flags())
628 return False, 1, False
627 return False, 1, False
629
628
630
629
631 @internaltool(b'forcedump', mergeonly, binary=True, symlink=True)
630 @internaltool(b'forcedump', mergeonly, binary=True, symlink=True)
632 def _forcedump(repo, mynode, local, other, base, toolconf, backup):
631 def _forcedump(repo, mynode, local, other, base, toolconf, backup):
633 """
632 """
634 Creates three versions of the files as same as :dump, but omits premerge.
633 Creates three versions of the files as same as :dump, but omits premerge.
635 """
634 """
636 return _idump(repo, mynode, local, other, base, toolconf, backup)
635 return _idump(repo, mynode, local, other, base, toolconf, backup)
637
636
638
637
639 def _xmergeimm(repo, mynode, local, other, base, toolconf, backup):
638 def _xmergeimm(repo, mynode, local, other, base, toolconf, backup):
640 # In-memory merge simply raises an exception on all external merge tools,
639 # In-memory merge simply raises an exception on all external merge tools,
641 # for now.
640 # for now.
642 #
641 #
643 # It would be possible to run most tools with temporary files, but this
642 # It would be possible to run most tools with temporary files, but this
644 # raises the question of what to do if the user only partially resolves the
643 # raises the question of what to do if the user only partially resolves the
645 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/
644 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/
646 # directory and tell the user how to get it is my best idea, but it's
645 # directory and tell the user how to get it is my best idea, but it's
647 # clunky.)
646 # clunky.)
648 raise error.InMemoryMergeConflictsError(
647 raise error.InMemoryMergeConflictsError(
649 b'in-memory merge does not support external merge tools'
648 b'in-memory merge does not support external merge tools'
650 )
649 )
651
650
652
651
653 def _describemerge(ui, repo, mynode, fcl, fcb, fco, env, toolpath, args):
652 def _describemerge(ui, repo, mynode, fcl, fcb, fco, env, toolpath, args):
654 tmpl = ui.config(b'command-templates', b'pre-merge-tool-output')
653 tmpl = ui.config(b'command-templates', b'pre-merge-tool-output')
655 if not tmpl:
654 if not tmpl:
656 return
655 return
657
656
658 mappingdict = templateutil.mappingdict
657 mappingdict = templateutil.mappingdict
659 props = {
658 props = {
660 b'ctx': fcl.changectx(),
659 b'ctx': fcl.changectx(),
661 b'node': hex(mynode),
660 b'node': hex(mynode),
662 b'path': fcl.path(),
661 b'path': fcl.path(),
663 b'local': mappingdict(
662 b'local': mappingdict(
664 {
663 {
665 b'ctx': fcl.changectx(),
664 b'ctx': fcl.changectx(),
666 b'fctx': fcl,
665 b'fctx': fcl,
667 b'node': hex(mynode),
666 b'node': hex(mynode),
668 b'name': _(b'local'),
667 b'name': _(b'local'),
669 b'islink': b'l' in fcl.flags(),
668 b'islink': b'l' in fcl.flags(),
670 b'label': env[b'HG_MY_LABEL'],
669 b'label': env[b'HG_MY_LABEL'],
671 }
670 }
672 ),
671 ),
673 b'base': mappingdict(
672 b'base': mappingdict(
674 {
673 {
675 b'ctx': fcb.changectx(),
674 b'ctx': fcb.changectx(),
676 b'fctx': fcb,
675 b'fctx': fcb,
677 b'name': _(b'base'),
676 b'name': _(b'base'),
678 b'islink': b'l' in fcb.flags(),
677 b'islink': b'l' in fcb.flags(),
679 b'label': env[b'HG_BASE_LABEL'],
678 b'label': env[b'HG_BASE_LABEL'],
680 }
679 }
681 ),
680 ),
682 b'other': mappingdict(
681 b'other': mappingdict(
683 {
682 {
684 b'ctx': fco.changectx(),
683 b'ctx': fco.changectx(),
685 b'fctx': fco,
684 b'fctx': fco,
686 b'name': _(b'other'),
685 b'name': _(b'other'),
687 b'islink': b'l' in fco.flags(),
686 b'islink': b'l' in fco.flags(),
688 b'label': env[b'HG_OTHER_LABEL'],
687 b'label': env[b'HG_OTHER_LABEL'],
689 }
688 }
690 ),
689 ),
691 b'toolpath': toolpath,
690 b'toolpath': toolpath,
692 b'toolargs': args,
691 b'toolargs': args,
693 }
692 }
694
693
695 # TODO: make all of this something that can be specified on a per-tool basis
694 # TODO: make all of this something that can be specified on a per-tool basis
696 tmpl = templater.unquotestring(tmpl)
695 tmpl = templater.unquotestring(tmpl)
697
696
698 # Not using cmdutil.rendertemplate here since it causes errors importing
697 # Not using cmdutil.rendertemplate here since it causes errors importing
699 # things for us to import cmdutil.
698 # things for us to import cmdutil.
700 tres = formatter.templateresources(ui, repo)
699 tres = formatter.templateresources(ui, repo)
701 t = formatter.maketemplater(
700 t = formatter.maketemplater(
702 ui, tmpl, defaults=templatekw.keywords, resources=tres
701 ui, tmpl, defaults=templatekw.keywords, resources=tres
703 )
702 )
704 ui.status(t.renderdefault(props))
703 ui.status(t.renderdefault(props))
705
704
706
705
707 def _xmerge(repo, mynode, local, other, base, toolconf, backup):
706 def _xmerge(repo, mynode, local, other, base, toolconf, backup):
708 fcd = local.fctx
707 fcd = local.fctx
709 fco = other.fctx
708 fco = other.fctx
710 fca = base.fctx
709 fca = base.fctx
711 tool, toolpath, binary, symlink, scriptfn = toolconf
710 tool, toolpath, binary, symlink, scriptfn = toolconf
712 uipathfn = scmutil.getuipathfn(repo)
711 uipathfn = scmutil.getuipathfn(repo)
713 if fcd.isabsent() or fco.isabsent():
712 if fcd.isabsent() or fco.isabsent():
714 repo.ui.warn(
713 repo.ui.warn(
715 _(b'warning: %s cannot merge change/delete conflict for %s\n')
714 _(b'warning: %s cannot merge change/delete conflict for %s\n')
716 % (tool, uipathfn(fcd.path()))
715 % (tool, uipathfn(fcd.path()))
717 )
716 )
718 return False, 1, None
717 return False, 1, None
719 localpath = _workingpath(repo, fcd)
718 localpath = _workingpath(repo, fcd)
720 args = _toolstr(repo.ui, tool, b"args")
719 args = _toolstr(repo.ui, tool, b"args")
721
720
722 with _maketempfiles(
721 with _maketempfiles(
723 repo, fco, fca, repo.wvfs.join(backup.path()), b"$output" in args
722 repo, fco, fca, repo.wvfs.join(backup.path()), b"$output" in args
724 ) as temppaths:
723 ) as temppaths:
725 basepath, otherpath, localoutputpath = temppaths
724 basepath, otherpath, localoutputpath = temppaths
726 outpath = b""
725 outpath = b""
726
727 def format_label(input):
728 if input.label_detail:
729 return b'%s: %s' % (input.label, input.label_detail)
730 else:
731 return input.label
732
727 env = {
733 env = {
728 b'HG_FILE': fcd.path(),
734 b'HG_FILE': fcd.path(),
729 b'HG_MY_NODE': short(mynode),
735 b'HG_MY_NODE': short(mynode),
730 b'HG_OTHER_NODE': short(fco.changectx().node()),
736 b'HG_OTHER_NODE': short(fco.changectx().node()),
731 b'HG_BASE_NODE': short(fca.changectx().node()),
737 b'HG_BASE_NODE': short(fca.changectx().node()),
732 b'HG_MY_ISLINK': b'l' in fcd.flags(),
738 b'HG_MY_ISLINK': b'l' in fcd.flags(),
733 b'HG_OTHER_ISLINK': b'l' in fco.flags(),
739 b'HG_OTHER_ISLINK': b'l' in fco.flags(),
734 b'HG_BASE_ISLINK': b'l' in fca.flags(),
740 b'HG_BASE_ISLINK': b'l' in fca.flags(),
735 b'HG_MY_LABEL': local.label,
741 b'HG_MY_LABEL': format_label(local),
736 b'HG_OTHER_LABEL': other.label,
742 b'HG_OTHER_LABEL': format_label(other),
737 b'HG_BASE_LABEL': base.label,
743 b'HG_BASE_LABEL': format_label(base),
738 }
744 }
739 ui = repo.ui
745 ui = repo.ui
740
746
741 if b"$output" in args:
747 if b"$output" in args:
742 # read input from backup, write to original
748 # read input from backup, write to original
743 outpath = localpath
749 outpath = localpath
744 localpath = localoutputpath
750 localpath = localoutputpath
745 replace = {
751 replace = {
746 b'local': localpath,
752 b'local': localpath,
747 b'base': basepath,
753 b'base': basepath,
748 b'other': otherpath,
754 b'other': otherpath,
749 b'output': outpath,
755 b'output': outpath,
750 b'labellocal': local.label,
756 b'labellocal': format_label(local),
751 b'labelother': other.label,
757 b'labelother': format_label(other),
752 b'labelbase': base.label,
758 b'labelbase': format_label(base),
753 }
759 }
754 args = util.interpolate(
760 args = util.interpolate(
755 br'\$',
761 br'\$',
756 replace,
762 replace,
757 args,
763 args,
758 lambda s: procutil.shellquote(util.localpath(s)),
764 lambda s: procutil.shellquote(util.localpath(s)),
759 )
765 )
760 if _toolbool(ui, tool, b"gui"):
766 if _toolbool(ui, tool, b"gui"):
761 repo.ui.status(
767 repo.ui.status(
762 _(b'running merge tool %s for file %s\n')
768 _(b'running merge tool %s for file %s\n')
763 % (tool, uipathfn(fcd.path()))
769 % (tool, uipathfn(fcd.path()))
764 )
770 )
765 if scriptfn is None:
771 if scriptfn is None:
766 cmd = toolpath + b' ' + args
772 cmd = toolpath + b' ' + args
767 repo.ui.debug(b'launching merge tool: %s\n' % cmd)
773 repo.ui.debug(b'launching merge tool: %s\n' % cmd)
768 _describemerge(ui, repo, mynode, fcd, fca, fco, env, toolpath, args)
774 _describemerge(ui, repo, mynode, fcd, fca, fco, env, toolpath, args)
769 r = ui.system(
775 r = ui.system(
770 cmd, cwd=repo.root, environ=env, blockedtag=b'mergetool'
776 cmd, cwd=repo.root, environ=env, blockedtag=b'mergetool'
771 )
777 )
772 else:
778 else:
773 repo.ui.debug(
779 repo.ui.debug(
774 b'launching python merge script: %s:%s\n' % (toolpath, scriptfn)
780 b'launching python merge script: %s:%s\n' % (toolpath, scriptfn)
775 )
781 )
776 r = 0
782 r = 0
777 try:
783 try:
778 # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil
784 # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil
779 from . import extensions
785 from . import extensions
780
786
781 mod = extensions.loadpath(toolpath, b'hgmerge.%s' % tool)
787 mod = extensions.loadpath(toolpath, b'hgmerge.%s' % tool)
782 except Exception:
788 except Exception:
783 raise error.Abort(
789 raise error.Abort(
784 _(b"loading python merge script failed: %s") % toolpath
790 _(b"loading python merge script failed: %s") % toolpath
785 )
791 )
786 mergefn = getattr(mod, scriptfn, None)
792 mergefn = getattr(mod, scriptfn, None)
787 if mergefn is None:
793 if mergefn is None:
788 raise error.Abort(
794 raise error.Abort(
789 _(b"%s does not have function: %s") % (toolpath, scriptfn)
795 _(b"%s does not have function: %s") % (toolpath, scriptfn)
790 )
796 )
791 argslist = procutil.shellsplit(args)
797 argslist = procutil.shellsplit(args)
792 # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil
798 # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil
793 from . import hook
799 from . import hook
794
800
795 ret, raised = hook.pythonhook(
801 ret, raised = hook.pythonhook(
796 ui, repo, b"merge", toolpath, mergefn, {b'args': argslist}, True
802 ui, repo, b"merge", toolpath, mergefn, {b'args': argslist}, True
797 )
803 )
798 if raised:
804 if raised:
799 r = 1
805 r = 1
800 repo.ui.debug(b'merge tool returned: %d\n' % r)
806 repo.ui.debug(b'merge tool returned: %d\n' % r)
801 return True, r, False
807 return True, r, False
802
808
803
809
804 def _populate_label_detail(input, template, pad):
810 def _populate_label_detail(input, template):
805 """Applies the given template to the ctx, prefixed by the label.
811 """Applies the given template to the ctx and stores it in the input."""
806
807 Pad is the minimum width of the label prefix, so that multiple markers
808 can have aligned templated parts.
809 """
810 ctx = input.fctx.changectx()
812 ctx = input.fctx.changectx()
811 if ctx.node() is None:
813 if ctx.node() is None:
812 ctx = ctx.p1()
814 ctx = ctx.p1()
813
815
814 props = {b'ctx': ctx}
816 props = {b'ctx': ctx}
815 templateresult = template.renderdefault(props)
817 templateresult = template.renderdefault(props)
816
818 input.label_detail = templateresult.splitlines()[0] # split for safety
817 label = (b'%s:' % input.label).ljust(pad + 1)
818 mark = b'%s %s' % (label, templateresult)
819 mark = mark.splitlines()[0] # split for safety
820
821 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
822 input.label = stringutil.ellipsis(mark, 80 - 8)
823
819
824
820
825 def _populate_label_details(repo, inputs, tool=None):
821 def _populate_label_details(repo, inputs, tool=None):
826 """Formats the given labels using the conflict marker template.
822 """Populates the label details using the conflict marker template."""
827
828 Returns a list of formatted labels.
829 """
830 ui = repo.ui
823 ui = repo.ui
831 template = ui.config(b'command-templates', b'mergemarker')
824 template = ui.config(b'command-templates', b'mergemarker')
832 if tool is not None:
825 if tool is not None:
833 template = _toolstr(ui, tool, b'mergemarkertemplate', template)
826 template = _toolstr(ui, tool, b'mergemarkertemplate', template)
834 template = templater.unquotestring(template)
827 template = templater.unquotestring(template)
835 tres = formatter.templateresources(ui, repo)
828 tres = formatter.templateresources(ui, repo)
836 tmpl = formatter.maketemplater(
829 tmpl = formatter.maketemplater(
837 ui, template, defaults=templatekw.keywords, resources=tres
830 ui, template, defaults=templatekw.keywords, resources=tres
838 )
831 )
839
832
840 pad = max(len(input.label) for input in inputs)
841
842 for input in inputs:
833 for input in inputs:
843 _populate_label_detail(input, tmpl, pad)
834 _populate_label_detail(input, tmpl)
844
835
845
836
846 def partextras(labels):
837 def partextras(labels):
847 """Return a dictionary of extra labels for use in prompts to the user
838 """Return a dictionary of extra labels for use in prompts to the user
848
839
849 Intended use is in strings of the form "(l)ocal%(l)s".
840 Intended use is in strings of the form "(l)ocal%(l)s".
850 """
841 """
851 if labels is None:
842 if labels is None:
852 return {
843 return {
853 b"l": b"",
844 b"l": b"",
854 b"o": b"",
845 b"o": b"",
855 }
846 }
856
847
857 return {
848 return {
858 b"l": b" [%s]" % labels[0],
849 b"l": b" [%s]" % labels[0],
859 b"o": b" [%s]" % labels[1],
850 b"o": b" [%s]" % labels[1],
860 }
851 }
861
852
862
853
863 def _restorebackup(fcd, backup):
854 def _restorebackup(fcd, backup):
864 # TODO: Add a workingfilectx.write(otherfilectx) path so we can use
855 # TODO: Add a workingfilectx.write(otherfilectx) path so we can use
865 # util.copy here instead.
856 # util.copy here instead.
866 fcd.write(backup.data(), fcd.flags())
857 fcd.write(backup.data(), fcd.flags())
867
858
868
859
869 def _makebackup(repo, ui, wctx, fcd):
860 def _makebackup(repo, ui, wctx, fcd):
870 """Makes and returns a filectx-like object for ``fcd``'s backup file.
861 """Makes and returns a filectx-like object for ``fcd``'s backup file.
871
862
872 In addition to preserving the user's pre-existing modifications to `fcd`
863 In addition to preserving the user's pre-existing modifications to `fcd`
873 (if any), the backup is used to undo certain premerges, confirm whether a
864 (if any), the backup is used to undo certain premerges, confirm whether a
874 merge changed anything, and determine what line endings the new file should
865 merge changed anything, and determine what line endings the new file should
875 have.
866 have.
876
867
877 Backups only need to be written once since their content doesn't change
868 Backups only need to be written once since their content doesn't change
878 afterwards.
869 afterwards.
879 """
870 """
880 if fcd.isabsent():
871 if fcd.isabsent():
881 return None
872 return None
882 # TODO: Break this import cycle somehow. (filectx -> ctx -> fileset ->
873 # TODO: Break this import cycle somehow. (filectx -> ctx -> fileset ->
883 # merge -> filemerge). (I suspect the fileset import is the weakest link)
874 # merge -> filemerge). (I suspect the fileset import is the weakest link)
884 from . import context
875 from . import context
885
876
886 backup = scmutil.backuppath(ui, repo, fcd.path())
877 backup = scmutil.backuppath(ui, repo, fcd.path())
887 inworkingdir = backup.startswith(repo.wvfs.base) and not backup.startswith(
878 inworkingdir = backup.startswith(repo.wvfs.base) and not backup.startswith(
888 repo.vfs.base
879 repo.vfs.base
889 )
880 )
890 if isinstance(fcd, context.overlayworkingfilectx) and inworkingdir:
881 if isinstance(fcd, context.overlayworkingfilectx) and inworkingdir:
891 # If the backup file is to be in the working directory, and we're
882 # If the backup file is to be in the working directory, and we're
892 # merging in-memory, we must redirect the backup to the memory context
883 # merging in-memory, we must redirect the backup to the memory context
893 # so we don't disturb the working directory.
884 # so we don't disturb the working directory.
894 relpath = backup[len(repo.wvfs.base) + 1 :]
885 relpath = backup[len(repo.wvfs.base) + 1 :]
895 wctx[relpath].write(fcd.data(), fcd.flags())
886 wctx[relpath].write(fcd.data(), fcd.flags())
896 return wctx[relpath]
887 return wctx[relpath]
897 else:
888 else:
898 # Otherwise, write to wherever path the user specified the backups
889 # Otherwise, write to wherever path the user specified the backups
899 # should go. We still need to switch based on whether the source is
890 # should go. We still need to switch based on whether the source is
900 # in-memory so we can use the fast path of ``util.copy`` if both are
891 # in-memory so we can use the fast path of ``util.copy`` if both are
901 # on disk.
892 # on disk.
902 if isinstance(fcd, context.overlayworkingfilectx):
893 if isinstance(fcd, context.overlayworkingfilectx):
903 util.writefile(backup, fcd.data())
894 util.writefile(backup, fcd.data())
904 else:
895 else:
905 a = _workingpath(repo, fcd)
896 a = _workingpath(repo, fcd)
906 util.copyfile(a, backup)
897 util.copyfile(a, backup)
907 # A arbitraryfilectx is returned, so we can run the same functions on
898 # A arbitraryfilectx is returned, so we can run the same functions on
908 # the backup context regardless of where it lives.
899 # the backup context regardless of where it lives.
909 return context.arbitraryfilectx(backup, repo=repo)
900 return context.arbitraryfilectx(backup, repo=repo)
910
901
911
902
912 @contextlib.contextmanager
903 @contextlib.contextmanager
913 def _maketempfiles(repo, fco, fca, localpath, uselocalpath):
904 def _maketempfiles(repo, fco, fca, localpath, uselocalpath):
914 """Writes out `fco` and `fca` as temporary files, and (if uselocalpath)
905 """Writes out `fco` and `fca` as temporary files, and (if uselocalpath)
915 copies `localpath` to another temporary file, so an external merge tool may
906 copies `localpath` to another temporary file, so an external merge tool may
916 use them.
907 use them.
917 """
908 """
918 tmproot = None
909 tmproot = None
919 tmprootprefix = repo.ui.config(b'experimental', b'mergetempdirprefix')
910 tmprootprefix = repo.ui.config(b'experimental', b'mergetempdirprefix')
920 if tmprootprefix:
911 if tmprootprefix:
921 tmproot = pycompat.mkdtemp(prefix=tmprootprefix)
912 tmproot = pycompat.mkdtemp(prefix=tmprootprefix)
922
913
923 def maketempfrompath(prefix, path):
914 def maketempfrompath(prefix, path):
924 fullbase, ext = os.path.splitext(path)
915 fullbase, ext = os.path.splitext(path)
925 pre = b"%s~%s" % (os.path.basename(fullbase), prefix)
916 pre = b"%s~%s" % (os.path.basename(fullbase), prefix)
926 if tmproot:
917 if tmproot:
927 name = os.path.join(tmproot, pre)
918 name = os.path.join(tmproot, pre)
928 if ext:
919 if ext:
929 name += ext
920 name += ext
930 f = open(name, "wb")
921 f = open(name, "wb")
931 else:
922 else:
932 fd, name = pycompat.mkstemp(prefix=pre + b'.', suffix=ext)
923 fd, name = pycompat.mkstemp(prefix=pre + b'.', suffix=ext)
933 f = os.fdopen(fd, "wb")
924 f = os.fdopen(fd, "wb")
934 return f, name
925 return f, name
935
926
936 def tempfromcontext(prefix, ctx):
927 def tempfromcontext(prefix, ctx):
937 f, name = maketempfrompath(prefix, ctx.path())
928 f, name = maketempfrompath(prefix, ctx.path())
938 data = ctx.decodeddata()
929 data = ctx.decodeddata()
939 f.write(data)
930 f.write(data)
940 f.close()
931 f.close()
941 return name
932 return name
942
933
943 b = tempfromcontext(b"base", fca)
934 b = tempfromcontext(b"base", fca)
944 c = tempfromcontext(b"other", fco)
935 c = tempfromcontext(b"other", fco)
945 d = localpath
936 d = localpath
946 if uselocalpath:
937 if uselocalpath:
947 # We start off with this being the backup filename, so remove the .orig
938 # We start off with this being the backup filename, so remove the .orig
948 # to make syntax-highlighting more likely.
939 # to make syntax-highlighting more likely.
949 if d.endswith(b'.orig'):
940 if d.endswith(b'.orig'):
950 d, _ = os.path.splitext(d)
941 d, _ = os.path.splitext(d)
951 f, d = maketempfrompath(b"local", d)
942 f, d = maketempfrompath(b"local", d)
952 with open(localpath, b'rb') as src:
943 with open(localpath, b'rb') as src:
953 f.write(src.read())
944 f.write(src.read())
954 f.close()
945 f.close()
955
946
956 try:
947 try:
957 yield b, c, d
948 yield b, c, d
958 finally:
949 finally:
959 if tmproot:
950 if tmproot:
960 shutil.rmtree(tmproot)
951 shutil.rmtree(tmproot)
961 else:
952 else:
962 util.unlink(b)
953 util.unlink(b)
963 util.unlink(c)
954 util.unlink(c)
964 # if not uselocalpath, d is the 'orig'/backup file which we
955 # if not uselocalpath, d is the 'orig'/backup file which we
965 # shouldn't delete.
956 # shouldn't delete.
966 if d and uselocalpath:
957 if d and uselocalpath:
967 util.unlink(d)
958 util.unlink(d)
968
959
969
960
970 def filemerge(repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
961 def filemerge(repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
971 """perform a 3-way merge in the working directory
962 """perform a 3-way merge in the working directory
972
963
973 mynode = parent node before merge
964 mynode = parent node before merge
974 orig = original local filename before merge
965 orig = original local filename before merge
975 fco = other file context
966 fco = other file context
976 fca = ancestor file context
967 fca = ancestor file context
977 fcd = local file context for current/destination file
968 fcd = local file context for current/destination file
978
969
979 Returns whether the merge is complete, the return value of the merge, and
970 Returns whether the merge is complete, the return value of the merge, and
980 a boolean indicating whether the file was deleted from disk."""
971 a boolean indicating whether the file was deleted from disk."""
981
972
982 if not fco.cmp(fcd): # files identical?
973 if not fco.cmp(fcd): # files identical?
983 return None, False
974 return None, False
984
975
985 ui = repo.ui
976 ui = repo.ui
986 fd = fcd.path()
977 fd = fcd.path()
987 uipathfn = scmutil.getuipathfn(repo)
978 uipathfn = scmutil.getuipathfn(repo)
988 fduipath = uipathfn(fd)
979 fduipath = uipathfn(fd)
989 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
980 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
990 symlink = b'l' in fcd.flags() + fco.flags()
981 symlink = b'l' in fcd.flags() + fco.flags()
991 changedelete = fcd.isabsent() or fco.isabsent()
982 changedelete = fcd.isabsent() or fco.isabsent()
992 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
983 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
993 scriptfn = None
984 scriptfn = None
994 if tool in internals and tool.startswith(b'internal:'):
985 if tool in internals and tool.startswith(b'internal:'):
995 # normalize to new-style names (':merge' etc)
986 # normalize to new-style names (':merge' etc)
996 tool = tool[len(b'internal') :]
987 tool = tool[len(b'internal') :]
997 if toolpath and toolpath.startswith(b'python:'):
988 if toolpath and toolpath.startswith(b'python:'):
998 invalidsyntax = False
989 invalidsyntax = False
999 if toolpath.count(b':') >= 2:
990 if toolpath.count(b':') >= 2:
1000 script, scriptfn = toolpath[7:].rsplit(b':', 1)
991 script, scriptfn = toolpath[7:].rsplit(b':', 1)
1001 if not scriptfn:
992 if not scriptfn:
1002 invalidsyntax = True
993 invalidsyntax = True
1003 # missing :callable can lead to spliting on windows drive letter
994 # missing :callable can lead to spliting on windows drive letter
1004 if b'\\' in scriptfn or b'/' in scriptfn:
995 if b'\\' in scriptfn or b'/' in scriptfn:
1005 invalidsyntax = True
996 invalidsyntax = True
1006 else:
997 else:
1007 invalidsyntax = True
998 invalidsyntax = True
1008 if invalidsyntax:
999 if invalidsyntax:
1009 raise error.Abort(_(b"invalid 'python:' syntax: %s") % toolpath)
1000 raise error.Abort(_(b"invalid 'python:' syntax: %s") % toolpath)
1010 toolpath = script
1001 toolpath = script
1011 ui.debug(
1002 ui.debug(
1012 b"picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
1003 b"picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
1013 % (
1004 % (
1014 tool,
1005 tool,
1015 fduipath,
1006 fduipath,
1016 pycompat.bytestr(binary),
1007 pycompat.bytestr(binary),
1017 pycompat.bytestr(symlink),
1008 pycompat.bytestr(symlink),
1018 pycompat.bytestr(changedelete),
1009 pycompat.bytestr(changedelete),
1019 )
1010 )
1020 )
1011 )
1021
1012
1022 if tool in internals:
1013 if tool in internals:
1023 func = internals[tool]
1014 func = internals[tool]
1024 mergetype = func.mergetype
1015 mergetype = func.mergetype
1025 onfailure = func.onfailure
1016 onfailure = func.onfailure
1026 precheck = func.precheck
1017 precheck = func.precheck
1027 isexternal = False
1018 isexternal = False
1028 else:
1019 else:
1029 if wctx.isinmemory():
1020 if wctx.isinmemory():
1030 func = _xmergeimm
1021 func = _xmergeimm
1031 else:
1022 else:
1032 func = _xmerge
1023 func = _xmerge
1033 mergetype = fullmerge
1024 mergetype = fullmerge
1034 onfailure = _(b"merging %s failed!\n")
1025 onfailure = _(b"merging %s failed!\n")
1035 precheck = None
1026 precheck = None
1036 isexternal = True
1027 isexternal = True
1037
1028
1038 toolconf = tool, toolpath, binary, symlink, scriptfn
1029 toolconf = tool, toolpath, binary, symlink, scriptfn
1039
1030
1040 if not labels:
1031 if not labels:
1041 labels = [b'local', b'other']
1032 labels = [b'local', b'other']
1042 if len(labels) < 3:
1033 if len(labels) < 3:
1043 labels.append(b'base')
1034 labels.append(b'base')
1044 local = simplemerge.MergeInput(fcd, labels[0])
1035 local = simplemerge.MergeInput(fcd, labels[0])
1045 other = simplemerge.MergeInput(fco, labels[1])
1036 other = simplemerge.MergeInput(fco, labels[1])
1046 base = simplemerge.MergeInput(fca, labels[2])
1037 base = simplemerge.MergeInput(fca, labels[2])
1047 if mergetype == nomerge:
1038 if mergetype == nomerge:
1048 return func(
1039 return func(
1049 repo,
1040 repo,
1050 mynode,
1041 mynode,
1051 local,
1042 local,
1052 other,
1043 other,
1053 base,
1044 base,
1054 toolconf,
1045 toolconf,
1055 )
1046 )
1056
1047
1057 if orig != fco.path():
1048 if orig != fco.path():
1058 ui.status(
1049 ui.status(
1059 _(b"merging %s and %s to %s\n")
1050 _(b"merging %s and %s to %s\n")
1060 % (uipathfn(orig), uipathfn(fco.path()), fduipath)
1051 % (uipathfn(orig), uipathfn(fco.path()), fduipath)
1061 )
1052 )
1062 else:
1053 else:
1063 ui.status(_(b"merging %s\n") % fduipath)
1054 ui.status(_(b"merging %s\n") % fduipath)
1064
1055
1065 ui.debug(b"my %s other %s ancestor %s\n" % (fcd, fco, fca))
1056 ui.debug(b"my %s other %s ancestor %s\n" % (fcd, fco, fca))
1066
1057
1067 if precheck and not precheck(repo, mynode, fcd, fco, fca, toolconf):
1058 if precheck and not precheck(repo, mynode, fcd, fco, fca, toolconf):
1068 if onfailure:
1059 if onfailure:
1069 if wctx.isinmemory():
1060 if wctx.isinmemory():
1070 raise error.InMemoryMergeConflictsError(
1061 raise error.InMemoryMergeConflictsError(
1071 b'in-memory merge does not support merge conflicts'
1062 b'in-memory merge does not support merge conflicts'
1072 )
1063 )
1073 ui.warn(onfailure % fduipath)
1064 ui.warn(onfailure % fduipath)
1074 return 1, False
1065 return 1, False
1075
1066
1076 backup = _makebackup(repo, ui, wctx, fcd)
1067 backup = _makebackup(repo, ui, wctx, fcd)
1077 r = 1
1068 r = 1
1078 try:
1069 try:
1079 internalmarkerstyle = ui.config(b'ui', b'mergemarkers')
1070 internalmarkerstyle = ui.config(b'ui', b'mergemarkers')
1080 if isexternal:
1071 if isexternal:
1081 markerstyle = _toolstr(ui, tool, b'mergemarkers')
1072 markerstyle = _toolstr(ui, tool, b'mergemarkers')
1082 else:
1073 else:
1083 markerstyle = internalmarkerstyle
1074 markerstyle = internalmarkerstyle
1084
1075
1085 if mergetype == fullmerge:
1076 if mergetype == fullmerge:
1086 # conflict markers generated by premerge will use 'detailed'
1077 # conflict markers generated by premerge will use 'detailed'
1087 # settings if either ui.mergemarkers or the tool's mergemarkers
1078 # settings if either ui.mergemarkers or the tool's mergemarkers
1088 # setting is 'detailed'. This way tools can have basic labels in
1079 # setting is 'detailed'. This way tools can have basic labels in
1089 # space-constrained areas of the UI, but still get full information
1080 # space-constrained areas of the UI, but still get full information
1090 # in conflict markers if premerge is 'keep' or 'keep-merge3'.
1081 # in conflict markers if premerge is 'keep' or 'keep-merge3'.
1091 labeltool = None
1082 labeltool = None
1092 if markerstyle != b'basic':
1083 if markerstyle != b'basic':
1093 # respect 'tool's mergemarkertemplate (which defaults to
1084 # respect 'tool's mergemarkertemplate (which defaults to
1094 # command-templates.mergemarker)
1085 # command-templates.mergemarker)
1095 labeltool = tool
1086 labeltool = tool
1096 if internalmarkerstyle != b'basic' or markerstyle != b'basic':
1087 if internalmarkerstyle != b'basic' or markerstyle != b'basic':
1097 _populate_label_details(
1088 _populate_label_details(
1098 repo, [local, other, base], tool=labeltool
1089 repo, [local, other, base], tool=labeltool
1099 )
1090 )
1100
1091
1101 r = _premerge(
1092 r = _premerge(
1102 repo,
1093 repo,
1103 local,
1094 local,
1104 other,
1095 other,
1105 base,
1096 base,
1106 toolconf,
1097 toolconf,
1107 backup,
1098 backup,
1108 )
1099 )
1109 # we're done if premerge was successful (r is 0)
1100 # we're done if premerge was successful (r is 0)
1110 if not r:
1101 if not r:
1111 return r, False
1102 return r, False
1112
1103
1113 # Reset to basic labels
1104 # Reset to basic labels
1114 local.label = labels[0]
1105 local.label_detail = None
1115 other.label = labels[1]
1106 other.label_detail = None
1116 base.label = labels[2]
1107 base.label_detail = None
1117
1108
1118 if markerstyle != b'basic':
1109 if markerstyle != b'basic':
1119 _populate_label_details(repo, [local, other, base], tool=tool)
1110 _populate_label_details(repo, [local, other, base], tool=tool)
1120
1111
1121 needcheck, r, deleted = func(
1112 needcheck, r, deleted = func(
1122 repo,
1113 repo,
1123 mynode,
1114 mynode,
1124 local,
1115 local,
1125 other,
1116 other,
1126 base,
1117 base,
1127 toolconf,
1118 toolconf,
1128 backup,
1119 backup,
1129 )
1120 )
1130
1121
1131 if needcheck:
1122 if needcheck:
1132 r = _check(repo, r, ui, tool, fcd, backup)
1123 r = _check(repo, r, ui, tool, fcd, backup)
1133
1124
1134 if r:
1125 if r:
1135 if onfailure:
1126 if onfailure:
1136 if wctx.isinmemory():
1127 if wctx.isinmemory():
1137 raise error.InMemoryMergeConflictsError(
1128 raise error.InMemoryMergeConflictsError(
1138 b'in-memory merge '
1129 b'in-memory merge '
1139 b'does not support '
1130 b'does not support '
1140 b'merge conflicts'
1131 b'merge conflicts'
1141 )
1132 )
1142 ui.warn(onfailure % fduipath)
1133 ui.warn(onfailure % fduipath)
1143 _onfilemergefailure(ui)
1134 _onfilemergefailure(ui)
1144
1135
1145 return r, deleted
1136 return r, deleted
1146 finally:
1137 finally:
1147 if not r and backup is not None:
1138 if not r and backup is not None:
1148 backup.remove()
1139 backup.remove()
1149
1140
1150
1141
1151 def _haltmerge():
1142 def _haltmerge():
1152 msg = _(b'merge halted after failed merge (see hg resolve)')
1143 msg = _(b'merge halted after failed merge (see hg resolve)')
1153 raise error.InterventionRequired(msg)
1144 raise error.InterventionRequired(msg)
1154
1145
1155
1146
1156 def _onfilemergefailure(ui):
1147 def _onfilemergefailure(ui):
1157 action = ui.config(b'merge', b'on-failure')
1148 action = ui.config(b'merge', b'on-failure')
1158 if action == b'prompt':
1149 if action == b'prompt':
1159 msg = _(b'continue merge operation (yn)?$$ &Yes $$ &No')
1150 msg = _(b'continue merge operation (yn)?$$ &Yes $$ &No')
1160 if ui.promptchoice(msg, 0) == 1:
1151 if ui.promptchoice(msg, 0) == 1:
1161 _haltmerge()
1152 _haltmerge()
1162 if action == b'halt':
1153 if action == b'halt':
1163 _haltmerge()
1154 _haltmerge()
1164 # default action is 'continue', in which case we neither prompt nor halt
1155 # default action is 'continue', in which case we neither prompt nor halt
1165
1156
1166
1157
1167 def hasconflictmarkers(data):
1158 def hasconflictmarkers(data):
1168 # Detect lines starting with a string of 7 identical characters from the
1159 # Detect lines starting with a string of 7 identical characters from the
1169 # subset Mercurial uses for conflict markers, followed by either the end of
1160 # subset Mercurial uses for conflict markers, followed by either the end of
1170 # line or a space and some text. Note that using [<>=+|-]{7} would detect
1161 # line or a space and some text. Note that using [<>=+|-]{7} would detect
1171 # `<><><><><` as a conflict marker, which we don't want.
1162 # `<><><><><` as a conflict marker, which we don't want.
1172 return bool(
1163 return bool(
1173 re.search(
1164 re.search(
1174 br"^([<>=+|-])\1{6}( .*)$",
1165 br"^([<>=+|-])\1{6}( .*)$",
1175 data,
1166 data,
1176 re.MULTILINE,
1167 re.MULTILINE,
1177 )
1168 )
1178 )
1169 )
1179
1170
1180
1171
1181 def _check(repo, r, ui, tool, fcd, backup):
1172 def _check(repo, r, ui, tool, fcd, backup):
1182 fd = fcd.path()
1173 fd = fcd.path()
1183 uipathfn = scmutil.getuipathfn(repo)
1174 uipathfn = scmutil.getuipathfn(repo)
1184
1175
1185 if not r and (
1176 if not r and (
1186 _toolbool(ui, tool, b"checkconflicts")
1177 _toolbool(ui, tool, b"checkconflicts")
1187 or b'conflicts' in _toollist(ui, tool, b"check")
1178 or b'conflicts' in _toollist(ui, tool, b"check")
1188 ):
1179 ):
1189 if hasconflictmarkers(fcd.data()):
1180 if hasconflictmarkers(fcd.data()):
1190 r = 1
1181 r = 1
1191
1182
1192 checked = False
1183 checked = False
1193 if b'prompt' in _toollist(ui, tool, b"check"):
1184 if b'prompt' in _toollist(ui, tool, b"check"):
1194 checked = True
1185 checked = True
1195 if ui.promptchoice(
1186 if ui.promptchoice(
1196 _(b"was merge of '%s' successful (yn)?$$ &Yes $$ &No")
1187 _(b"was merge of '%s' successful (yn)?$$ &Yes $$ &No")
1197 % uipathfn(fd),
1188 % uipathfn(fd),
1198 1,
1189 1,
1199 ):
1190 ):
1200 r = 1
1191 r = 1
1201
1192
1202 if (
1193 if (
1203 not r
1194 not r
1204 and not checked
1195 and not checked
1205 and (
1196 and (
1206 _toolbool(ui, tool, b"checkchanged")
1197 _toolbool(ui, tool, b"checkchanged")
1207 or b'changed' in _toollist(ui, tool, b"check")
1198 or b'changed' in _toollist(ui, tool, b"check")
1208 )
1199 )
1209 ):
1200 ):
1210 if backup is not None and not fcd.cmp(backup):
1201 if backup is not None and not fcd.cmp(backup):
1211 if ui.promptchoice(
1202 if ui.promptchoice(
1212 _(
1203 _(
1213 b" output file %s appears unchanged\n"
1204 b" output file %s appears unchanged\n"
1214 b"was merge successful (yn)?"
1205 b"was merge successful (yn)?"
1215 b"$$ &Yes $$ &No"
1206 b"$$ &Yes $$ &No"
1216 )
1207 )
1217 % uipathfn(fd),
1208 % uipathfn(fd),
1218 1,
1209 1,
1219 ):
1210 ):
1220 r = 1
1211 r = 1
1221
1212
1222 if backup is not None and _toolbool(ui, tool, b"fixeol"):
1213 if backup is not None and _toolbool(ui, tool, b"fixeol"):
1223 _matcheol(_workingpath(repo, fcd), backup)
1214 _matcheol(_workingpath(repo, fcd), backup)
1224
1215
1225 return r
1216 return r
1226
1217
1227
1218
1228 def _workingpath(repo, ctx):
1219 def _workingpath(repo, ctx):
1229 return repo.wjoin(ctx.path())
1220 return repo.wjoin(ctx.path())
1230
1221
1231
1222
1232 def loadinternalmerge(ui, extname, registrarobj):
1223 def loadinternalmerge(ui, extname, registrarobj):
1233 """Load internal merge tool from specified registrarobj"""
1224 """Load internal merge tool from specified registrarobj"""
1234 for name, func in pycompat.iteritems(registrarobj._table):
1225 for name, func in pycompat.iteritems(registrarobj._table):
1235 fullname = b':' + name
1226 fullname = b':' + name
1236 internals[fullname] = func
1227 internals[fullname] = func
1237 internals[b'internal:' + name] = func
1228 internals[b'internal:' + name] = func
1238 internalsdoc[fullname] = func
1229 internalsdoc[fullname] = func
1239
1230
1240 capabilities = sorted([k for k, v in func.capabilities.items() if v])
1231 capabilities = sorted([k for k, v in func.capabilities.items() if v])
1241 if capabilities:
1232 if capabilities:
1242 capdesc = b" (actual capabilities: %s)" % b', '.join(
1233 capdesc = b" (actual capabilities: %s)" % b', '.join(
1243 capabilities
1234 capabilities
1244 )
1235 )
1245 func.__doc__ = func.__doc__ + pycompat.sysstr(b"\n\n%s" % capdesc)
1236 func.__doc__ = func.__doc__ + pycompat.sysstr(b"\n\n%s" % capdesc)
1246
1237
1247 # to put i18n comments into hg.pot for automatically generated texts
1238 # to put i18n comments into hg.pot for automatically generated texts
1248
1239
1249 # i18n: "binary" and "symlink" are keywords
1240 # i18n: "binary" and "symlink" are keywords
1250 # i18n: this text is added automatically
1241 # i18n: this text is added automatically
1251 _(b" (actual capabilities: binary, symlink)")
1242 _(b" (actual capabilities: binary, symlink)")
1252 # i18n: "binary" is keyword
1243 # i18n: "binary" is keyword
1253 # i18n: this text is added automatically
1244 # i18n: this text is added automatically
1254 _(b" (actual capabilities: binary)")
1245 _(b" (actual capabilities: binary)")
1255 # i18n: "symlink" is keyword
1246 # i18n: "symlink" is keyword
1256 # i18n: this text is added automatically
1247 # i18n: this text is added automatically
1257 _(b" (actual capabilities: symlink)")
1248 _(b" (actual capabilities: symlink)")
1258
1249
1259
1250
1260 # load built-in merge tools explicitly to setup internalsdoc
1251 # load built-in merge tools explicitly to setup internalsdoc
1261 loadinternalmerge(None, None, internaltool)
1252 loadinternalmerge(None, None, internaltool)
1262
1253
1263 # tell hggettext to extract docstrings from these functions:
1254 # tell hggettext to extract docstrings from these functions:
1264 i18nfunctions = internals.values()
1255 i18nfunctions = internals.values()
@@ -1,524 +1,538 b''
1 # Copyright (C) 2004, 2005 Canonical Ltd
1 # Copyright (C) 2004, 2005 Canonical Ltd
2 #
2 #
3 # This program is free software; you can redistribute it and/or modify
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
6 # (at your option) any later version.
7 #
7 #
8 # This program is distributed in the hope that it will be useful,
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
11 # GNU General Public License for more details.
12 #
12 #
13 # You should have received a copy of the GNU General Public License
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15
15
16 # mbp: "you know that thing where cvs gives you conflict markers?"
16 # mbp: "you know that thing where cvs gives you conflict markers?"
17 # s: "i hate that."
17 # s: "i hate that."
18
18
19 from __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 from .i18n import _
21 from .i18n import _
22 from .thirdparty import attr
22 from .thirdparty import attr
23 from . import (
23 from . import (
24 error,
24 error,
25 mdiff,
25 mdiff,
26 pycompat,
26 pycompat,
27 )
27 )
28 from .utils import stringutil
28 from .utils import stringutil
29
29
30
30
31 def intersect(ra, rb):
31 def intersect(ra, rb):
32 """Given two ranges return the range where they intersect or None.
32 """Given two ranges return the range where they intersect or None.
33
33
34 >>> intersect((0, 10), (0, 6))
34 >>> intersect((0, 10), (0, 6))
35 (0, 6)
35 (0, 6)
36 >>> intersect((0, 10), (5, 15))
36 >>> intersect((0, 10), (5, 15))
37 (5, 10)
37 (5, 10)
38 >>> intersect((0, 10), (10, 15))
38 >>> intersect((0, 10), (10, 15))
39 >>> intersect((0, 9), (10, 15))
39 >>> intersect((0, 9), (10, 15))
40 >>> intersect((0, 9), (7, 15))
40 >>> intersect((0, 9), (7, 15))
41 (7, 9)
41 (7, 9)
42 """
42 """
43 assert ra[0] <= ra[1]
43 assert ra[0] <= ra[1]
44 assert rb[0] <= rb[1]
44 assert rb[0] <= rb[1]
45
45
46 sa = max(ra[0], rb[0])
46 sa = max(ra[0], rb[0])
47 sb = min(ra[1], rb[1])
47 sb = min(ra[1], rb[1])
48 if sa < sb:
48 if sa < sb:
49 return sa, sb
49 return sa, sb
50 else:
50 else:
51 return None
51 return None
52
52
53
53
54 def compare_range(a, astart, aend, b, bstart, bend):
54 def compare_range(a, astart, aend, b, bstart, bend):
55 """Compare a[astart:aend] == b[bstart:bend], without slicing."""
55 """Compare a[astart:aend] == b[bstart:bend], without slicing."""
56 if (aend - astart) != (bend - bstart):
56 if (aend - astart) != (bend - bstart):
57 return False
57 return False
58 for ia, ib in zip(
58 for ia, ib in zip(
59 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
59 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
60 ):
60 ):
61 if a[ia] != b[ib]:
61 if a[ia] != b[ib]:
62 return False
62 return False
63 else:
63 else:
64 return True
64 return True
65
65
66
66
67 class Merge3Text(object):
67 class Merge3Text(object):
68 """3-way merge of texts.
68 """3-way merge of texts.
69
69
70 Given strings BASE, OTHER, THIS, tries to produce a combined text
70 Given strings BASE, OTHER, THIS, tries to produce a combined text
71 incorporating the changes from both BASE->OTHER and BASE->THIS."""
71 incorporating the changes from both BASE->OTHER and BASE->THIS."""
72
72
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
74 self.basetext = basetext
74 self.basetext = basetext
75 self.atext = atext
75 self.atext = atext
76 self.btext = btext
76 self.btext = btext
77 if base is None:
77 if base is None:
78 base = mdiff.splitnewlines(basetext)
78 base = mdiff.splitnewlines(basetext)
79 if a is None:
79 if a is None:
80 a = mdiff.splitnewlines(atext)
80 a = mdiff.splitnewlines(atext)
81 if b is None:
81 if b is None:
82 b = mdiff.splitnewlines(btext)
82 b = mdiff.splitnewlines(btext)
83 self.base = base
83 self.base = base
84 self.a = a
84 self.a = a
85 self.b = b
85 self.b = b
86
86
87 def merge_groups(self):
87 def merge_groups(self):
88 """Yield sequence of line groups. Each one is a tuple:
88 """Yield sequence of line groups. Each one is a tuple:
89
89
90 'unchanged', lines
90 'unchanged', lines
91 Lines unchanged from base
91 Lines unchanged from base
92
92
93 'a', lines
93 'a', lines
94 Lines taken from a
94 Lines taken from a
95
95
96 'same', lines
96 'same', lines
97 Lines taken from a (and equal to b)
97 Lines taken from a (and equal to b)
98
98
99 'b', lines
99 'b', lines
100 Lines taken from b
100 Lines taken from b
101
101
102 'conflict', (base_lines, a_lines, b_lines)
102 'conflict', (base_lines, a_lines, b_lines)
103 Lines from base were changed to either a or b and conflict.
103 Lines from base were changed to either a or b and conflict.
104 """
104 """
105 for t in self.merge_regions():
105 for t in self.merge_regions():
106 what = t[0]
106 what = t[0]
107 if what == b'unchanged':
107 if what == b'unchanged':
108 yield what, self.base[t[1] : t[2]]
108 yield what, self.base[t[1] : t[2]]
109 elif what == b'a' or what == b'same':
109 elif what == b'a' or what == b'same':
110 yield what, self.a[t[1] : t[2]]
110 yield what, self.a[t[1] : t[2]]
111 elif what == b'b':
111 elif what == b'b':
112 yield what, self.b[t[1] : t[2]]
112 yield what, self.b[t[1] : t[2]]
113 elif what == b'conflict':
113 elif what == b'conflict':
114 yield (
114 yield (
115 what,
115 what,
116 (
116 (
117 self.base[t[1] : t[2]],
117 self.base[t[1] : t[2]],
118 self.a[t[3] : t[4]],
118 self.a[t[3] : t[4]],
119 self.b[t[5] : t[6]],
119 self.b[t[5] : t[6]],
120 ),
120 ),
121 )
121 )
122 else:
122 else:
123 raise ValueError(what)
123 raise ValueError(what)
124
124
125 def merge_regions(self):
125 def merge_regions(self):
126 """Return sequences of matching and conflicting regions.
126 """Return sequences of matching and conflicting regions.
127
127
128 This returns tuples, where the first value says what kind we
128 This returns tuples, where the first value says what kind we
129 have:
129 have:
130
130
131 'unchanged', start, end
131 'unchanged', start, end
132 Take a region of base[start:end]
132 Take a region of base[start:end]
133
133
134 'same', astart, aend
134 'same', astart, aend
135 b and a are different from base but give the same result
135 b and a are different from base but give the same result
136
136
137 'a', start, end
137 'a', start, end
138 Non-clashing insertion from a[start:end]
138 Non-clashing insertion from a[start:end]
139
139
140 'conflict', zstart, zend, astart, aend, bstart, bend
140 'conflict', zstart, zend, astart, aend, bstart, bend
141 Conflict between a and b, with z as common ancestor
141 Conflict between a and b, with z as common ancestor
142
142
143 Method is as follows:
143 Method is as follows:
144
144
145 The two sequences align only on regions which match the base
145 The two sequences align only on regions which match the base
146 and both descendants. These are found by doing a two-way diff
146 and both descendants. These are found by doing a two-way diff
147 of each one against the base, and then finding the
147 of each one against the base, and then finding the
148 intersections between those regions. These "sync regions"
148 intersections between those regions. These "sync regions"
149 are by definition unchanged in both and easily dealt with.
149 are by definition unchanged in both and easily dealt with.
150
150
151 The regions in between can be in any of three cases:
151 The regions in between can be in any of three cases:
152 conflicted, or changed on only one side.
152 conflicted, or changed on only one side.
153 """
153 """
154
154
155 # section a[0:ia] has been disposed of, etc
155 # section a[0:ia] has been disposed of, etc
156 iz = ia = ib = 0
156 iz = ia = ib = 0
157
157
158 for region in self.find_sync_regions():
158 for region in self.find_sync_regions():
159 zmatch, zend, amatch, aend, bmatch, bend = region
159 zmatch, zend, amatch, aend, bmatch, bend = region
160 # print 'match base [%d:%d]' % (zmatch, zend)
160 # print 'match base [%d:%d]' % (zmatch, zend)
161
161
162 matchlen = zend - zmatch
162 matchlen = zend - zmatch
163 assert matchlen >= 0
163 assert matchlen >= 0
164 assert matchlen == (aend - amatch)
164 assert matchlen == (aend - amatch)
165 assert matchlen == (bend - bmatch)
165 assert matchlen == (bend - bmatch)
166
166
167 len_a = amatch - ia
167 len_a = amatch - ia
168 len_b = bmatch - ib
168 len_b = bmatch - ib
169 len_base = zmatch - iz
169 len_base = zmatch - iz
170 assert len_a >= 0
170 assert len_a >= 0
171 assert len_b >= 0
171 assert len_b >= 0
172 assert len_base >= 0
172 assert len_base >= 0
173
173
174 # print 'unmatched a=%d, b=%d' % (len_a, len_b)
174 # print 'unmatched a=%d, b=%d' % (len_a, len_b)
175
175
176 if len_a or len_b:
176 if len_a or len_b:
177 # try to avoid actually slicing the lists
177 # try to avoid actually slicing the lists
178 equal_a = compare_range(
178 equal_a = compare_range(
179 self.a, ia, amatch, self.base, iz, zmatch
179 self.a, ia, amatch, self.base, iz, zmatch
180 )
180 )
181 equal_b = compare_range(
181 equal_b = compare_range(
182 self.b, ib, bmatch, self.base, iz, zmatch
182 self.b, ib, bmatch, self.base, iz, zmatch
183 )
183 )
184 same = compare_range(self.a, ia, amatch, self.b, ib, bmatch)
184 same = compare_range(self.a, ia, amatch, self.b, ib, bmatch)
185
185
186 if same:
186 if same:
187 yield b'same', ia, amatch
187 yield b'same', ia, amatch
188 elif equal_a and not equal_b:
188 elif equal_a and not equal_b:
189 yield b'b', ib, bmatch
189 yield b'b', ib, bmatch
190 elif equal_b and not equal_a:
190 elif equal_b and not equal_a:
191 yield b'a', ia, amatch
191 yield b'a', ia, amatch
192 elif not equal_a and not equal_b:
192 elif not equal_a and not equal_b:
193 yield b'conflict', iz, zmatch, ia, amatch, ib, bmatch
193 yield b'conflict', iz, zmatch, ia, amatch, ib, bmatch
194 else:
194 else:
195 raise AssertionError(b"can't handle a=b=base but unmatched")
195 raise AssertionError(b"can't handle a=b=base but unmatched")
196
196
197 ia = amatch
197 ia = amatch
198 ib = bmatch
198 ib = bmatch
199 iz = zmatch
199 iz = zmatch
200
200
201 # if the same part of the base was deleted on both sides
201 # if the same part of the base was deleted on both sides
202 # that's OK, we can just skip it.
202 # that's OK, we can just skip it.
203
203
204 if matchlen > 0:
204 if matchlen > 0:
205 assert ia == amatch
205 assert ia == amatch
206 assert ib == bmatch
206 assert ib == bmatch
207 assert iz == zmatch
207 assert iz == zmatch
208
208
209 yield b'unchanged', zmatch, zend
209 yield b'unchanged', zmatch, zend
210 iz = zend
210 iz = zend
211 ia = aend
211 ia = aend
212 ib = bend
212 ib = bend
213
213
214 def find_sync_regions(self):
214 def find_sync_regions(self):
215 """Return a list of sync regions, where both descendants match the base.
215 """Return a list of sync regions, where both descendants match the base.
216
216
217 Generates a list of (base1, base2, a1, a2, b1, b2). There is
217 Generates a list of (base1, base2, a1, a2, b1, b2). There is
218 always a zero-length sync region at the end of all the files.
218 always a zero-length sync region at the end of all the files.
219 """
219 """
220
220
221 ia = ib = 0
221 ia = ib = 0
222 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
222 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
223 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
223 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
224 len_a = len(amatches)
224 len_a = len(amatches)
225 len_b = len(bmatches)
225 len_b = len(bmatches)
226
226
227 sl = []
227 sl = []
228
228
229 while ia < len_a and ib < len_b:
229 while ia < len_a and ib < len_b:
230 abase, amatch, alen = amatches[ia]
230 abase, amatch, alen = amatches[ia]
231 bbase, bmatch, blen = bmatches[ib]
231 bbase, bmatch, blen = bmatches[ib]
232
232
233 # there is an unconflicted block at i; how long does it
233 # there is an unconflicted block at i; how long does it
234 # extend? until whichever one ends earlier.
234 # extend? until whichever one ends earlier.
235 i = intersect((abase, abase + alen), (bbase, bbase + blen))
235 i = intersect((abase, abase + alen), (bbase, bbase + blen))
236 if i:
236 if i:
237 intbase = i[0]
237 intbase = i[0]
238 intend = i[1]
238 intend = i[1]
239 intlen = intend - intbase
239 intlen = intend - intbase
240
240
241 # found a match of base[i[0], i[1]]; this may be less than
241 # found a match of base[i[0], i[1]]; this may be less than
242 # the region that matches in either one
242 # the region that matches in either one
243 assert intlen <= alen
243 assert intlen <= alen
244 assert intlen <= blen
244 assert intlen <= blen
245 assert abase <= intbase
245 assert abase <= intbase
246 assert bbase <= intbase
246 assert bbase <= intbase
247
247
248 asub = amatch + (intbase - abase)
248 asub = amatch + (intbase - abase)
249 bsub = bmatch + (intbase - bbase)
249 bsub = bmatch + (intbase - bbase)
250 aend = asub + intlen
250 aend = asub + intlen
251 bend = bsub + intlen
251 bend = bsub + intlen
252
252
253 assert self.base[intbase:intend] == self.a[asub:aend], (
253 assert self.base[intbase:intend] == self.a[asub:aend], (
254 self.base[intbase:intend],
254 self.base[intbase:intend],
255 self.a[asub:aend],
255 self.a[asub:aend],
256 )
256 )
257
257
258 assert self.base[intbase:intend] == self.b[bsub:bend]
258 assert self.base[intbase:intend] == self.b[bsub:bend]
259
259
260 sl.append((intbase, intend, asub, aend, bsub, bend))
260 sl.append((intbase, intend, asub, aend, bsub, bend))
261
261
262 # advance whichever one ends first in the base text
262 # advance whichever one ends first in the base text
263 if (abase + alen) < (bbase + blen):
263 if (abase + alen) < (bbase + blen):
264 ia += 1
264 ia += 1
265 else:
265 else:
266 ib += 1
266 ib += 1
267
267
268 intbase = len(self.base)
268 intbase = len(self.base)
269 abase = len(self.a)
269 abase = len(self.a)
270 bbase = len(self.b)
270 bbase = len(self.b)
271 sl.append((intbase, intbase, abase, abase, bbase, bbase))
271 sl.append((intbase, intbase, abase, abase, bbase, bbase))
272
272
273 return sl
273 return sl
274
274
275
275
276 def _verifytext(text, path, ui, opts):
276 def _verifytext(text, path, ui, opts):
277 """verifies that text is non-binary (unless opts[text] is passed,
277 """verifies that text is non-binary (unless opts[text] is passed,
278 then we just warn)"""
278 then we just warn)"""
279 if stringutil.binary(text):
279 if stringutil.binary(text):
280 msg = _(b"%s looks like a binary file.") % path
280 msg = _(b"%s looks like a binary file.") % path
281 if not opts.get('quiet'):
281 if not opts.get('quiet'):
282 ui.warn(_(b'warning: %s\n') % msg)
282 ui.warn(_(b'warning: %s\n') % msg)
283 if not opts.get('text'):
283 if not opts.get('text'):
284 raise error.Abort(msg)
284 raise error.Abort(msg)
285 return text
285 return text
286
286
287
287
288 def _format_labels(*inputs):
288 def _format_labels(*inputs):
289 pad = max(len(input.label) if input.label else 0 for input in inputs)
289 labels = []
290 labels = []
290 for input in inputs:
291 for input in inputs:
291 if input.label:
292 if input.label:
292 labels.append(input.label)
293 if input.label_detail:
294 label = (
295 (input.label + b':').ljust(pad + 1)
296 + b' '
297 + input.label_detail
298 )
299 else:
300 label = input.label
301 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
302 labels.append(stringutil.ellipsis(label, 80 - 8))
293 else:
303 else:
294 labels.append(None)
304 labels.append(None)
295 return labels
305 return labels
296
306
297
307
298 def _detect_newline(m3):
308 def _detect_newline(m3):
299 if len(m3.a) > 0:
309 if len(m3.a) > 0:
300 if m3.a[0].endswith(b'\r\n'):
310 if m3.a[0].endswith(b'\r\n'):
301 return b'\r\n'
311 return b'\r\n'
302 elif m3.a[0].endswith(b'\r'):
312 elif m3.a[0].endswith(b'\r'):
303 return b'\r'
313 return b'\r'
304 return b'\n'
314 return b'\n'
305
315
306
316
307 def _minimize(a_lines, b_lines):
317 def _minimize(a_lines, b_lines):
308 """Trim conflict regions of lines where A and B sides match.
318 """Trim conflict regions of lines where A and B sides match.
309
319
310 Lines where both A and B have made the same changes at the beginning
320 Lines where both A and B have made the same changes at the beginning
311 or the end of each merge region are eliminated from the conflict
321 or the end of each merge region are eliminated from the conflict
312 region and are instead considered the same.
322 region and are instead considered the same.
313 """
323 """
314 alen = len(a_lines)
324 alen = len(a_lines)
315 blen = len(b_lines)
325 blen = len(b_lines)
316
326
317 # find matches at the front
327 # find matches at the front
318 ii = 0
328 ii = 0
319 while ii < alen and ii < blen and a_lines[ii] == b_lines[ii]:
329 while ii < alen and ii < blen and a_lines[ii] == b_lines[ii]:
320 ii += 1
330 ii += 1
321 startmatches = ii
331 startmatches = ii
322
332
323 # find matches at the end
333 # find matches at the end
324 ii = 0
334 ii = 0
325 while ii < alen and ii < blen and a_lines[-ii - 1] == b_lines[-ii - 1]:
335 while ii < alen and ii < blen and a_lines[-ii - 1] == b_lines[-ii - 1]:
326 ii += 1
336 ii += 1
327 endmatches = ii
337 endmatches = ii
328
338
329 lines_before = a_lines[:startmatches]
339 lines_before = a_lines[:startmatches]
330 new_a_lines = a_lines[startmatches : alen - endmatches]
340 new_a_lines = a_lines[startmatches : alen - endmatches]
331 new_b_lines = b_lines[startmatches : blen - endmatches]
341 new_b_lines = b_lines[startmatches : blen - endmatches]
332 lines_after = a_lines[alen - endmatches :]
342 lines_after = a_lines[alen - endmatches :]
333 return lines_before, new_a_lines, new_b_lines, lines_after
343 return lines_before, new_a_lines, new_b_lines, lines_after
334
344
335
345
336 def render_minimized(
346 def render_minimized(
337 m3,
347 m3,
338 name_a=None,
348 name_a=None,
339 name_b=None,
349 name_b=None,
340 start_marker=b'<<<<<<<',
350 start_marker=b'<<<<<<<',
341 mid_marker=b'=======',
351 mid_marker=b'=======',
342 end_marker=b'>>>>>>>',
352 end_marker=b'>>>>>>>',
343 ):
353 ):
344 """Return merge in cvs-like form."""
354 """Return merge in cvs-like form."""
345 newline = _detect_newline(m3)
355 newline = _detect_newline(m3)
346 conflicts = False
356 conflicts = False
347 if name_a:
357 if name_a:
348 start_marker = start_marker + b' ' + name_a
358 start_marker = start_marker + b' ' + name_a
349 if name_b:
359 if name_b:
350 end_marker = end_marker + b' ' + name_b
360 end_marker = end_marker + b' ' + name_b
351 merge_groups = m3.merge_groups()
361 merge_groups = m3.merge_groups()
352 lines = []
362 lines = []
353 for what, group_lines in merge_groups:
363 for what, group_lines in merge_groups:
354 if what == b'conflict':
364 if what == b'conflict':
355 conflicts = True
365 conflicts = True
356 base_lines, a_lines, b_lines = group_lines
366 base_lines, a_lines, b_lines = group_lines
357 minimized = _minimize(a_lines, b_lines)
367 minimized = _minimize(a_lines, b_lines)
358 lines_before, a_lines, b_lines, lines_after = minimized
368 lines_before, a_lines, b_lines, lines_after = minimized
359 lines.extend(lines_before)
369 lines.extend(lines_before)
360 lines.append(start_marker + newline)
370 lines.append(start_marker + newline)
361 lines.extend(a_lines)
371 lines.extend(a_lines)
362 lines.append(mid_marker + newline)
372 lines.append(mid_marker + newline)
363 lines.extend(b_lines)
373 lines.extend(b_lines)
364 lines.append(end_marker + newline)
374 lines.append(end_marker + newline)
365 lines.extend(lines_after)
375 lines.extend(lines_after)
366 else:
376 else:
367 lines.extend(group_lines)
377 lines.extend(group_lines)
368 return lines, conflicts
378 return lines, conflicts
369
379
370
380
371 def render_merge3(m3, name_a, name_b, name_base):
381 def render_merge3(m3, name_a, name_b, name_base):
372 """Render conflicts as 3-way conflict markers."""
382 """Render conflicts as 3-way conflict markers."""
373 newline = _detect_newline(m3)
383 newline = _detect_newline(m3)
374 conflicts = False
384 conflicts = False
375 lines = []
385 lines = []
376 for what, group_lines in m3.merge_groups():
386 for what, group_lines in m3.merge_groups():
377 if what == b'conflict':
387 if what == b'conflict':
378 base_lines, a_lines, b_lines = group_lines
388 base_lines, a_lines, b_lines = group_lines
379 conflicts = True
389 conflicts = True
380 lines.append(b'<<<<<<< ' + name_a + newline)
390 lines.append(b'<<<<<<< ' + name_a + newline)
381 lines.extend(a_lines)
391 lines.extend(a_lines)
382 lines.append(b'||||||| ' + name_base + newline)
392 lines.append(b'||||||| ' + name_base + newline)
383 lines.extend(base_lines)
393 lines.extend(base_lines)
384 lines.append(b'=======' + newline)
394 lines.append(b'=======' + newline)
385 lines.extend(b_lines)
395 lines.extend(b_lines)
386 lines.append(b'>>>>>>> ' + name_b + newline)
396 lines.append(b'>>>>>>> ' + name_b + newline)
387 else:
397 else:
388 lines.extend(group_lines)
398 lines.extend(group_lines)
389 return lines, conflicts
399 return lines, conflicts
390
400
391
401
392 def render_mergediff(m3, name_a, name_b, name_base):
402 def render_mergediff(m3, name_a, name_b, name_base):
393 """Render conflicts as conflict markers with one snapshot and one diff."""
403 """Render conflicts as conflict markers with one snapshot and one diff."""
394 newline = _detect_newline(m3)
404 newline = _detect_newline(m3)
395 lines = []
405 lines = []
396 conflicts = False
406 conflicts = False
397 for what, group_lines in m3.merge_groups():
407 for what, group_lines in m3.merge_groups():
398 if what == b'conflict':
408 if what == b'conflict':
399 base_lines, a_lines, b_lines = group_lines
409 base_lines, a_lines, b_lines = group_lines
400 base_text = b''.join(base_lines)
410 base_text = b''.join(base_lines)
401 b_blocks = list(
411 b_blocks = list(
402 mdiff.allblocks(
412 mdiff.allblocks(
403 base_text,
413 base_text,
404 b''.join(b_lines),
414 b''.join(b_lines),
405 lines1=base_lines,
415 lines1=base_lines,
406 lines2=b_lines,
416 lines2=b_lines,
407 )
417 )
408 )
418 )
409 a_blocks = list(
419 a_blocks = list(
410 mdiff.allblocks(
420 mdiff.allblocks(
411 base_text,
421 base_text,
412 b''.join(a_lines),
422 b''.join(a_lines),
413 lines1=base_lines,
423 lines1=base_lines,
414 lines2=b_lines,
424 lines2=b_lines,
415 )
425 )
416 )
426 )
417
427
418 def matching_lines(blocks):
428 def matching_lines(blocks):
419 return sum(
429 return sum(
420 block[1] - block[0]
430 block[1] - block[0]
421 for block, kind in blocks
431 for block, kind in blocks
422 if kind == b'='
432 if kind == b'='
423 )
433 )
424
434
425 def diff_lines(blocks, lines1, lines2):
435 def diff_lines(blocks, lines1, lines2):
426 for block, kind in blocks:
436 for block, kind in blocks:
427 if kind == b'=':
437 if kind == b'=':
428 for line in lines1[block[0] : block[1]]:
438 for line in lines1[block[0] : block[1]]:
429 yield b' ' + line
439 yield b' ' + line
430 else:
440 else:
431 for line in lines1[block[0] : block[1]]:
441 for line in lines1[block[0] : block[1]]:
432 yield b'-' + line
442 yield b'-' + line
433 for line in lines2[block[2] : block[3]]:
443 for line in lines2[block[2] : block[3]]:
434 yield b'+' + line
444 yield b'+' + line
435
445
436 lines.append(b"<<<<<<<" + newline)
446 lines.append(b"<<<<<<<" + newline)
437 if matching_lines(a_blocks) < matching_lines(b_blocks):
447 if matching_lines(a_blocks) < matching_lines(b_blocks):
438 lines.append(b"======= " + name_a + newline)
448 lines.append(b"======= " + name_a + newline)
439 lines.extend(a_lines)
449 lines.extend(a_lines)
440 lines.append(b"------- " + name_base + newline)
450 lines.append(b"------- " + name_base + newline)
441 lines.append(b"+++++++ " + name_b + newline)
451 lines.append(b"+++++++ " + name_b + newline)
442 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
452 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
443 else:
453 else:
444 lines.append(b"------- " + name_base + newline)
454 lines.append(b"------- " + name_base + newline)
445 lines.append(b"+++++++ " + name_a + newline)
455 lines.append(b"+++++++ " + name_a + newline)
446 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
456 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
447 lines.append(b"======= " + name_b + newline)
457 lines.append(b"======= " + name_b + newline)
448 lines.extend(b_lines)
458 lines.extend(b_lines)
449 lines.append(b">>>>>>>" + newline)
459 lines.append(b">>>>>>>" + newline)
450 conflicts = True
460 conflicts = True
451 else:
461 else:
452 lines.extend(group_lines)
462 lines.extend(group_lines)
453 return lines, conflicts
463 return lines, conflicts
454
464
455
465
456 def _resolve(m3, sides):
466 def _resolve(m3, sides):
457 lines = []
467 lines = []
458 for what, group_lines in m3.merge_groups():
468 for what, group_lines in m3.merge_groups():
459 if what == b'conflict':
469 if what == b'conflict':
460 for side in sides:
470 for side in sides:
461 lines.extend(group_lines[side])
471 lines.extend(group_lines[side])
462 else:
472 else:
463 lines.extend(group_lines)
473 lines.extend(group_lines)
464 return lines
474 return lines
465
475
466
476
467 @attr.s
477 @attr.s
468 class MergeInput(object):
478 class MergeInput(object):
469 fctx = attr.ib()
479 fctx = attr.ib()
470 label = attr.ib(default=None)
480 label = attr.ib(default=None)
481 # If the "detail" part is set, then that is rendered after the label and
482 # separated by a ':'. The label is padded to make the ':' aligned among all
483 # merge inputs.
484 label_detail = attr.ib(default=None)
471
485
472
486
473 def simplemerge(ui, local, base, other, **opts):
487 def simplemerge(ui, local, base, other, **opts):
474 """Performs the simplemerge algorithm.
488 """Performs the simplemerge algorithm.
475
489
476 The merged result is written into `localctx`.
490 The merged result is written into `localctx`.
477 """
491 """
478
492
479 def readctx(ctx):
493 def readctx(ctx):
480 # Merges were always run in the working copy before, which means
494 # Merges were always run in the working copy before, which means
481 # they used decoded data, if the user defined any repository
495 # they used decoded data, if the user defined any repository
482 # filters.
496 # filters.
483 #
497 #
484 # Maintain that behavior today for BC, though perhaps in the future
498 # Maintain that behavior today for BC, though perhaps in the future
485 # it'd be worth considering whether merging encoded data (what the
499 # it'd be worth considering whether merging encoded data (what the
486 # repository usually sees) might be more useful.
500 # repository usually sees) might be more useful.
487 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
501 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
488
502
489 try:
503 try:
490 localtext = readctx(local.fctx)
504 localtext = readctx(local.fctx)
491 basetext = readctx(base.fctx)
505 basetext = readctx(base.fctx)
492 othertext = readctx(other.fctx)
506 othertext = readctx(other.fctx)
493 except error.Abort:
507 except error.Abort:
494 return True
508 return True
495
509
496 m3 = Merge3Text(basetext, localtext, othertext)
510 m3 = Merge3Text(basetext, localtext, othertext)
497 conflicts = False
511 conflicts = False
498 mode = opts.get('mode', b'merge')
512 mode = opts.get('mode', b'merge')
499 if mode == b'union':
513 if mode == b'union':
500 lines = _resolve(m3, (1, 2))
514 lines = _resolve(m3, (1, 2))
501 elif mode == b'local':
515 elif mode == b'local':
502 lines = _resolve(m3, (1,))
516 lines = _resolve(m3, (1,))
503 elif mode == b'other':
517 elif mode == b'other':
504 lines = _resolve(m3, (2,))
518 lines = _resolve(m3, (2,))
505 else:
519 else:
506 if mode == b'mergediff':
520 if mode == b'mergediff':
507 labels = _format_labels(local, other, base)
521 labels = _format_labels(local, other, base)
508 lines, conflicts = render_mergediff(m3, *labels)
522 lines, conflicts = render_mergediff(m3, *labels)
509 elif mode == b'merge3':
523 elif mode == b'merge3':
510 labels = _format_labels(local, other, base)
524 labels = _format_labels(local, other, base)
511 lines, conflicts = render_merge3(m3, *labels)
525 lines, conflicts = render_merge3(m3, *labels)
512 else:
526 else:
513 labels = _format_labels(local, other)
527 labels = _format_labels(local, other)
514 lines, conflicts = render_minimized(m3, *labels)
528 lines, conflicts = render_minimized(m3, *labels)
515
529
516 mergedtext = b''.join(lines)
530 mergedtext = b''.join(lines)
517 if opts.get('print'):
531 if opts.get('print'):
518 ui.fout.write(mergedtext)
532 ui.fout.write(mergedtext)
519 else:
533 else:
520 # local.fctx.flags() already has the merged flags (done in
534 # local.fctx.flags() already has the merged flags (done in
521 # mergestate.resolve())
535 # mergestate.resolve())
522 local.fctx.write(mergedtext, local.fctx.flags())
536 local.fctx.write(mergedtext, local.fctx.flags())
523
537
524 return conflicts
538 return conflicts
@@ -1,25 +1,31 b''
1 == New Features ==
1 == New Features ==
2
2
3
3
4 == Default Format Change ==
4 == Default Format Change ==
5
5
6 These changes affects newly created repositories (or new clone) done with
6 These changes affects newly created repositories (or new clone) done with
7 Mercurial XXX.
7 Mercurial XXX.
8
8
9
9
10 == New Experimental Features ==
10 == New Experimental Features ==
11
11
12 == Bug Fixes ==
12 == Bug Fixes ==
13
13
14 The `--no-check` and `--no-merge` now properly overwrite the behavior from `commands.update.check`.
14 The `--no-check` and `--no-merge` now properly overwrite the behavior from `commands.update.check`.
15
15
16 == Backwards Compatibility Changes ==
16 == Backwards Compatibility Changes ==
17
17
18 The remotefilelog extension now requires an appropiate excludepattern
18 The remotefilelog extension now requires an appropiate excludepattern
19 for subrepositories.
19 for subrepositories.
20
20
21 The labels passed to merge tools have changed slightly. Merge tools can get
22 labels passed to them if you include `$labellocal`, `$labelbase`, and/or
23 `$labelother` in the `merge-tool.<tool name>.args` configuration. These labels
24 used to have some space-padding, and truncation to fit within 72 columns. Both
25 the padding and the truncation has been removed.
26
21 == Internal API Changes ==
27 == Internal API Changes ==
22
28
23 The following functions have been removed:
29 The following functions have been removed:
24
30
25 Miscellaneous:
31 Miscellaneous:
@@ -1,2129 +1,2129 b''
1 test merge-tools configuration - mostly exercising filemerge.py
1 test merge-tools configuration - mostly exercising filemerge.py
2
2
3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [ui]
5 > [ui]
6 > merge=
6 > merge=
7 > [commands]
7 > [commands]
8 > merge.require-rev=True
8 > merge.require-rev=True
9 > EOF
9 > EOF
10 $ hg init repo
10 $ hg init repo
11 $ cd repo
11 $ cd repo
12
12
13 revision 0
13 revision 0
14
14
15 $ echo "revision 0" > f
15 $ echo "revision 0" > f
16 $ echo "space" >> f
16 $ echo "space" >> f
17 $ hg commit -Am "revision 0"
17 $ hg commit -Am "revision 0"
18 adding f
18 adding f
19
19
20 revision 1
20 revision 1
21
21
22 $ echo "revision 1" > f
22 $ echo "revision 1" > f
23 $ echo "space" >> f
23 $ echo "space" >> f
24 $ hg commit -Am "revision 1"
24 $ hg commit -Am "revision 1"
25 $ hg update 0 > /dev/null
25 $ hg update 0 > /dev/null
26
26
27 revision 2
27 revision 2
28
28
29 $ echo "revision 2" > f
29 $ echo "revision 2" > f
30 $ echo "space" >> f
30 $ echo "space" >> f
31 $ hg commit -Am "revision 2"
31 $ hg commit -Am "revision 2"
32 created new head
32 created new head
33 $ hg update 0 > /dev/null
33 $ hg update 0 > /dev/null
34
34
35 revision 3 - simple to merge
35 revision 3 - simple to merge
36
36
37 $ echo "revision 3" >> f
37 $ echo "revision 3" >> f
38 $ hg commit -Am "revision 3"
38 $ hg commit -Am "revision 3"
39 created new head
39 created new head
40
40
41 revision 4 - hard to merge
41 revision 4 - hard to merge
42
42
43 $ hg update 0 > /dev/null
43 $ hg update 0 > /dev/null
44 $ echo "revision 4" > f
44 $ echo "revision 4" > f
45 $ hg commit -Am "revision 4"
45 $ hg commit -Am "revision 4"
46 created new head
46 created new head
47
47
48 $ echo "[merge-tools]" > .hg/hgrc
48 $ echo "[merge-tools]" > .hg/hgrc
49
49
50 $ beforemerge() {
50 $ beforemerge() {
51 > cat .hg/hgrc
51 > cat .hg/hgrc
52 > echo "# hg update -C 1"
52 > echo "# hg update -C 1"
53 > hg update -C 1 > /dev/null
53 > hg update -C 1 > /dev/null
54 > }
54 > }
55 $ aftermerge() {
55 $ aftermerge() {
56 > echo "# cat f"
56 > echo "# cat f"
57 > cat f
57 > cat f
58 > echo "# hg stat"
58 > echo "# hg stat"
59 > hg stat
59 > hg stat
60 > echo "# hg resolve --list"
60 > echo "# hg resolve --list"
61 > hg resolve --list
61 > hg resolve --list
62 > rm -f f.orig
62 > rm -f f.orig
63 > }
63 > }
64
64
65 Tool selection
65 Tool selection
66
66
67 default is internal merge:
67 default is internal merge:
68
68
69 $ beforemerge
69 $ beforemerge
70 [merge-tools]
70 [merge-tools]
71 # hg update -C 1
71 # hg update -C 1
72
72
73 hg merge -r 2
73 hg merge -r 2
74 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
74 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
75 running from a devel copy, not a temp installation
75 running from a devel copy, not a temp installation
76
76
77 $ PATH="/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
77 $ PATH="/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
78 merging f
78 merging f
79 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
79 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
80 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
80 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
81 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
81 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
82 [1]
82 [1]
83 $ aftermerge
83 $ aftermerge
84 # cat f
84 # cat f
85 <<<<<<< working copy: ef83787e2614 - test: revision 1
85 <<<<<<< working copy: ef83787e2614 - test: revision 1
86 revision 1
86 revision 1
87 =======
87 =======
88 revision 2
88 revision 2
89 >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2
89 >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2
90 space
90 space
91 # hg stat
91 # hg stat
92 M f
92 M f
93 ? f.orig
93 ? f.orig
94 # hg resolve --list
94 # hg resolve --list
95 U f
95 U f
96
96
97 simplest hgrc using false for merge:
97 simplest hgrc using false for merge:
98
98
99 $ echo "false.whatever=" >> .hg/hgrc
99 $ echo "false.whatever=" >> .hg/hgrc
100 $ beforemerge
100 $ beforemerge
101 [merge-tools]
101 [merge-tools]
102 false.whatever=
102 false.whatever=
103 # hg update -C 1
103 # hg update -C 1
104 $ hg merge -r 2
104 $ hg merge -r 2
105 merging f
105 merging f
106 merging f failed!
106 merging f failed!
107 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
107 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
108 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
108 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
109 [1]
109 [1]
110 $ aftermerge
110 $ aftermerge
111 # cat f
111 # cat f
112 revision 1
112 revision 1
113 space
113 space
114 # hg stat
114 # hg stat
115 M f
115 M f
116 ? f.orig
116 ? f.orig
117 # hg resolve --list
117 # hg resolve --list
118 U f
118 U f
119
119
120 #if unix-permissions
120 #if unix-permissions
121
121
122 unexecutable file in $PATH shouldn't be found:
122 unexecutable file in $PATH shouldn't be found:
123
123
124 $ echo "echo fail" > false
124 $ echo "echo fail" > false
125 $ hg up -qC 1
125 $ hg up -qC 1
126 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
126 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
127 merging f
127 merging f
128 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
128 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
129 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
129 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
130 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
130 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
131 [1]
131 [1]
132 $ rm false
132 $ rm false
133
133
134 #endif
134 #endif
135
135
136 executable directory in $PATH shouldn't be found:
136 executable directory in $PATH shouldn't be found:
137
137
138 $ mkdir false
138 $ mkdir false
139 $ hg up -qC 1
139 $ hg up -qC 1
140 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
140 $ PATH="`pwd`:/usr/sbin" "$PYTHON" "$BINDIR"/hg merge -r 2
141 merging f
141 merging f
142 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
142 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
143 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
143 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
144 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
144 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
145 [1]
145 [1]
146 $ rmdir false
146 $ rmdir false
147
147
148 true with higher .priority gets precedence:
148 true with higher .priority gets precedence:
149
149
150 $ echo "true.priority=1" >> .hg/hgrc
150 $ echo "true.priority=1" >> .hg/hgrc
151 $ beforemerge
151 $ beforemerge
152 [merge-tools]
152 [merge-tools]
153 false.whatever=
153 false.whatever=
154 true.priority=1
154 true.priority=1
155 # hg update -C 1
155 # hg update -C 1
156 $ hg merge -r 2
156 $ hg merge -r 2
157 merging f
157 merging f
158 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
158 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
159 (branch merge, don't forget to commit)
159 (branch merge, don't forget to commit)
160 $ aftermerge
160 $ aftermerge
161 # cat f
161 # cat f
162 revision 1
162 revision 1
163 space
163 space
164 # hg stat
164 # hg stat
165 M f
165 M f
166 # hg resolve --list
166 # hg resolve --list
167 R f
167 R f
168
168
169 unless lowered on command line:
169 unless lowered on command line:
170
170
171 $ beforemerge
171 $ beforemerge
172 [merge-tools]
172 [merge-tools]
173 false.whatever=
173 false.whatever=
174 true.priority=1
174 true.priority=1
175 # hg update -C 1
175 # hg update -C 1
176 $ hg merge -r 2 --config merge-tools.true.priority=-7
176 $ hg merge -r 2 --config merge-tools.true.priority=-7
177 merging f
177 merging f
178 merging f failed!
178 merging f failed!
179 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
179 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
180 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
180 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
181 [1]
181 [1]
182 $ aftermerge
182 $ aftermerge
183 # cat f
183 # cat f
184 revision 1
184 revision 1
185 space
185 space
186 # hg stat
186 # hg stat
187 M f
187 M f
188 ? f.orig
188 ? f.orig
189 # hg resolve --list
189 # hg resolve --list
190 U f
190 U f
191
191
192 or false set higher on command line:
192 or false set higher on command line:
193
193
194 $ beforemerge
194 $ beforemerge
195 [merge-tools]
195 [merge-tools]
196 false.whatever=
196 false.whatever=
197 true.priority=1
197 true.priority=1
198 # hg update -C 1
198 # hg update -C 1
199 $ hg merge -r 2 --config merge-tools.false.priority=117
199 $ hg merge -r 2 --config merge-tools.false.priority=117
200 merging f
200 merging f
201 merging f failed!
201 merging f failed!
202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
203 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
203 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
204 [1]
204 [1]
205 $ aftermerge
205 $ aftermerge
206 # cat f
206 # cat f
207 revision 1
207 revision 1
208 space
208 space
209 # hg stat
209 # hg stat
210 M f
210 M f
211 ? f.orig
211 ? f.orig
212 # hg resolve --list
212 # hg resolve --list
213 U f
213 U f
214
214
215 or true set to disabled:
215 or true set to disabled:
216 $ beforemerge
216 $ beforemerge
217 [merge-tools]
217 [merge-tools]
218 false.whatever=
218 false.whatever=
219 true.priority=1
219 true.priority=1
220 # hg update -C 1
220 # hg update -C 1
221 $ hg merge -r 2 --config merge-tools.true.disabled=yes
221 $ hg merge -r 2 --config merge-tools.true.disabled=yes
222 merging f
222 merging f
223 merging f failed!
223 merging f failed!
224 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
224 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
225 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
225 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
226 [1]
226 [1]
227 $ aftermerge
227 $ aftermerge
228 # cat f
228 # cat f
229 revision 1
229 revision 1
230 space
230 space
231 # hg stat
231 # hg stat
232 M f
232 M f
233 ? f.orig
233 ? f.orig
234 # hg resolve --list
234 # hg resolve --list
235 U f
235 U f
236
236
237 or true.executable not found in PATH:
237 or true.executable not found in PATH:
238
238
239 $ beforemerge
239 $ beforemerge
240 [merge-tools]
240 [merge-tools]
241 false.whatever=
241 false.whatever=
242 true.priority=1
242 true.priority=1
243 # hg update -C 1
243 # hg update -C 1
244 $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
244 $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
245 merging f
245 merging f
246 merging f failed!
246 merging f failed!
247 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
247 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
248 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
248 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
249 [1]
249 [1]
250 $ aftermerge
250 $ aftermerge
251 # cat f
251 # cat f
252 revision 1
252 revision 1
253 space
253 space
254 # hg stat
254 # hg stat
255 M f
255 M f
256 ? f.orig
256 ? f.orig
257 # hg resolve --list
257 # hg resolve --list
258 U f
258 U f
259
259
260 or true.executable with bogus path:
260 or true.executable with bogus path:
261
261
262 $ beforemerge
262 $ beforemerge
263 [merge-tools]
263 [merge-tools]
264 false.whatever=
264 false.whatever=
265 true.priority=1
265 true.priority=1
266 # hg update -C 1
266 # hg update -C 1
267 $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
267 $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
268 merging f
268 merging f
269 merging f failed!
269 merging f failed!
270 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
270 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
271 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
271 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
272 [1]
272 [1]
273 $ aftermerge
273 $ aftermerge
274 # cat f
274 # cat f
275 revision 1
275 revision 1
276 space
276 space
277 # hg stat
277 # hg stat
278 M f
278 M f
279 ? f.orig
279 ? f.orig
280 # hg resolve --list
280 # hg resolve --list
281 U f
281 U f
282
282
283 but true.executable set to cat found in PATH works:
283 but true.executable set to cat found in PATH works:
284
284
285 $ echo "true.executable=cat" >> .hg/hgrc
285 $ echo "true.executable=cat" >> .hg/hgrc
286 $ beforemerge
286 $ beforemerge
287 [merge-tools]
287 [merge-tools]
288 false.whatever=
288 false.whatever=
289 true.priority=1
289 true.priority=1
290 true.executable=cat
290 true.executable=cat
291 # hg update -C 1
291 # hg update -C 1
292 $ hg merge -r 2
292 $ hg merge -r 2
293 merging f
293 merging f
294 revision 1
294 revision 1
295 space
295 space
296 revision 0
296 revision 0
297 space
297 space
298 revision 2
298 revision 2
299 space
299 space
300 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
300 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
301 (branch merge, don't forget to commit)
301 (branch merge, don't forget to commit)
302 $ aftermerge
302 $ aftermerge
303 # cat f
303 # cat f
304 revision 1
304 revision 1
305 space
305 space
306 # hg stat
306 # hg stat
307 M f
307 M f
308 # hg resolve --list
308 # hg resolve --list
309 R f
309 R f
310
310
311 and true.executable set to cat with path works:
311 and true.executable set to cat with path works:
312
312
313 $ beforemerge
313 $ beforemerge
314 [merge-tools]
314 [merge-tools]
315 false.whatever=
315 false.whatever=
316 true.priority=1
316 true.priority=1
317 true.executable=cat
317 true.executable=cat
318 # hg update -C 1
318 # hg update -C 1
319 $ hg merge -r 2 --config merge-tools.true.executable=cat
319 $ hg merge -r 2 --config merge-tools.true.executable=cat
320 merging f
320 merging f
321 revision 1
321 revision 1
322 space
322 space
323 revision 0
323 revision 0
324 space
324 space
325 revision 2
325 revision 2
326 space
326 space
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 (branch merge, don't forget to commit)
328 (branch merge, don't forget to commit)
329 $ aftermerge
329 $ aftermerge
330 # cat f
330 # cat f
331 revision 1
331 revision 1
332 space
332 space
333 # hg stat
333 # hg stat
334 M f
334 M f
335 # hg resolve --list
335 # hg resolve --list
336 R f
336 R f
337
337
338 executable set to python script that succeeds:
338 executable set to python script that succeeds:
339
339
340 $ cat > "$TESTTMP/myworkingmerge.py" <<EOF
340 $ cat > "$TESTTMP/myworkingmerge.py" <<EOF
341 > def myworkingmergefn(ui, repo, args, **kwargs):
341 > def myworkingmergefn(ui, repo, args, **kwargs):
342 > return False
342 > return False
343 > EOF
343 > EOF
344 $ beforemerge
344 $ beforemerge
345 [merge-tools]
345 [merge-tools]
346 false.whatever=
346 false.whatever=
347 true.priority=1
347 true.priority=1
348 true.executable=cat
348 true.executable=cat
349 # hg update -C 1
349 # hg update -C 1
350 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:myworkingmergefn"
350 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:myworkingmergefn"
351 merging f
351 merging f
352 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
352 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
353 (branch merge, don't forget to commit)
353 (branch merge, don't forget to commit)
354 $ aftermerge
354 $ aftermerge
355 # cat f
355 # cat f
356 revision 1
356 revision 1
357 space
357 space
358 # hg stat
358 # hg stat
359 M f
359 M f
360 # hg resolve --list
360 # hg resolve --list
361 R f
361 R f
362
362
363 executable set to python script that fails:
363 executable set to python script that fails:
364
364
365 $ cat > "$TESTTMP/mybrokenmerge.py" <<EOF
365 $ cat > "$TESTTMP/mybrokenmerge.py" <<EOF
366 > def mybrokenmergefn(ui, repo, args, **kwargs):
366 > def mybrokenmergefn(ui, repo, args, **kwargs):
367 > ui.write(b"some fail message\n")
367 > ui.write(b"some fail message\n")
368 > return True
368 > return True
369 > EOF
369 > EOF
370 $ beforemerge
370 $ beforemerge
371 [merge-tools]
371 [merge-tools]
372 false.whatever=
372 false.whatever=
373 true.priority=1
373 true.priority=1
374 true.executable=cat
374 true.executable=cat
375 # hg update -C 1
375 # hg update -C 1
376 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/mybrokenmerge.py:mybrokenmergefn"
376 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/mybrokenmerge.py:mybrokenmergefn"
377 merging f
377 merging f
378 some fail message
378 some fail message
379 abort: $TESTTMP/mybrokenmerge.py hook failed
379 abort: $TESTTMP/mybrokenmerge.py hook failed
380 [40]
380 [40]
381 $ aftermerge
381 $ aftermerge
382 # cat f
382 # cat f
383 revision 1
383 revision 1
384 space
384 space
385 # hg stat
385 # hg stat
386 ? f.orig
386 ? f.orig
387 # hg resolve --list
387 # hg resolve --list
388 U f
388 U f
389
389
390 executable set to python script that is missing function:
390 executable set to python script that is missing function:
391
391
392 $ beforemerge
392 $ beforemerge
393 [merge-tools]
393 [merge-tools]
394 false.whatever=
394 false.whatever=
395 true.priority=1
395 true.priority=1
396 true.executable=cat
396 true.executable=cat
397 # hg update -C 1
397 # hg update -C 1
398 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:missingFunction"
398 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:missingFunction"
399 merging f
399 merging f
400 abort: $TESTTMP/myworkingmerge.py does not have function: missingFunction
400 abort: $TESTTMP/myworkingmerge.py does not have function: missingFunction
401 [255]
401 [255]
402 $ aftermerge
402 $ aftermerge
403 # cat f
403 # cat f
404 revision 1
404 revision 1
405 space
405 space
406 # hg stat
406 # hg stat
407 ? f.orig
407 ? f.orig
408 # hg resolve --list
408 # hg resolve --list
409 U f
409 U f
410
410
411 executable set to missing python script:
411 executable set to missing python script:
412
412
413 $ beforemerge
413 $ beforemerge
414 [merge-tools]
414 [merge-tools]
415 false.whatever=
415 false.whatever=
416 true.priority=1
416 true.priority=1
417 true.executable=cat
417 true.executable=cat
418 # hg update -C 1
418 # hg update -C 1
419 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/missingpythonscript.py:mergefn"
419 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/missingpythonscript.py:mergefn"
420 merging f
420 merging f
421 abort: loading python merge script failed: $TESTTMP/missingpythonscript.py
421 abort: loading python merge script failed: $TESTTMP/missingpythonscript.py
422 [255]
422 [255]
423 $ aftermerge
423 $ aftermerge
424 # cat f
424 # cat f
425 revision 1
425 revision 1
426 space
426 space
427 # hg stat
427 # hg stat
428 ? f.orig
428 ? f.orig
429 # hg resolve --list
429 # hg resolve --list
430 U f
430 U f
431
431
432 executable set to python script but callable function is missing:
432 executable set to python script but callable function is missing:
433
433
434 $ beforemerge
434 $ beforemerge
435 [merge-tools]
435 [merge-tools]
436 false.whatever=
436 false.whatever=
437 true.priority=1
437 true.priority=1
438 true.executable=cat
438 true.executable=cat
439 # hg update -C 1
439 # hg update -C 1
440 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py"
440 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py"
441 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py
441 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py
442 [255]
442 [255]
443 $ aftermerge
443 $ aftermerge
444 # cat f
444 # cat f
445 revision 1
445 revision 1
446 space
446 space
447 # hg stat
447 # hg stat
448 # hg resolve --list
448 # hg resolve --list
449 U f
449 U f
450
450
451 executable set to python script but callable function is empty string:
451 executable set to python script but callable function is empty string:
452
452
453 $ beforemerge
453 $ beforemerge
454 [merge-tools]
454 [merge-tools]
455 false.whatever=
455 false.whatever=
456 true.priority=1
456 true.priority=1
457 true.executable=cat
457 true.executable=cat
458 # hg update -C 1
458 # hg update -C 1
459 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:"
459 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/myworkingmerge.py:"
460 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py:
460 abort: invalid 'python:' syntax: python:$TESTTMP/myworkingmerge.py:
461 [255]
461 [255]
462 $ aftermerge
462 $ aftermerge
463 # cat f
463 # cat f
464 revision 1
464 revision 1
465 space
465 space
466 # hg stat
466 # hg stat
467 # hg resolve --list
467 # hg resolve --list
468 U f
468 U f
469
469
470 executable set to python script but callable function is missing and path contains colon:
470 executable set to python script but callable function is missing and path contains colon:
471
471
472 $ beforemerge
472 $ beforemerge
473 [merge-tools]
473 [merge-tools]
474 false.whatever=
474 false.whatever=
475 true.priority=1
475 true.priority=1
476 true.executable=cat
476 true.executable=cat
477 # hg update -C 1
477 # hg update -C 1
478 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/some:dir/myworkingmerge.py"
478 $ hg merge -r 2 --config merge-tools.true.executable="python:$TESTTMP/some:dir/myworkingmerge.py"
479 abort: invalid 'python:' syntax: python:$TESTTMP/some:dir/myworkingmerge.py
479 abort: invalid 'python:' syntax: python:$TESTTMP/some:dir/myworkingmerge.py
480 [255]
480 [255]
481 $ aftermerge
481 $ aftermerge
482 # cat f
482 # cat f
483 revision 1
483 revision 1
484 space
484 space
485 # hg stat
485 # hg stat
486 # hg resolve --list
486 # hg resolve --list
487 U f
487 U f
488
488
489 executable set to python script filename that contains spaces:
489 executable set to python script filename that contains spaces:
490
490
491 $ mkdir -p "$TESTTMP/my path"
491 $ mkdir -p "$TESTTMP/my path"
492 $ cat > "$TESTTMP/my path/my working merge with spaces in filename.py" <<EOF
492 $ cat > "$TESTTMP/my path/my working merge with spaces in filename.py" <<EOF
493 > def myworkingmergefn(ui, repo, args, **kwargs):
493 > def myworkingmergefn(ui, repo, args, **kwargs):
494 > return False
494 > return False
495 > EOF
495 > EOF
496 $ beforemerge
496 $ beforemerge
497 [merge-tools]
497 [merge-tools]
498 false.whatever=
498 false.whatever=
499 true.priority=1
499 true.priority=1
500 true.executable=cat
500 true.executable=cat
501 # hg update -C 1
501 # hg update -C 1
502 $ hg merge -r 2 --config "merge-tools.true.executable=python:$TESTTMP/my path/my working merge with spaces in filename.py:myworkingmergefn"
502 $ hg merge -r 2 --config "merge-tools.true.executable=python:$TESTTMP/my path/my working merge with spaces in filename.py:myworkingmergefn"
503 merging f
503 merging f
504 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
504 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
505 (branch merge, don't forget to commit)
505 (branch merge, don't forget to commit)
506 $ aftermerge
506 $ aftermerge
507 # cat f
507 # cat f
508 revision 1
508 revision 1
509 space
509 space
510 # hg stat
510 # hg stat
511 M f
511 M f
512 # hg resolve --list
512 # hg resolve --list
513 R f
513 R f
514
514
515 #if unix-permissions
515 #if unix-permissions
516
516
517 environment variables in true.executable are handled:
517 environment variables in true.executable are handled:
518
518
519 $ echo 'echo "custom merge tool"' > .hg/merge.sh
519 $ echo 'echo "custom merge tool"' > .hg/merge.sh
520 $ beforemerge
520 $ beforemerge
521 [merge-tools]
521 [merge-tools]
522 false.whatever=
522 false.whatever=
523 true.priority=1
523 true.priority=1
524 true.executable=cat
524 true.executable=cat
525 # hg update -C 1
525 # hg update -C 1
526 $ hg --config merge-tools.true.executable='sh' \
526 $ hg --config merge-tools.true.executable='sh' \
527 > --config merge-tools.true.args=.hg/merge.sh \
527 > --config merge-tools.true.args=.hg/merge.sh \
528 > merge -r 2
528 > merge -r 2
529 merging f
529 merging f
530 custom merge tool
530 custom merge tool
531 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
531 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
532 (branch merge, don't forget to commit)
532 (branch merge, don't forget to commit)
533 $ aftermerge
533 $ aftermerge
534 # cat f
534 # cat f
535 revision 1
535 revision 1
536 space
536 space
537 # hg stat
537 # hg stat
538 M f
538 M f
539 # hg resolve --list
539 # hg resolve --list
540 R f
540 R f
541
541
542 #endif
542 #endif
543
543
544 Tool selection and merge-patterns
544 Tool selection and merge-patterns
545
545
546 merge-patterns specifies new tool false:
546 merge-patterns specifies new tool false:
547
547
548 $ beforemerge
548 $ beforemerge
549 [merge-tools]
549 [merge-tools]
550 false.whatever=
550 false.whatever=
551 true.priority=1
551 true.priority=1
552 true.executable=cat
552 true.executable=cat
553 # hg update -C 1
553 # hg update -C 1
554 $ hg merge -r 2 --config merge-patterns.f=false
554 $ hg merge -r 2 --config merge-patterns.f=false
555 merging f
555 merging f
556 merging f failed!
556 merging f failed!
557 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
557 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
558 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
558 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
559 [1]
559 [1]
560 $ aftermerge
560 $ aftermerge
561 # cat f
561 # cat f
562 revision 1
562 revision 1
563 space
563 space
564 # hg stat
564 # hg stat
565 M f
565 M f
566 ? f.orig
566 ? f.orig
567 # hg resolve --list
567 # hg resolve --list
568 U f
568 U f
569
569
570 merge-patterns specifies executable not found in PATH and gets warning:
570 merge-patterns specifies executable not found in PATH and gets warning:
571
571
572 $ beforemerge
572 $ beforemerge
573 [merge-tools]
573 [merge-tools]
574 false.whatever=
574 false.whatever=
575 true.priority=1
575 true.priority=1
576 true.executable=cat
576 true.executable=cat
577 # hg update -C 1
577 # hg update -C 1
578 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
578 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
579 couldn't find merge tool true (for pattern f)
579 couldn't find merge tool true (for pattern f)
580 merging f
580 merging f
581 merging f failed!
581 merging f failed!
582 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
582 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
583 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
583 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
584 [1]
584 [1]
585 $ aftermerge
585 $ aftermerge
586 # cat f
586 # cat f
587 revision 1
587 revision 1
588 space
588 space
589 # hg stat
589 # hg stat
590 M f
590 M f
591 ? f.orig
591 ? f.orig
592 # hg resolve --list
592 # hg resolve --list
593 U f
593 U f
594
594
595 merge-patterns specifies executable with bogus path and gets warning:
595 merge-patterns specifies executable with bogus path and gets warning:
596
596
597 $ beforemerge
597 $ beforemerge
598 [merge-tools]
598 [merge-tools]
599 false.whatever=
599 false.whatever=
600 true.priority=1
600 true.priority=1
601 true.executable=cat
601 true.executable=cat
602 # hg update -C 1
602 # hg update -C 1
603 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
603 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
604 couldn't find merge tool true (for pattern f)
604 couldn't find merge tool true (for pattern f)
605 merging f
605 merging f
606 merging f failed!
606 merging f failed!
607 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
607 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
608 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
608 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
609 [1]
609 [1]
610 $ aftermerge
610 $ aftermerge
611 # cat f
611 # cat f
612 revision 1
612 revision 1
613 space
613 space
614 # hg stat
614 # hg stat
615 M f
615 M f
616 ? f.orig
616 ? f.orig
617 # hg resolve --list
617 # hg resolve --list
618 U f
618 U f
619
619
620 ui.merge overrules priority
620 ui.merge overrules priority
621
621
622 ui.merge specifies false:
622 ui.merge specifies false:
623
623
624 $ beforemerge
624 $ beforemerge
625 [merge-tools]
625 [merge-tools]
626 false.whatever=
626 false.whatever=
627 true.priority=1
627 true.priority=1
628 true.executable=cat
628 true.executable=cat
629 # hg update -C 1
629 # hg update -C 1
630 $ hg merge -r 2 --config ui.merge=false
630 $ hg merge -r 2 --config ui.merge=false
631 merging f
631 merging f
632 merging f failed!
632 merging f failed!
633 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
633 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
634 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
634 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
635 [1]
635 [1]
636 $ aftermerge
636 $ aftermerge
637 # cat f
637 # cat f
638 revision 1
638 revision 1
639 space
639 space
640 # hg stat
640 # hg stat
641 M f
641 M f
642 ? f.orig
642 ? f.orig
643 # hg resolve --list
643 # hg resolve --list
644 U f
644 U f
645
645
646 ui.merge specifies internal:fail:
646 ui.merge specifies internal:fail:
647
647
648 $ beforemerge
648 $ beforemerge
649 [merge-tools]
649 [merge-tools]
650 false.whatever=
650 false.whatever=
651 true.priority=1
651 true.priority=1
652 true.executable=cat
652 true.executable=cat
653 # hg update -C 1
653 # hg update -C 1
654 $ hg merge -r 2 --config ui.merge=internal:fail
654 $ hg merge -r 2 --config ui.merge=internal:fail
655 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
655 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
656 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
656 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
657 [1]
657 [1]
658 $ aftermerge
658 $ aftermerge
659 # cat f
659 # cat f
660 revision 1
660 revision 1
661 space
661 space
662 # hg stat
662 # hg stat
663 M f
663 M f
664 # hg resolve --list
664 # hg resolve --list
665 U f
665 U f
666
666
667 ui.merge specifies :local (without internal prefix):
667 ui.merge specifies :local (without internal prefix):
668
668
669 $ beforemerge
669 $ beforemerge
670 [merge-tools]
670 [merge-tools]
671 false.whatever=
671 false.whatever=
672 true.priority=1
672 true.priority=1
673 true.executable=cat
673 true.executable=cat
674 # hg update -C 1
674 # hg update -C 1
675 $ hg merge -r 2 --config ui.merge=:local
675 $ hg merge -r 2 --config ui.merge=:local
676 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
676 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
677 (branch merge, don't forget to commit)
677 (branch merge, don't forget to commit)
678 $ aftermerge
678 $ aftermerge
679 # cat f
679 # cat f
680 revision 1
680 revision 1
681 space
681 space
682 # hg stat
682 # hg stat
683 M f
683 M f
684 # hg resolve --list
684 # hg resolve --list
685 R f
685 R f
686
686
687 ui.merge specifies internal:other:
687 ui.merge specifies internal:other:
688
688
689 $ beforemerge
689 $ beforemerge
690 [merge-tools]
690 [merge-tools]
691 false.whatever=
691 false.whatever=
692 true.priority=1
692 true.priority=1
693 true.executable=cat
693 true.executable=cat
694 # hg update -C 1
694 # hg update -C 1
695 $ hg merge -r 2 --config ui.merge=internal:other
695 $ hg merge -r 2 --config ui.merge=internal:other
696 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
696 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
697 (branch merge, don't forget to commit)
697 (branch merge, don't forget to commit)
698 $ aftermerge
698 $ aftermerge
699 # cat f
699 # cat f
700 revision 2
700 revision 2
701 space
701 space
702 # hg stat
702 # hg stat
703 M f
703 M f
704 # hg resolve --list
704 # hg resolve --list
705 R f
705 R f
706
706
707 ui.merge specifies internal:prompt:
707 ui.merge specifies internal:prompt:
708
708
709 $ beforemerge
709 $ beforemerge
710 [merge-tools]
710 [merge-tools]
711 false.whatever=
711 false.whatever=
712 true.priority=1
712 true.priority=1
713 true.executable=cat
713 true.executable=cat
714 # hg update -C 1
714 # hg update -C 1
715 $ hg merge -r 2 --config ui.merge=internal:prompt
715 $ hg merge -r 2 --config ui.merge=internal:prompt
716 file 'f' needs to be resolved.
716 file 'f' needs to be resolved.
717 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
717 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
718 What do you want to do? u
718 What do you want to do? u
719 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
719 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
720 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
720 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
721 [1]
721 [1]
722 $ aftermerge
722 $ aftermerge
723 # cat f
723 # cat f
724 revision 1
724 revision 1
725 space
725 space
726 # hg stat
726 # hg stat
727 M f
727 M f
728 # hg resolve --list
728 # hg resolve --list
729 U f
729 U f
730
730
731 ui.merge specifies :prompt, with 'leave unresolved' chosen
731 ui.merge specifies :prompt, with 'leave unresolved' chosen
732
732
733 $ beforemerge
733 $ beforemerge
734 [merge-tools]
734 [merge-tools]
735 false.whatever=
735 false.whatever=
736 true.priority=1
736 true.priority=1
737 true.executable=cat
737 true.executable=cat
738 # hg update -C 1
738 # hg update -C 1
739 $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
739 $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
740 > u
740 > u
741 > EOF
741 > EOF
742 file 'f' needs to be resolved.
742 file 'f' needs to be resolved.
743 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
743 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
744 What do you want to do? u
744 What do you want to do? u
745 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
745 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
746 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
746 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
747 [1]
747 [1]
748 $ aftermerge
748 $ aftermerge
749 # cat f
749 # cat f
750 revision 1
750 revision 1
751 space
751 space
752 # hg stat
752 # hg stat
753 M f
753 M f
754 # hg resolve --list
754 # hg resolve --list
755 U f
755 U f
756
756
757 prompt with EOF
757 prompt with EOF
758
758
759 $ beforemerge
759 $ beforemerge
760 [merge-tools]
760 [merge-tools]
761 false.whatever=
761 false.whatever=
762 true.priority=1
762 true.priority=1
763 true.executable=cat
763 true.executable=cat
764 # hg update -C 1
764 # hg update -C 1
765 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
765 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
766 file 'f' needs to be resolved.
766 file 'f' needs to be resolved.
767 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
767 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
768 What do you want to do?
768 What do you want to do?
769 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
769 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
770 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
770 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
771 [1]
771 [1]
772 $ aftermerge
772 $ aftermerge
773 # cat f
773 # cat f
774 revision 1
774 revision 1
775 space
775 space
776 # hg stat
776 # hg stat
777 M f
777 M f
778 # hg resolve --list
778 # hg resolve --list
779 U f
779 U f
780 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
780 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
781 file 'f' needs to be resolved.
781 file 'f' needs to be resolved.
782 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
782 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
783 What do you want to do?
783 What do you want to do?
784 [1]
784 [1]
785 $ aftermerge
785 $ aftermerge
786 # cat f
786 # cat f
787 revision 1
787 revision 1
788 space
788 space
789 # hg stat
789 # hg stat
790 M f
790 M f
791 ? f.orig
791 ? f.orig
792 # hg resolve --list
792 # hg resolve --list
793 U f
793 U f
794 $ rm f
794 $ rm f
795 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
795 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
796 file 'f' needs to be resolved.
796 file 'f' needs to be resolved.
797 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
797 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
798 What do you want to do?
798 What do you want to do?
799 [1]
799 [1]
800 $ aftermerge
800 $ aftermerge
801 # cat f
801 # cat f
802 revision 1
802 revision 1
803 space
803 space
804 # hg stat
804 # hg stat
805 M f
805 M f
806 # hg resolve --list
806 # hg resolve --list
807 U f
807 U f
808 $ hg resolve --all --config ui.merge=internal:prompt
808 $ hg resolve --all --config ui.merge=internal:prompt
809 file 'f' needs to be resolved.
809 file 'f' needs to be resolved.
810 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
810 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
811 What do you want to do? u
811 What do you want to do? u
812 [1]
812 [1]
813 $ aftermerge
813 $ aftermerge
814 # cat f
814 # cat f
815 revision 1
815 revision 1
816 space
816 space
817 # hg stat
817 # hg stat
818 M f
818 M f
819 ? f.orig
819 ? f.orig
820 # hg resolve --list
820 # hg resolve --list
821 U f
821 U f
822
822
823 ui.merge specifies internal:dump:
823 ui.merge specifies internal:dump:
824
824
825 $ beforemerge
825 $ beforemerge
826 [merge-tools]
826 [merge-tools]
827 false.whatever=
827 false.whatever=
828 true.priority=1
828 true.priority=1
829 true.executable=cat
829 true.executable=cat
830 # hg update -C 1
830 # hg update -C 1
831 $ hg merge -r 2 --config ui.merge=internal:dump
831 $ hg merge -r 2 --config ui.merge=internal:dump
832 merging f
832 merging f
833 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
833 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
834 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
834 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
835 [1]
835 [1]
836 $ aftermerge
836 $ aftermerge
837 # cat f
837 # cat f
838 revision 1
838 revision 1
839 space
839 space
840 # hg stat
840 # hg stat
841 M f
841 M f
842 ? f.base
842 ? f.base
843 ? f.local
843 ? f.local
844 ? f.orig
844 ? f.orig
845 ? f.other
845 ? f.other
846 # hg resolve --list
846 # hg resolve --list
847 U f
847 U f
848
848
849 f.base:
849 f.base:
850
850
851 $ cat f.base
851 $ cat f.base
852 revision 0
852 revision 0
853 space
853 space
854
854
855 f.local:
855 f.local:
856
856
857 $ cat f.local
857 $ cat f.local
858 revision 1
858 revision 1
859 space
859 space
860
860
861 f.other:
861 f.other:
862
862
863 $ cat f.other
863 $ cat f.other
864 revision 2
864 revision 2
865 space
865 space
866 $ rm f.base f.local f.other
866 $ rm f.base f.local f.other
867
867
868 check that internal:dump doesn't dump files if premerge runs
868 check that internal:dump doesn't dump files if premerge runs
869 successfully
869 successfully
870
870
871 $ beforemerge
871 $ beforemerge
872 [merge-tools]
872 [merge-tools]
873 false.whatever=
873 false.whatever=
874 true.priority=1
874 true.priority=1
875 true.executable=cat
875 true.executable=cat
876 # hg update -C 1
876 # hg update -C 1
877 $ hg merge -r 3 --config ui.merge=internal:dump
877 $ hg merge -r 3 --config ui.merge=internal:dump
878 merging f
878 merging f
879 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
879 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
880 (branch merge, don't forget to commit)
880 (branch merge, don't forget to commit)
881
881
882 $ aftermerge
882 $ aftermerge
883 # cat f
883 # cat f
884 revision 1
884 revision 1
885 space
885 space
886 revision 3
886 revision 3
887 # hg stat
887 # hg stat
888 M f
888 M f
889 # hg resolve --list
889 # hg resolve --list
890 R f
890 R f
891
891
892 check that internal:forcedump dumps files, even if local and other can
892 check that internal:forcedump dumps files, even if local and other can
893 be merged easily
893 be merged easily
894
894
895 $ beforemerge
895 $ beforemerge
896 [merge-tools]
896 [merge-tools]
897 false.whatever=
897 false.whatever=
898 true.priority=1
898 true.priority=1
899 true.executable=cat
899 true.executable=cat
900 # hg update -C 1
900 # hg update -C 1
901 $ hg merge -r 3 --config ui.merge=internal:forcedump
901 $ hg merge -r 3 --config ui.merge=internal:forcedump
902 merging f
902 merging f
903 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
903 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
904 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
904 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
905 [1]
905 [1]
906 $ aftermerge
906 $ aftermerge
907 # cat f
907 # cat f
908 revision 1
908 revision 1
909 space
909 space
910 # hg stat
910 # hg stat
911 M f
911 M f
912 ? f.base
912 ? f.base
913 ? f.local
913 ? f.local
914 ? f.orig
914 ? f.orig
915 ? f.other
915 ? f.other
916 # hg resolve --list
916 # hg resolve --list
917 U f
917 U f
918
918
919 $ cat f.base
919 $ cat f.base
920 revision 0
920 revision 0
921 space
921 space
922
922
923 $ cat f.local
923 $ cat f.local
924 revision 1
924 revision 1
925 space
925 space
926
926
927 $ cat f.other
927 $ cat f.other
928 revision 0
928 revision 0
929 space
929 space
930 revision 3
930 revision 3
931
931
932 $ rm -f f.base f.local f.other
932 $ rm -f f.base f.local f.other
933
933
934 ui.merge specifies internal:other but is overruled by pattern for false:
934 ui.merge specifies internal:other but is overruled by pattern for false:
935
935
936 $ beforemerge
936 $ beforemerge
937 [merge-tools]
937 [merge-tools]
938 false.whatever=
938 false.whatever=
939 true.priority=1
939 true.priority=1
940 true.executable=cat
940 true.executable=cat
941 # hg update -C 1
941 # hg update -C 1
942 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
942 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
943 merging f
943 merging f
944 merging f failed!
944 merging f failed!
945 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
945 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
946 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
946 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
947 [1]
947 [1]
948 $ aftermerge
948 $ aftermerge
949 # cat f
949 # cat f
950 revision 1
950 revision 1
951 space
951 space
952 # hg stat
952 # hg stat
953 M f
953 M f
954 ? f.orig
954 ? f.orig
955 # hg resolve --list
955 # hg resolve --list
956 U f
956 U f
957
957
958 Premerge
958 Premerge
959
959
960 ui.merge specifies internal:other but is overruled by --tool=false
960 ui.merge specifies internal:other but is overruled by --tool=false
961
961
962 $ beforemerge
962 $ beforemerge
963 [merge-tools]
963 [merge-tools]
964 false.whatever=
964 false.whatever=
965 true.priority=1
965 true.priority=1
966 true.executable=cat
966 true.executable=cat
967 # hg update -C 1
967 # hg update -C 1
968 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
968 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
969 merging f
969 merging f
970 merging f failed!
970 merging f failed!
971 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
971 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
972 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
972 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
973 [1]
973 [1]
974 $ aftermerge
974 $ aftermerge
975 # cat f
975 # cat f
976 revision 1
976 revision 1
977 space
977 space
978 # hg stat
978 # hg stat
979 M f
979 M f
980 ? f.orig
980 ? f.orig
981 # hg resolve --list
981 # hg resolve --list
982 U f
982 U f
983
983
984 HGMERGE specifies internal:other but is overruled by --tool=false
984 HGMERGE specifies internal:other but is overruled by --tool=false
985
985
986 $ HGMERGE=internal:other ; export HGMERGE
986 $ HGMERGE=internal:other ; export HGMERGE
987 $ beforemerge
987 $ beforemerge
988 [merge-tools]
988 [merge-tools]
989 false.whatever=
989 false.whatever=
990 true.priority=1
990 true.priority=1
991 true.executable=cat
991 true.executable=cat
992 # hg update -C 1
992 # hg update -C 1
993 $ hg merge -r 2 --tool=false
993 $ hg merge -r 2 --tool=false
994 merging f
994 merging f
995 merging f failed!
995 merging f failed!
996 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
996 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
997 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
997 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
998 [1]
998 [1]
999 $ aftermerge
999 $ aftermerge
1000 # cat f
1000 # cat f
1001 revision 1
1001 revision 1
1002 space
1002 space
1003 # hg stat
1003 # hg stat
1004 M f
1004 M f
1005 ? f.orig
1005 ? f.orig
1006 # hg resolve --list
1006 # hg resolve --list
1007 U f
1007 U f
1008
1008
1009 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
1009 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
1010
1010
1011 update is a merge ...
1011 update is a merge ...
1012
1012
1013 (this also tests that files reverted with '--rev REV' are treated as
1013 (this also tests that files reverted with '--rev REV' are treated as
1014 "modified", even if none of mode, size and timestamp of them isn't
1014 "modified", even if none of mode, size and timestamp of them isn't
1015 changed on the filesystem (see also issue4583))
1015 changed on the filesystem (see also issue4583))
1016
1016
1017 $ cat >> $HGRCPATH <<EOF
1017 $ cat >> $HGRCPATH <<EOF
1018 > [fakedirstatewritetime]
1018 > [fakedirstatewritetime]
1019 > # emulate invoking dirstate.write() via repo.status()
1019 > # emulate invoking dirstate.write() via repo.status()
1020 > # at 2000-01-01 00:00
1020 > # at 2000-01-01 00:00
1021 > fakenow = 200001010000
1021 > fakenow = 200001010000
1022 > EOF
1022 > EOF
1023
1023
1024 $ beforemerge
1024 $ beforemerge
1025 [merge-tools]
1025 [merge-tools]
1026 false.whatever=
1026 false.whatever=
1027 true.priority=1
1027 true.priority=1
1028 true.executable=cat
1028 true.executable=cat
1029 # hg update -C 1
1029 # hg update -C 1
1030 $ hg update -q 0
1030 $ hg update -q 0
1031 $ f -s f
1031 $ f -s f
1032 f: size=17
1032 f: size=17
1033 $ touch -t 200001010000 f
1033 $ touch -t 200001010000 f
1034 $ hg debugrebuildstate
1034 $ hg debugrebuildstate
1035 $ cat >> $HGRCPATH <<EOF
1035 $ cat >> $HGRCPATH <<EOF
1036 > [extensions]
1036 > [extensions]
1037 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1037 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1038 > EOF
1038 > EOF
1039 $ hg revert -q -r 1 .
1039 $ hg revert -q -r 1 .
1040 $ cat >> $HGRCPATH <<EOF
1040 $ cat >> $HGRCPATH <<EOF
1041 > [extensions]
1041 > [extensions]
1042 > fakedirstatewritetime = !
1042 > fakedirstatewritetime = !
1043 > EOF
1043 > EOF
1044 $ f -s f
1044 $ f -s f
1045 f: size=17
1045 f: size=17
1046 $ touch -t 200001010000 f
1046 $ touch -t 200001010000 f
1047 $ hg status f
1047 $ hg status f
1048 M f
1048 M f
1049 $ hg update -r 2
1049 $ hg update -r 2
1050 merging f
1050 merging f
1051 revision 1
1051 revision 1
1052 space
1052 space
1053 revision 0
1053 revision 0
1054 space
1054 space
1055 revision 2
1055 revision 2
1056 space
1056 space
1057 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1057 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1058 $ aftermerge
1058 $ aftermerge
1059 # cat f
1059 # cat f
1060 revision 1
1060 revision 1
1061 space
1061 space
1062 # hg stat
1062 # hg stat
1063 M f
1063 M f
1064 # hg resolve --list
1064 # hg resolve --list
1065 R f
1065 R f
1066
1066
1067 update should also have --tool
1067 update should also have --tool
1068
1068
1069 $ beforemerge
1069 $ beforemerge
1070 [merge-tools]
1070 [merge-tools]
1071 false.whatever=
1071 false.whatever=
1072 true.priority=1
1072 true.priority=1
1073 true.executable=cat
1073 true.executable=cat
1074 # hg update -C 1
1074 # hg update -C 1
1075 $ hg update -q 0
1075 $ hg update -q 0
1076 $ f -s f
1076 $ f -s f
1077 f: size=17
1077 f: size=17
1078 $ touch -t 200001010000 f
1078 $ touch -t 200001010000 f
1079 $ hg debugrebuildstate
1079 $ hg debugrebuildstate
1080 $ cat >> $HGRCPATH <<EOF
1080 $ cat >> $HGRCPATH <<EOF
1081 > [extensions]
1081 > [extensions]
1082 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1082 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1083 > EOF
1083 > EOF
1084 $ hg revert -q -r 1 .
1084 $ hg revert -q -r 1 .
1085 $ cat >> $HGRCPATH <<EOF
1085 $ cat >> $HGRCPATH <<EOF
1086 > [extensions]
1086 > [extensions]
1087 > fakedirstatewritetime = !
1087 > fakedirstatewritetime = !
1088 > EOF
1088 > EOF
1089 $ f -s f
1089 $ f -s f
1090 f: size=17
1090 f: size=17
1091 $ touch -t 200001010000 f
1091 $ touch -t 200001010000 f
1092 $ hg status f
1092 $ hg status f
1093 M f
1093 M f
1094 $ hg update -r 2 --tool false
1094 $ hg update -r 2 --tool false
1095 merging f
1095 merging f
1096 merging f failed!
1096 merging f failed!
1097 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1097 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1098 use 'hg resolve' to retry unresolved file merges
1098 use 'hg resolve' to retry unresolved file merges
1099 [1]
1099 [1]
1100 $ aftermerge
1100 $ aftermerge
1101 # cat f
1101 # cat f
1102 revision 1
1102 revision 1
1103 space
1103 space
1104 # hg stat
1104 # hg stat
1105 M f
1105 M f
1106 ? f.orig
1106 ? f.orig
1107 # hg resolve --list
1107 # hg resolve --list
1108 U f
1108 U f
1109
1109
1110 Default is silent simplemerge:
1110 Default is silent simplemerge:
1111
1111
1112 $ beforemerge
1112 $ beforemerge
1113 [merge-tools]
1113 [merge-tools]
1114 false.whatever=
1114 false.whatever=
1115 true.priority=1
1115 true.priority=1
1116 true.executable=cat
1116 true.executable=cat
1117 # hg update -C 1
1117 # hg update -C 1
1118 $ hg merge -r 3
1118 $ hg merge -r 3
1119 merging f
1119 merging f
1120 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1120 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1121 (branch merge, don't forget to commit)
1121 (branch merge, don't forget to commit)
1122 $ aftermerge
1122 $ aftermerge
1123 # cat f
1123 # cat f
1124 revision 1
1124 revision 1
1125 space
1125 space
1126 revision 3
1126 revision 3
1127 # hg stat
1127 # hg stat
1128 M f
1128 M f
1129 # hg resolve --list
1129 # hg resolve --list
1130 R f
1130 R f
1131
1131
1132 .premerge=True is same:
1132 .premerge=True is same:
1133
1133
1134 $ beforemerge
1134 $ beforemerge
1135 [merge-tools]
1135 [merge-tools]
1136 false.whatever=
1136 false.whatever=
1137 true.priority=1
1137 true.priority=1
1138 true.executable=cat
1138 true.executable=cat
1139 # hg update -C 1
1139 # hg update -C 1
1140 $ hg merge -r 3 --config merge-tools.true.premerge=True
1140 $ hg merge -r 3 --config merge-tools.true.premerge=True
1141 merging f
1141 merging f
1142 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1142 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1143 (branch merge, don't forget to commit)
1143 (branch merge, don't forget to commit)
1144 $ aftermerge
1144 $ aftermerge
1145 # cat f
1145 # cat f
1146 revision 1
1146 revision 1
1147 space
1147 space
1148 revision 3
1148 revision 3
1149 # hg stat
1149 # hg stat
1150 M f
1150 M f
1151 # hg resolve --list
1151 # hg resolve --list
1152 R f
1152 R f
1153
1153
1154 .premerge=False executes merge-tool:
1154 .premerge=False executes merge-tool:
1155
1155
1156 $ beforemerge
1156 $ beforemerge
1157 [merge-tools]
1157 [merge-tools]
1158 false.whatever=
1158 false.whatever=
1159 true.priority=1
1159 true.priority=1
1160 true.executable=cat
1160 true.executable=cat
1161 # hg update -C 1
1161 # hg update -C 1
1162 $ hg merge -r 3 --config merge-tools.true.premerge=False
1162 $ hg merge -r 3 --config merge-tools.true.premerge=False
1163 merging f
1163 merging f
1164 revision 1
1164 revision 1
1165 space
1165 space
1166 revision 0
1166 revision 0
1167 space
1167 space
1168 revision 0
1168 revision 0
1169 space
1169 space
1170 revision 3
1170 revision 3
1171 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1171 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1172 (branch merge, don't forget to commit)
1172 (branch merge, don't forget to commit)
1173 $ aftermerge
1173 $ aftermerge
1174 # cat f
1174 # cat f
1175 revision 1
1175 revision 1
1176 space
1176 space
1177 # hg stat
1177 # hg stat
1178 M f
1178 M f
1179 # hg resolve --list
1179 # hg resolve --list
1180 R f
1180 R f
1181
1181
1182 premerge=keep keeps conflict markers in:
1182 premerge=keep keeps conflict markers in:
1183
1183
1184 $ beforemerge
1184 $ beforemerge
1185 [merge-tools]
1185 [merge-tools]
1186 false.whatever=
1186 false.whatever=
1187 true.priority=1
1187 true.priority=1
1188 true.executable=cat
1188 true.executable=cat
1189 # hg update -C 1
1189 # hg update -C 1
1190 $ hg merge -r 4 --config merge-tools.true.premerge=keep
1190 $ hg merge -r 4 --config merge-tools.true.premerge=keep
1191 merging f
1191 merging f
1192 <<<<<<< working copy: ef83787e2614 - test: revision 1
1192 <<<<<<< working copy: ef83787e2614 - test: revision 1
1193 revision 1
1193 revision 1
1194 space
1194 space
1195 =======
1195 =======
1196 revision 4
1196 revision 4
1197 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1197 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1198 revision 0
1198 revision 0
1199 space
1199 space
1200 revision 4
1200 revision 4
1201 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1201 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1202 (branch merge, don't forget to commit)
1202 (branch merge, don't forget to commit)
1203 $ aftermerge
1203 $ aftermerge
1204 # cat f
1204 # cat f
1205 <<<<<<< working copy: ef83787e2614 - test: revision 1
1205 <<<<<<< working copy: ef83787e2614 - test: revision 1
1206 revision 1
1206 revision 1
1207 space
1207 space
1208 =======
1208 =======
1209 revision 4
1209 revision 4
1210 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1210 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1211 # hg stat
1211 # hg stat
1212 M f
1212 M f
1213 # hg resolve --list
1213 # hg resolve --list
1214 R f
1214 R f
1215
1215
1216 premerge=keep-merge3 keeps conflict markers with base content:
1216 premerge=keep-merge3 keeps conflict markers with base content:
1217
1217
1218 $ beforemerge
1218 $ beforemerge
1219 [merge-tools]
1219 [merge-tools]
1220 false.whatever=
1220 false.whatever=
1221 true.priority=1
1221 true.priority=1
1222 true.executable=cat
1222 true.executable=cat
1223 # hg update -C 1
1223 # hg update -C 1
1224 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
1224 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
1225 merging f
1225 merging f
1226 <<<<<<< working copy: ef83787e2614 - test: revision 1
1226 <<<<<<< working copy: ef83787e2614 - test: revision 1
1227 revision 1
1227 revision 1
1228 space
1228 space
1229 ||||||| base: ffd2bda21d6e - test: revision 0
1229 ||||||| base: ffd2bda21d6e - test: revision 0
1230 revision 0
1230 revision 0
1231 space
1231 space
1232 =======
1232 =======
1233 revision 4
1233 revision 4
1234 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1234 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1235 revision 0
1235 revision 0
1236 space
1236 space
1237 revision 4
1237 revision 4
1238 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1238 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1239 (branch merge, don't forget to commit)
1239 (branch merge, don't forget to commit)
1240 $ aftermerge
1240 $ aftermerge
1241 # cat f
1241 # cat f
1242 <<<<<<< working copy: ef83787e2614 - test: revision 1
1242 <<<<<<< working copy: ef83787e2614 - test: revision 1
1243 revision 1
1243 revision 1
1244 space
1244 space
1245 ||||||| base: ffd2bda21d6e - test: revision 0
1245 ||||||| base: ffd2bda21d6e - test: revision 0
1246 revision 0
1246 revision 0
1247 space
1247 space
1248 =======
1248 =======
1249 revision 4
1249 revision 4
1250 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1250 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1251 # hg stat
1251 # hg stat
1252 M f
1252 M f
1253 # hg resolve --list
1253 # hg resolve --list
1254 R f
1254 R f
1255
1255
1256 premerge=keep-mergediff keeps conflict markers with base content:
1256 premerge=keep-mergediff keeps conflict markers with base content:
1257
1257
1258 $ beforemerge
1258 $ beforemerge
1259 [merge-tools]
1259 [merge-tools]
1260 false.whatever=
1260 false.whatever=
1261 true.priority=1
1261 true.priority=1
1262 true.executable=cat
1262 true.executable=cat
1263 # hg update -C 1
1263 # hg update -C 1
1264 $ hg merge -r 4 --config merge-tools.true.premerge=keep-mergediff
1264 $ hg merge -r 4 --config merge-tools.true.premerge=keep-mergediff
1265 merging f
1265 merging f
1266 <<<<<<<
1266 <<<<<<<
1267 ------- base: ffd2bda21d6e - test: revision 0
1267 ------- base: ffd2bda21d6e - test: revision 0
1268 +++++++ working copy: ef83787e2614 - test: revision 1
1268 +++++++ working copy: ef83787e2614 - test: revision 1
1269 -revision 0
1269 -revision 0
1270 +revision 1
1270 +revision 1
1271 space
1271 space
1272 ======= merge rev: 81448d39c9a0 - test: revision 4
1272 ======= merge rev: 81448d39c9a0 - test: revision 4
1273 revision 4
1273 revision 4
1274 >>>>>>>
1274 >>>>>>>
1275 revision 0
1275 revision 0
1276 space
1276 space
1277 revision 4
1277 revision 4
1278 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1278 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1279 (branch merge, don't forget to commit)
1279 (branch merge, don't forget to commit)
1280 $ aftermerge
1280 $ aftermerge
1281 # cat f
1281 # cat f
1282 <<<<<<<
1282 <<<<<<<
1283 ------- base: ffd2bda21d6e - test: revision 0
1283 ------- base: ffd2bda21d6e - test: revision 0
1284 +++++++ working copy: ef83787e2614 - test: revision 1
1284 +++++++ working copy: ef83787e2614 - test: revision 1
1285 -revision 0
1285 -revision 0
1286 +revision 1
1286 +revision 1
1287 space
1287 space
1288 ======= merge rev: 81448d39c9a0 - test: revision 4
1288 ======= merge rev: 81448d39c9a0 - test: revision 4
1289 revision 4
1289 revision 4
1290 >>>>>>>
1290 >>>>>>>
1291 # hg stat
1291 # hg stat
1292 M f
1292 M f
1293 # hg resolve --list
1293 # hg resolve --list
1294 R f
1294 R f
1295
1295
1296 premerge=keep respects ui.mergemarkers=basic:
1296 premerge=keep respects ui.mergemarkers=basic:
1297
1297
1298 $ beforemerge
1298 $ beforemerge
1299 [merge-tools]
1299 [merge-tools]
1300 false.whatever=
1300 false.whatever=
1301 true.priority=1
1301 true.priority=1
1302 true.executable=cat
1302 true.executable=cat
1303 # hg update -C 1
1303 # hg update -C 1
1304 $ hg merge -r 4 --config merge-tools.true.premerge=keep --config ui.mergemarkers=basic
1304 $ hg merge -r 4 --config merge-tools.true.premerge=keep --config ui.mergemarkers=basic
1305 merging f
1305 merging f
1306 <<<<<<< working copy
1306 <<<<<<< working copy
1307 revision 1
1307 revision 1
1308 space
1308 space
1309 =======
1309 =======
1310 revision 4
1310 revision 4
1311 >>>>>>> merge rev
1311 >>>>>>> merge rev
1312 revision 0
1312 revision 0
1313 space
1313 space
1314 revision 4
1314 revision 4
1315 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1315 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1316 (branch merge, don't forget to commit)
1316 (branch merge, don't forget to commit)
1317 $ aftermerge
1317 $ aftermerge
1318 # cat f
1318 # cat f
1319 <<<<<<< working copy
1319 <<<<<<< working copy
1320 revision 1
1320 revision 1
1321 space
1321 space
1322 =======
1322 =======
1323 revision 4
1323 revision 4
1324 >>>>>>> merge rev
1324 >>>>>>> merge rev
1325 # hg stat
1325 # hg stat
1326 M f
1326 M f
1327 # hg resolve --list
1327 # hg resolve --list
1328 R f
1328 R f
1329
1329
1330 premerge=keep ignores ui.mergemarkers=basic if true.mergemarkers=detailed:
1330 premerge=keep ignores ui.mergemarkers=basic if true.mergemarkers=detailed:
1331
1331
1332 $ beforemerge
1332 $ beforemerge
1333 [merge-tools]
1333 [merge-tools]
1334 false.whatever=
1334 false.whatever=
1335 true.priority=1
1335 true.priority=1
1336 true.executable=cat
1336 true.executable=cat
1337 # hg update -C 1
1337 # hg update -C 1
1338 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1338 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1339 > --config ui.mergemarkers=basic \
1339 > --config ui.mergemarkers=basic \
1340 > --config merge-tools.true.mergemarkers=detailed
1340 > --config merge-tools.true.mergemarkers=detailed
1341 merging f
1341 merging f
1342 <<<<<<< working copy: ef83787e2614 - test: revision 1
1342 <<<<<<< working copy: ef83787e2614 - test: revision 1
1343 revision 1
1343 revision 1
1344 space
1344 space
1345 =======
1345 =======
1346 revision 4
1346 revision 4
1347 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1347 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1348 revision 0
1348 revision 0
1349 space
1349 space
1350 revision 4
1350 revision 4
1351 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1351 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1352 (branch merge, don't forget to commit)
1352 (branch merge, don't forget to commit)
1353 $ aftermerge
1353 $ aftermerge
1354 # cat f
1354 # cat f
1355 <<<<<<< working copy: ef83787e2614 - test: revision 1
1355 <<<<<<< working copy: ef83787e2614 - test: revision 1
1356 revision 1
1356 revision 1
1357 space
1357 space
1358 =======
1358 =======
1359 revision 4
1359 revision 4
1360 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1360 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1361 # hg stat
1361 # hg stat
1362 M f
1362 M f
1363 # hg resolve --list
1363 # hg resolve --list
1364 R f
1364 R f
1365
1365
1366 premerge=keep respects ui.mergemarkertemplate instead of
1366 premerge=keep respects ui.mergemarkertemplate instead of
1367 true.mergemarkertemplate if true.mergemarkers=basic:
1367 true.mergemarkertemplate if true.mergemarkers=basic:
1368
1368
1369 $ beforemerge
1369 $ beforemerge
1370 [merge-tools]
1370 [merge-tools]
1371 false.whatever=
1371 false.whatever=
1372 true.priority=1
1372 true.priority=1
1373 true.executable=cat
1373 true.executable=cat
1374 # hg update -C 1
1374 # hg update -C 1
1375 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1375 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1376 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1376 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1377 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}'
1377 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}'
1378 merging f
1378 merging f
1379 <<<<<<< working copy: uitmpl 1
1379 <<<<<<< working copy: uitmpl 1
1380 revision 1
1380 revision 1
1381 space
1381 space
1382 =======
1382 =======
1383 revision 4
1383 revision 4
1384 >>>>>>> merge rev: uitmpl 4
1384 >>>>>>> merge rev: uitmpl 4
1385 revision 0
1385 revision 0
1386 space
1386 space
1387 revision 4
1387 revision 4
1388 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1388 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1389 (branch merge, don't forget to commit)
1389 (branch merge, don't forget to commit)
1390 $ aftermerge
1390 $ aftermerge
1391 # cat f
1391 # cat f
1392 <<<<<<< working copy: uitmpl 1
1392 <<<<<<< working copy: uitmpl 1
1393 revision 1
1393 revision 1
1394 space
1394 space
1395 =======
1395 =======
1396 revision 4
1396 revision 4
1397 >>>>>>> merge rev: uitmpl 4
1397 >>>>>>> merge rev: uitmpl 4
1398 # hg stat
1398 # hg stat
1399 M f
1399 M f
1400 # hg resolve --list
1400 # hg resolve --list
1401 R f
1401 R f
1402
1402
1403 premerge=keep respects true.mergemarkertemplate instead of
1403 premerge=keep respects true.mergemarkertemplate instead of
1404 true.mergemarkertemplate if true.mergemarkers=detailed:
1404 true.mergemarkertemplate if true.mergemarkers=detailed:
1405
1405
1406 $ beforemerge
1406 $ beforemerge
1407 [merge-tools]
1407 [merge-tools]
1408 false.whatever=
1408 false.whatever=
1409 true.priority=1
1409 true.priority=1
1410 true.executable=cat
1410 true.executable=cat
1411 # hg update -C 1
1411 # hg update -C 1
1412 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1412 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1413 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1413 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1414 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1414 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1415 > --config merge-tools.true.mergemarkers=detailed
1415 > --config merge-tools.true.mergemarkers=detailed
1416 merging f
1416 merging f
1417 <<<<<<< working copy: tooltmpl ef83787e2614
1417 <<<<<<< working copy: tooltmpl ef83787e2614
1418 revision 1
1418 revision 1
1419 space
1419 space
1420 =======
1420 =======
1421 revision 4
1421 revision 4
1422 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1422 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1423 revision 0
1423 revision 0
1424 space
1424 space
1425 revision 4
1425 revision 4
1426 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1426 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1427 (branch merge, don't forget to commit)
1427 (branch merge, don't forget to commit)
1428 $ aftermerge
1428 $ aftermerge
1429 # cat f
1429 # cat f
1430 <<<<<<< working copy: tooltmpl ef83787e2614
1430 <<<<<<< working copy: tooltmpl ef83787e2614
1431 revision 1
1431 revision 1
1432 space
1432 space
1433 =======
1433 =======
1434 revision 4
1434 revision 4
1435 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1435 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1436 # hg stat
1436 # hg stat
1437 M f
1437 M f
1438 # hg resolve --list
1438 # hg resolve --list
1439 R f
1439 R f
1440
1440
1441 Tool execution
1441 Tool execution
1442
1442
1443 set tools.args explicit to include $base $local $other $output:
1443 set tools.args explicit to include $base $local $other $output:
1444
1444
1445 $ beforemerge
1445 $ beforemerge
1446 [merge-tools]
1446 [merge-tools]
1447 false.whatever=
1447 false.whatever=
1448 true.priority=1
1448 true.priority=1
1449 true.executable=cat
1449 true.executable=cat
1450 # hg update -C 1
1450 # hg update -C 1
1451 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1451 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1452 > | sed 's,==> .* <==,==> ... <==,g'
1452 > | sed 's,==> .* <==,==> ... <==,g'
1453 merging f
1453 merging f
1454 ==> ... <==
1454 ==> ... <==
1455 revision 0
1455 revision 0
1456 space
1456 space
1457
1457
1458 ==> ... <==
1458 ==> ... <==
1459 revision 1
1459 revision 1
1460 space
1460 space
1461
1461
1462 ==> ... <==
1462 ==> ... <==
1463 revision 2
1463 revision 2
1464 space
1464 space
1465
1465
1466 ==> ... <==
1466 ==> ... <==
1467 revision 1
1467 revision 1
1468 space
1468 space
1469 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1469 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1470 (branch merge, don't forget to commit)
1470 (branch merge, don't forget to commit)
1471 $ aftermerge
1471 $ aftermerge
1472 # cat f
1472 # cat f
1473 revision 1
1473 revision 1
1474 space
1474 space
1475 # hg stat
1475 # hg stat
1476 M f
1476 M f
1477 # hg resolve --list
1477 # hg resolve --list
1478 R f
1478 R f
1479
1479
1480 Merge with "echo mergeresult > $local":
1480 Merge with "echo mergeresult > $local":
1481
1481
1482 $ beforemerge
1482 $ beforemerge
1483 [merge-tools]
1483 [merge-tools]
1484 false.whatever=
1484 false.whatever=
1485 true.priority=1
1485 true.priority=1
1486 true.executable=cat
1486 true.executable=cat
1487 # hg update -C 1
1487 # hg update -C 1
1488 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1488 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1489 merging f
1489 merging f
1490 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1490 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1491 (branch merge, don't forget to commit)
1491 (branch merge, don't forget to commit)
1492 $ aftermerge
1492 $ aftermerge
1493 # cat f
1493 # cat f
1494 mergeresult
1494 mergeresult
1495 # hg stat
1495 # hg stat
1496 M f
1496 M f
1497 # hg resolve --list
1497 # hg resolve --list
1498 R f
1498 R f
1499
1499
1500 - and $local is the file f:
1500 - and $local is the file f:
1501
1501
1502 $ beforemerge
1502 $ beforemerge
1503 [merge-tools]
1503 [merge-tools]
1504 false.whatever=
1504 false.whatever=
1505 true.priority=1
1505 true.priority=1
1506 true.executable=cat
1506 true.executable=cat
1507 # hg update -C 1
1507 # hg update -C 1
1508 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1508 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1509 merging f
1509 merging f
1510 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1510 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1511 (branch merge, don't forget to commit)
1511 (branch merge, don't forget to commit)
1512 $ aftermerge
1512 $ aftermerge
1513 # cat f
1513 # cat f
1514 mergeresult
1514 mergeresult
1515 # hg stat
1515 # hg stat
1516 M f
1516 M f
1517 # hg resolve --list
1517 # hg resolve --list
1518 R f
1518 R f
1519
1519
1520 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1520 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1521
1521
1522 $ beforemerge
1522 $ beforemerge
1523 [merge-tools]
1523 [merge-tools]
1524 false.whatever=
1524 false.whatever=
1525 true.priority=1
1525 true.priority=1
1526 true.executable=cat
1526 true.executable=cat
1527 # hg update -C 1
1527 # hg update -C 1
1528 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1528 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1529 merging f
1529 merging f
1530 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1530 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1531 (branch merge, don't forget to commit)
1531 (branch merge, don't forget to commit)
1532 $ aftermerge
1532 $ aftermerge
1533 # cat f
1533 # cat f
1534 mergeresult
1534 mergeresult
1535 # hg stat
1535 # hg stat
1536 M f
1536 M f
1537 # hg resolve --list
1537 # hg resolve --list
1538 R f
1538 R f
1539
1539
1540 Merge using tool with a path that must be quoted:
1540 Merge using tool with a path that must be quoted:
1541
1541
1542 $ beforemerge
1542 $ beforemerge
1543 [merge-tools]
1543 [merge-tools]
1544 false.whatever=
1544 false.whatever=
1545 true.priority=1
1545 true.priority=1
1546 true.executable=cat
1546 true.executable=cat
1547 # hg update -C 1
1547 # hg update -C 1
1548 $ cat <<EOF > 'my merge tool'
1548 $ cat <<EOF > 'my merge tool'
1549 > cat "\$1" "\$2" "\$3" > "\$4"
1549 > cat "\$1" "\$2" "\$3" > "\$4"
1550 > EOF
1550 > EOF
1551 $ hg --config merge-tools.true.executable='sh' \
1551 $ hg --config merge-tools.true.executable='sh' \
1552 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1552 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1553 > merge -r 2
1553 > merge -r 2
1554 merging f
1554 merging f
1555 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1555 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1556 (branch merge, don't forget to commit)
1556 (branch merge, don't forget to commit)
1557 $ rm -f 'my merge tool'
1557 $ rm -f 'my merge tool'
1558 $ aftermerge
1558 $ aftermerge
1559 # cat f
1559 # cat f
1560 revision 0
1560 revision 0
1561 space
1561 space
1562 revision 1
1562 revision 1
1563 space
1563 space
1564 revision 2
1564 revision 2
1565 space
1565 space
1566 # hg stat
1566 # hg stat
1567 M f
1567 M f
1568 # hg resolve --list
1568 # hg resolve --list
1569 R f
1569 R f
1570
1570
1571 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1571 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1572 that they're quoted properly as well. This is using the default 'basic'
1572 that they're quoted properly as well. This is using the default 'basic'
1573 mergemarkers even though ui.mergemarkers is 'detailed', so it's ignoring both
1573 mergemarkers even though ui.mergemarkers is 'detailed', so it's ignoring both
1574 mergemarkertemplate settings:
1574 mergemarkertemplate settings:
1575
1575
1576 $ beforemerge
1576 $ beforemerge
1577 [merge-tools]
1577 [merge-tools]
1578 false.whatever=
1578 false.whatever=
1579 true.priority=1
1579 true.priority=1
1580 true.executable=cat
1580 true.executable=cat
1581 # hg update -C 1
1581 # hg update -C 1
1582 $ cat <<EOF > printargs_merge_tool
1582 $ cat <<EOF > printargs_merge_tool
1583 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1583 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1584 > EOF
1584 > EOF
1585 $ hg --config merge-tools.true.executable='sh' \
1585 $ hg --config merge-tools.true.executable='sh' \
1586 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1586 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1587 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1587 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1588 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1588 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1589 > --config ui.mergemarkers=detailed \
1589 > --config ui.mergemarkers=detailed \
1590 > merge -r 2
1590 > merge -r 2
1591 merging f
1591 merging f
1592 arg: "ll:working copy"
1592 arg: "ll:working copy"
1593 arg: "lo:"
1593 arg: "lo:"
1594 arg: "merge rev"
1594 arg: "merge rev"
1595 arg: "lb:base: */f~base.*" (glob)
1595 arg: "lb:base: */f~base.*" (glob)
1596 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1596 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1597 (branch merge, don't forget to commit)
1597 (branch merge, don't forget to commit)
1598 $ rm -f 'printargs_merge_tool'
1598 $ rm -f 'printargs_merge_tool'
1599
1599
1600 Same test with experimental.mergetempdirprefix set:
1600 Same test with experimental.mergetempdirprefix set:
1601
1601
1602 $ beforemerge
1602 $ beforemerge
1603 [merge-tools]
1603 [merge-tools]
1604 false.whatever=
1604 false.whatever=
1605 true.priority=1
1605 true.priority=1
1606 true.executable=cat
1606 true.executable=cat
1607 # hg update -C 1
1607 # hg update -C 1
1608 $ cat <<EOF > printargs_merge_tool
1608 $ cat <<EOF > printargs_merge_tool
1609 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1609 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1610 > EOF
1610 > EOF
1611 $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
1611 $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
1612 > --config merge-tools.true.executable='sh' \
1612 > --config merge-tools.true.executable='sh' \
1613 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1613 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1614 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1614 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1615 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1615 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1616 > --config ui.mergemarkers=detailed \
1616 > --config ui.mergemarkers=detailed \
1617 > merge -r 2
1617 > merge -r 2
1618 merging f
1618 merging f
1619 arg: "ll:working copy"
1619 arg: "ll:working copy"
1620 arg: "lo:"
1620 arg: "lo:"
1621 arg: "merge rev"
1621 arg: "merge rev"
1622 arg: "lb:base: */hgmerge.*/f~base" (glob)
1622 arg: "lb:base: */hgmerge.*/f~base" (glob)
1623 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1623 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1624 (branch merge, don't forget to commit)
1624 (branch merge, don't forget to commit)
1625 $ rm -f 'printargs_merge_tool'
1625 $ rm -f 'printargs_merge_tool'
1626
1626
1627 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1627 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1628 that they're quoted properly as well. This is using 'detailed' mergemarkers,
1628 that they're quoted properly as well. This is using 'detailed' mergemarkers,
1629 even though ui.mergemarkers is 'basic', and using the tool's
1629 even though ui.mergemarkers is 'basic', and using the tool's
1630 mergemarkertemplate:
1630 mergemarkertemplate:
1631
1631
1632 $ beforemerge
1632 $ beforemerge
1633 [merge-tools]
1633 [merge-tools]
1634 false.whatever=
1634 false.whatever=
1635 true.priority=1
1635 true.priority=1
1636 true.executable=cat
1636 true.executable=cat
1637 # hg update -C 1
1637 # hg update -C 1
1638 $ cat <<EOF > printargs_merge_tool
1638 $ cat <<EOF > printargs_merge_tool
1639 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1639 > while test \$# -gt 0; do echo arg: \""\$1"\"; shift; done
1640 > EOF
1640 > EOF
1641 $ hg --config merge-tools.true.executable='sh' \
1641 $ hg --config merge-tools.true.executable='sh' \
1642 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1642 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1643 > --config merge-tools.true.mergemarkers=detailed \
1643 > --config merge-tools.true.mergemarkers=detailed \
1644 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1644 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1645 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1645 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1646 > --config ui.mergemarkers=basic \
1646 > --config ui.mergemarkers=basic \
1647 > merge -r 2
1647 > merge -r 2
1648 merging f
1648 merging f
1649 arg: "ll:working copy: tooltmpl ef83787e2614"
1649 arg: "ll:working copy: tooltmpl ef83787e2614"
1650 arg: "lo:"
1650 arg: "lo:"
1651 arg: "merge rev: tooltmpl 0185f4e0cf02"
1651 arg: "merge rev: tooltmpl 0185f4e0cf02"
1652 arg: "lb:base: */f~base.*" (glob)
1652 arg: "lb:base: */f~base.*" (glob)
1653 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1653 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1654 (branch merge, don't forget to commit)
1654 (branch merge, don't forget to commit)
1655 $ rm -f 'printargs_merge_tool'
1655 $ rm -f 'printargs_merge_tool'
1656
1656
1657 The merge tool still gets labellocal and labelother as 'basic' even when
1657 The merge tool still gets labellocal and labelother as 'basic' even when
1658 premerge=keep is used and has 'detailed' markers:
1658 premerge=keep is used and has 'detailed' markers:
1659
1659
1660 $ beforemerge
1660 $ beforemerge
1661 [merge-tools]
1661 [merge-tools]
1662 false.whatever=
1662 false.whatever=
1663 true.priority=1
1663 true.priority=1
1664 true.executable=cat
1664 true.executable=cat
1665 # hg update -C 1
1665 # hg update -C 1
1666 $ cat <<EOF > mytool
1666 $ cat <<EOF > mytool
1667 > echo labellocal: \""\$1"\"
1667 > echo labellocal: \""\$1"\"
1668 > echo labelother: \""\$2"\"
1668 > echo labelother: \""\$2"\"
1669 > echo "output (arg)": \""\$3"\"
1669 > echo "output (arg)": \""\$3"\"
1670 > echo "output (contents)":
1670 > echo "output (contents)":
1671 > cat "\$3"
1671 > cat "\$3"
1672 > EOF
1672 > EOF
1673 $ hg --config merge-tools.true.executable='sh' \
1673 $ hg --config merge-tools.true.executable='sh' \
1674 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1674 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1675 > --config merge-tools.true.premerge=keep \
1675 > --config merge-tools.true.premerge=keep \
1676 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1676 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1677 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1677 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1678 > --config ui.mergemarkers=detailed \
1678 > --config ui.mergemarkers=detailed \
1679 > merge -r 2
1679 > merge -r 2
1680 merging f
1680 merging f
1681 labellocal: "working copy"
1681 labellocal: "working copy"
1682 labelother: "merge rev"
1682 labelother: "merge rev"
1683 output (arg): "$TESTTMP/repo/f"
1683 output (arg): "$TESTTMP/repo/f"
1684 output (contents):
1684 output (contents):
1685 <<<<<<< working copy: uitmpl 1
1685 <<<<<<< working copy: uitmpl 1
1686 revision 1
1686 revision 1
1687 =======
1687 =======
1688 revision 2
1688 revision 2
1689 >>>>>>> merge rev: uitmpl 2
1689 >>>>>>> merge rev: uitmpl 2
1690 space
1690 space
1691 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1691 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1692 (branch merge, don't forget to commit)
1692 (branch merge, don't forget to commit)
1693 $ rm -f 'mytool'
1693 $ rm -f 'mytool'
1694
1694
1695 premerge=keep uses the *tool's* mergemarkertemplate if tool's
1695 premerge=keep uses the *tool's* mergemarkertemplate if tool's
1696 mergemarkers=detailed; labellocal and labelother also use the tool's template
1696 mergemarkers=detailed; labellocal and labelother also use the tool's template
1697
1697
1698 $ beforemerge
1698 $ beforemerge
1699 [merge-tools]
1699 [merge-tools]
1700 false.whatever=
1700 false.whatever=
1701 true.priority=1
1701 true.priority=1
1702 true.executable=cat
1702 true.executable=cat
1703 # hg update -C 1
1703 # hg update -C 1
1704 $ cat <<EOF > mytool
1704 $ cat <<EOF > mytool
1705 > echo labellocal: \""\$1"\"
1705 > echo labellocal: \""\$1"\"
1706 > echo labelother: \""\$2"\"
1706 > echo labelother: \""\$2"\"
1707 > echo "output (arg)": \""\$3"\"
1707 > echo "output (arg)": \""\$3"\"
1708 > echo "output (contents)":
1708 > echo "output (contents)":
1709 > cat "\$3"
1709 > cat "\$3"
1710 > EOF
1710 > EOF
1711 $ hg --config merge-tools.true.executable='sh' \
1711 $ hg --config merge-tools.true.executable='sh' \
1712 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1712 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1713 > --config merge-tools.true.premerge=keep \
1713 > --config merge-tools.true.premerge=keep \
1714 > --config merge-tools.true.mergemarkers=detailed \
1714 > --config merge-tools.true.mergemarkers=detailed \
1715 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1715 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1716 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1716 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1717 > --config ui.mergemarkers=detailed \
1717 > --config ui.mergemarkers=detailed \
1718 > merge -r 2
1718 > merge -r 2
1719 merging f
1719 merging f
1720 labellocal: "working copy: tooltmpl ef83787e2614"
1720 labellocal: "working copy: tooltmpl ef83787e2614"
1721 labelother: "merge rev: tooltmpl 0185f4e0cf02"
1721 labelother: "merge rev: tooltmpl 0185f4e0cf02"
1722 output (arg): "$TESTTMP/repo/f"
1722 output (arg): "$TESTTMP/repo/f"
1723 output (contents):
1723 output (contents):
1724 <<<<<<< working copy: tooltmpl ef83787e2614
1724 <<<<<<< working copy: tooltmpl ef83787e2614
1725 revision 1
1725 revision 1
1726 =======
1726 =======
1727 revision 2
1727 revision 2
1728 >>>>>>> merge rev: tooltmpl 0185f4e0cf02
1728 >>>>>>> merge rev: tooltmpl 0185f4e0cf02
1729 space
1729 space
1730 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1730 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1731 (branch merge, don't forget to commit)
1731 (branch merge, don't forget to commit)
1732 $ rm -f 'mytool'
1732 $ rm -f 'mytool'
1733
1733
1734 Issue3581: Merging a filename that needs to be quoted
1734 Issue3581: Merging a filename that needs to be quoted
1735 (This test doesn't work on Windows filesystems even on Linux, so check
1735 (This test doesn't work on Windows filesystems even on Linux, so check
1736 for Unix-like permission)
1736 for Unix-like permission)
1737
1737
1738 #if unix-permissions
1738 #if unix-permissions
1739 $ beforemerge
1739 $ beforemerge
1740 [merge-tools]
1740 [merge-tools]
1741 false.whatever=
1741 false.whatever=
1742 true.priority=1
1742 true.priority=1
1743 true.executable=cat
1743 true.executable=cat
1744 # hg update -C 1
1744 # hg update -C 1
1745 $ echo "revision 5" > '"; exit 1; echo "'
1745 $ echo "revision 5" > '"; exit 1; echo "'
1746 $ hg commit -Am "revision 5"
1746 $ hg commit -Am "revision 5"
1747 adding "; exit 1; echo "
1747 adding "; exit 1; echo "
1748 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1748 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1749 $ hg update -C 1 > /dev/null
1749 $ hg update -C 1 > /dev/null
1750 $ echo "revision 6" > '"; exit 1; echo "'
1750 $ echo "revision 6" > '"; exit 1; echo "'
1751 $ hg commit -Am "revision 6"
1751 $ hg commit -Am "revision 6"
1752 adding "; exit 1; echo "
1752 adding "; exit 1; echo "
1753 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1753 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1754 created new head
1754 created new head
1755 $ hg merge --config merge-tools.true.executable="true" -r 5
1755 $ hg merge --config merge-tools.true.executable="true" -r 5
1756 merging "; exit 1; echo "
1756 merging "; exit 1; echo "
1757 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1757 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1758 (branch merge, don't forget to commit)
1758 (branch merge, don't forget to commit)
1759 $ hg update -C 1 > /dev/null
1759 $ hg update -C 1 > /dev/null
1760
1760
1761 #else
1761 #else
1762
1762
1763 Match the non-portable filename commits above for test stability
1763 Match the non-portable filename commits above for test stability
1764
1764
1765 $ hg import --bypass -q - << EOF
1765 $ hg import --bypass -q - << EOF
1766 > # HG changeset patch
1766 > # HG changeset patch
1767 > revision 5
1767 > revision 5
1768 >
1768 >
1769 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1769 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1770 > new file mode 100644
1770 > new file mode 100644
1771 > --- /dev/null
1771 > --- /dev/null
1772 > +++ b/"; exit 1; echo "
1772 > +++ b/"; exit 1; echo "
1773 > @@ -0,0 +1,1 @@
1773 > @@ -0,0 +1,1 @@
1774 > +revision 5
1774 > +revision 5
1775 > EOF
1775 > EOF
1776
1776
1777 $ hg import --bypass -q - << EOF
1777 $ hg import --bypass -q - << EOF
1778 > # HG changeset patch
1778 > # HG changeset patch
1779 > revision 6
1779 > revision 6
1780 >
1780 >
1781 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1781 > diff --git a/"; exit 1; echo " b/"; exit 1; echo "
1782 > new file mode 100644
1782 > new file mode 100644
1783 > --- /dev/null
1783 > --- /dev/null
1784 > +++ b/"; exit 1; echo "
1784 > +++ b/"; exit 1; echo "
1785 > @@ -0,0 +1,1 @@
1785 > @@ -0,0 +1,1 @@
1786 > +revision 6
1786 > +revision 6
1787 > EOF
1787 > EOF
1788
1788
1789 #endif
1789 #endif
1790
1790
1791 Merge post-processing
1791 Merge post-processing
1792
1792
1793 cat is a bad merge-tool and doesn't change:
1793 cat is a bad merge-tool and doesn't change:
1794
1794
1795 $ beforemerge
1795 $ beforemerge
1796 [merge-tools]
1796 [merge-tools]
1797 false.whatever=
1797 false.whatever=
1798 true.priority=1
1798 true.priority=1
1799 true.executable=cat
1799 true.executable=cat
1800 # hg update -C 1
1800 # hg update -C 1
1801 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1801 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1802 merging f
1802 merging f
1803 revision 1
1803 revision 1
1804 space
1804 space
1805 revision 0
1805 revision 0
1806 space
1806 space
1807 revision 2
1807 revision 2
1808 space
1808 space
1809 output file f appears unchanged
1809 output file f appears unchanged
1810 was merge successful (yn)? n
1810 was merge successful (yn)? n
1811 merging f failed!
1811 merging f failed!
1812 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1812 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1813 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1813 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1814 [1]
1814 [1]
1815 $ aftermerge
1815 $ aftermerge
1816 # cat f
1816 # cat f
1817 revision 1
1817 revision 1
1818 space
1818 space
1819 # hg stat
1819 # hg stat
1820 M f
1820 M f
1821 ? f.orig
1821 ? f.orig
1822 # hg resolve --list
1822 # hg resolve --list
1823 U f
1823 U f
1824
1824
1825 missingbinary is a merge-tool that doesn't exist:
1825 missingbinary is a merge-tool that doesn't exist:
1826
1826
1827 $ echo "missingbinary.executable=doesnotexist" >> .hg/hgrc
1827 $ echo "missingbinary.executable=doesnotexist" >> .hg/hgrc
1828 $ beforemerge
1828 $ beforemerge
1829 [merge-tools]
1829 [merge-tools]
1830 false.whatever=
1830 false.whatever=
1831 true.priority=1
1831 true.priority=1
1832 true.executable=cat
1832 true.executable=cat
1833 missingbinary.executable=doesnotexist
1833 missingbinary.executable=doesnotexist
1834 # hg update -C 1
1834 # hg update -C 1
1835 $ hg merge -y -r 2 --config ui.merge=missingbinary
1835 $ hg merge -y -r 2 --config ui.merge=missingbinary
1836 couldn't find merge tool missingbinary (for pattern f)
1836 couldn't find merge tool missingbinary (for pattern f)
1837 merging f
1837 merging f
1838 revision 1
1838 revision 1
1839 space
1839 space
1840 revision 0
1840 revision 0
1841 space
1841 space
1842 revision 2
1842 revision 2
1843 space
1843 space
1844 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1844 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1845 (branch merge, don't forget to commit)
1845 (branch merge, don't forget to commit)
1846
1846
1847 $ hg update -q -C 1
1847 $ hg update -q -C 1
1848 $ rm f
1848 $ rm f
1849
1849
1850 internal merge cannot handle symlinks and shouldn't try:
1850 internal merge cannot handle symlinks and shouldn't try:
1851
1851
1852 #if symlink
1852 #if symlink
1853
1853
1854 $ ln -s symlink f
1854 $ ln -s symlink f
1855 $ hg commit -qm 'f is symlink'
1855 $ hg commit -qm 'f is symlink'
1856
1856
1857 #else
1857 #else
1858
1858
1859 $ hg import --bypass -q - << EOF
1859 $ hg import --bypass -q - << EOF
1860 > # HG changeset patch
1860 > # HG changeset patch
1861 > f is symlink
1861 > f is symlink
1862 >
1862 >
1863 > diff --git a/f b/f
1863 > diff --git a/f b/f
1864 > old mode 100644
1864 > old mode 100644
1865 > new mode 120000
1865 > new mode 120000
1866 > --- a/f
1866 > --- a/f
1867 > +++ b/f
1867 > +++ b/f
1868 > @@ -1,2 +1,1 @@
1868 > @@ -1,2 +1,1 @@
1869 > -revision 1
1869 > -revision 1
1870 > -space
1870 > -space
1871 > +symlink
1871 > +symlink
1872 > \ No newline at end of file
1872 > \ No newline at end of file
1873 > EOF
1873 > EOF
1874
1874
1875 Resolve 'other [destination] changed f which local [working copy] deleted' prompt
1875 Resolve 'other [destination] changed f which local [working copy] deleted' prompt
1876 $ hg up -q -C --config ui.interactive=True << EOF
1876 $ hg up -q -C --config ui.interactive=True << EOF
1877 > c
1877 > c
1878 > EOF
1878 > EOF
1879
1879
1880 #endif
1880 #endif
1881
1881
1882 $ hg merge -r 2 --tool internal:merge
1882 $ hg merge -r 2 --tool internal:merge
1883 merging f
1883 merging f
1884 warning: internal :merge cannot merge symlinks for f
1884 warning: internal :merge cannot merge symlinks for f
1885 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1885 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1886 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1886 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1887 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1887 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1888 [1]
1888 [1]
1889
1889
1890 Verify naming of temporary files and that extension is preserved:
1890 Verify naming of temporary files and that extension is preserved:
1891
1891
1892 $ hg update -q -C 1
1892 $ hg update -q -C 1
1893 $ hg mv f f.txt
1893 $ hg mv f f.txt
1894 $ hg ci -qm "f.txt"
1894 $ hg ci -qm "f.txt"
1895 $ hg update -q -C 2
1895 $ hg update -q -C 2
1896 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1896 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1897 merging f and f.txt to f.txt
1897 merging f and f.txt to f.txt
1898 */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/repo/f.txt (glob)
1898 */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/repo/f.txt (glob)
1899 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1899 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1900 (branch merge, don't forget to commit)
1900 (branch merge, don't forget to commit)
1901
1901
1902 Verify naming of temporary files and that extension is preserved
1902 Verify naming of temporary files and that extension is preserved
1903 (experimental.mergetempdirprefix version):
1903 (experimental.mergetempdirprefix version):
1904
1904
1905 $ hg update -q -C 1
1905 $ hg update -q -C 1
1906 $ hg mv f f.txt
1906 $ hg mv f f.txt
1907 $ hg ci -qm "f.txt"
1907 $ hg ci -qm "f.txt"
1908 warning: commit already existed in the repository!
1908 warning: commit already existed in the repository!
1909 $ hg update -q -C 2
1909 $ hg update -q -C 2
1910 $ hg merge -y -r tip --tool echo \
1910 $ hg merge -y -r tip --tool echo \
1911 > --config merge-tools.echo.args='$base $local $other $output' \
1911 > --config merge-tools.echo.args='$base $local $other $output' \
1912 > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
1912 > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
1913 merging f and f.txt to f.txt
1913 merging f and f.txt to f.txt
1914 $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/repo/f.txt (glob)
1914 $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/repo/f.txt (glob)
1915 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1915 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1916 (branch merge, don't forget to commit)
1916 (branch merge, don't forget to commit)
1917
1917
1918 Binary files capability checking
1918 Binary files capability checking
1919
1919
1920 $ hg update -q -C 0
1920 $ hg update -q -C 0
1921 $ "$PYTHON" <<EOF
1921 $ "$PYTHON" <<EOF
1922 > with open('b', 'wb') as fp:
1922 > with open('b', 'wb') as fp:
1923 > fp.write(b'\x00\x01\x02\x03')
1923 > fp.write(b'\x00\x01\x02\x03')
1924 > EOF
1924 > EOF
1925 $ hg add b
1925 $ hg add b
1926 $ hg commit -qm "add binary file (#1)"
1926 $ hg commit -qm "add binary file (#1)"
1927
1927
1928 $ hg update -q -C 0
1928 $ hg update -q -C 0
1929 $ "$PYTHON" <<EOF
1929 $ "$PYTHON" <<EOF
1930 > with open('b', 'wb') as fp:
1930 > with open('b', 'wb') as fp:
1931 > fp.write(b'\x03\x02\x01\x00')
1931 > fp.write(b'\x03\x02\x01\x00')
1932 > EOF
1932 > EOF
1933 $ hg add b
1933 $ hg add b
1934 $ hg commit -qm "add binary file (#2)"
1934 $ hg commit -qm "add binary file (#2)"
1935
1935
1936 By default, binary files capability of internal merge tools is not
1936 By default, binary files capability of internal merge tools is not
1937 checked strictly.
1937 checked strictly.
1938
1938
1939 (for merge-patterns, chosen unintentionally)
1939 (for merge-patterns, chosen unintentionally)
1940
1940
1941 $ hg merge 9 \
1941 $ hg merge 9 \
1942 > --config merge-patterns.b=:merge-other \
1942 > --config merge-patterns.b=:merge-other \
1943 > --config merge-patterns.re:[a-z]=:other
1943 > --config merge-patterns.re:[a-z]=:other
1944 warning: check merge-patterns configurations, if ':merge-other' for binary file 'b' is unintentional
1944 warning: check merge-patterns configurations, if ':merge-other' for binary file 'b' is unintentional
1945 (see 'hg help merge-tools' for binary files capability)
1945 (see 'hg help merge-tools' for binary files capability)
1946 merging b
1946 merging b
1947 warning: b looks like a binary file.
1947 warning: b looks like a binary file.
1948 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1948 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1949 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1949 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1950 [1]
1950 [1]
1951 (Testing that commands.merge.require-rev doesn't break --abort)
1951 (Testing that commands.merge.require-rev doesn't break --abort)
1952 $ hg merge --abort -q
1952 $ hg merge --abort -q
1953
1953
1954 (for ui.merge, ignored unintentionally)
1954 (for ui.merge, ignored unintentionally)
1955
1955
1956 $ hg merge 9 \
1956 $ hg merge 9 \
1957 > --config merge-tools.:other.binary=true \
1957 > --config merge-tools.:other.binary=true \
1958 > --config ui.merge=:other
1958 > --config ui.merge=:other
1959 tool :other (for pattern b) can't handle binary
1959 tool :other (for pattern b) can't handle binary
1960 tool true can't handle binary
1960 tool true can't handle binary
1961 tool :other can't handle binary
1961 tool :other can't handle binary
1962 tool false can't handle binary
1962 tool false can't handle binary
1963 no tool found to merge b
1963 no tool found to merge b
1964 file 'b' needs to be resolved.
1964 file 'b' needs to be resolved.
1965 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
1965 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
1966 What do you want to do? u
1966 What do you want to do? u
1967 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1967 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1968 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1968 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1969 [1]
1969 [1]
1970 $ hg merge --abort -q
1970 $ hg merge --abort -q
1971
1971
1972 With merge.strict-capability-check=true, binary files capability of
1972 With merge.strict-capability-check=true, binary files capability of
1973 internal merge tools is checked strictly.
1973 internal merge tools is checked strictly.
1974
1974
1975 $ f --hexdump b
1975 $ f --hexdump b
1976 b:
1976 b:
1977 0000: 03 02 01 00 |....|
1977 0000: 03 02 01 00 |....|
1978
1978
1979 (for merge-patterns)
1979 (for merge-patterns)
1980
1980
1981 $ hg merge 9 --config merge.strict-capability-check=true \
1981 $ hg merge 9 --config merge.strict-capability-check=true \
1982 > --config merge-tools.:merge-other.binary=true \
1982 > --config merge-tools.:merge-other.binary=true \
1983 > --config merge-patterns.b=:merge-other \
1983 > --config merge-patterns.b=:merge-other \
1984 > --config merge-patterns.re:[a-z]=:other
1984 > --config merge-patterns.re:[a-z]=:other
1985 tool :merge-other (for pattern b) can't handle binary
1985 tool :merge-other (for pattern b) can't handle binary
1986 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1986 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1987 (branch merge, don't forget to commit)
1987 (branch merge, don't forget to commit)
1988 $ f --hexdump b
1988 $ f --hexdump b
1989 b:
1989 b:
1990 0000: 00 01 02 03 |....|
1990 0000: 00 01 02 03 |....|
1991 $ hg merge --abort -q
1991 $ hg merge --abort -q
1992
1992
1993 (for ui.merge)
1993 (for ui.merge)
1994
1994
1995 $ hg merge 9 --config merge.strict-capability-check=true \
1995 $ hg merge 9 --config merge.strict-capability-check=true \
1996 > --config ui.merge=:other
1996 > --config ui.merge=:other
1997 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1997 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1998 (branch merge, don't forget to commit)
1998 (branch merge, don't forget to commit)
1999 $ f --hexdump b
1999 $ f --hexdump b
2000 b:
2000 b:
2001 0000: 00 01 02 03 |....|
2001 0000: 00 01 02 03 |....|
2002 $ hg merge --abort -q
2002 $ hg merge --abort -q
2003
2003
2004 Check that the extra information is printed correctly
2004 Check that the extra information is printed correctly
2005
2005
2006 $ hg merge 9 \
2006 $ hg merge 9 \
2007 > --config merge-tools.testecho.executable='echo' \
2007 > --config merge-tools.testecho.executable='echo' \
2008 > --config merge-tools.testecho.args='merge runs here ...' \
2008 > --config merge-tools.testecho.args='merge runs here ...' \
2009 > --config merge-tools.testecho.binary=True \
2009 > --config merge-tools.testecho.binary=True \
2010 > --config ui.merge=testecho \
2010 > --config ui.merge=testecho \
2011 > --config ui.pre-merge-tool-output-template='\n{label("extmerge.running_merge_tool", "Running merge tool for {path} ({toolpath}):")}\n{separate("\n", extmerge_section(local), extmerge_section(base), extmerge_section(other))}\n' \
2011 > --config ui.pre-merge-tool-output-template='\n{label("extmerge.running_merge_tool", "Running merge tool for {path} ({toolpath}):")}\n{separate("\n", extmerge_section(local), extmerge_section(base), extmerge_section(other))}\n' \
2012 > --config 'templatealias.extmerge_section(sect)="- {pad("{sect.name} ({sect.label})", 20, left=True)}: {revset(sect.node)%"{rev}:{shortest(node,8)} {desc|firstline} {separate(" ", tags, bookmarks, branch)}"}"'
2012 > --config 'templatealias.extmerge_section(sect)="- {pad("{sect.name} ({sect.label})", 20, left=True)}: {revset(sect.node)%"{rev}:{shortest(node,8)} {desc|firstline} {separate(" ", tags, bookmarks, branch)}"}"'
2013 merging b
2013 merging b
2014
2014
2015 Running merge tool for b ("*/bin/echo.exe"): (glob) (windows !)
2015 Running merge tool for b ("*/bin/echo.exe"): (glob) (windows !)
2016 Running merge tool for b (*/bin/echo): (glob) (no-windows !)
2016 Running merge tool for b (*/bin/echo): (glob) (no-windows !)
2017 - local (working copy): 10:2d1f533d add binary file (#2) tip default
2017 - local (working copy): 10:2d1f533d add binary file (#2) tip default
2018 - base (base): -1:00000000 default
2018 - base (base): -1:00000000 default
2019 - other (merge rev): 9:1e7ad7d7 add binary file (#1) default
2019 - other (merge rev): 9:1e7ad7d7 add binary file (#1) default
2020 merge runs here ...
2020 merge runs here ...
2021 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
2021 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
2022 (branch merge, don't forget to commit)
2022 (branch merge, don't forget to commit)
2023
2023
2024 Check that debugpicktool examines which merge tool is chosen for
2024 Check that debugpicktool examines which merge tool is chosen for
2025 specified file as expected
2025 specified file as expected
2026
2026
2027 $ beforemerge
2027 $ beforemerge
2028 [merge-tools]
2028 [merge-tools]
2029 false.whatever=
2029 false.whatever=
2030 true.priority=1
2030 true.priority=1
2031 true.executable=cat
2031 true.executable=cat
2032 missingbinary.executable=doesnotexist
2032 missingbinary.executable=doesnotexist
2033 # hg update -C 1
2033 # hg update -C 1
2034
2034
2035 (default behavior: checking files in the working parent context)
2035 (default behavior: checking files in the working parent context)
2036
2036
2037 $ hg manifest
2037 $ hg manifest
2038 f
2038 f
2039 $ hg debugpickmergetool
2039 $ hg debugpickmergetool
2040 f = true
2040 f = true
2041
2041
2042 (-X/-I and file patterns limmit examination targets)
2042 (-X/-I and file patterns limmit examination targets)
2043
2043
2044 $ hg debugpickmergetool -X f
2044 $ hg debugpickmergetool -X f
2045 $ hg debugpickmergetool unknown
2045 $ hg debugpickmergetool unknown
2046 unknown: no such file in rev ef83787e2614
2046 unknown: no such file in rev ef83787e2614
2047
2047
2048 (--changedelete emulates merging change and delete)
2048 (--changedelete emulates merging change and delete)
2049
2049
2050 $ hg debugpickmergetool --changedelete
2050 $ hg debugpickmergetool --changedelete
2051 f = :prompt
2051 f = :prompt
2052
2052
2053 (-r REV causes checking files in specified revision)
2053 (-r REV causes checking files in specified revision)
2054
2054
2055 $ hg manifest -r 8
2055 $ hg manifest -r 8
2056 f.txt
2056 f.txt
2057 $ hg debugpickmergetool -r 8
2057 $ hg debugpickmergetool -r 8
2058 f.txt = true
2058 f.txt = true
2059
2059
2060 #if symlink
2060 #if symlink
2061
2061
2062 (symlink causes chosing :prompt)
2062 (symlink causes chosing :prompt)
2063
2063
2064 $ hg debugpickmergetool -r 6d00b3726f6e
2064 $ hg debugpickmergetool -r 6d00b3726f6e
2065 f = :prompt
2065 f = :prompt
2066
2066
2067 (by default, it is assumed that no internal merge tools has symlinks
2067 (by default, it is assumed that no internal merge tools has symlinks
2068 capability)
2068 capability)
2069
2069
2070 $ hg debugpickmergetool \
2070 $ hg debugpickmergetool \
2071 > -r 6d00b3726f6e \
2071 > -r 6d00b3726f6e \
2072 > --config merge-tools.:merge-other.symlink=true \
2072 > --config merge-tools.:merge-other.symlink=true \
2073 > --config merge-patterns.f=:merge-other \
2073 > --config merge-patterns.f=:merge-other \
2074 > --config merge-patterns.re:[f]=:merge-local \
2074 > --config merge-patterns.re:[f]=:merge-local \
2075 > --config merge-patterns.re:[a-z]=:other
2075 > --config merge-patterns.re:[a-z]=:other
2076 f = :prompt
2076 f = :prompt
2077
2077
2078 $ hg debugpickmergetool \
2078 $ hg debugpickmergetool \
2079 > -r 6d00b3726f6e \
2079 > -r 6d00b3726f6e \
2080 > --config merge-tools.:other.symlink=true \
2080 > --config merge-tools.:other.symlink=true \
2081 > --config ui.merge=:other
2081 > --config ui.merge=:other
2082 f = :prompt
2082 f = :prompt
2083
2083
2084 (with strict-capability-check=true, actual symlink capabilities are
2084 (with strict-capability-check=true, actual symlink capabilities are
2085 checked striclty)
2085 checked striclty)
2086
2086
2087 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2087 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2088 > -r 6d00b3726f6e \
2088 > -r 6d00b3726f6e \
2089 > --config merge-tools.:merge-other.symlink=true \
2089 > --config merge-tools.:merge-other.symlink=true \
2090 > --config merge-patterns.f=:merge-other \
2090 > --config merge-patterns.f=:merge-other \
2091 > --config merge-patterns.re:[f]=:merge-local \
2091 > --config merge-patterns.re:[f]=:merge-local \
2092 > --config merge-patterns.re:[a-z]=:other
2092 > --config merge-patterns.re:[a-z]=:other
2093 f = :other
2093 f = :other
2094
2094
2095 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2095 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2096 > -r 6d00b3726f6e \
2096 > -r 6d00b3726f6e \
2097 > --config ui.merge=:other
2097 > --config ui.merge=:other
2098 f = :other
2098 f = :other
2099
2099
2100 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2100 $ hg debugpickmergetool --config merge.strict-capability-check=true \
2101 > -r 6d00b3726f6e \
2101 > -r 6d00b3726f6e \
2102 > --config merge-tools.:merge-other.symlink=true \
2102 > --config merge-tools.:merge-other.symlink=true \
2103 > --config ui.merge=:merge-other
2103 > --config ui.merge=:merge-other
2104 f = :prompt
2104 f = :prompt
2105
2105
2106 #endif
2106 #endif
2107
2107
2108 (--verbose shows some configurations)
2108 (--verbose shows some configurations)
2109
2109
2110 $ hg debugpickmergetool --tool foobar -v
2110 $ hg debugpickmergetool --tool foobar -v
2111 with --tool 'foobar'
2111 with --tool 'foobar'
2112 f = foobar
2112 f = foobar
2113
2113
2114 $ HGMERGE=false hg debugpickmergetool -v
2114 $ HGMERGE=false hg debugpickmergetool -v
2115 with HGMERGE='false'
2115 with HGMERGE='false'
2116 f = false
2116 f = false
2117
2117
2118 $ hg debugpickmergetool --config ui.merge=false -v
2118 $ hg debugpickmergetool --config ui.merge=false -v
2119 with ui.merge='false'
2119 with ui.merge='false'
2120 f = false
2120 f = false
2121
2121
2122 (--debug shows errors detected intermediately)
2122 (--debug shows errors detected intermediately)
2123
2123
2124 $ hg debugpickmergetool --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool --debug f
2124 $ hg debugpickmergetool --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool --debug f
2125 couldn't find merge tool true (for pattern f)
2125 couldn't find merge tool true (for pattern f)
2126 couldn't find merge tool true
2126 couldn't find merge tool true
2127 f = false
2127 f = false
2128
2128
2129 $ cd ..
2129 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now