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