##// END OF EJS Templates
filemerge: move :merge-local/other symlink check to precheck...
Siddharth Agarwal -
r26893:19c4b93c default
parent child Browse files
Show More
@@ -1,585 +1,581 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 filecmp
10 import filecmp
11 import os
11 import os
12 import re
12 import re
13 import tempfile
13 import tempfile
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import short
16 from .node import short
17
17
18 from . import (
18 from . import (
19 error,
19 error,
20 match,
20 match,
21 simplemerge,
21 simplemerge,
22 tagmerge,
22 tagmerge,
23 templatekw,
23 templatekw,
24 templater,
24 templater,
25 util,
25 util,
26 )
26 )
27
27
28 def _toolstr(ui, tool, part, default=""):
28 def _toolstr(ui, tool, part, default=""):
29 return ui.config("merge-tools", tool + "." + part, default)
29 return ui.config("merge-tools", tool + "." + part, default)
30
30
31 def _toolbool(ui, tool, part, default=False):
31 def _toolbool(ui, tool, part, default=False):
32 return ui.configbool("merge-tools", tool + "." + part, default)
32 return ui.configbool("merge-tools", tool + "." + part, default)
33
33
34 def _toollist(ui, tool, part, default=[]):
34 def _toollist(ui, tool, part, default=[]):
35 return ui.configlist("merge-tools", tool + "." + part, default)
35 return ui.configlist("merge-tools", tool + "." + part, default)
36
36
37 internals = {}
37 internals = {}
38 # Merge tools to document.
38 # Merge tools to document.
39 internalsdoc = {}
39 internalsdoc = {}
40
40
41 # internal tool merge types
41 # internal tool merge types
42 nomerge = None
42 nomerge = None
43 mergeonly = 'mergeonly' # just the full merge, no premerge
43 mergeonly = 'mergeonly' # just the full merge, no premerge
44 fullmerge = 'fullmerge' # both premerge and merge
44 fullmerge = 'fullmerge' # both premerge and merge
45
45
46 def internaltool(name, mergetype, onfailure=None, precheck=None):
46 def internaltool(name, mergetype, onfailure=None, precheck=None):
47 '''return a decorator for populating internal merge tool table'''
47 '''return a decorator for populating internal merge tool table'''
48 def decorator(func):
48 def decorator(func):
49 fullname = ':' + name
49 fullname = ':' + name
50 func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
50 func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
51 internals[fullname] = func
51 internals[fullname] = func
52 internals['internal:' + name] = func
52 internals['internal:' + name] = func
53 internalsdoc[fullname] = func
53 internalsdoc[fullname] = func
54 func.mergetype = mergetype
54 func.mergetype = mergetype
55 func.onfailure = onfailure
55 func.onfailure = onfailure
56 func.precheck = precheck
56 func.precheck = precheck
57 return func
57 return func
58 return decorator
58 return decorator
59
59
60 def _findtool(ui, tool):
60 def _findtool(ui, tool):
61 if tool in internals:
61 if tool in internals:
62 return tool
62 return tool
63 return findexternaltool(ui, tool)
63 return findexternaltool(ui, tool)
64
64
65 def findexternaltool(ui, tool):
65 def findexternaltool(ui, tool):
66 for kn in ("regkey", "regkeyalt"):
66 for kn in ("regkey", "regkeyalt"):
67 k = _toolstr(ui, tool, kn)
67 k = _toolstr(ui, tool, kn)
68 if not k:
68 if not k:
69 continue
69 continue
70 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
70 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
71 if p:
71 if p:
72 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
72 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
73 if p:
73 if p:
74 return p
74 return p
75 exe = _toolstr(ui, tool, "executable", tool)
75 exe = _toolstr(ui, tool, "executable", tool)
76 return util.findexe(util.expandpath(exe))
76 return util.findexe(util.expandpath(exe))
77
77
78 def _picktool(repo, ui, path, binary, symlink):
78 def _picktool(repo, ui, path, binary, symlink):
79 def check(tool, pat, symlink, binary):
79 def check(tool, pat, symlink, binary):
80 tmsg = tool
80 tmsg = tool
81 if pat:
81 if pat:
82 tmsg += " specified for " + pat
82 tmsg += " specified for " + pat
83 if not _findtool(ui, tool):
83 if not _findtool(ui, tool):
84 if pat: # explicitly requested tool deserves a warning
84 if pat: # explicitly requested tool deserves a warning
85 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
85 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
86 else: # configured but non-existing tools are more silent
86 else: # configured but non-existing tools are more silent
87 ui.note(_("couldn't find merge tool %s\n") % tmsg)
87 ui.note(_("couldn't find merge tool %s\n") % tmsg)
88 elif symlink and not _toolbool(ui, tool, "symlink"):
88 elif symlink and not _toolbool(ui, tool, "symlink"):
89 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
89 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
90 elif binary and not _toolbool(ui, tool, "binary"):
90 elif binary and not _toolbool(ui, tool, "binary"):
91 ui.warn(_("tool %s can't handle binary\n") % tmsg)
91 ui.warn(_("tool %s can't handle binary\n") % tmsg)
92 elif not util.gui() and _toolbool(ui, tool, "gui"):
92 elif not util.gui() and _toolbool(ui, tool, "gui"):
93 ui.warn(_("tool %s requires a GUI\n") % tmsg)
93 ui.warn(_("tool %s requires a GUI\n") % tmsg)
94 else:
94 else:
95 return True
95 return True
96 return False
96 return False
97
97
98 # internal config: ui.forcemerge
98 # internal config: ui.forcemerge
99 # forcemerge comes from command line arguments, highest priority
99 # forcemerge comes from command line arguments, highest priority
100 force = ui.config('ui', 'forcemerge')
100 force = ui.config('ui', 'forcemerge')
101 if force:
101 if force:
102 toolpath = _findtool(ui, force)
102 toolpath = _findtool(ui, force)
103 if toolpath:
103 if toolpath:
104 return (force, util.shellquote(toolpath))
104 return (force, util.shellquote(toolpath))
105 else:
105 else:
106 # mimic HGMERGE if given tool not found
106 # mimic HGMERGE if given tool not found
107 return (force, force)
107 return (force, force)
108
108
109 # HGMERGE takes next precedence
109 # HGMERGE takes next precedence
110 hgmerge = os.environ.get("HGMERGE")
110 hgmerge = os.environ.get("HGMERGE")
111 if hgmerge:
111 if hgmerge:
112 return (hgmerge, hgmerge)
112 return (hgmerge, hgmerge)
113
113
114 # then patterns
114 # then patterns
115 for pat, tool in ui.configitems("merge-patterns"):
115 for pat, tool in ui.configitems("merge-patterns"):
116 mf = match.match(repo.root, '', [pat])
116 mf = match.match(repo.root, '', [pat])
117 if mf(path) and check(tool, pat, symlink, False):
117 if mf(path) and check(tool, pat, symlink, False):
118 toolpath = _findtool(ui, tool)
118 toolpath = _findtool(ui, tool)
119 return (tool, util.shellquote(toolpath))
119 return (tool, util.shellquote(toolpath))
120
120
121 # then merge tools
121 # then merge tools
122 tools = {}
122 tools = {}
123 disabled = set()
123 disabled = set()
124 for k, v in ui.configitems("merge-tools"):
124 for k, v in ui.configitems("merge-tools"):
125 t = k.split('.')[0]
125 t = k.split('.')[0]
126 if t not in tools:
126 if t not in tools:
127 tools[t] = int(_toolstr(ui, t, "priority", "0"))
127 tools[t] = int(_toolstr(ui, t, "priority", "0"))
128 if _toolbool(ui, t, "disabled", False):
128 if _toolbool(ui, t, "disabled", False):
129 disabled.add(t)
129 disabled.add(t)
130 names = tools.keys()
130 names = tools.keys()
131 tools = sorted([(-p, t) for t, p in tools.items() if t not in disabled])
131 tools = sorted([(-p, t) for t, p in tools.items() if t not in disabled])
132 uimerge = ui.config("ui", "merge")
132 uimerge = ui.config("ui", "merge")
133 if uimerge:
133 if uimerge:
134 if uimerge not in names:
134 if uimerge not in names:
135 return (uimerge, uimerge)
135 return (uimerge, uimerge)
136 tools.insert(0, (None, uimerge)) # highest priority
136 tools.insert(0, (None, uimerge)) # highest priority
137 tools.append((None, "hgmerge")) # the old default, if found
137 tools.append((None, "hgmerge")) # the old default, if found
138 for p, t in tools:
138 for p, t in tools:
139 if check(t, None, symlink, binary):
139 if check(t, None, symlink, binary):
140 toolpath = _findtool(ui, t)
140 toolpath = _findtool(ui, t)
141 return (t, util.shellquote(toolpath))
141 return (t, util.shellquote(toolpath))
142
142
143 # internal merge or prompt as last resort
143 # internal merge or prompt as last resort
144 if symlink or binary:
144 if symlink or binary:
145 return ":prompt", None
145 return ":prompt", None
146 return ":merge", None
146 return ":merge", None
147
147
148 def _eoltype(data):
148 def _eoltype(data):
149 "Guess the EOL type of a file"
149 "Guess the EOL type of a file"
150 if '\0' in data: # binary
150 if '\0' in data: # binary
151 return None
151 return None
152 if '\r\n' in data: # Windows
152 if '\r\n' in data: # Windows
153 return '\r\n'
153 return '\r\n'
154 if '\r' in data: # Old Mac
154 if '\r' in data: # Old Mac
155 return '\r'
155 return '\r'
156 if '\n' in data: # UNIX
156 if '\n' in data: # UNIX
157 return '\n'
157 return '\n'
158 return None # unknown
158 return None # unknown
159
159
160 def _matcheol(file, origfile):
160 def _matcheol(file, origfile):
161 "Convert EOL markers in a file to match origfile"
161 "Convert EOL markers in a file to match origfile"
162 tostyle = _eoltype(util.readfile(origfile))
162 tostyle = _eoltype(util.readfile(origfile))
163 if tostyle:
163 if tostyle:
164 data = util.readfile(file)
164 data = util.readfile(file)
165 style = _eoltype(data)
165 style = _eoltype(data)
166 if style:
166 if style:
167 newdata = data.replace(style, tostyle)
167 newdata = data.replace(style, tostyle)
168 if newdata != data:
168 if newdata != data:
169 util.writefile(file, newdata)
169 util.writefile(file, newdata)
170
170
171 @internaltool('prompt', nomerge)
171 @internaltool('prompt', nomerge)
172 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
172 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
173 """Asks the user which of the local or the other version to keep as
173 """Asks the user which of the local or the other version to keep as
174 the merged version."""
174 the merged version."""
175 ui = repo.ui
175 ui = repo.ui
176 fd = fcd.path()
176 fd = fcd.path()
177
177
178 index = ui.promptchoice(_(" no tool found to merge %s\n"
178 index = ui.promptchoice(_(" no tool found to merge %s\n"
179 "keep (l)ocal or take (o)ther?"
179 "keep (l)ocal or take (o)ther?"
180 "$$ &Local $$ &Other") % fd, 0)
180 "$$ &Local $$ &Other") % fd, 0)
181 choice = ['local', 'other'][index]
181 choice = ['local', 'other'][index]
182
182
183 if choice == 'other':
183 if choice == 'other':
184 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
184 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
185 else:
185 else:
186 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
186 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
187
187
188 @internaltool('local', nomerge)
188 @internaltool('local', nomerge)
189 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
189 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
190 """Uses the local version of files as the merged version."""
190 """Uses the local version of files as the merged version."""
191 return 0
191 return 0
192
192
193 @internaltool('other', nomerge)
193 @internaltool('other', nomerge)
194 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
194 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
195 """Uses the other version of files as the merged version."""
195 """Uses the other version of files as the merged version."""
196 repo.wwrite(fcd.path(), fco.data(), fco.flags())
196 repo.wwrite(fcd.path(), fco.data(), fco.flags())
197 return 0
197 return 0
198
198
199 @internaltool('fail', nomerge)
199 @internaltool('fail', nomerge)
200 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
200 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
201 """
201 """
202 Rather than attempting to merge files that were modified on both
202 Rather than attempting to merge files that were modified on both
203 branches, it marks them as unresolved. The resolve command must be
203 branches, it marks them as unresolved. The resolve command must be
204 used to resolve these conflicts."""
204 used to resolve these conflicts."""
205 return 1
205 return 1
206
206
207 def _premerge(repo, toolconf, files, labels=None):
207 def _premerge(repo, toolconf, files, labels=None):
208 tool, toolpath, binary, symlink = toolconf
208 tool, toolpath, binary, symlink = toolconf
209 if symlink:
209 if symlink:
210 return 1
210 return 1
211 a, b, c, back = files
211 a, b, c, back = files
212
212
213 ui = repo.ui
213 ui = repo.ui
214
214
215 validkeep = ['keep', 'keep-merge3']
215 validkeep = ['keep', 'keep-merge3']
216
216
217 # do we attempt to simplemerge first?
217 # do we attempt to simplemerge first?
218 try:
218 try:
219 premerge = _toolbool(ui, tool, "premerge", not binary)
219 premerge = _toolbool(ui, tool, "premerge", not binary)
220 except error.ConfigError:
220 except error.ConfigError:
221 premerge = _toolstr(ui, tool, "premerge").lower()
221 premerge = _toolstr(ui, tool, "premerge").lower()
222 if premerge not in validkeep:
222 if premerge not in validkeep:
223 _valid = ', '.join(["'" + v + "'" for v in validkeep])
223 _valid = ', '.join(["'" + v + "'" for v in validkeep])
224 raise error.ConfigError(_("%s.premerge not valid "
224 raise error.ConfigError(_("%s.premerge not valid "
225 "('%s' is neither boolean nor %s)") %
225 "('%s' is neither boolean nor %s)") %
226 (tool, premerge, _valid))
226 (tool, premerge, _valid))
227
227
228 if premerge:
228 if premerge:
229 if premerge == 'keep-merge3':
229 if premerge == 'keep-merge3':
230 if not labels:
230 if not labels:
231 labels = _defaultconflictlabels
231 labels = _defaultconflictlabels
232 if len(labels) < 3:
232 if len(labels) < 3:
233 labels.append('base')
233 labels.append('base')
234 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
234 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
235 if not r:
235 if not r:
236 ui.debug(" premerge successful\n")
236 ui.debug(" premerge successful\n")
237 return 0
237 return 0
238 if premerge not in validkeep:
238 if premerge not in validkeep:
239 util.copyfile(back, a) # restore from backup and try again
239 util.copyfile(back, a) # restore from backup and try again
240 return 1 # continue merging
240 return 1 # continue merging
241
241
242 def _symlinkcheck(repo, mynode, orig, fcd, fco, fca, toolconf):
242 def _symlinkcheck(repo, mynode, orig, fcd, fco, fca, toolconf):
243 tool, toolpath, binary, symlink = toolconf
243 tool, toolpath, binary, symlink = toolconf
244 if symlink:
244 if symlink:
245 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
245 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
246 'for %s\n') % (tool, fcd.path()))
246 'for %s\n') % (tool, fcd.path()))
247 return False
247 return False
248 return True
248 return True
249
249
250 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
250 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
251 """
251 """
252 Uses the internal non-interactive simple merge algorithm for merging
252 Uses the internal non-interactive simple merge algorithm for merging
253 files. It will fail if there are any conflicts and leave markers in
253 files. It will fail if there are any conflicts and leave markers in
254 the partially merged file. Markers will have two sections, one for each side
254 the partially merged file. Markers will have two sections, one for each side
255 of merge, unless mode equals 'union' which suppresses the markers."""
255 of merge, unless mode equals 'union' which suppresses the markers."""
256 a, b, c, back = files
256 a, b, c, back = files
257
257
258 ui = repo.ui
258 ui = repo.ui
259
259
260 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
260 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
261 return True, r
261 return True, r
262
262
263 @internaltool('union', fullmerge,
263 @internaltool('union', fullmerge,
264 _("warning: conflicts while merging %s! "
264 _("warning: conflicts while merging %s! "
265 "(edit, then use 'hg resolve --mark')\n"),
265 "(edit, then use 'hg resolve --mark')\n"),
266 precheck=_symlinkcheck)
266 precheck=_symlinkcheck)
267 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
267 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
268 """
268 """
269 Uses the internal non-interactive simple merge algorithm for merging
269 Uses the internal non-interactive simple merge algorithm for merging
270 files. It will use both left and right sides for conflict regions.
270 files. It will use both left and right sides for conflict regions.
271 No markers are inserted."""
271 No markers are inserted."""
272 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
272 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
273 files, labels, 'union')
273 files, labels, 'union')
274
274
275 @internaltool('merge', fullmerge,
275 @internaltool('merge', fullmerge,
276 _("warning: conflicts while merging %s! "
276 _("warning: conflicts while merging %s! "
277 "(edit, then use 'hg resolve --mark')\n"),
277 "(edit, then use 'hg resolve --mark')\n"),
278 precheck=_symlinkcheck)
278 precheck=_symlinkcheck)
279 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
279 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
280 """
280 """
281 Uses the internal non-interactive simple merge algorithm for merging
281 Uses the internal non-interactive simple merge algorithm for merging
282 files. It will fail if there are any conflicts and leave markers in
282 files. It will fail if there are any conflicts and leave markers in
283 the partially merged file. Markers will have two sections, one for each side
283 the partially merged file. Markers will have two sections, one for each side
284 of merge."""
284 of merge."""
285 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
285 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
286 files, labels, 'merge')
286 files, labels, 'merge')
287
287
288 @internaltool('merge3', fullmerge,
288 @internaltool('merge3', fullmerge,
289 _("warning: conflicts while merging %s! "
289 _("warning: conflicts while merging %s! "
290 "(edit, then use 'hg resolve --mark')\n"),
290 "(edit, then use 'hg resolve --mark')\n"),
291 precheck=_symlinkcheck)
291 precheck=_symlinkcheck)
292 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
292 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
293 """
293 """
294 Uses the internal non-interactive simple merge algorithm for merging
294 Uses the internal non-interactive simple merge algorithm for merging
295 files. It will fail if there are any conflicts and leave markers in
295 files. It will fail if there are any conflicts and leave markers in
296 the partially merged file. Marker will have three sections, one from each
296 the partially merged file. Marker will have three sections, one from each
297 side of the merge and one for the base content."""
297 side of the merge and one for the base content."""
298 if not labels:
298 if not labels:
299 labels = _defaultconflictlabels
299 labels = _defaultconflictlabels
300 if len(labels) < 3:
300 if len(labels) < 3:
301 labels.append('base')
301 labels.append('base')
302 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
302 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
303
303
304 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
304 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
305 labels=None, localorother=None):
305 labels=None, localorother=None):
306 """
306 """
307 Generic driver for _imergelocal and _imergeother
307 Generic driver for _imergelocal and _imergeother
308 """
308 """
309 assert localorother is not None
309 assert localorother is not None
310 tool, toolpath, binary, symlink = toolconf
310 tool, toolpath, binary, symlink = toolconf
311 if symlink:
312 repo.ui.warn(_('warning: :merge-%s cannot merge symlinks '
313 'for %s\n') % (localorother, fcd.path()))
314 return False, 1
315 a, b, c, back = files
311 a, b, c, back = files
316 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
312 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
317 localorother=localorother)
313 localorother=localorother)
318 return True, r
314 return True, r
319
315
320 @internaltool('merge-local', mergeonly)
316 @internaltool('merge-local', mergeonly, precheck=_symlinkcheck)
321 def _imergelocal(*args, **kwargs):
317 def _imergelocal(*args, **kwargs):
322 """
318 """
323 Like :merge, but resolve all conflicts non-interactively in favor
319 Like :merge, but resolve all conflicts non-interactively in favor
324 of the local changes."""
320 of the local changes."""
325 success, status = _imergeauto(localorother='local', *args, **kwargs)
321 success, status = _imergeauto(localorother='local', *args, **kwargs)
326 return success, status
322 return success, status
327
323
328 @internaltool('merge-other', mergeonly)
324 @internaltool('merge-other', mergeonly, precheck=_symlinkcheck)
329 def _imergeother(*args, **kwargs):
325 def _imergeother(*args, **kwargs):
330 """
326 """
331 Like :merge, but resolve all conflicts non-interactively in favor
327 Like :merge, but resolve all conflicts non-interactively in favor
332 of the other changes."""
328 of the other changes."""
333 success, status = _imergeauto(localorother='other', *args, **kwargs)
329 success, status = _imergeauto(localorother='other', *args, **kwargs)
334 return success, status
330 return success, status
335
331
336 @internaltool('tagmerge', mergeonly,
332 @internaltool('tagmerge', mergeonly,
337 _("automatic tag merging of %s failed! "
333 _("automatic tag merging of %s failed! "
338 "(use 'hg resolve --tool :merge' or another merge "
334 "(use 'hg resolve --tool :merge' or another merge "
339 "tool of your choice)\n"))
335 "tool of your choice)\n"))
340 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
336 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
341 """
337 """
342 Uses the internal tag merge algorithm (experimental).
338 Uses the internal tag merge algorithm (experimental).
343 """
339 """
344 return tagmerge.merge(repo, fcd, fco, fca)
340 return tagmerge.merge(repo, fcd, fco, fca)
345
341
346 @internaltool('dump', fullmerge)
342 @internaltool('dump', fullmerge)
347 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
343 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
348 """
344 """
349 Creates three versions of the files to merge, containing the
345 Creates three versions of the files to merge, containing the
350 contents of local, other and base. These files can then be used to
346 contents of local, other and base. These files can then be used to
351 perform a merge manually. If the file to be merged is named
347 perform a merge manually. If the file to be merged is named
352 ``a.txt``, these files will accordingly be named ``a.txt.local``,
348 ``a.txt``, these files will accordingly be named ``a.txt.local``,
353 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
349 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
354 same directory as ``a.txt``."""
350 same directory as ``a.txt``."""
355 a, b, c, back = files
351 a, b, c, back = files
356
352
357 fd = fcd.path()
353 fd = fcd.path()
358
354
359 util.copyfile(a, a + ".local")
355 util.copyfile(a, a + ".local")
360 repo.wwrite(fd + ".other", fco.data(), fco.flags())
356 repo.wwrite(fd + ".other", fco.data(), fco.flags())
361 repo.wwrite(fd + ".base", fca.data(), fca.flags())
357 repo.wwrite(fd + ".base", fca.data(), fca.flags())
362 return False, 1
358 return False, 1
363
359
364 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
360 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
365 tool, toolpath, binary, symlink = toolconf
361 tool, toolpath, binary, symlink = toolconf
366 a, b, c, back = files
362 a, b, c, back = files
367 out = ""
363 out = ""
368 env = {'HG_FILE': fcd.path(),
364 env = {'HG_FILE': fcd.path(),
369 'HG_MY_NODE': short(mynode),
365 'HG_MY_NODE': short(mynode),
370 'HG_OTHER_NODE': str(fco.changectx()),
366 'HG_OTHER_NODE': str(fco.changectx()),
371 'HG_BASE_NODE': str(fca.changectx()),
367 'HG_BASE_NODE': str(fca.changectx()),
372 'HG_MY_ISLINK': 'l' in fcd.flags(),
368 'HG_MY_ISLINK': 'l' in fcd.flags(),
373 'HG_OTHER_ISLINK': 'l' in fco.flags(),
369 'HG_OTHER_ISLINK': 'l' in fco.flags(),
374 'HG_BASE_ISLINK': 'l' in fca.flags(),
370 'HG_BASE_ISLINK': 'l' in fca.flags(),
375 }
371 }
376
372
377 ui = repo.ui
373 ui = repo.ui
378
374
379 args = _toolstr(ui, tool, "args", '$local $base $other')
375 args = _toolstr(ui, tool, "args", '$local $base $other')
380 if "$output" in args:
376 if "$output" in args:
381 out, a = a, back # read input from backup, write to original
377 out, a = a, back # read input from backup, write to original
382 replace = {'local': a, 'base': b, 'other': c, 'output': out}
378 replace = {'local': a, 'base': b, 'other': c, 'output': out}
383 args = util.interpolate(r'\$', replace, args,
379 args = util.interpolate(r'\$', replace, args,
384 lambda s: util.shellquote(util.localpath(s)))
380 lambda s: util.shellquote(util.localpath(s)))
385 cmd = toolpath + ' ' + args
381 cmd = toolpath + ' ' + args
386 repo.ui.debug('launching merge tool: %s\n' % cmd)
382 repo.ui.debug('launching merge tool: %s\n' % cmd)
387 r = ui.system(cmd, cwd=repo.root, environ=env)
383 r = ui.system(cmd, cwd=repo.root, environ=env)
388 repo.ui.debug('merge tool returned: %s\n' % r)
384 repo.ui.debug('merge tool returned: %s\n' % r)
389 return True, r
385 return True, r
390
386
391 def _formatconflictmarker(repo, ctx, template, label, pad):
387 def _formatconflictmarker(repo, ctx, template, label, pad):
392 """Applies the given template to the ctx, prefixed by the label.
388 """Applies the given template to the ctx, prefixed by the label.
393
389
394 Pad is the minimum width of the label prefix, so that multiple markers
390 Pad is the minimum width of the label prefix, so that multiple markers
395 can have aligned templated parts.
391 can have aligned templated parts.
396 """
392 """
397 if ctx.node() is None:
393 if ctx.node() is None:
398 ctx = ctx.p1()
394 ctx = ctx.p1()
399
395
400 props = templatekw.keywords.copy()
396 props = templatekw.keywords.copy()
401 props['templ'] = template
397 props['templ'] = template
402 props['ctx'] = ctx
398 props['ctx'] = ctx
403 props['repo'] = repo
399 props['repo'] = repo
404 templateresult = template('conflictmarker', **props)
400 templateresult = template('conflictmarker', **props)
405
401
406 label = ('%s:' % label).ljust(pad + 1)
402 label = ('%s:' % label).ljust(pad + 1)
407 mark = '%s %s' % (label, templater.stringify(templateresult))
403 mark = '%s %s' % (label, templater.stringify(templateresult))
408
404
409 if mark:
405 if mark:
410 mark = mark.splitlines()[0] # split for safety
406 mark = mark.splitlines()[0] # split for safety
411
407
412 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
408 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
413 return util.ellipsis(mark, 80 - 8)
409 return util.ellipsis(mark, 80 - 8)
414
410
415 _defaultconflictmarker = ('{node|short} ' +
411 _defaultconflictmarker = ('{node|short} ' +
416 '{ifeq(tags, "tip", "", "{tags} ")}' +
412 '{ifeq(tags, "tip", "", "{tags} ")}' +
417 '{if(bookmarks, "{bookmarks} ")}' +
413 '{if(bookmarks, "{bookmarks} ")}' +
418 '{ifeq(branch, "default", "", "{branch} ")}' +
414 '{ifeq(branch, "default", "", "{branch} ")}' +
419 '- {author|user}: {desc|firstline}')
415 '- {author|user}: {desc|firstline}')
420
416
421 _defaultconflictlabels = ['local', 'other']
417 _defaultconflictlabels = ['local', 'other']
422
418
423 def _formatlabels(repo, fcd, fco, fca, labels):
419 def _formatlabels(repo, fcd, fco, fca, labels):
424 """Formats the given labels using the conflict marker template.
420 """Formats the given labels using the conflict marker template.
425
421
426 Returns a list of formatted labels.
422 Returns a list of formatted labels.
427 """
423 """
428 cd = fcd.changectx()
424 cd = fcd.changectx()
429 co = fco.changectx()
425 co = fco.changectx()
430 ca = fca.changectx()
426 ca = fca.changectx()
431
427
432 ui = repo.ui
428 ui = repo.ui
433 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
429 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
434 tmpl = templater.templater(None, cache={'conflictmarker': template})
430 tmpl = templater.templater(None, cache={'conflictmarker': template})
435
431
436 pad = max(len(l) for l in labels)
432 pad = max(len(l) for l in labels)
437
433
438 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
434 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
439 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
435 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
440 if len(labels) > 2:
436 if len(labels) > 2:
441 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
437 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
442 return newlabels
438 return newlabels
443
439
444 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
440 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
445 """perform a 3-way merge in the working directory
441 """perform a 3-way merge in the working directory
446
442
447 premerge = whether this is a premerge
443 premerge = whether this is a premerge
448 mynode = parent node before merge
444 mynode = parent node before merge
449 orig = original local filename before merge
445 orig = original local filename before merge
450 fco = other file context
446 fco = other file context
451 fca = ancestor file context
447 fca = ancestor file context
452 fcd = local file context for current/destination file
448 fcd = local file context for current/destination file
453
449
454 Returns whether the merge is complete, and the return value of the merge.
450 Returns whether the merge is complete, and the return value of the merge.
455 """
451 """
456
452
457 def temp(prefix, ctx):
453 def temp(prefix, ctx):
458 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
454 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
459 (fd, name) = tempfile.mkstemp(prefix=pre)
455 (fd, name) = tempfile.mkstemp(prefix=pre)
460 data = repo.wwritedata(ctx.path(), ctx.data())
456 data = repo.wwritedata(ctx.path(), ctx.data())
461 f = os.fdopen(fd, "wb")
457 f = os.fdopen(fd, "wb")
462 f.write(data)
458 f.write(data)
463 f.close()
459 f.close()
464 return name
460 return name
465
461
466 if not fco.cmp(fcd): # files identical?
462 if not fco.cmp(fcd): # files identical?
467 return True, None
463 return True, None
468
464
469 ui = repo.ui
465 ui = repo.ui
470 fd = fcd.path()
466 fd = fcd.path()
471 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
467 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
472 symlink = 'l' in fcd.flags() + fco.flags()
468 symlink = 'l' in fcd.flags() + fco.flags()
473 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
469 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
474 if tool in internals and tool.startswith('internal:'):
470 if tool in internals and tool.startswith('internal:'):
475 # normalize to new-style names (':merge' etc)
471 # normalize to new-style names (':merge' etc)
476 tool = tool[len('internal'):]
472 tool = tool[len('internal'):]
477 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
473 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
478 (tool, fd, binary, symlink))
474 (tool, fd, binary, symlink))
479
475
480 if tool in internals:
476 if tool in internals:
481 func = internals[tool]
477 func = internals[tool]
482 mergetype = func.mergetype
478 mergetype = func.mergetype
483 onfailure = func.onfailure
479 onfailure = func.onfailure
484 precheck = func.precheck
480 precheck = func.precheck
485 else:
481 else:
486 func = _xmerge
482 func = _xmerge
487 mergetype = fullmerge
483 mergetype = fullmerge
488 onfailure = _("merging %s failed!\n")
484 onfailure = _("merging %s failed!\n")
489 precheck = None
485 precheck = None
490
486
491 toolconf = tool, toolpath, binary, symlink
487 toolconf = tool, toolpath, binary, symlink
492
488
493 if mergetype == nomerge:
489 if mergetype == nomerge:
494 return True, func(repo, mynode, orig, fcd, fco, fca, toolconf)
490 return True, func(repo, mynode, orig, fcd, fco, fca, toolconf)
495
491
496 if premerge:
492 if premerge:
497 if orig != fco.path():
493 if orig != fco.path():
498 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
494 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
499 else:
495 else:
500 ui.status(_("merging %s\n") % fd)
496 ui.status(_("merging %s\n") % fd)
501
497
502 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
498 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
503
499
504 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
500 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
505 toolconf):
501 toolconf):
506 if onfailure:
502 if onfailure:
507 ui.warn(onfailure % fd)
503 ui.warn(onfailure % fd)
508 return True, 1
504 return True, 1
509
505
510 a = repo.wjoin(fd)
506 a = repo.wjoin(fd)
511 b = temp("base", fca)
507 b = temp("base", fca)
512 c = temp("other", fco)
508 c = temp("other", fco)
513 back = a + ".orig"
509 back = a + ".orig"
514 if premerge:
510 if premerge:
515 util.copyfile(a, back)
511 util.copyfile(a, back)
516 files = (a, b, c, back)
512 files = (a, b, c, back)
517
513
518 r = 1
514 r = 1
519 try:
515 try:
520 markerstyle = ui.config('ui', 'mergemarkers', 'basic')
516 markerstyle = ui.config('ui', 'mergemarkers', 'basic')
521 if not labels:
517 if not labels:
522 labels = _defaultconflictlabels
518 labels = _defaultconflictlabels
523 if markerstyle != 'basic':
519 if markerstyle != 'basic':
524 labels = _formatlabels(repo, fcd, fco, fca, labels)
520 labels = _formatlabels(repo, fcd, fco, fca, labels)
525
521
526 if premerge and mergetype == fullmerge:
522 if premerge and mergetype == fullmerge:
527 r = _premerge(repo, toolconf, files, labels=labels)
523 r = _premerge(repo, toolconf, files, labels=labels)
528 # complete if premerge successful (r is 0)
524 # complete if premerge successful (r is 0)
529 return not r, r
525 return not r, r
530
526
531 needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, files,
527 needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, files,
532 labels=labels)
528 labels=labels)
533 if needcheck:
529 if needcheck:
534 r = _check(r, ui, tool, fcd, files)
530 r = _check(r, ui, tool, fcd, files)
535
531
536 if r:
532 if r:
537 if onfailure:
533 if onfailure:
538 ui.warn(onfailure % fd)
534 ui.warn(onfailure % fd)
539
535
540 return True, r
536 return True, r
541 finally:
537 finally:
542 if not r:
538 if not r:
543 util.unlink(back)
539 util.unlink(back)
544 util.unlink(b)
540 util.unlink(b)
545 util.unlink(c)
541 util.unlink(c)
546
542
547 def _check(r, ui, tool, fcd, files):
543 def _check(r, ui, tool, fcd, files):
548 fd = fcd.path()
544 fd = fcd.path()
549 a, b, c, back = files
545 a, b, c, back = files
550
546
551 if not r and (_toolbool(ui, tool, "checkconflicts") or
547 if not r and (_toolbool(ui, tool, "checkconflicts") or
552 'conflicts' in _toollist(ui, tool, "check")):
548 'conflicts' in _toollist(ui, tool, "check")):
553 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
549 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
554 re.MULTILINE):
550 re.MULTILINE):
555 r = 1
551 r = 1
556
552
557 checked = False
553 checked = False
558 if 'prompt' in _toollist(ui, tool, "check"):
554 if 'prompt' in _toollist(ui, tool, "check"):
559 checked = True
555 checked = True
560 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
556 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
561 "$$ &Yes $$ &No") % fd, 1):
557 "$$ &Yes $$ &No") % fd, 1):
562 r = 1
558 r = 1
563
559
564 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
560 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
565 'changed' in
561 'changed' in
566 _toollist(ui, tool, "check")):
562 _toollist(ui, tool, "check")):
567 if filecmp.cmp(a, back):
563 if filecmp.cmp(a, back):
568 if ui.promptchoice(_(" output file %s appears unchanged\n"
564 if ui.promptchoice(_(" output file %s appears unchanged\n"
569 "was merge successful (yn)?"
565 "was merge successful (yn)?"
570 "$$ &Yes $$ &No") % fd, 1):
566 "$$ &Yes $$ &No") % fd, 1):
571 r = 1
567 r = 1
572
568
573 if _toolbool(ui, tool, "fixeol"):
569 if _toolbool(ui, tool, "fixeol"):
574 _matcheol(a, back)
570 _matcheol(a, back)
575
571
576 return r
572 return r
577
573
578 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
574 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
579 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
575 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
580
576
581 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
577 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
582 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
578 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
583
579
584 # tell hggettext to extract docstrings from these functions:
580 # tell hggettext to extract docstrings from these functions:
585 i18nfunctions = internals.values()
581 i18nfunctions = internals.values()
@@ -1,439 +1,439 b''
1 #require symlink execbit
1 #require symlink execbit
2
2
3 $ tellmeabout() {
3 $ tellmeabout() {
4 > if [ -h $1 ]; then
4 > if [ -h $1 ]; then
5 > echo $1 is a symlink:
5 > echo $1 is a symlink:
6 > $TESTDIR/readlink.py $1
6 > $TESTDIR/readlink.py $1
7 > elif [ -x $1 ]; then
7 > elif [ -x $1 ]; then
8 > echo $1 is an executable file with content:
8 > echo $1 is an executable file with content:
9 > cat $1
9 > cat $1
10 > else
10 > else
11 > echo $1 is a plain file with content:
11 > echo $1 is a plain file with content:
12 > cat $1
12 > cat $1
13 > fi
13 > fi
14 > }
14 > }
15
15
16 $ hg init test1
16 $ hg init test1
17 $ cd test1
17 $ cd test1
18
18
19 $ echo a > a
19 $ echo a > a
20 $ hg ci -Aqmadd
20 $ hg ci -Aqmadd
21 $ chmod +x a
21 $ chmod +x a
22 $ hg ci -mexecutable
22 $ hg ci -mexecutable
23
23
24 $ hg up -q 0
24 $ hg up -q 0
25 $ rm a
25 $ rm a
26 $ ln -s symlink a
26 $ ln -s symlink a
27 $ hg ci -msymlink
27 $ hg ci -msymlink
28 created new head
28 created new head
29
29
30 Symlink is local parent, executable is other:
30 Symlink is local parent, executable is other:
31
31
32 $ hg merge --debug
32 $ hg merge --debug
33 searching for copies back to rev 1
33 searching for copies back to rev 1
34 resolving manifests
34 resolving manifests
35 branchmerge: True, force: False, partial: False
35 branchmerge: True, force: False, partial: False
36 ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
36 ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
37 preserving a for resolve of a
37 preserving a for resolve of a
38 a: versions differ -> m (premerge)
38 a: versions differ -> m (premerge)
39 picked tool ':merge' for a (binary False symlink True)
39 picked tool ':merge' for a (binary False symlink True)
40 merging a
40 merging a
41 my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
41 my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
42 warning: internal :merge cannot merge symlinks for a
42 warning: internal :merge cannot merge symlinks for a
43 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
43 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
44 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
44 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
45 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
45 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
46 [1]
46 [1]
47
47
48 $ tellmeabout a
48 $ tellmeabout a
49 a is a symlink:
49 a is a symlink:
50 a -> symlink
50 a -> symlink
51 $ hg resolve a --tool internal:other
51 $ hg resolve a --tool internal:other
52 (no more unresolved files)
52 (no more unresolved files)
53 $ tellmeabout a
53 $ tellmeabout a
54 a is an executable file with content:
54 a is an executable file with content:
55 a
55 a
56 $ hg st
56 $ hg st
57 M a
57 M a
58 ? a.orig
58 ? a.orig
59
59
60 Symlink is other parent, executable is local:
60 Symlink is other parent, executable is local:
61
61
62 $ hg update -C 1
62 $ hg update -C 1
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64
64
65 $ hg merge --debug --tool :union
65 $ hg merge --debug --tool :union
66 searching for copies back to rev 1
66 searching for copies back to rev 1
67 resolving manifests
67 resolving manifests
68 branchmerge: True, force: False, partial: False
68 branchmerge: True, force: False, partial: False
69 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
69 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
70 preserving a for resolve of a
70 preserving a for resolve of a
71 a: versions differ -> m (premerge)
71 a: versions differ -> m (premerge)
72 picked tool ':union' for a (binary False symlink True)
72 picked tool ':union' for a (binary False symlink True)
73 merging a
73 merging a
74 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
74 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
75 warning: internal :union cannot merge symlinks for a
75 warning: internal :union cannot merge symlinks for a
76 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
76 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
77 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
77 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
78 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
78 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
79 [1]
79 [1]
80
80
81 $ tellmeabout a
81 $ tellmeabout a
82 a is an executable file with content:
82 a is an executable file with content:
83 a
83 a
84
84
85 $ hg update -C 1
85 $ hg update -C 1
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87
87
88 $ hg merge --debug --tool :merge3
88 $ hg merge --debug --tool :merge3
89 searching for copies back to rev 1
89 searching for copies back to rev 1
90 resolving manifests
90 resolving manifests
91 branchmerge: True, force: False, partial: False
91 branchmerge: True, force: False, partial: False
92 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
92 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
93 preserving a for resolve of a
93 preserving a for resolve of a
94 a: versions differ -> m (premerge)
94 a: versions differ -> m (premerge)
95 picked tool ':merge3' for a (binary False symlink True)
95 picked tool ':merge3' for a (binary False symlink True)
96 merging a
96 merging a
97 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
97 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
98 warning: internal :merge3 cannot merge symlinks for a
98 warning: internal :merge3 cannot merge symlinks for a
99 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
99 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
100 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
100 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
101 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
101 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
102 [1]
102 [1]
103
103
104 $ tellmeabout a
104 $ tellmeabout a
105 a is an executable file with content:
105 a is an executable file with content:
106 a
106 a
107
107
108 $ hg update -C 1
108 $ hg update -C 1
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110
110
111 $ hg merge --debug --tool :merge-local
111 $ hg merge --debug --tool :merge-local
112 searching for copies back to rev 1
112 searching for copies back to rev 1
113 resolving manifests
113 resolving manifests
114 branchmerge: True, force: False, partial: False
114 branchmerge: True, force: False, partial: False
115 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
115 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
116 preserving a for resolve of a
116 preserving a for resolve of a
117 a: versions differ -> m (premerge)
117 a: versions differ -> m (premerge)
118 picked tool ':merge-local' for a (binary False symlink True)
118 picked tool ':merge-local' for a (binary False symlink True)
119 merging a
119 merging a
120 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
120 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
121 warning: :merge-local cannot merge symlinks for a
121 warning: internal :merge-local cannot merge symlinks for a
122 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
122 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
123 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
123 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
124 [1]
124 [1]
125
125
126 $ tellmeabout a
126 $ tellmeabout a
127 a is an executable file with content:
127 a is an executable file with content:
128 a
128 a
129
129
130 $ hg update -C 1
130 $ hg update -C 1
131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132
132
133 $ hg merge --debug --tool :merge-other
133 $ hg merge --debug --tool :merge-other
134 searching for copies back to rev 1
134 searching for copies back to rev 1
135 resolving manifests
135 resolving manifests
136 branchmerge: True, force: False, partial: False
136 branchmerge: True, force: False, partial: False
137 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
137 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
138 preserving a for resolve of a
138 preserving a for resolve of a
139 a: versions differ -> m (premerge)
139 a: versions differ -> m (premerge)
140 picked tool ':merge-other' for a (binary False symlink True)
140 picked tool ':merge-other' for a (binary False symlink True)
141 merging a
141 merging a
142 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
142 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
143 warning: :merge-other cannot merge symlinks for a
143 warning: internal :merge-other cannot merge symlinks for a
144 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
144 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
145 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
145 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
146 [1]
146 [1]
147
147
148 $ tellmeabout a
148 $ tellmeabout a
149 a is an executable file with content:
149 a is an executable file with content:
150 a
150 a
151
151
152 Update to link without local change should get us a symlink (issue3316):
152 Update to link without local change should get us a symlink (issue3316):
153
153
154 $ hg up -C 0
154 $ hg up -C 0
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 $ hg up
156 $ hg up
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 $ hg st
158 $ hg st
159 ? a.orig
159 ? a.orig
160
160
161 Update to link with local change should cause a merge prompt (issue3200):
161 Update to link with local change should cause a merge prompt (issue3200):
162
162
163 $ hg up -Cq 0
163 $ hg up -Cq 0
164 $ echo data > a
164 $ echo data > a
165 $ HGMERGE= hg up -y --debug
165 $ HGMERGE= hg up -y --debug
166 searching for copies back to rev 2
166 searching for copies back to rev 2
167 resolving manifests
167 resolving manifests
168 branchmerge: False, force: False, partial: False
168 branchmerge: False, force: False, partial: False
169 ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
169 ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
170 preserving a for resolve of a
170 preserving a for resolve of a
171 a: versions differ -> m (premerge)
171 a: versions differ -> m (premerge)
172 (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
172 (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
173 picked tool ':prompt' for a (binary False symlink True)
173 picked tool ':prompt' for a (binary False symlink True)
174 no tool found to merge a
174 no tool found to merge a
175 keep (l)ocal or take (o)ther? l
175 keep (l)ocal or take (o)ther? l
176 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
176 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
177 $ hg diff --git
177 $ hg diff --git
178 diff --git a/a b/a
178 diff --git a/a b/a
179 old mode 120000
179 old mode 120000
180 new mode 100644
180 new mode 100644
181 --- a/a
181 --- a/a
182 +++ b/a
182 +++ b/a
183 @@ -1,1 +1,1 @@
183 @@ -1,1 +1,1 @@
184 -symlink
184 -symlink
185 \ No newline at end of file
185 \ No newline at end of file
186 +data
186 +data
187
187
188
188
189 Test only 'l' change - happens rarely, except when recovering from situations
189 Test only 'l' change - happens rarely, except when recovering from situations
190 where that was what happened.
190 where that was what happened.
191
191
192 $ hg init test2
192 $ hg init test2
193 $ cd test2
193 $ cd test2
194 $ printf base > f
194 $ printf base > f
195 $ hg ci -Aqm0
195 $ hg ci -Aqm0
196 $ echo file > f
196 $ echo file > f
197 $ echo content >> f
197 $ echo content >> f
198 $ hg ci -qm1
198 $ hg ci -qm1
199 $ hg up -qr0
199 $ hg up -qr0
200 $ rm f
200 $ rm f
201 $ ln -s base f
201 $ ln -s base f
202 $ hg ci -qm2
202 $ hg ci -qm2
203 $ hg merge
203 $ hg merge
204 merging f
204 merging f
205 warning: internal :merge cannot merge symlinks for f
205 warning: internal :merge cannot merge symlinks for f
206 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
206 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
207 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
207 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
208 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
208 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
209 [1]
209 [1]
210 $ tellmeabout f
210 $ tellmeabout f
211 f is a symlink:
211 f is a symlink:
212 f -> base
212 f -> base
213
213
214 $ hg up -Cqr1
214 $ hg up -Cqr1
215 $ hg merge
215 $ hg merge
216 merging f
216 merging f
217 warning: internal :merge cannot merge symlinks for f
217 warning: internal :merge cannot merge symlinks for f
218 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
218 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
219 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
219 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
220 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
220 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
221 [1]
221 [1]
222 $ tellmeabout f
222 $ tellmeabout f
223 f is a plain file with content:
223 f is a plain file with content:
224 file
224 file
225 content
225 content
226
226
227 $ cd ..
227 $ cd ..
228
228
229 Test removed 'x' flag merged with change to symlink
229 Test removed 'x' flag merged with change to symlink
230
230
231 $ hg init test3
231 $ hg init test3
232 $ cd test3
232 $ cd test3
233 $ echo f > f
233 $ echo f > f
234 $ chmod +x f
234 $ chmod +x f
235 $ hg ci -Aqm0
235 $ hg ci -Aqm0
236 $ chmod -x f
236 $ chmod -x f
237 $ hg ci -qm1
237 $ hg ci -qm1
238 $ hg up -qr0
238 $ hg up -qr0
239 $ rm f
239 $ rm f
240 $ ln -s dangling f
240 $ ln -s dangling f
241 $ hg ci -qm2
241 $ hg ci -qm2
242 $ hg merge
242 $ hg merge
243 merging f
243 merging f
244 warning: internal :merge cannot merge symlinks for f
244 warning: internal :merge cannot merge symlinks for f
245 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
245 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
246 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
246 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
247 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
247 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
248 [1]
248 [1]
249 $ tellmeabout f
249 $ tellmeabout f
250 f is a symlink:
250 f is a symlink:
251 f -> dangling
251 f -> dangling
252
252
253 $ hg up -Cqr1
253 $ hg up -Cqr1
254 $ hg merge
254 $ hg merge
255 merging f
255 merging f
256 warning: internal :merge cannot merge symlinks for f
256 warning: internal :merge cannot merge symlinks for f
257 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
257 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
258 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
258 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
259 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
259 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
260 [1]
260 [1]
261 $ tellmeabout f
261 $ tellmeabout f
262 f is a plain file with content:
262 f is a plain file with content:
263 f
263 f
264
264
265 Test removed 'x' flag merged with content change - both ways
265 Test removed 'x' flag merged with content change - both ways
266
266
267 $ hg up -Cqr0
267 $ hg up -Cqr0
268 $ echo change > f
268 $ echo change > f
269 $ hg ci -qm3
269 $ hg ci -qm3
270 $ hg merge -r1
270 $ hg merge -r1
271 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 (branch merge, don't forget to commit)
272 (branch merge, don't forget to commit)
273 $ tellmeabout f
273 $ tellmeabout f
274 f is a plain file with content:
274 f is a plain file with content:
275 change
275 change
276
276
277 $ hg up -qCr1
277 $ hg up -qCr1
278 $ hg merge -r3
278 $ hg merge -r3
279 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 (branch merge, don't forget to commit)
280 (branch merge, don't forget to commit)
281 $ tellmeabout f
281 $ tellmeabout f
282 f is a plain file with content:
282 f is a plain file with content:
283 change
283 change
284
284
285 $ cd ..
285 $ cd ..
286
286
287 Test merge with no common ancestor:
287 Test merge with no common ancestor:
288 a: just different
288 a: just different
289 b: x vs -, different (cannot calculate x, cannot ask merge tool)
289 b: x vs -, different (cannot calculate x, cannot ask merge tool)
290 c: x vs -, same (cannot calculate x, merge tool is no good)
290 c: x vs -, same (cannot calculate x, merge tool is no good)
291 d: x vs l, different
291 d: x vs l, different
292 e: x vs l, same
292 e: x vs l, same
293 f: - vs l, different
293 f: - vs l, different
294 g: - vs l, same
294 g: - vs l, same
295 h: l vs l, different
295 h: l vs l, different
296 (where same means the filelog entry is shared and there thus is an ancestor!)
296 (where same means the filelog entry is shared and there thus is an ancestor!)
297
297
298 $ hg init test4
298 $ hg init test4
299 $ cd test4
299 $ cd test4
300 $ echo 0 > 0
300 $ echo 0 > 0
301 $ hg ci -Aqm0
301 $ hg ci -Aqm0
302
302
303 $ echo 1 > a
303 $ echo 1 > a
304 $ echo 1 > b
304 $ echo 1 > b
305 $ chmod +x b
305 $ chmod +x b
306 $ echo x > c
306 $ echo x > c
307 $ chmod +x c
307 $ chmod +x c
308 $ echo 1 > d
308 $ echo 1 > d
309 $ chmod +x d
309 $ chmod +x d
310 $ printf x > e
310 $ printf x > e
311 $ chmod +x e
311 $ chmod +x e
312 $ echo 1 > f
312 $ echo 1 > f
313 $ printf x > g
313 $ printf x > g
314 $ ln -s 1 h
314 $ ln -s 1 h
315 $ hg ci -qAm1
315 $ hg ci -qAm1
316
316
317 $ hg up -qr0
317 $ hg up -qr0
318 $ echo 2 > a
318 $ echo 2 > a
319 $ echo 2 > b
319 $ echo 2 > b
320 $ echo x > c
320 $ echo x > c
321 $ ln -s 2 d
321 $ ln -s 2 d
322 $ ln -s x e
322 $ ln -s x e
323 $ ln -s 2 f
323 $ ln -s 2 f
324 $ ln -s x g
324 $ ln -s x g
325 $ ln -s 2 h
325 $ ln -s 2 h
326 $ hg ci -Aqm2
326 $ hg ci -Aqm2
327
327
328 $ hg merge
328 $ hg merge
329 merging a
329 merging a
330 warning: cannot merge flags for b
330 warning: cannot merge flags for b
331 merging b
331 merging b
332 warning: cannot merge flags for c
332 warning: cannot merge flags for c
333 merging d
333 merging d
334 warning: internal :merge cannot merge symlinks for d
334 warning: internal :merge cannot merge symlinks for d
335 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
335 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
336 merging f
336 merging f
337 warning: internal :merge cannot merge symlinks for f
337 warning: internal :merge cannot merge symlinks for f
338 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
338 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
339 merging h
339 merging h
340 warning: internal :merge cannot merge symlinks for h
340 warning: internal :merge cannot merge symlinks for h
341 warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
341 warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
342 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
342 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
343 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
343 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
344 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
344 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
345 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
345 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
346 [1]
346 [1]
347 $ hg resolve -l
347 $ hg resolve -l
348 U a
348 U a
349 U b
349 U b
350 U d
350 U d
351 U f
351 U f
352 U h
352 U h
353 $ tellmeabout a
353 $ tellmeabout a
354 a is a plain file with content:
354 a is a plain file with content:
355 <<<<<<< local: 0139c5610547 - test: 2
355 <<<<<<< local: 0139c5610547 - test: 2
356 2
356 2
357 =======
357 =======
358 1
358 1
359 >>>>>>> other: 97e29675e796 - test: 1
359 >>>>>>> other: 97e29675e796 - test: 1
360 $ tellmeabout b
360 $ tellmeabout b
361 b is a plain file with content:
361 b is a plain file with content:
362 <<<<<<< local: 0139c5610547 - test: 2
362 <<<<<<< local: 0139c5610547 - test: 2
363 2
363 2
364 =======
364 =======
365 1
365 1
366 >>>>>>> other: 97e29675e796 - test: 1
366 >>>>>>> other: 97e29675e796 - test: 1
367 $ tellmeabout c
367 $ tellmeabout c
368 c is a plain file with content:
368 c is a plain file with content:
369 x
369 x
370 $ tellmeabout d
370 $ tellmeabout d
371 d is a symlink:
371 d is a symlink:
372 d -> 2
372 d -> 2
373 $ tellmeabout e
373 $ tellmeabout e
374 e is a symlink:
374 e is a symlink:
375 e -> x
375 e -> x
376 $ tellmeabout f
376 $ tellmeabout f
377 f is a symlink:
377 f is a symlink:
378 f -> 2
378 f -> 2
379 $ tellmeabout g
379 $ tellmeabout g
380 g is a symlink:
380 g is a symlink:
381 g -> x
381 g -> x
382 $ tellmeabout h
382 $ tellmeabout h
383 h is a symlink:
383 h is a symlink:
384 h -> 2
384 h -> 2
385
385
386 $ hg up -Cqr1
386 $ hg up -Cqr1
387 $ hg merge
387 $ hg merge
388 merging a
388 merging a
389 warning: cannot merge flags for b
389 warning: cannot merge flags for b
390 merging b
390 merging b
391 warning: cannot merge flags for c
391 warning: cannot merge flags for c
392 merging d
392 merging d
393 warning: internal :merge cannot merge symlinks for d
393 warning: internal :merge cannot merge symlinks for d
394 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
394 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
395 merging f
395 merging f
396 warning: internal :merge cannot merge symlinks for f
396 warning: internal :merge cannot merge symlinks for f
397 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
397 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
398 merging h
398 merging h
399 warning: internal :merge cannot merge symlinks for h
399 warning: internal :merge cannot merge symlinks for h
400 warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
400 warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
401 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
401 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
402 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
402 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
403 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
403 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
404 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
404 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
405 [1]
405 [1]
406 $ tellmeabout a
406 $ tellmeabout a
407 a is a plain file with content:
407 a is a plain file with content:
408 <<<<<<< local: 97e29675e796 - test: 1
408 <<<<<<< local: 97e29675e796 - test: 1
409 1
409 1
410 =======
410 =======
411 2
411 2
412 >>>>>>> other: 0139c5610547 - test: 2
412 >>>>>>> other: 0139c5610547 - test: 2
413 $ tellmeabout b
413 $ tellmeabout b
414 b is an executable file with content:
414 b is an executable file with content:
415 <<<<<<< local: 97e29675e796 - test: 1
415 <<<<<<< local: 97e29675e796 - test: 1
416 1
416 1
417 =======
417 =======
418 2
418 2
419 >>>>>>> other: 0139c5610547 - test: 2
419 >>>>>>> other: 0139c5610547 - test: 2
420 $ tellmeabout c
420 $ tellmeabout c
421 c is an executable file with content:
421 c is an executable file with content:
422 x
422 x
423 $ tellmeabout d
423 $ tellmeabout d
424 d is an executable file with content:
424 d is an executable file with content:
425 1
425 1
426 $ tellmeabout e
426 $ tellmeabout e
427 e is an executable file with content:
427 e is an executable file with content:
428 x (no-eol)
428 x (no-eol)
429 $ tellmeabout f
429 $ tellmeabout f
430 f is a plain file with content:
430 f is a plain file with content:
431 1
431 1
432 $ tellmeabout g
432 $ tellmeabout g
433 g is a plain file with content:
433 g is a plain file with content:
434 x (no-eol)
434 x (no-eol)
435 $ tellmeabout h
435 $ tellmeabout h
436 h is a symlink:
436 h is a symlink:
437 h -> 1
437 h -> 1
438
438
439 $ cd ..
439 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now