MaxSem has uploaded a new change for review.
https://gerrit.wikimedia.org/r/99541
Change subject: WIP: add new one-column diff mode
......................................................................
WIP: add new one-column diff mode
Change-Id: I203eefd80dc4e22f55b7fc22b835dfa1212ec02d
---
M config.m4
M php_wikidiff2.cpp
M php_wikidiff2.h
A wikidiff2-inline.cpp
A wikidiff2-inline.h
A wikidiff2-table.cpp
A wikidiff2-table.h
M wikidiff2.cpp
M wikidiff2.h
9 files changed, 312 insertions(+), 152 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/php/wikidiff2
refs/changes/41/99541/1
diff --git a/config.m4 b/config.m4
index 0904d50..cf7a12b 100644
--- a/config.m4
+++ b/config.m4
@@ -37,5 +37,5 @@
PHP_SUBST(WIKIDIFF2_SHARED_LIBADD)
AC_DEFINE(HAVE_WIKIDIFF2, 1, [ ])
export CXXFLAGS="-Wno-write-strings $CXXFLAGS"
- PHP_NEW_EXTENSION(wikidiff2, php_wikidiff2.cpp wikidiff2.cpp, $ext_shared)
+ PHP_NEW_EXTENSION(wikidiff2, php_wikidiff2.cpp wikidiff2.cpp
wikidiff2-table.cpp wikidiff2-inline.cpp, $ext_shared)
fi
diff --git a/php_wikidiff2.cpp b/php_wikidiff2.cpp
index 9ca7106..e3be0f1 100644
--- a/php_wikidiff2.cpp
+++ b/php_wikidiff2.cpp
@@ -9,11 +9,14 @@
#include "ext/standard/info.h"
#include "php_wikidiff2.h"
#include "wikidiff2.h"
+#include "wikidiff2-table.h"
+#include "wikidiff2-inline.h"
static int le_wikidiff2;
zend_function_entry wikidiff2_functions[] = {
- PHP_FE(wikidiff2_do_diff, NULL)
+ PHP_FE(wikidiff2_do_diff, NULL)
+ PHP_FE(wikidiff2_inline_diff, NULL)
{NULL, NULL, NULL}
};
@@ -26,11 +29,11 @@
wikidiff2_functions,
PHP_MINIT(wikidiff2),
PHP_MSHUTDOWN(wikidiff2),
- PHP_RINIT(wikidiff2),
+ PHP_RINIT(wikidiff2),
PHP_RSHUTDOWN(wikidiff2),
PHP_MINFO(wikidiff2),
#if ZEND_MODULE_API_NO >= 20010901
- "0.1",
+ "0.2",
#endif
STANDARD_MODULE_PROPERTIES
};
@@ -82,7 +85,7 @@
int text2_len;
long numContextLines;
- if (zend_parse_parameters(argc TSRMLS_CC, "ssl", &text1, &text1_len,
&text2,
+ if (zend_parse_parameters(argc TSRMLS_CC, "ssl", &text1, &text1_len,
&text2,
&text2_len, &numContextLines) == FAILURE)
{
return;
@@ -90,7 +93,7 @@
try {
- Wikidiff2 wikidiff2;
+ Wikidiff2_table wikidiff2;
Wikidiff2::String text1String(text1, text1_len);
Wikidiff2::String text2String(text2, text2_len);
const Wikidiff2::String & ret = wikidiff2.execute(text1String,
text2String, numContextLines);
@@ -101,6 +104,41 @@
zend_error(E_WARNING, "Unknown exception in
wikidiff2_do_diff().");
}
}
+
+/* {{{ proto string wikidiff2_inline_diff(string text1, string text2, int
numContextLines)
+ *
+ * Warning: the input text must be valid UTF-8! Do not pass user input directly
+ * to this function.
+ */
+PHP_FUNCTION(wikidiff2_inline_diff)
+{
+ char *text1 = NULL;
+ char *text2 = NULL;
+ int argc = ZEND_NUM_ARGS();
+ int text1_len;
+ int text2_len;
+ long numContextLines;
+
+ if (zend_parse_parameters(argc TSRMLS_CC, "ssl", &text1, &text1_len,
&text2,
+ &text2_len, &numContextLines) == FAILURE)
+ {
+ return;
+ }
+
+
+ try {
+ Wikidiff2_inline wikidiff2;
+ Wikidiff2::String text1String(text1, text1_len);
+ Wikidiff2::String text2String(text2, text2_len);
+ const Wikidiff2::String& ret = wikidiff2.execute(text1String,
text2String, numContextLines);
+ RETURN_STRINGL( const_cast<char*>(ret.data()), ret.size(), 1);
+ } catch (std::bad_alloc &e) {
+ zend_error(E_WARNING, "Out of memory in
wikidiff2_inline_diff().");
+ } catch (...) {
+ zend_error(E_WARNING, "Unknown exception in
wikidiff2_inline_diff().");
+ }
+}
+
/* }}} */
diff --git a/php_wikidiff2.h b/php_wikidiff2.h
index ec45e39..973233f 100644
--- a/php_wikidiff2.h
+++ b/php_wikidiff2.h
@@ -43,6 +43,7 @@
PHP_MINFO_FUNCTION(wikidiff2);
PHP_FUNCTION(wikidiff2_do_diff);
+PHP_FUNCTION(wikidiff2_inline_diff);
diff --git a/wikidiff2-inline.cpp b/wikidiff2-inline.cpp
new file mode 100644
index 0000000..e9de8e5
--- /dev/null
+++ b/wikidiff2-inline.cpp
@@ -0,0 +1,75 @@
+#include "wikidiff2-inline.h"
+
+void Wikidiff2_inline::printAdd(const String& line)
+{
+ printWrappedLine("<div><ins>", line, "</ins></div>");
+}
+
+void Wikidiff2_inline::printDelete(const String& line)
+{
+ printWrappedLine("<div><del>", line, "</del></div>");
+}
+
+void Wikidiff2_inline::printWordDiff(const String& text1, const String& text2)
+{
+ WordVector words1, words2;
+
+ explodeWords(text1, words1);
+ explodeWords(text2, words2);
+ WordDiff worddiff(words1, words2);
+ String word;
+
+ for (unsigned i = 0; i < worddiff.size(); ++i) {
+ DiffOp<Word> & op = worddiff[i];
+ int n, j;
+ if (op.op == DiffOp<Word>::copy) {
+ n = op.from.size();
+ for (j=0; j<n; j++) {
+ op.from[j]->get_whole(word);
+ printText(word);
+ }
+ } else if (op.op == DiffOp<Word>::del) {
+ n = op.from.size();
+ result += "<del>";
+ for (j=0; j<n; j++) {
+ op.from[j]->get_whole(word);
+ printText(word);
+ }
+ result += "</del>";
+ } else if (op.op == DiffOp<Word>::add) {
+ n = op.to.size();
+ result += "<ins>";
+ for (j=0; j<n; j++) {
+ op.to[j]->get_whole(word);
+ printText(word);
+ }
+ result += "</ins>";
+ } else if (op.op == DiffOp<Word>::change) {
+ }
+ }
+}
+
+void Wikidiff2_inline::printBlockHeader(int leftLine, int rightLine)
+{
+ char buf[256]; // should be plenty
+ snprintf(buf, sizeof(buf),
+ "<div class=\"mw-inline-diff-header\"><!-- LINES %u,%u
--></div>",
+ leftLine, rightLine);
+ result += buf;
+}
+
+void Wikidiff2_inline::printContext(const String & input)
+{
+ printWrappedLine("<div>", input, "</div>");
+}
+
+void Wikidiff2_inline::printWrappedLine(const char* pre, const String& line,
const char* post)
+{
+ result += pre;
+ if (line.empty()) {
+ result += " ";
+ } else {
+ printText(line);
+ }
+ result += post;
+}
diff --git a/wikidiff2-inline.h b/wikidiff2-inline.h
new file mode 100644
index 0000000..153c199
--- /dev/null
+++ b/wikidiff2-inline.h
@@ -0,0 +1,18 @@
+#ifndef WIKIDIFF2_INLINE_H
+#define WIKIDIFF2_INLINE_H
+
+#include "wikidiff2.h"
+
+class Wikidiff2_inline: public Wikidiff2 {
+ public:
+ protected:
+ void printAdd(const String& line);
+ void printDelete(const String& line);
+ void printWordDiff(const String& text1, const String& text2);
+ void printBlockHeader(int leftLine, int rightLine);
+ void printContext(const String& input);
+
+ void printWrappedLine(const char* pre, const String& line,
const char* post);
+};
+
+#endif
diff --git a/wikidiff2-table.cpp b/wikidiff2-table.cpp
new file mode 100644
index 0000000..573beab
--- /dev/null
+++ b/wikidiff2-table.cpp
@@ -0,0 +1,124 @@
+#include <stdio.h>
+//#include <string.h>
+#include "wikidiff2.h"
+#include "wikidiff2-table.h"
+
+void Wikidiff2_table::printAdd(const String & line)
+{
+ result += "<tr>\n"
+ " <td colspan=\"2\" class=\"diff-empty\"> </td>\n"
+ " <td class=\"diff-marker\">+</td>\n"
+ " <td class=\"diff-addedline\">";
+ printTextWithDiv(line);
+ result += "</td>\n</tr>\n";
+}
+
+void Wikidiff2_table::printDelete(const String & line)
+{
+ result += "<tr>\n"
+ " <td class=\"diff-marker\">−</td>\n"
+ " <td class=\"diff-deletedline\">";
+ printTextWithDiv(line);
+ result += "</td>\n"
+ " <td colspan=\"2\" class=\"diff-empty\"> </td>\n"
+ "</tr>\n";
+}
+
+void Wikidiff2_table::printWordDiff(const String & text1, const String & text2)
+{
+ WordVector words1, words2;
+
+ explodeWords(text1, words1);
+ explodeWords(text2, words2);
+ WordDiff worddiff(words1, words2);
+
+ //debugPrintWordDiff(worddiff);
+
+ // print twice; first for left side, then for right side
+ result += "<tr>\n"
+ " <td class=\"diff-marker\">−</td>\n"
+ " <td class=\"diff-deletedline\"><div>";
+ printWordDiffSide(worddiff, false);
+ result += "</div></td>\n"
+ " <td class=\"diff-marker\">+</td>\n"
+ " <td class=\"diff-addedline\"><div>";
+ printWordDiffSide(worddiff, true);
+ result += "</div></td>\n"
+ "</tr>\n";
+}
+
+void Wikidiff2_table::printWordDiffSide(WordDiff &worddiff, bool added)
+{
+ String word;
+ for (unsigned i = 0; i < worddiff.size(); ++i) {
+ DiffOp<Word> & op = worddiff[i];
+ int n, j;
+ if (op.op == DiffOp<Word>::copy) {
+ n = op.from.size();
+ if (added) {
+ for (j=0; j<n; j++) {
+ op.to[j]->get_whole(word);
+ printText(word);
+ }
+ } else {
+ for (j=0; j<n; j++) {
+ op.from[j]->get_whole(word);
+ printText(word);
+ }
+ }
+ } else if (!added && (op.op == DiffOp<Word>::del || op.op ==
DiffOp<Word>::change)) {
+ n = op.from.size();
+ result += "<del class=\"diffchange
diffchange-inline\">";
+ for (j=0; j<n; j++) {
+ op.from[j]->get_whole(word);
+ printText(word);
+ }
+ result += "</del>";
+ } else if (added && (op.op == DiffOp<Word>::add || op.op ==
DiffOp<Word>::change)) {
+ n = op.to.size();
+ result += "<ins class=\"diffchange
diffchange-inline\">";
+ for (j=0; j<n; j++) {
+ op.to[j]->get_whole(word);
+ printText(word);
+ }
+ result += "</ins>";
+ }
+ }
+}
+
+void Wikidiff2_table::printTextWithDiv(const String & input)
+{
+ // Wrap string in a <div> if it's not empty
+ if (input.size() > 0) {
+ result.append("<div>");
+ printText(input);
+ result.append("</div>");
+ }
+}
+
+void Wikidiff2_table::printBlockHeader(int leftLine, int rightLine)
+{
+ char buf[256]; // should be plenty
+ snprintf(buf, sizeof(buf),
+ "<tr>\n"
+ " <td colspan=\"2\" class=\"diff-lineno\"><!--LINE
%u--></td>\n"
+ " <td colspan=\"2\" class=\"diff-lineno\"><!--LINE
%u--></td>\n"
+ "</tr>\n",
+ leftLine, rightLine);
+ result += buf;
+}
+
+void Wikidiff2_table::printContext(const String & input)
+{
+ result +=
+ "<tr>\n"
+ " <td class=\"diff-marker\"> </td>\n"
+ " <td class=\"diff-context\">";
+ printTextWithDiv(input);
+ result +=
+ "</td>\n"
+ " <td class=\"diff-marker\"> </td>\n"
+ " <td class=\"diff-context\">";
+ printTextWithDiv(input);
+ result += "</td>\n</tr>\n";
+}
diff --git a/wikidiff2-table.h b/wikidiff2-table.h
new file mode 100644
index 0000000..463d49e
--- /dev/null
+++ b/wikidiff2-table.h
@@ -0,0 +1,19 @@
+#ifndef WIKIDIFF2_TABLE_H
+#define WIKIDIFF2_TABLE_H
+
+#include "wikidiff2.h"
+
+class Wikidiff2_table: public Wikidiff2 {
+ public:
+ protected:
+ void printAdd(const String& line);
+ void printDelete(const String& line);
+ void printWordDiff(const String& text1, const String & text2);
+ void printTextWithDiv(const String& input);
+ void printBlockHeader(int leftLine, int rightLine);
+ void printContext(const String& input);
+
+ void printWordDiffSide(WordDiff& worddiff, bool added);
+};
+
+#endif
diff --git a/wikidiff2.cpp b/wikidiff2.cpp
index 857ee37..6eedd24 100644
--- a/wikidiff2.cpp
+++ b/wikidiff2.cpp
@@ -1,7 +1,7 @@
/**
- * Diff formatter, based on code by Steinar H. Gunderson, converted to work
with the
+ * Diff formatter, based on code by Steinar H. Gunderson, converted to work
with the
* Dairiki diff engine by Tim Starling
- *
+ *
* GPL.
*/
@@ -12,12 +12,13 @@
#include <thai/thwchar.h>
#include <thai/thbrk.h>
-void Wikidiff2::diffLines(const StringVector & lines1, const StringVector &
lines2,
+
+void Wikidiff2::diffLines(const StringVector & lines1, const StringVector &
lines2,
int numContextLines)
{
// first do line-level diff
StringDiff linediff(lines1, lines2);
-
+
int ctx = 0;
int from_index = 1, to_index = 1;
@@ -29,13 +30,9 @@
int n, j, n1, n2;
// Line 1 changed, show heading with no leading context
if (linediff[i].op != DiffOp<String>::copy && i == 0) {
- result +=
- "<tr>\n"
- " <td colspan=\"2\"
class=\"diff-lineno\"><!--LINE 1--></td>\n"
- " <td colspan=\"2\"
class=\"diff-lineno\"><!--LINE 1--></td>\n"
- "</tr>\n";
+ printBlockHeader(1, 1);
}
-
+
switch (linediff[i].op) {
case DiffOp<String>::add:
// inserted lines
@@ -60,29 +57,10 @@
if ((i != 0 && j < numContextLines)
/*trailing*/
|| (i !=
linediff.size() - 1 && j >= n - numContextLines)) /*leading*/ {
if (showLineNumber) {
- // Print Line: heading
- char buf[256]; //
should be plenty
- snprintf(buf, 256,
- "<tr>\n"
- " <td
colspan=\"2\" class=\"diff-lineno\"><!--LINE %u--></td>\n"
- " <td
colspan=\"2\" class=\"diff-lineno\"><!--LINE %u--></td>\n"
- "</tr>\n",
- from_index,
to_index);
- result += buf;
+
printBlockHeader(from_index, to_index);
showLineNumber = false;
}
- // Print context
- result +=
- "<tr>\n"
- " <td
class=\"diff-marker\"> </td>\n"
- " <td
class=\"diff-context\">";
-
printTextWithDiv(*linediff[i].from[j]);
- result +=
- "</td>\n"
- " <td
class=\"diff-marker\"> </td>\n"
- " <td
class=\"diff-context\">";
-
printTextWithDiv(*linediff[i].from[j]);
- result += "</td>\n</tr>\n";
+
printContext(*linediff[i].from[j]);
} else {
showLineNumber = true;
}
@@ -116,54 +94,10 @@
}
}
-void Wikidiff2::printAdd(const String & line)
-{
- result += "<tr>\n"
- " <td colspan=\"2\" class=\"diff-empty\"> </td>\n"
- " <td class=\"diff-marker\">+</td>\n"
- " <td class=\"diff-addedline\">";
- printTextWithDiv(line);
- result += "</td>\n</tr>\n";
-}
-
-void Wikidiff2::printDelete(const String & line)
-{
- result += "<tr>\n"
- " <td class=\"diff-marker\">−</td>\n"
- " <td class=\"diff-deletedline\">";
- printTextWithDiv(line);
- result += "</td>\n"
- " <td colspan=\"2\" class=\"diff-empty\"> </td>\n"
- "</tr>\n";
-}
-
-void Wikidiff2::printWordDiff(const String & text1, const String & text2)
-{
- WordVector words1, words2;
-
- explodeWords(text1, words1);
- explodeWords(text2, words2);
- WordDiff worddiff(words1, words2);
-
- //debugPrintWordDiff(worddiff);
-
- // print twice; first for left side, then for right side
- result += "<tr>\n"
- " <td class=\"diff-marker\">−</td>\n"
- " <td class=\"diff-deletedline\"><div>";
- printWordDiffSide(worddiff, false);
- result += "</div></td>\n"
- " <td class=\"diff-marker\">+</td>\n"
- " <td class=\"diff-addedline\"><div>";
- printWordDiffSide(worddiff, true);
- result += "</div></td>\n"
- "</tr>\n";
-}
-
void Wikidiff2::debugPrintWordDiff(WordDiff & worddiff)
{
for (unsigned i = 0; i < worddiff.size(); ++i) {
- DiffOp<Word> & op = worddiff[i];
+ DiffOp<Word> & op = worddiff[i];
switch (op.op) {
case DiffOp<Word>::copy:
result += "Copy\n";
@@ -205,55 +139,6 @@
}
}
-void Wikidiff2::printWordDiffSide(WordDiff &worddiff, bool added)
-{
- String word;
- for (unsigned i = 0; i < worddiff.size(); ++i) {
- DiffOp<Word> & op = worddiff[i];
- int n, j;
- if (op.op == DiffOp<Word>::copy) {
- n = op.from.size();
- if (added) {
- for (j=0; j<n; j++) {
- op.to[j]->get_whole(word);
- printText(word);
- }
- } else {
- for (j=0; j<n; j++) {
- op.from[j]->get_whole(word);
- printText(word);
- }
- }
- } else if (!added && (op.op == DiffOp<Word>::del || op.op ==
DiffOp<Word>::change)) {
- n = op.from.size();
- result += "<del class=\"diffchange
diffchange-inline\">";
- for (j=0; j<n; j++) {
- op.from[j]->get_whole(word);
- printText(word);
- }
- result += "</del>";
- } else if (added && (op.op == DiffOp<Word>::add || op.op ==
DiffOp<Word>::change)) {
- n = op.to.size();
- result += "<ins class=\"diffchange
diffchange-inline\">";
- for (j=0; j<n; j++) {
- op.to[j]->get_whole(word);
- printText(word);
- }
- result += "</ins>";
- }
- }
-}
-
-void Wikidiff2::printTextWithDiv(const String & input)
-{
- // Wrap string in a <div> if it's not empty
- if (input.size() > 0) {
- result.append("<div>");
- printText(input);
- result.append("</div>");
- }
-}
-
void Wikidiff2::printText(const String & input)
{
size_t start = 0;
@@ -283,7 +168,7 @@
// Weak UTF-8 decoder
// Will return garbage on invalid input (overshort sequences, overlong
sequences, etc.)
-int Wikidiff2::nextUtf8Char(String::const_iterator & p, String::const_iterator
& charStart,
+int Wikidiff2::nextUtf8Char(String::const_iterator & p, String::const_iterator
& charStart,
String::const_iterator end)
{
int c = 0;
@@ -326,11 +211,11 @@
// Split a string into words
//
-// TODO: I think the best way to do this would be to use ICU BreakIterator
-// instead of libthai + DIY. Basically you'd run BreakIterators from several
+// TODO: I think the best way to do this would be to use ICU BreakIterator
+// instead of libthai + DIY. Basically you'd run BreakIterators from several
// different locales (en, th, ja) and merge the results, i.e. if a break occurs
-// in any locale at a given position, split the string. I don't know if the
-// quality of the Thai dictionary in ICU matches the one in libthai, we would
+// in any locale at a given position, split the string. I don't know if the
+// quality of the Thai dictionary in ICU matches the one in libthai, we would
// have to check this somehow.
void Wikidiff2::explodeWords(const String & text, WordVector &words)
{
@@ -342,7 +227,7 @@
// Decode the UTF-8 in the string.
// * Save the character sizes (in bytes)
- // * Convert the string to TIS-620, which is the internal character set
of libthai.
+ // * Convert the string to TIS-620, which is the internal character set
of libthai.
// * Save the character offsets of any break positions (same format as
libthai).
String tisText, charSizes;
@@ -385,13 +270,13 @@
IntVector thaiBreakPositions;
tisText += '\0';
thaiBreakPositions.resize(tisText.size());
- int numBreaks = th_brk((const thchar_t*)(tisText.data()),
+ int numBreaks = th_brk((const thchar_t*)(tisText.data()),
&thaiBreakPositions[0],
thaiBreakPositions.size());
thaiBreakPositions.resize(numBreaks);
breaks.insert(thaiBreakPositions.begin(),
thaiBreakPositions.end());
}
- // Add a fake end-of-string character and have a break on it, so that
the
+ // Add a fake end-of-string character and have a break on it, so that
the
// last word gets added without special handling
breaks.insert(charSizes.size());
charSizes += (char)0;
@@ -431,7 +316,7 @@
while (ptr != text.end()) {
String::const_iterator ptr2 = std::find(ptr, text.end(), '\n');
lines.push_back(String(ptr, ptr2));
-
+
ptr = ptr2;
if (ptr != text.end()) {
++ptr;
@@ -444,7 +329,7 @@
// Allocate some result space to avoid excessive copying
result.clear();
result.reserve(text1.size() + text2.size() + 10000);
-
+
// Split input strings into lines
StringVector lines1;
StringVector lines2;
@@ -457,4 +342,3 @@
// Return a reference to the result buffer
return result;
}
-
diff --git a/wikidiff2.h b/wikidiff2.h
index d714514..c8fea3d 100644
--- a/wikidiff2.h
+++ b/wikidiff2.h
@@ -35,26 +35,27 @@
protected:
String result;
- void diffLines(const StringVector & lines1, const StringVector
& lines2,
+ virtual void diffLines(const StringVector & lines1, const
StringVector & lines2,
int numContextLines);
- void printAdd(const String & line);
- void printDelete(const String & line);
- void printWordDiff(const String & text1, const String & text2);
- void printWordDiffSide(WordDiff &worddiff, bool added);
- void printTextWithDiv(const String & input);
+ virtual void printAdd(const String & line) = 0;
+ virtual void printDelete(const String & line) = 0;
+ virtual void printWordDiff(const String & text1, const String &
text2) = 0;
+ virtual void printBlockHeader(int leftLine, int rightLine) = 0;
+ virtual void printContext(const String & input) = 0;
+
void printText(const String & input);
inline bool isLetter(int ch);
inline bool isSpace(int ch);
void debugPrintWordDiff(WordDiff & worddiff);
- int nextUtf8Char(String::const_iterator & p,
String::const_iterator & charStart,
+ int nextUtf8Char(String::const_iterator & p,
String::const_iterator & charStart,
String::const_iterator end);
void explodeWords(const String & text, WordVector &tokens);
void explodeLines(const String & text, StringVector &lines);
};
-bool Wikidiff2::isLetter(int ch)
+inline bool Wikidiff2::isLetter(int ch)
{
// Standard alphanumeric
if ((ch >= '0' && ch <= '9') ||
@@ -73,12 +74,12 @@
return true;
}
-bool Wikidiff2::isSpace(int ch)
+inline bool Wikidiff2::isSpace(int ch)
{
return ch == ' ' || ch == '\t';
}
-const Wikidiff2::String & Wikidiff2::getResult() const
+inline const Wikidiff2::String & Wikidiff2::getResult() const
{
return result;
}
--
To view, visit https://gerrit.wikimedia.org/r/99541
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I203eefd80dc4e22f55b7fc22b835dfa1212ec02d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/php/wikidiff2
Gerrit-Branch: master
Gerrit-Owner: MaxSem <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits