14 """perform a 3-way merge in the working directory |
14 """perform a 3-way merge in the working directory |
15 |
15 |
16 fw = filename in the working directory and first parent |
16 fw = filename in the working directory and first parent |
17 fo = filename in other parent |
17 fo = filename in other parent |
18 fd = destination filename |
18 fd = destination filename |
19 my = fileid in first parent |
|
20 other = fileid in second parent |
|
21 wctx, mctx = working and merge changecontexts |
19 wctx, mctx = working and merge changecontexts |
22 move = whether to move or copy the file to the destination |
20 move = whether to move or copy the file to the destination |
23 |
21 |
24 TODO: |
22 TODO: |
25 if fw is copied in the working directory, we get confused |
23 if fw is copied in the working directory, we get confused |
210 # are files different? |
208 # are files different? |
211 if n != m2[f]: |
209 if n != m2[f]: |
212 a = ma.get(f, nullid) |
210 a = ma.get(f, nullid) |
213 # are both different from the ancestor? |
211 # are both different from the ancestor? |
214 if not overwrite and n != a and m2[f] != a: |
212 if not overwrite and n != a and m2[f] != a: |
215 act("versions differ", f, "m", fmerge(f), n[:20], m2[f]) |
213 act("versions differ", f, "m", fmerge(f)) |
216 # are we clobbering? |
214 # are we clobbering? |
217 # is remote's version newer? |
215 # is remote's version newer? |
218 # or are we going back in time and clean? |
216 # or are we going back in time and clean? |
219 elif overwrite or m2[f] != a or (backwards and not n[20:]): |
217 elif overwrite or m2[f] != a or (backwards and not n[20:]): |
220 act("remote is newer", f, "g", m2.execf(f), m2[f]) |
218 act("remote is newer", f, "g", m2.execf(f), m2[f]) |
227 act("update permissions", f, "e", m2.execf(f)) |
225 act("update permissions", f, "e", m2.execf(f)) |
228 elif f in copy: |
226 elif f in copy: |
229 f2 = copy[f] |
227 f2 = copy[f] |
230 if f in ma: # case 3,20 A/B/A |
228 if f in ma: # case 3,20 A/B/A |
231 act("remote moved", |
229 act("remote moved", |
232 f, "c", f2, f2, m1[f], m2[f2], fmerge(f, f2, f), True) |
230 f, "c", f2, f2, fmerge(f, f2, f), True) |
233 else: |
231 else: |
234 if f2 in m1: # case 2 A,B/B/B |
232 if f2 in m1: # case 2 A,B/B/B |
235 act("local copied", |
233 act("local copied", |
236 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False) |
234 f, "c", f2, f, fmerge(f, f2, f2), False) |
237 else: # case 4,21 A/B/B |
235 else: # case 4,21 A/B/B |
238 act("local moved", |
236 act("local moved", |
239 f, "c", f2, f, m1[f], m2[f2], fmerge(f, f2, f2), False) |
237 f, "c", f2, f, fmerge(f, f2, f2), False) |
240 elif f in ma: |
238 elif f in ma: |
241 if n != ma[f] and not overwrite: |
239 if n != ma[f] and not overwrite: |
242 if repo.ui.prompt( |
240 if repo.ui.prompt( |
243 (_(" local changed %s which remote deleted\n") % f) + |
241 (_(" local changed %s which remote deleted\n") % f) + |
244 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"): |
242 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("d"): |
258 if f in copy: |
256 if f in copy: |
259 f2 = copy[f] |
257 f2 = copy[f] |
260 if f2 not in m2: # already seen |
258 if f2 not in m2: # already seen |
261 continue |
259 continue |
262 # rename case 1, A/A,B/A |
260 # rename case 1, A/A,B/A |
263 act("remote copied", |
261 act("remote copied", f2, "c", f, f, fmerge(f2, f, f2), False) |
264 f2, "c", f, f, m1[f2], m2[f], fmerge(f2, f, f2), False) |
|
265 elif f in ma: |
262 elif f in ma: |
266 if overwrite or backwards: |
263 if overwrite or backwards: |
267 act("recreating", f, "g", m2.execf(f), n) |
264 act("recreating", f, "g", m2.execf(f), n) |
268 elif n != ma[f]: |
265 elif n != ma[f]: |
269 if repo.ui.prompt( |
266 if repo.ui.prompt( |
291 if inst.errno != errno.ENOENT: |
288 if inst.errno != errno.ENOENT: |
292 repo.ui.warn(_("update failed to remove %s: %s!\n") % |
289 repo.ui.warn(_("update failed to remove %s: %s!\n") % |
293 (f, inst.strerror)) |
290 (f, inst.strerror)) |
294 removed +=1 |
291 removed +=1 |
295 elif m == "c": # copy |
292 elif m == "c": # copy |
296 f2, fd, my, other, flag, move = a[2:] |
293 f2, fd, flag, move = a[2:] |
297 repo.ui.status(_("merging %s and %s to %s\n") % (f, f2, fd)) |
294 repo.ui.status(_("merging %s and %s to %s\n") % (f, f2, fd)) |
298 if filemerge(repo, f, f2, fd, wctx, mctx, move): |
295 if filemerge(repo, f, f2, fd, wctx, mctx, move): |
299 unresolved += 1 |
296 unresolved += 1 |
300 util.set_exec(repo.wjoin(fd), flag) |
297 util.set_exec(repo.wjoin(fd), flag) |
301 merged += 1 |
298 merged += 1 |
302 elif m == "m": # merge |
299 elif m == "m": # merge |
303 flag, my, other = a[2:] |
300 flag = a[2] |
304 repo.ui.status(_("merging %s\n") % f) |
301 repo.ui.status(_("merging %s\n") % f) |
305 if filemerge(repo, f, f, f, wctx, mctx, False): |
302 if filemerge(repo, f, f, f, wctx, mctx, False): |
306 unresolved += 1 |
303 unresolved += 1 |
307 util.set_exec(repo.wjoin(f), flag) |
304 util.set_exec(repo.wjoin(f), flag) |
308 merged += 1 |
305 merged += 1 |
309 elif m == "g": # get |
306 elif m == "g": # get |
310 flag, node = a[2:] |
307 flag, node = a[2:] |
311 repo.ui.note(_("getting %s\n") % f) |
308 repo.ui.note(_("getting %s\n") % f) |
312 t = repo.file(f).read(node) |
309 t = mctx.filectx(f).data() |
313 repo.wwrite(f, t) |
310 repo.wwrite(f, t) |
314 util.set_exec(repo.wjoin(f), flag) |
311 util.set_exec(repo.wjoin(f), flag) |
315 updated += 1 |
312 updated += 1 |
316 elif m == "e": # exec |
313 elif m == "e": # exec |
317 flag = a[2:] |
314 flag = a[2] |
318 util.set_exec(repo.wjoin(f), flag) |
315 util.set_exec(repo.wjoin(f), flag) |
319 |
316 |
320 return updated, merged, removed, unresolved |
317 return updated, merged, removed, unresolved |
321 |
318 |
322 def recordupdates(repo, action, branchmerge): |
319 def recordupdates(repo, action, branchmerge, mctx): |
323 for a in action: |
320 for a in action: |
324 f, m = a[:2] |
321 f, m = a[:2] |
325 if m == "r": # remove |
322 if m == "r": # remove |
326 if branchmerge: |
323 if branchmerge: |
327 repo.dirstate.update([f], 'r') |
324 repo.dirstate.update([f], 'r') |
333 if branchmerge: |
330 if branchmerge: |
334 repo.dirstate.update([f], 'n', st_mtime=-1) |
331 repo.dirstate.update([f], 'n', st_mtime=-1) |
335 else: |
332 else: |
336 repo.dirstate.update([f], 'n') |
333 repo.dirstate.update([f], 'n') |
337 elif m == "m": # merge |
334 elif m == "m": # merge |
338 flag, my, other = a[2:] |
335 flag = a[2] |
339 if branchmerge: |
336 if branchmerge: |
340 # We've done a branch merge, mark this file as merged |
337 # We've done a branch merge, mark this file as merged |
341 # so that we properly record the merger later |
338 # so that we properly record the merger later |
342 repo.dirstate.update([f], 'm') |
339 repo.dirstate.update([f], 'm') |
343 else: |
340 else: |
345 # we set the dirstate to emulate a normal checkout |
342 # we set the dirstate to emulate a normal checkout |
346 # of that file some time in the past. Thus our |
343 # of that file some time in the past. Thus our |
347 # merge will appear as a normal local file |
344 # merge will appear as a normal local file |
348 # modification. |
345 # modification. |
349 fl = repo.file(f) |
346 fl = repo.file(f) |
350 f_len = fl.size(fl.rev(other)) |
347 f_len = mctx.filectx(f).size() |
351 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) |
348 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) |
352 elif m == "c": # copy |
349 elif m == "c": # copy |
353 f2, fd, my, other, flag, move = a[2:] |
350 f2, fd, flag, move = a[2:] |
354 if branchmerge: |
351 if branchmerge: |
355 # We've done a branch merge, mark this file as merged |
352 # We've done a branch merge, mark this file as merged |
356 # so that we properly record the merger later |
353 # so that we properly record the merger later |
357 repo.dirstate.update([fd], 'm') |
354 repo.dirstate.update([fd], 'm') |
358 else: |
355 else: |
360 # we set the dirstate to emulate a normal checkout |
357 # we set the dirstate to emulate a normal checkout |
361 # of that file some time in the past. Thus our |
358 # of that file some time in the past. Thus our |
362 # merge will appear as a normal local file |
359 # merge will appear as a normal local file |
363 # modification. |
360 # modification. |
364 fl = repo.file(f) |
361 fl = repo.file(f) |
365 f_len = fl.size(fl.rev(other)) |
362 f_len = mctx.filectx(f).size() |
366 repo.dirstate.update([fd], 'n', st_size=f_len, st_mtime=-1) |
363 repo.dirstate.update([fd], 'n', st_size=f_len, st_mtime=-1) |
367 if move: |
364 if move: |
368 repo.dirstate.update([f], 'r') |
365 repo.dirstate.update([f], 'r') |
369 if f != fd: |
366 if f != fd: |
370 repo.dirstate.copy(f, fd) |
367 repo.dirstate.copy(f, fd) |
435 |
432 |
436 updated, merged, removed, unresolved = applyupdates(repo, action, wc, p2) |
433 updated, merged, removed, unresolved = applyupdates(repo, action, wc, p2) |
437 |
434 |
438 # update dirstate |
435 # update dirstate |
439 if not partial: |
436 if not partial: |
440 recordupdates(repo, action, branchmerge) |
437 recordupdates(repo, action, branchmerge, p2) |
441 repo.dirstate.setparents(fp1, fp2) |
438 repo.dirstate.setparents(fp1, fp2) |
442 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved) |
439 repo.hook('update', parent1=xp1, parent2=xp2, error=unresolved) |
443 |
440 |
444 if show_stats: |
441 if show_stats: |
445 stats = ((updated, _("updated")), |
442 stats = ((updated, _("updated")), |