##// END OF EJS Templates
Merge with -stable
Matt Mackall -
r6533:65f1b974 merge default
parent child Browse files
Show More
@@ -0,0 +1,56 b''
1 #!/bin/sh
2
3 # make sure that the internal merge tools (internal:fail, internal:local, and
4 # internal:other) are used when matched by a merge-pattern in hgrc
5
6 unset HGMERGE # make sure HGMERGE doesn't interfere with the test
7
8 hg init
9
10 echo "# initial file contents"
11 echo "line 1" > f
12 echo "line 2" >> f
13 echo "line 3" >> f
14 hg commit -Am "revision 0" -d "1000000 0"
15 cat f
16 echo "# branch 1: editing line 1"
17 sed 's/line 1/first line/' f > f.new
18 mv f.new f
19 hg commit -Am "edited first line" -d "1000000 0"
20
21 echo "# branch 2: editing line 3"
22 hg update 0
23 sed 's/line 3/third line/' f > f.new
24 mv f.new f
25 hg commit -Am "edited third line" -d "1000000 0"
26
27 echo "# merge using internal:fail tool"
28 echo "[merge-patterns]" > .hg/hgrc
29 echo "* = internal:fail" >> .hg/hgrc
30 hg merge
31 cat f
32 hg stat
33
34 echo "# merge using internal:local tool"
35 hg update -C 2
36 sed 's/internal:fail/internal:local/' .hg/hgrc > .hg/hgrc.new
37 mv .hg/hgrc.new .hg/hgrc
38 hg merge
39 cat f
40 hg stat
41
42 echo "# merge using internal:other tool"
43 hg update -C 2
44 sed 's/internal:local/internal:other/' .hg/hgrc > .hg/hgrc.new
45 mv .hg/hgrc.new .hg/hgrc
46 hg merge
47 cat f
48 hg stat
49
50 echo "# merge using default tool"
51 hg update -C 2
52 rm .hg/hgrc
53 hg merge
54 cat f
55 hg stat
56
@@ -0,0 +1,43 b''
1 # initial file contents
2 adding f
3 line 1
4 line 2
5 line 3
6 # branch 1: editing line 1
7 # branch 2: editing line 3
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 created new head
10 # merge using internal:fail tool
11 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
12 There are unresolved merges, you can redo the full merge using:
13 hg update -C 2
14 hg merge 1
15 line 1
16 line 2
17 third line
18 M f
19 # merge using internal:local tool
20 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
22 (branch merge, don't forget to commit)
23 line 1
24 line 2
25 third line
26 M f
27 # merge using internal:other tool
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
30 (branch merge, don't forget to commit)
31 first line
32 line 2
33 line 3
34 M f
35 # merge using default tool
36 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 merging f
38 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
39 (branch merge, don't forget to commit)
40 first line
41 line 2
42 third line
43 M f
@@ -1,217 +1,219 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import nullrev, short
8 from node import nullrev, short
9 from i18n import _
9 from i18n import _
10 import util, os, tempfile, simplemerge, re, filecmp
10 import util, os, tempfile, simplemerge, re, filecmp
11
11
12 def _toolstr(ui, tool, part, default=""):
12 def _toolstr(ui, tool, part, default=""):
13 return ui.config("merge-tools", tool + "." + part, default)
13 return ui.config("merge-tools", tool + "." + part, default)
14
14
15 def _toolbool(ui, tool, part, default=False):
15 def _toolbool(ui, tool, part, default=False):
16 return ui.configbool("merge-tools", tool + "." + part, default)
16 return ui.configbool("merge-tools", tool + "." + part, default)
17
17
18 def _findtool(ui, tool):
18 def _findtool(ui, tool):
19 if tool in ("internal:fail", "internal:local", "internal:other"):
20 return tool
19 k = _toolstr(ui, tool, "regkey")
21 k = _toolstr(ui, tool, "regkey")
20 if k:
22 if k:
21 p = util.lookup_reg(k, _toolstr(ui, tool, "regname"))
23 p = util.lookup_reg(k, _toolstr(ui, tool, "regname"))
22 if p:
24 if p:
23 p = util.find_exe(p + _toolstr(ui, tool, "regappend"))
25 p = util.find_exe(p + _toolstr(ui, tool, "regappend"))
24 if p:
26 if p:
25 return p
27 return p
26 return util.find_exe(_toolstr(ui, tool, "executable", tool))
28 return util.find_exe(_toolstr(ui, tool, "executable", tool))
27
29
28 def _picktool(repo, ui, path, binary, symlink):
30 def _picktool(repo, ui, path, binary, symlink):
29 def check(tool, pat, symlink, binary):
31 def check(tool, pat, symlink, binary):
30 tmsg = tool
32 tmsg = tool
31 if pat:
33 if pat:
32 tmsg += " specified for " + pat
34 tmsg += " specified for " + pat
33 if pat and not _findtool(ui, tool): # skip search if not matching
35 if pat and not _findtool(ui, tool): # skip search if not matching
34 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
36 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
35 elif symlink and not _toolbool(ui, tool, "symlink"):
37 elif symlink and not _toolbool(ui, tool, "symlink"):
36 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
38 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
37 elif binary and not _toolbool(ui, tool, "binary"):
39 elif binary and not _toolbool(ui, tool, "binary"):
38 ui.warn(_("tool %s can't handle binary\n") % tmsg)
40 ui.warn(_("tool %s can't handle binary\n") % tmsg)
39 elif not util.gui() and _toolbool(ui, tool, "gui"):
41 elif not util.gui() and _toolbool(ui, tool, "gui"):
40 ui.warn(_("tool %s requires a GUI\n") % tmsg)
42 ui.warn(_("tool %s requires a GUI\n") % tmsg)
41 else:
43 else:
42 return True
44 return True
43 return False
45 return False
44
46
45 # HGMERGE takes precedence
47 # HGMERGE takes precedence
46 hgmerge = os.environ.get("HGMERGE")
48 hgmerge = os.environ.get("HGMERGE")
47 if hgmerge:
49 if hgmerge:
48 return (hgmerge, hgmerge)
50 return (hgmerge, hgmerge)
49
51
50 # then patterns
52 # then patterns
51 for pat, tool in ui.configitems("merge-patterns"):
53 for pat, tool in ui.configitems("merge-patterns"):
52 mf = util.matcher(repo.root, "", [pat], [], [])[1]
54 mf = util.matcher(repo.root, "", [pat], [], [])[1]
53 if mf(path) and check(tool, pat, symlink, False):
55 if mf(path) and check(tool, pat, symlink, False):
54 toolpath = _findtool(ui, tool)
56 toolpath = _findtool(ui, tool)
55 return (tool, '"' + toolpath + '"')
57 return (tool, '"' + toolpath + '"')
56
58
57 # then merge tools
59 # then merge tools
58 tools = {}
60 tools = {}
59 for k,v in ui.configitems("merge-tools"):
61 for k,v in ui.configitems("merge-tools"):
60 t = k.split('.')[0]
62 t = k.split('.')[0]
61 if t not in tools:
63 if t not in tools:
62 tools[t] = int(_toolstr(ui, t, "priority", "0"))
64 tools[t] = int(_toolstr(ui, t, "priority", "0"))
63 names = tools.keys()
65 names = tools.keys()
64 tools = [(-p,t) for t,p in tools.items()]
66 tools = [(-p,t) for t,p in tools.items()]
65 tools.sort()
67 tools.sort()
66 uimerge = ui.config("ui", "merge")
68 uimerge = ui.config("ui", "merge")
67 if uimerge:
69 if uimerge:
68 if uimerge not in names:
70 if uimerge not in names:
69 return (uimerge, uimerge)
71 return (uimerge, uimerge)
70 tools.insert(0, (None, uimerge)) # highest priority
72 tools.insert(0, (None, uimerge)) # highest priority
71 tools.append((None, "hgmerge")) # the old default, if found
73 tools.append((None, "hgmerge")) # the old default, if found
72 for p,t in tools:
74 for p,t in tools:
73 toolpath = _findtool(ui, t)
75 toolpath = _findtool(ui, t)
74 if toolpath and check(t, None, symlink, binary):
76 if toolpath and check(t, None, symlink, binary):
75 return (t, '"' + toolpath + '"')
77 return (t, '"' + toolpath + '"')
76 # internal merge as last resort
78 # internal merge as last resort
77 return (not (symlink or binary) and "internal:merge" or None, None)
79 return (not (symlink or binary) and "internal:merge" or None, None)
78
80
79 def _eoltype(data):
81 def _eoltype(data):
80 "Guess the EOL type of a file"
82 "Guess the EOL type of a file"
81 if '\0' in data: # binary
83 if '\0' in data: # binary
82 return None
84 return None
83 if '\r\n' in data: # Windows
85 if '\r\n' in data: # Windows
84 return '\r\n'
86 return '\r\n'
85 if '\r' in data: # Old Mac
87 if '\r' in data: # Old Mac
86 return '\r'
88 return '\r'
87 if '\n' in data: # UNIX
89 if '\n' in data: # UNIX
88 return '\n'
90 return '\n'
89 return None # unknown
91 return None # unknown
90
92
91 def _matcheol(file, origfile):
93 def _matcheol(file, origfile):
92 "Convert EOL markers in a file to match origfile"
94 "Convert EOL markers in a file to match origfile"
93 tostyle = _eoltype(open(origfile, "rb").read())
95 tostyle = _eoltype(open(origfile, "rb").read())
94 if tostyle:
96 if tostyle:
95 data = open(file, "rb").read()
97 data = open(file, "rb").read()
96 style = _eoltype(data)
98 style = _eoltype(data)
97 if style:
99 if style:
98 newdata = data.replace(style, tostyle)
100 newdata = data.replace(style, tostyle)
99 if newdata != data:
101 if newdata != data:
100 open(file, "wb").write(newdata)
102 open(file, "wb").write(newdata)
101
103
102 def filemerge(repo, mynode, orig, fcd, fco, fca):
104 def filemerge(repo, mynode, orig, fcd, fco, fca):
103 """perform a 3-way merge in the working directory
105 """perform a 3-way merge in the working directory
104
106
105 mynode = parent node before merge
107 mynode = parent node before merge
106 orig = original local filename before merge
108 orig = original local filename before merge
107 fco = other file context
109 fco = other file context
108 fca = ancestor file context
110 fca = ancestor file context
109 fcd = local file context for current/destination file
111 fcd = local file context for current/destination file
110 """
112 """
111
113
112 def temp(prefix, ctx):
114 def temp(prefix, ctx):
113 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
115 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
114 (fd, name) = tempfile.mkstemp(prefix=pre)
116 (fd, name) = tempfile.mkstemp(prefix=pre)
115 data = repo.wwritedata(ctx.path(), ctx.data())
117 data = repo.wwritedata(ctx.path(), ctx.data())
116 f = os.fdopen(fd, "wb")
118 f = os.fdopen(fd, "wb")
117 f.write(data)
119 f.write(data)
118 f.close()
120 f.close()
119 return name
121 return name
120
122
121 def isbin(ctx):
123 def isbin(ctx):
122 try:
124 try:
123 return util.binary(ctx.data())
125 return util.binary(ctx.data())
124 except IOError:
126 except IOError:
125 return False
127 return False
126
128
127 if not fco.cmp(fcd.data()): # files identical?
129 if not fco.cmp(fcd.data()): # files identical?
128 return None
130 return None
129
131
130 ui = repo.ui
132 ui = repo.ui
131 fd = fcd.path()
133 fd = fcd.path()
132 binary = isbin(fcd) or isbin(fco) or isbin(fca)
134 binary = isbin(fcd) or isbin(fco) or isbin(fca)
133 symlink = fcd.islink() or fco.islink()
135 symlink = fcd.islink() or fco.islink()
134 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
136 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
135 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
137 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
136 (tool, fd, binary, symlink))
138 (tool, fd, binary, symlink))
137
139
138 if not tool:
140 if not tool:
139 tool = "internal:local"
141 tool = "internal:local"
140 if ui.prompt(_(" no tool found to merge %s\n"
142 if ui.prompt(_(" no tool found to merge %s\n"
141 "keep (l)ocal or take (o)ther?") % fd,
143 "keep (l)ocal or take (o)ther?") % fd,
142 _("[lo]"), _("l")) != _("l"):
144 _("[lo]"), _("l")) != _("l"):
143 tool = "internal:other"
145 tool = "internal:other"
144 if tool == "internal:local":
146 if tool == "internal:local":
145 return 0
147 return 0
146 if tool == "internal:other":
148 if tool == "internal:other":
147 repo.wwrite(fd, fco.data(), fco.fileflags())
149 repo.wwrite(fd, fco.data(), fco.fileflags())
148 return 0
150 return 0
149 if tool == "internal:fail":
151 if tool == "internal:fail":
150 return 1
152 return 1
151
153
152 # do the actual merge
154 # do the actual merge
153 a = repo.wjoin(fd)
155 a = repo.wjoin(fd)
154 b = temp("base", fca)
156 b = temp("base", fca)
155 c = temp("other", fco)
157 c = temp("other", fco)
156 out = ""
158 out = ""
157 back = a + ".orig"
159 back = a + ".orig"
158 util.copyfile(a, back)
160 util.copyfile(a, back)
159
161
160 if orig != fco.path():
162 if orig != fco.path():
161 repo.ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
163 repo.ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
162 else:
164 else:
163 repo.ui.status(_("merging %s\n") % fd)
165 repo.ui.status(_("merging %s\n") % fd)
164
166
165 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca))
167 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca))
166
168
167 # do we attempt to simplemerge first?
169 # do we attempt to simplemerge first?
168 if _toolbool(ui, tool, "premerge", not (binary or symlink)):
170 if _toolbool(ui, tool, "premerge", not (binary or symlink)):
169 r = simplemerge.simplemerge(a, b, c, quiet=True)
171 r = simplemerge.simplemerge(a, b, c, quiet=True)
170 if not r:
172 if not r:
171 ui.debug(_(" premerge successful\n"))
173 ui.debug(_(" premerge successful\n"))
172 os.unlink(back)
174 os.unlink(back)
173 os.unlink(b)
175 os.unlink(b)
174 os.unlink(c)
176 os.unlink(c)
175 return 0
177 return 0
176 util.copyfile(back, a) # restore from backup and try again
178 util.copyfile(back, a) # restore from backup and try again
177
179
178 env = dict(HG_FILE=fd,
180 env = dict(HG_FILE=fd,
179 HG_MY_NODE=short(mynode),
181 HG_MY_NODE=short(mynode),
180 HG_OTHER_NODE=str(fco.changectx()),
182 HG_OTHER_NODE=str(fco.changectx()),
181 HG_MY_ISLINK=fcd.islink(),
183 HG_MY_ISLINK=fcd.islink(),
182 HG_OTHER_ISLINK=fco.islink(),
184 HG_OTHER_ISLINK=fco.islink(),
183 HG_BASE_ISLINK=fca.islink())
185 HG_BASE_ISLINK=fca.islink())
184
186
185 if tool == "internal:merge":
187 if tool == "internal:merge":
186 r = simplemerge.simplemerge(a, b, c, label=['local', 'other'])
188 r = simplemerge.simplemerge(a, b, c, label=['local', 'other'])
187 else:
189 else:
188 args = _toolstr(ui, tool, "args", '$local $base $other')
190 args = _toolstr(ui, tool, "args", '$local $base $other')
189 if "$output" in args:
191 if "$output" in args:
190 out, a = a, back # read input from backup, write to original
192 out, a = a, back # read input from backup, write to original
191 replace = dict(local=a, base=b, other=c, output=out)
193 replace = dict(local=a, base=b, other=c, output=out)
192 args = re.sub("\$(local|base|other|output)",
194 args = re.sub("\$(local|base|other|output)",
193 lambda x: '"%s"' % replace[x.group()[1:]], args)
195 lambda x: '"%s"' % replace[x.group()[1:]], args)
194 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
196 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
195
197
196 if not r and _toolbool(ui, tool, "checkconflicts"):
198 if not r and _toolbool(ui, tool, "checkconflicts"):
197 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()):
199 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()):
198 r = 1
200 r = 1
199
201
200 if not r and _toolbool(ui, tool, "checkchanged"):
202 if not r and _toolbool(ui, tool, "checkchanged"):
201 if filecmp.cmp(repo.wjoin(fd), back):
203 if filecmp.cmp(repo.wjoin(fd), back):
202 if ui.prompt(_(" output file %s appears unchanged\n"
204 if ui.prompt(_(" output file %s appears unchanged\n"
203 "was merge successful (yn)?") % fd,
205 "was merge successful (yn)?") % fd,
204 _("[yn]"), _("n")) != _("y"):
206 _("[yn]"), _("n")) != _("y"):
205 r = 1
207 r = 1
206
208
207 if _toolbool(ui, tool, "fixeol"):
209 if _toolbool(ui, tool, "fixeol"):
208 _matcheol(repo.wjoin(fd), back)
210 _matcheol(repo.wjoin(fd), back)
209
211
210 if r:
212 if r:
211 repo.ui.warn(_("merging %s failed!\n") % fd)
213 repo.ui.warn(_("merging %s failed!\n") % fd)
212 else:
214 else:
213 os.unlink(back)
215 os.unlink(back)
214
216
215 os.unlink(b)
217 os.unlink(b)
216 os.unlink(c)
218 os.unlink(c)
217 return r
219 return r
General Comments 0
You need to be logged in to leave comments. Login now