diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -503,6 +503,99 @@
             meta[r'parent'] = commit[r'parents'][0]
     return meta
 
+# The constants are from https://secure.phabricator.com/diffusion/ARC/
+# browse/master/src/parser/diff/ArcanistDiffChangeType.php
+# They are very stable - haven't been changed since Jan 2011, the first commit
+# of the arcanist project.
+class changetype(object):
+    ADD = 1
+    CHANGE = 2
+    DELETE = 3
+    MOVE_AWAY = 4
+    COPY_AWAY = 5
+    MOVE_HERE = 6
+    COPY_HERE = 7
+    MULTICOPY = 8
+
+class filetype(object):
+    TEXT = 1
+
+def getpatchbodyfromdiff(diff):
+    """get patch body (without commit message or metadata) from a diff dict
+
+    This is similar to differential.getrawdiff API. But we reconstruct the diff
+    from a diff object fetched earlier via differential.querydiffs API.
+
+    Currently it does not support binary files. However it seems the
+    "differential.createrawdiff" API (used by phabsend) couldn't really handle
+    base85 binaries so binary files are broken at uploading time, therefore
+    this limitation is probably fine.
+    """
+    out = util.stringio()
+    write = out.write
+
+    for c in diff[r'changes']:
+        path = encoding.unitolocal(c[r'currentPath'] or u'')
+        oldpath = encoding.unitolocal(c[r'oldPath'] or u'')
+        patha = 'a/%s' % (oldpath or path)
+        pathb = 'b/%s' % (path or oldpath)
+
+        ftype = int(c[r'fileType'])
+        if ftype != filetype.TEXT:
+            raise error.Abort(_('unsupported file type %s: %s') % (ftype, path))
+
+        ctype = int(c[r'type'])
+        if ctype in [changetype.MULTICOPY, changetype.MOVE_AWAY,
+                     changetype.COPY_AWAY]:
+            # ignore these, COPY_HERE or MOVE_HERE will cover them
+            continue
+
+        def getmode(name):
+            s = (c[name] or {}).get(r'unix:filemode', r'100644')
+            return encoding.unitolocal(s)
+
+        oldmode = getmode(r'oldProperties')
+        newmode = getmode(r'newProperties')
+
+        write('diff --git %s %s\n' % (patha, pathb))
+        if ctype == changetype.ADD:
+            write('new file mode %s\n' % newmode)
+            patha = '/dev/null'
+        elif ctype == changetype.DELETE:
+            write('deleted file mode %s\n' % oldmode)
+            pathb = '/dev/null'
+        else:
+            if oldmode != newmode:
+                write('old mode %s\nnew mode %s\n' % (oldmode, newmode))
+            if ctype == changetype.CHANGE:
+                pass
+            elif ctype == changetype.MOVE_HERE:
+                write('rename from %s\nrename to %s\n' % (oldpath, path))
+            elif ctype == changetype.COPY_HERE:
+                write('copy from %s\ncopy to %s\n' % (oldpath, path))
+            else:
+                raise error.Abort(_('unsupported change type %s: %s')
+                                  % (ctype, path))
+
+        if c[r'hunks']:
+            write('--- %s\n+++ %s\n' % (patha, pathb))
+        for h in c[r'hunks']:
+            oldoff = int(h[r'oldOffset'])
+            oldlen = int(h[r'oldLength'])
+            newoff = int(h[r'newOffset'])
+            newlen = int(h[r'newLength'])
+            write('@@ -%d,%d +%d,%d @@\n' % (oldoff, oldlen, newoff, newlen))
+            write(encoding.unitolocal(h[r'corpus']))
+
+    # normalize the patch by trimming context to 3 lines
+    headers = patch.parsepatch(out.getvalue(), maxcontext=3)
+    out = util.stringio()
+    for header in headers:
+        header.write(out)
+        for hunk in header.hunks:
+            hunk.write(out)
+    return out.getvalue()
+
 def readpatch(repo, params, write, stack=False):
     """generate plain-text patch readable by 'hg import'
 
@@ -518,10 +611,8 @@
 
     # Generate patch for each drev
     for drev in drevs:
-        repo.ui.note(_('reading D%s\n') % drev[r'id'])
-
         diffid = max(int(v) for v in drev[r'diffs'])
-        body = callconduit(repo, 'differential.getrawdiff', {'diffID': diffid})
+        body = getpatchbodyfromdiff(diffs[str(diffid)])
         desc = getdescfromdrev(drev)
         header = '# HG changeset patch\n'