--- a/mercurial/merge.py Thu Nov 30 17:36:33 2006 -0600
+++ b/mercurial/merge.py Thu Nov 30 17:36:33 2006 -0600
@@ -122,11 +122,20 @@
return
c2 = ctx(of, man[of])
ca = c.ancestor(c2)
- if not ca or c == ca or c2 == ca:
+ if not ca: # unrelated
return
if ca.path() == c.path() or ca.path() == c2.path():
+ fullcopy[c.path()] = of
+ if c == ca or c2 == ca: # no merge needed, ignore copy
+ return
copy[c.path()] = of
+ def dirs(files):
+ d = {}
+ for f in files:
+ d[os.path.dirname(f)] = True
+ return d
+
if not repo.ui.configbool("merge", "followcopies", True):
return {}
@@ -136,6 +145,7 @@
dcopies = repo.dirstate.copies()
copy = {}
+ fullcopy = {}
u1 = nonoverlap(m1, m2, ma)
u2 = nonoverlap(m2, m1, ma)
ctx = util.cachefunc(lambda f, n: repo.filectx(f, fileid=n[:20]))
@@ -146,6 +156,38 @@
for f in u2:
checkcopies(ctx(f, m2[f]), m1)
+ if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
+ return copy
+
+ # generate a directory move map
+ d1, d2 = dirs(m1), dirs(m2)
+ invalid = {}
+ dirmove = {}
+
+ for dst, src in fullcopy.items():
+ dsrc, ddst = os.path.dirname(src), os.path.dirname(dst)
+ if dsrc in invalid:
+ continue
+ elif (dsrc in d1 and ddst in d1) or (dsrc in d2 and ddst in d2):
+ invalid[dsrc] = True
+ elif dsrc in dirmove and dirmove[dsrc] != ddst:
+ invalid[dsrc] = True
+ del dirmove[dsrc]
+ else:
+ dirmove[dsrc] = ddst
+
+ del d1, d2, invalid
+
+ if not dirmove:
+ return copy
+
+ # check unaccounted nonoverlapping files
+ for f in u1 + u2:
+ if f not in fullcopy:
+ d = os.path.dirname(f)
+ if d in dirmove:
+ copy[f] = dirmove[d] + "/" + os.path.basename(f)
+
return copy
def manifestmerge(repo, p1, p2, pa, overwrite, partial):
@@ -210,7 +252,10 @@
continue
elif f in copy:
f2 = copy[f]
- if f2 in m1: # case 2 A,B/B/B
+ if f2 not in m2: # directory rename
+ act("remote renamed directory to " + f2, "d",
+ f, None, f2, m1.execf(f))
+ elif f2 in m1: # case 2 A,B/B/B
act("local copied to " + f2, "m",
f, f2, f, fmerge(f, f2, f2), False)
else: # case 4,21 A/B/B
@@ -238,7 +283,10 @@
continue
if f in copy:
f2 = copy[f]
- if f2 in m2: # rename case 1, A/A,B/A
+ if f2 not in m1: # directory rename
+ act("local renamed directory to " + f2, "d",
+ None, f, f2, m2.execf(f))
+ elif f2 in m2: # rename case 1, A/A,B/A
act("remote copied to " + f, "m",
f2, f, f, fmerge(f2, f, f2), False)
else: # case 3,20 A/B/A
@@ -264,7 +312,7 @@
action.sort()
for a in action:
f, m = a[:2]
- if f[0] == "/":
+ if f and f[0] == "/":
continue
if m == "r": # remove
repo.ui.note(_("removing %s\n") % f)
@@ -300,6 +348,20 @@
repo.wwrite(f, t)
util.set_exec(repo.wjoin(f), flag)
updated += 1
+ elif m == "d": # directory rename
+ f2, fd, flag = a[2:]
+ if f:
+ repo.ui.note(_("moving %s to %s\n") % (f, fd))
+ t = wctx.filectx(f).data()
+ repo.wwrite(fd, t)
+ util.set_exec(repo.wjoin(fd), flag)
+ util.unlink(repo.wjoin(f))
+ if f2:
+ repo.ui.note(_("getting %s to %s\n") % (f2, fd))
+ t = mctx.filectx(f2).data()
+ repo.wwrite(fd, t)
+ util.set_exec(repo.wjoin(fd), flag)
+ updated += 1
elif m == "e": # exec
flag = a[2]
util.set_exec(repo.wjoin(f), flag)
@@ -345,6 +407,19 @@
repo.dirstate.update([fd], 'n', st_size=-1, st_mtime=-1)
if move:
repo.dirstate.forget([f])
+ elif m == "d": # directory rename
+ f2, fd, flag = a[2:]
+ if branchmerge:
+ repo.dirstate.update([fd], 'a')
+ if f:
+ repo.dirstate.update([f], 'r')
+ repo.dirstate.copy(f, fd)
+ if f2:
+ repo.dirstate.copy(f2, fd)
+ else:
+ repo.dirstate.update([fd], 'n')
+ if f:
+ repo.dirstate.forget([f])
def update(repo, node, branchmerge, force, partial, wlock):
"""