diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -632,6 +632,42 @@ class workingctx(changectx):
     def __contains__(self, key):
         return self._repo.dirstate[key] not in "?r"
 
+    def _buildflagfunc(self):
+        # Create a fallback function for getting file flags when the
+        # filesystem doesn't support them
+
+        copiesget = self._repo.dirstate.copies().get
+
+        if len(self._parents) < 2:
+            # when we have one parent, it's easy: copy from parent
+            man = self._parents[0].manifest()
+            def func(f):
+                f = copiesget(f, f)
+                return man.flags(f)
+        else:
+            # merges are tricky: we try to reconstruct the unstored
+            # result from the merge (issue1802)
+            p1, p2 = self._parents
+            pa = p1.ancestor(p2)
+            m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
+
+            def func(f):
+                f = copiesget(f, f) # may be wrong for merges with copies
+                fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
+                if fl1 == fl2:
+                    return fl1
+                if fl1 == fla:
+                    return fl2
+                if fl2 == fla:
+                    return fl1
+                return '' # punt for conflicts
+
+        return func
+
+    @propertycache
+    def _flagfunc(self):
+        return self._repo.dirstate.flagfunc(self._buildflagfunc)
+
     @propertycache
     def _manifest(self):
         """generate a manifest corresponding to the working directory"""
@@ -640,7 +676,6 @@ class workingctx(changectx):
             self.status(unknown=True)
 
         man = self._parents[0].manifest().copy()
-        copied = self._repo.dirstate.copies()
         if len(self._parents) > 1:
             man2 = self.p2().manifest()
             def getman(f):
@@ -649,10 +684,9 @@ class workingctx(changectx):
                 return man2
         else:
             getman = lambda f: man
-        def cf(f):
-            f = copied.get(f, f)
-            return getman(f).flags(f)
-        ff = self._repo.dirstate.flagfunc(cf)
+
+        copied = self._repo.dirstate.copies()
+        ff = self._flagfunc
         modified, added, removed, deleted = self._status
         unknown = self._unknown
         for i, l in (("a", added), ("m", modified), ("u", unknown)):
@@ -767,23 +801,10 @@ class workingctx(changectx):
             except KeyError:
                 return ''
 
-        orig = self._repo.dirstate.copies().get(path, path)
-
-        def findflag(ctx):
-            mnode = ctx.changeset()[0]
-            node, flag = self._repo.manifest.find(mnode, orig)
-            ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
-            try:
-                return ff(path)
-            except OSError:
-                pass
-
-        flag = findflag(self._parents[0])
-        if flag is None and len(self.parents()) > 1:
-            flag = findflag(self._parents[1])
-        if flag is None or self._repo.dirstate[path] == 'r':
+        try:
+            return self._flagfunc(path)
+        except OSError:
             return ''
-        return flag
 
     def filectx(self, path, filelog=None):
         """get a file context from the working directory"""
diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -131,17 +131,19 @@ class dirstate(object):
         # it's safe because f is always a relative path
         return self._rootdir + f
 
-    def flagfunc(self, fallback):
+    def flagfunc(self, buildfallback):
+        if self._checklink and self._checkexec:
+            def f(x):
+                p = self._join(x)
+                if os.path.islink(p):
+                    return 'l'
+                if util.isexec(p):
+                    return 'x'
+                return ''
+            return f
+
+        fallback = buildfallback()
         if self._checklink:
-            if self._checkexec:
-                def f(x):
-                    p = self._join(x)
-                    if os.path.islink(p):
-                        return 'l'
-                    if util.isexec(p):
-                        return 'x'
-                    return ''
-                return f
             def f(x):
                 if os.path.islink(self._join(x)):
                     return 'l'
@@ -157,7 +159,8 @@ class dirstate(object):
                     return 'x'
                 return ''
             return f
-        return fallback
+        else:
+            return fallback
 
     def getcwd(self):
         cwd = os.getcwd()
diff --git a/tests/test-issue1802.t b/tests/test-issue1802.t
new file mode 100644
--- /dev/null
+++ b/tests/test-issue1802.t
@@ -0,0 +1,69 @@
+Create extension that can disable exec checks:
+
+  $ cat > noexec.py <<EOF
+  > from mercurial import extensions, util
+  > def setflags(orig, f, l, x):
+  >     pass
+  > def checkexec(orig, path):
+  >     return False
+  > def extsetup(ui):
+  >     extensions.wrapfunction(util, 'setflags', setflags)
+  >     extensions.wrapfunction(util, 'checkexec', checkexec)
+  > EOF
+
+  $ hg init unix-repo
+  $ cd unix-repo
+  $ touch a
+  $ hg add a
+  $ hg commit -m 'unix: add a'
+  $ hg clone . ../win-repo
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ chmod +x a
+  $ hg commit -m 'unix: chmod a'
+  $ hg manifest -v
+  755 * a
+
+  $ cd ../win-repo
+
+  $ touch b
+  $ hg add b
+  $ hg commit -m 'win: add b'
+
+  $ hg manifest -v
+  644   a
+  644   b
+
+  $ hg pull
+  pulling from $TESTTMP/unix-repo
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+  $ hg manifest -v -r tip
+  755 * a
+
+Simulate a Windows merge:
+
+  $ hg --config extensions.n=$TESTTMP/noexec.py merge --debug
+    searching for copies back to rev 1
+    unmatched files in local:
+     b
+  resolving manifests
+   overwrite None partial False
+   ancestor a03b0deabf2b local d6fa54f68ae1+ remote 2d8bcf2dda39
+   a: update permissions -> e
+  updating: a 1/1 files (100.00%)
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
+Simulate a Windows commit:
+
+  $ hg --config extensions.n=$TESTTMP/noexec.py commit -m 'win: merge'
+
+  $ hg manifest -v
+  755 * a
+  644   b