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