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