diff --git a/mercurial/bdiff.c b/mercurial/bdiff.c
index b35cf47286a6bf7fe6e7f63f1d56e1b5f96bf4b3_bWVyY3VyaWFsL2JkaWZmLmM=..eeac5e17924354c4afeb6001ed69a06805189520_bWVyY3VyaWFsL2JkaWZmLmM= 100644
--- a/mercurial/bdiff.c
+++ b/mercurial/bdiff.c
@@ -425,8 +425,51 @@
 	return result ? result : PyErr_NoMemory();
 }
 
+/*
+ * If allws != 0, remove all whitespace (' ', \t and \r). Otherwise,
+ * reduce whitespace sequences to a single space and trim remaining whitespace
+ * from end of lines.
+ */
+static PyObject *fixws(PyObject *self, PyObject *args)
+{
+	PyObject *s, *result = NULL;
+	char allws, c;
+	const char *r;
+	int i, rlen, wlen = 0;
+	char *w;
+
+	if (!PyArg_ParseTuple(args, "Sb:fixws", &s, &allws))
+		return NULL;
+	r = PyBytes_AsString(s);
+	rlen = PyBytes_Size(s);
+
+	w = (char *)malloc(rlen);
+	if (!w)
+		goto nomem;
+
+	for (i = 0; i != rlen; i++) {
+		c = r[i];
+		if (c == ' ' || c == '\t' || c == '\r') {
+			if (!allws && (wlen == 0 || w[wlen - 1] != ' '))
+				w[wlen++] = ' ';
+		} else if (c == '\n' && !allws
+			  && wlen > 0 && w[wlen - 1] == ' ') {
+			w[wlen - 1] = '\n';
+		} else {
+			w[wlen++] = c;
+		}
+	}
+
+	result = PyBytes_FromStringAndSize(w, wlen);
+
+nomem:
+	free(w);
+	return result ? result : PyErr_NoMemory();
+}
+
+
 static char mdiff_doc[] = "Efficient binary diff.";
 
 static PyMethodDef methods[] = {
 	{"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
 	{"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
@@ -428,8 +471,9 @@
 static char mdiff_doc[] = "Efficient binary diff.";
 
 static PyMethodDef methods[] = {
 	{"bdiff", bdiff, METH_VARARGS, "calculate a binary diff\n"},
 	{"blocks", blocks, METH_VARARGS, "find a list of matching lines\n"},
+	{"fixws", fixws, METH_VARARGS, "normalize diff whitespaces\n"},
 	{NULL, NULL}
 };
 
diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
index b35cf47286a6bf7fe6e7f63f1d56e1b5f96bf4b3_bWVyY3VyaWFsL21kaWZmLnB5..eeac5e17924354c4afeb6001ed69a06805189520_bWVyY3VyaWFsL21kaWZmLnB5 100644
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -67,5 +67,5 @@
 
 def wsclean(opts, text, blank=True):
     if opts.ignorews:
-        text = re.sub('[ \t\r]+', '', text)
+        text = bdiff.fixws(text, 1)
     elif opts.ignorewsamount:
@@ -71,6 +71,5 @@
     elif opts.ignorewsamount:
-        text = re.sub('[ \t\r]+', ' ', text)
-        text = text.replace(' \n', '\n')
+        text = bdiff.fixws(text, 0)
     if blank and opts.ignoreblanklines:
         text = re.sub('\n+', '\n', text).strip('\n')
     return text
diff --git a/mercurial/pure/bdiff.py b/mercurial/pure/bdiff.py
index b35cf47286a6bf7fe6e7f63f1d56e1b5f96bf4b3_bWVyY3VyaWFsL3B1cmUvYmRpZmYucHk=..eeac5e17924354c4afeb6001ed69a06805189520_bWVyY3VyaWFsL3B1cmUvYmRpZmYucHk= 100644
--- a/mercurial/pure/bdiff.py
+++ b/mercurial/pure/bdiff.py
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import struct, difflib
+import struct, difflib, re
 
 def splitnewlines(text):
     '''like str.splitlines, but only split on newlines.'''
@@ -78,3 +78,10 @@
     d = _normalizeblocks(an, bn, d)
     return [(i, i + n, j, j + n) for (i, j, n) in d]
 
+def fixws(text, allws):
+    if allws:
+        text = re.sub('[ \t\r]+', '', text)
+    else:
+        text = re.sub('[ \t\r]+', ' ', text)
+        text = text.replace(' \n', '\n')
+    return text
diff --git a/tests/test-bdiff.py b/tests/test-bdiff.py
index b35cf47286a6bf7fe6e7f63f1d56e1b5f96bf4b3_dGVzdHMvdGVzdC1iZGlmZi5weQ==..eeac5e17924354c4afeb6001ed69a06805189520_dGVzdHMvdGVzdC1iZGlmZi5weQ== 100644
--- a/tests/test-bdiff.py
+++ b/tests/test-bdiff.py
@@ -50,3 +50,17 @@
 showdiff("x\n\nx\n\nx\n\nx\n\nz\n", "x\n\nx\n\ny\n\nx\n\ny\n\nx\n\nz\n")
 
 print "done"
+
+def testfixws(a, b, allws):
+    c = bdiff.fixws(a, allws)
+    if c != b:
+        print "*** fixws", repr(a), repr(b), allws
+        print "got:"
+        print repr(c)
+
+testfixws(" \ta\r b\t\n", "ab\n", 1)
+testfixws(" \ta\r b\t\n", " a b\n", 0)
+testfixws("", "", 1)
+testfixws("", "", 0)
+
+print "done"
diff --git a/tests/test-bdiff.py.out b/tests/test-bdiff.py.out
index b35cf47286a6bf7fe6e7f63f1d56e1b5f96bf4b3_dGVzdHMvdGVzdC1iZGlmZi5weS5vdXQ=..eeac5e17924354c4afeb6001ed69a06805189520_dGVzdHMvdGVzdC1iZGlmZi5weS5vdXQ= 100644
--- a/tests/test-bdiff.py.out
+++ b/tests/test-bdiff.py.out
@@ -21,3 +21,4 @@
 6 6 'y\n\n'
 9 9 'y\n\n'
 done
+done