3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> |
3 # Copyright 2006, 2007 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 nullid, nullrev |
8 from node import nullid, nullrev, hex |
9 from i18n import _ |
9 from i18n import _ |
10 import errno, util, os, filemerge, copies |
10 import errno, util, os, filemerge, copies, sha, shutil |
|
11 |
|
12 class mergestate(object): |
|
13 '''track 3-way merge state of individual files''' |
|
14 def __init__(self, repo): |
|
15 self._repo = repo |
|
16 self._state = {} |
|
17 self._data = {} |
|
18 def reset(self, node): |
|
19 self._local = node |
|
20 shutil.rmtree(self._repo.join("merge"), True) |
|
21 def add(self, fcl, fco, fca, fd, flags): |
|
22 hash = sha.sha(fcl.path()).hexdigest() |
|
23 self._repo.opener("merge/" + hash, "w").write(fcl.data()) |
|
24 self._state[fd] = 'u' |
|
25 self._data[fd] = (hash, fcl.path(), fca.path(), hex(fca.filenode()), |
|
26 fco.path(), flags) |
|
27 def __contains__(self, dfile): |
|
28 return dfile in self._state |
|
29 def __getitem__(self, dfile): |
|
30 return self._state[dfile] |
|
31 def mark(self, dfile, state): |
|
32 self._state[dfile] = state |
|
33 def resolve(self, dfile, wctx, octx): |
|
34 if self[dfile] == 'r': |
|
35 return 0 |
|
36 hash, lfile, afile, anode, ofile, flags = self._data[dfile] |
|
37 f = self._repo.opener("merge/" + hash) |
|
38 self._repo.wwrite(dfile, f.read(), flags) |
|
39 fcd = wctx[dfile] |
|
40 fco = octx[ofile] |
|
41 fca = self._repo.filectx(afile, fileid=anode) |
|
42 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca) |
|
43 if not r: |
|
44 util.set_flags(self._repo.wjoin(dfile), flags) |
|
45 self.mark(dfile, 'r') |
|
46 return r |
11 |
47 |
12 def _checkunknown(wctx, mctx): |
48 def _checkunknown(wctx, mctx): |
13 "check for collisions between unknown files and files in mctx" |
49 "check for collisions between unknown files and files in mctx" |
14 for f in wctx.unknown(): |
50 for f in wctx.unknown(): |
15 if f in mctx and mctx[f].cmp(wctx[f].data()): |
51 if f in mctx and mctx[f].cmp(wctx[f].data()): |
200 def applyupdates(repo, action, wctx, mctx): |
236 def applyupdates(repo, action, wctx, mctx): |
201 "apply the merge action list to the working directory" |
237 "apply the merge action list to the working directory" |
202 |
238 |
203 updated, merged, removed, unresolved = 0, 0, 0, 0 |
239 updated, merged, removed, unresolved = 0, 0, 0, 0 |
204 action.sort() |
240 action.sort() |
205 # prescan for copy/renames |
241 |
|
242 ms = mergestate(repo) |
|
243 ms.reset(wctx.parents()[0].node()) |
|
244 moves = [] |
|
245 |
|
246 # prescan for merges |
206 for a in action: |
247 for a in action: |
207 f, m = a[:2] |
248 f, m = a[:2] |
208 if m == 'm': # merge |
249 if m == 'm': # merge |
209 f2, fd, flags, move = a[2:] |
250 f2, fd, flags, move = a[2:] |
210 if f != fd: |
251 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd)) |
211 repo.ui.debug(_("copying %s to %s\n") % (f, fd)) |
252 fcl = wctx[f] |
212 repo.wwrite(fd, repo.wread(f), flags) |
253 fco = mctx[f2] |
|
254 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev) |
|
255 ms.add(fcl, fco, fca, fd, flags) |
|
256 if f != fd and move: |
|
257 moves.append(f) |
|
258 |
|
259 # remove renamed files after safely stored |
|
260 for f in moves: |
|
261 if util.lexists(repo.wjoin(f)): |
|
262 repo.ui.debug(_("removing %s\n") % f) |
|
263 os.unlink(repo.wjoin(f)) |
213 |
264 |
214 audit_path = util.path_auditor(repo.root) |
265 audit_path = util.path_auditor(repo.root) |
215 |
266 |
216 for a in action: |
267 for a in action: |
217 f, m = a[:2] |
268 f, m = a[:2] |
227 repo.ui.warn(_("update failed to remove %s: %s!\n") % |
278 repo.ui.warn(_("update failed to remove %s: %s!\n") % |
228 (f, inst.strerror)) |
279 (f, inst.strerror)) |
229 removed += 1 |
280 removed += 1 |
230 elif m == "m": # merge |
281 elif m == "m": # merge |
231 f2, fd, flags, move = a[2:] |
282 f2, fd, flags, move = a[2:] |
232 r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx) |
283 r = ms.resolve(fd, wctx, mctx) |
233 if r > 0: |
284 if r > 0: |
234 unresolved += 1 |
285 unresolved += 1 |
235 else: |
286 else: |
236 if r is None: |
287 if r is None: |
237 updated += 1 |
288 updated += 1 |
238 else: |
289 else: |
239 merged += 1 |
290 merged += 1 |
240 util.set_flags(repo.wjoin(fd), flags) |
|
241 if f != fd and move and util.lexists(repo.wjoin(f)): |
|
242 repo.ui.debug(_("removing %s\n") % f) |
|
243 os.unlink(repo.wjoin(f)) |
|
244 elif m == "g": # get |
291 elif m == "g": # get |
245 flags = a[2] |
292 flags = a[2] |
246 repo.ui.note(_("getting %s\n") % f) |
293 repo.ui.note(_("getting %s\n") % f) |
247 t = mctx.filectx(f).data() |
294 t = mctx.filectx(f).data() |
248 repo.wwrite(f, t, flags) |
295 repo.wwrite(f, t, flags) |