--- a/mercurial/merge.py Tue Jan 15 01:05:12 2013 +0100
+++ b/mercurial/merge.py Wed Jan 09 02:02:45 2013 +0100
@@ -45,11 +45,11 @@
f.write("\0".join([d] + v) + "\n")
f.close()
self._dirty = False
- def add(self, fcl, fco, fca, fd, flags):
+ def add(self, fcl, fco, fca, fd):
hash = util.sha1(fcl.path()).hexdigest()
self._repo.opener.write("merge/" + hash, fcl.data())
self._state[fd] = ['u', hash, fcl.path(), fca.path(),
- hex(fca.filenode()), fco.path(), flags]
+ hex(fca.filenode()), fco.path(), fcl.flags()]
self._dirty = True
def __contains__(self, dfile):
return dfile in self._state
@@ -67,12 +67,22 @@
if self[dfile] == 'r':
return 0
state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
+ fcd = wctx[dfile]
+ fco = octx[ofile]
+ fca = self._repo.filectx(afile, fileid=anode)
+ # "premerge" x flags
+ flo = fco.flags()
+ fla = fca.flags()
+ if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
+ if fca.node() == nullid:
+ self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
+ afile)
+ elif flags == fla:
+ flags = flo
+ # restore local
f = self._repo.opener("merge/" + hash)
self._repo.wwrite(dfile, f.read(), flags)
f.close()
- fcd = wctx[dfile]
- fco = octx[ofile]
- fca = self._repo.filectx(afile, fileid=anode)
r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
if r is None:
# no real conflict
@@ -183,32 +193,6 @@
partial = function to filter file lists
"""
- def fmerge(f, f2, fa):
- """merge flags"""
- a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
- if m == n: # flags agree
- return m # unchanged
- if m and n and not a: # flags set, don't agree, differ from parent
- r = repo.ui.promptchoice(
- _(" conflicting flags for %s\n"
- "(n)one, e(x)ec or sym(l)ink?") % f,
- (_("&None"), _("E&xec"), _("Sym&link")), 0)
- if r == 1:
- return "x" # Exec
- if r == 2:
- return "l" # Symlink
- return ""
- if m and m != a: # changed from a to m
- return m
- if n and n != a: # changed from a to n
- if (n == 'l' or a == 'l') and m1.get(f) != ma.get(f):
- # can't automatically merge symlink flag when there
- # are file-level conflicts here, let filemerge take
- # care of it
- return m
- return n
- return '' # flag was cleared
-
def act(msg, m, f, *args):
repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
actions.append((f, m) + args)
@@ -248,17 +232,25 @@
if partial and not partial(f):
continue
if f in m2:
- rflags = fmerge(f, f, f)
+ n2 = m2[f]
+ fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
+ nol = 'l' not in fl1 + fl2 + fla
a = ma.get(f, nullid)
- if n == m2[f] or m2[f] == a: # same or local newer
- # is file locally modified or flags need changing?
- # dirstate flags may need to be made current
- if m1.flags(f) != rflags or n[20:]:
- act("update permissions", "e", f, rflags)
- elif n == a: # remote newer
- act("remote is newer", "g", f, rflags)
- else: # both changed
- act("versions differ", "m", f, f, f, rflags, False)
+ if n == n2 and fl1 == fl2:
+ pass # same - keep local
+ elif n2 == a and fl2 == fla:
+ pass # remote unchanged - keep local
+ elif n == a and fl1 == fla: # local unchanged - use remote
+ if n == n2: # optimization: keep local content
+ act("update permissions", "e", f, fl2)
+ else:
+ act("remote is newer", "g", f, fl2)
+ elif nol and n2 == a: # remote only changed 'x'
+ act("update permissions", "e", f, fl2)
+ elif nol and n == a: # local only changed 'x'
+ act("remote is newer", "g", f, fl)
+ else: # both changed something
+ act("versions differ", "m", f, f, f, False)
elif f in copied: # files we'll deal with on m2 side
pass
elif f in movewithdir: # directory rename
@@ -267,8 +259,7 @@
m1.flags(f))
elif f in copy: # case 2 A,B/B/B or case 4,21 A/B/B
f2 = copy[f]
- act("local copied/moved to " + f2, "m", f, f2, f,
- fmerge(f, f2, f2), False)
+ act("local copied/moved to " + f2, "m", f, f2, f, False)
elif f in ma: # clean, a different, no remote
if n != ma[f]:
if repo.ui.promptchoice(
@@ -296,16 +287,15 @@
f2 = copy[f]
if f2 in m2: # rename case 1, A/A,B/A
act("remote copied to " + f, "m",
- f2, f, f, fmerge(f2, f, f2), False)
+ f2, f, f, False)
else: # case 3,20 A/B/A
act("remote moved to " + f, "m",
- f2, f, f, fmerge(f2, f, f2), True)
+ f2, f, f, True)
elif f not in ma:
if (not overwrite
and _checkunknownfile(repo, p1, p2, f)):
- rflags = fmerge(f, f, f)
act("remote differs from untracked local",
- "m", f, f, f, rflags, False)
+ "m", f, f, f, False)
else:
act("remote created", "g", f, m2.flags(f))
elif n != ma[f]:
@@ -341,7 +331,7 @@
for a in actions:
f, m = a[:2]
if m == "m": # merge
- f2, fd, flags, move = a[2:]
+ f2, fd, move = a[2:]
if fd == '.hgsubstate': # merged internally
continue
repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
@@ -356,7 +346,7 @@
fca = fcl.ancestor(fco, actx)
if not fca:
fca = repo.filectx(f, fileid=nullrev)
- ms.add(fcl, fco, fca, fd, flags)
+ ms.add(fcl, fco, fca, fd)
if f != fd and move:
moves.append(f)
@@ -390,7 +380,7 @@
subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
overwrite)
continue
- f2, fd, flags, move = a[2:]
+ f2, fd, move = a[2:]
audit(fd)
r = ms.resolve(fd, wctx, mctx)
if r is not None and r > 0:
@@ -484,7 +474,7 @@
else:
repo.dirstate.normal(f)
elif m == "m": # merge
- f2, fd, flag, move = a[2:]
+ f2, fd, move = a[2:]
if branchmerge:
# We've done a branch merge, mark this file as merged
# so that we properly record the merger later
--- a/tests/test-merge-types.t Tue Jan 15 01:05:12 2013 +0100
+++ b/tests/test-merge-types.t Wed Jan 09 02:02:45 2013 +0100
@@ -34,16 +34,28 @@
resolving manifests
overwrite: False, partial: False
ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
- conflicting flags for a
- (n)one, e(x)ec or sym(l)ink? n
- a: update permissions -> e
+ a: versions differ -> m
+ preserving a for resolve of a
updating: a 1/1 files (100.00%)
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ picked tool 'internal:merge' for a (binary False symlink True)
+ merging a
+ my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
+ warning: internal:merge cannot merge symlinks for a
+ merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout a
- a is a plain file with content:
- symlink (no-eol)
+ a is a symlink:
+ a -> symlink
+ $ hg resolve a --tool internal:other
+ $ tellmeabout a
+ a is an executable file with content:
+ a
+ $ hg st
+ M a
+ ? a.orig
Symlink is other parent, executable is local:
@@ -55,17 +67,21 @@
resolving manifests
overwrite: False, partial: False
ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
- conflicting flags for a
- (n)one, e(x)ec or sym(l)ink? n
- a: remote is newer -> g
+ a: versions differ -> m
+ preserving a for resolve of a
updating: a 1/1 files (100.00%)
- getting a
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ picked tool 'internal:merge' for a (binary False symlink True)
+ merging a
+ my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
+ warning: internal:merge cannot merge symlinks for a
+ merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout a
- a is a plain file with content:
- symlink (no-eol)
+ a is an executable file with content:
+ a
Update to link without local change should get us a symlink (issue3316):
@@ -74,6 +90,7 @@
$ hg up
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg st
+ ? a.orig
Update to link with local change should cause a merge prompt (issue3200):
@@ -119,18 +136,24 @@
$ ln -s base f
$ hg ci -qm2
$ hg merge
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ merging f
+ warning: internal:merge cannot merge symlinks for f
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout f
f is a symlink:
- f -> file
- content
-
+ f -> base
$ hg up -Cqr1
$ hg merge
- 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ merging f
+ warning: internal:merge cannot merge symlinks for f
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout f
f is a plain file with content:
file
@@ -152,19 +175,27 @@
$ ln -s dangling f
$ hg ci -qm2
$ hg merge
- 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ merging f
+ warning: internal:merge cannot merge symlinks for f
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout f
f is a symlink:
f -> dangling
$ hg up -Cqr1
$ hg merge
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- (branch merge, don't forget to commit)
+ merging f
+ warning: internal:merge cannot merge symlinks for f
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
$ tellmeabout f
- f is a symlink:
- f -> dangling
+ f is a plain file with content:
+ f
$ cd ..
@@ -210,13 +241,10 @@
$ hg ci -Aqm2
$ hg merge
- conflicting flags for e
- (n)one, e(x)ec or sym(l)ink? n
- conflicting flags for d
- (n)one, e(x)ec or sym(l)ink? n
merging a
warning: conflicts during merge.
merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+ warning: cannot merge flags for b
merging b
warning: conflicts during merge.
merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
@@ -229,7 +257,7 @@
merging h
warning: internal:merge cannot merge symlinks for h
merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
- 2 files updated, 0 files merged, 0 files removed, 5 files unresolved
+ 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
[1]
$ hg resolve -l
@@ -246,21 +274,21 @@
1
>>>>>>> other
$ tellmeabout b
- b is an executable file with content:
+ b is a plain file with content:
<<<<<<< local
2
=======
1
>>>>>>> other
$ tellmeabout c
- c is an executable file with content:
+ c is a plain file with content:
x
$ tellmeabout d
- d is a plain file with content:
- 2 (no-eol)
+ d is a symlink:
+ d -> 2
$ tellmeabout e
- e is a plain file with content:
- x (no-eol)
+ e is a symlink:
+ e -> x
$ tellmeabout f
f is a symlink:
f -> 2
@@ -273,13 +301,10 @@
$ hg up -Cqr1
$ hg merge
- conflicting flags for e
- (n)one, e(x)ec or sym(l)ink? n
- conflicting flags for d
- (n)one, e(x)ec or sym(l)ink? n
merging a
warning: conflicts during merge.
merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+ warning: cannot merge flags for b
merging b
warning: conflicts during merge.
merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
@@ -292,7 +317,7 @@
merging h
warning: internal:merge cannot merge symlinks for h
merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
- 1 files updated, 0 files merged, 0 files removed, 5 files unresolved
+ 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
[1]
$ tellmeabout a
@@ -310,13 +335,13 @@
2
>>>>>>> other
$ tellmeabout c
- c is an executable file with content:
+ c is a plain file with content:
x
$ tellmeabout d
- d is a plain file with content:
+ d is an executable file with content:
1
$ tellmeabout e
- e is a plain file with content:
+ e is an executable file with content:
x (no-eol)
$ tellmeabout f
f is a plain file with content: