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 |
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) |
97 if style: |
97 if style: |
98 newdata = data.replace(style, tostyle) |
98 newdata = data.replace(style, tostyle) |
99 if newdata != data: |
99 if newdata != data: |
100 open(file, "wb").write(newdata) |
100 open(file, "wb").write(newdata) |
101 |
101 |
102 def filemerge(repo, fw, fd, fo, wctx, mctx): |
102 def filemerge(repo, mynode, orig, fcd, fco, fca): |
103 """perform a 3-way merge in the working directory |
103 """perform a 3-way merge in the working directory |
104 |
104 |
105 fw = original filename in the working directory |
105 mynode = parent node before merge |
106 fd = destination filename in the working directory |
106 orig = original local filename before merge |
107 fo = filename in other parent |
107 fco = other file context |
108 wctx, mctx = working and merge changecontexts |
108 fca = ancestor file context |
|
109 fcd = local file context for current/destination file |
109 """ |
110 """ |
110 |
111 |
111 def temp(prefix, ctx): |
112 def temp(prefix, ctx): |
112 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) |
113 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) |
113 (fd, name) = tempfile.mkstemp(prefix=pre) |
114 (fd, name) = tempfile.mkstemp(prefix=pre) |
121 try: |
122 try: |
122 return util.binary(ctx.data()) |
123 return util.binary(ctx.data()) |
123 except IOError: |
124 except IOError: |
124 return False |
125 return False |
125 |
126 |
126 fco = mctx.filectx(fo) |
127 if not fco.cmp(fcd.data()): # files identical? |
127 if not fco.cmp(wctx.filectx(fd).data()): # files identical? |
|
128 return None |
128 return None |
129 |
129 |
130 ui = repo.ui |
130 ui = repo.ui |
131 fcm = wctx.filectx(fw) |
131 fd = fcd.path() |
132 fca = fcm.ancestor(fco) or repo.filectx(fw, fileid=nullrev) |
132 binary = isbin(fcd) or isbin(fco) or isbin(fca) |
133 binary = isbin(fcm) or isbin(fco) or isbin(fca) |
133 symlink = fcd.islink() or fco.islink() |
134 symlink = fcm.islink() or fco.islink() |
134 tool, toolpath = _picktool(repo, ui, fd, binary, symlink) |
135 tool, toolpath = _picktool(repo, ui, fw, binary, symlink) |
|
136 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") % |
135 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") % |
137 (tool, fw, binary, symlink)) |
136 (tool, fd, binary, symlink)) |
138 |
137 |
139 if not tool: |
138 if not tool: |
140 tool = "internal:local" |
139 tool = "internal:local" |
141 if ui.prompt(_(" no tool found to merge %s\n" |
140 if ui.prompt(_(" no tool found to merge %s\n" |
142 "keep (l)ocal or take (o)ther?") % fw, |
141 "keep (l)ocal or take (o)ther?") % fd, |
143 _("[lo]"), _("l")) != _("l"): |
142 _("[lo]"), _("l")) != _("l"): |
144 tool = "internal:other" |
143 tool = "internal:other" |
145 if tool == "internal:local": |
144 if tool == "internal:local": |
146 return 0 |
145 return 0 |
147 if tool == "internal:other": |
146 if tool == "internal:other": |
156 c = temp("other", fco) |
155 c = temp("other", fco) |
157 out = "" |
156 out = "" |
158 back = a + ".orig" |
157 back = a + ".orig" |
159 util.copyfile(a, back) |
158 util.copyfile(a, back) |
160 |
159 |
161 if fw != fo: |
160 if orig != fco.path(): |
162 repo.ui.status(_("merging %s and %s\n") % (fw, fo)) |
161 repo.ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) |
163 else: |
162 else: |
164 repo.ui.status(_("merging %s\n") % fw) |
163 repo.ui.status(_("merging %s\n") % fd) |
165 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca)) |
164 |
|
165 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca)) |
166 |
166 |
167 # do we attempt to simplemerge first? |
167 # do we attempt to simplemerge first? |
168 if _toolbool(ui, tool, "premerge", not (binary or symlink)): |
168 if _toolbool(ui, tool, "premerge", not (binary or symlink)): |
169 r = simplemerge.simplemerge(a, b, c, quiet=True) |
169 r = simplemerge.simplemerge(a, b, c, quiet=True) |
170 if not r: |
170 if not r: |
174 os.unlink(c) |
174 os.unlink(c) |
175 return 0 |
175 return 0 |
176 util.copyfile(back, a) # restore from backup and try again |
176 util.copyfile(back, a) # restore from backup and try again |
177 |
177 |
178 env = dict(HG_FILE=fd, |
178 env = dict(HG_FILE=fd, |
179 HG_MY_NODE=str(wctx.parents()[0]), |
179 HG_MY_NODE=short(mynode), |
180 HG_OTHER_NODE=str(mctx), |
180 HG_OTHER_NODE=str(fco.changectx()), |
181 HG_MY_ISLINK=fcm.islink(), |
181 HG_MY_ISLINK=fcd.islink(), |
182 HG_OTHER_ISLINK=fco.islink(), |
182 HG_OTHER_ISLINK=fco.islink(), |
183 HG_BASE_ISLINK=fca.islink()) |
183 HG_BASE_ISLINK=fca.islink()) |
184 |
184 |
185 if tool == "internal:merge": |
185 if tool == "internal:merge": |
186 r = simplemerge.simplemerge(a, b, c, label=['local', 'other']) |
186 r = simplemerge.simplemerge(a, b, c, label=['local', 'other']) |
192 args = re.sub("\$(local|base|other|output)", |
192 args = re.sub("\$(local|base|other|output)", |
193 lambda x: '"%s"' % replace[x.group()[1:]], args) |
193 lambda x: '"%s"' % replace[x.group()[1:]], args) |
194 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env) |
194 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env) |
195 |
195 |
196 if not r and _toolbool(ui, tool, "checkconflicts"): |
196 if not r and _toolbool(ui, tool, "checkconflicts"): |
197 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcm.data()): |
197 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()): |
198 r = 1 |
198 r = 1 |
199 |
199 |
200 if not r and _toolbool(ui, tool, "checkchanged"): |
200 if not r and _toolbool(ui, tool, "checkchanged"): |
201 if filecmp.cmp(repo.wjoin(fd), back): |
201 if filecmp.cmp(repo.wjoin(fd), back): |
202 if ui.prompt(_(" output file %s appears unchanged\n" |
202 if ui.prompt(_(" output file %s appears unchanged\n" |