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