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