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