Petr Onderka has submitted this change and it was merged.

Change subject: added support for IPv6 anonymous users
......................................................................


added support for IPv6 anonymous users

Change-Id: I2af5ef6d84b7cf98d3fcfcbe8391dcbd976f51b5
---
M CMakeLists.txt
A DumpObjects/DumpIpV6User.cpp
A DumpObjects/DumpIpV6User.h
M DumpObjects/DumpNamedUser.cpp
M DumpObjects/DumpNamedUser.h
M DumpObjects/DumpTraits.h
M DumpObjects/DumpUser.cpp
M Incremental dumps.vcxproj
M Objects/IpV4User.cpp
M Objects/IpV4User.h
A Objects/IpV6User.cpp
A Objects/IpV6User.h
A Objects/NamedUser.cpp
A Objects/NamedUser.h
M Objects/Revision.h
M Objects/User.cpp
M Objects/User.h
M StringHelpers.cpp
M StringHelpers.h
M XmlContributorProcessor.cpp
20 files changed, 336 insertions(+), 61 deletions(-)

Approvals:
  Petr Onderka: Verified; Looks good to me, approved



diff --git a/CMakeLists.txt b/CMakeLists.txt
index d3de7a4..c1f8af5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,7 @@
   Dump.cpp
   DumpException.cpp
   DumpObjects/DumpIpV4User.cpp
+  DumpObjects/DumpIpV6User.cpp
   DumpObjects/DumpNamedUser.cpp
   DumpObjects/DumpObject.cpp
   DumpObjects/FileHeader.cpp
@@ -16,6 +17,8 @@
   DumpObjects/DumpRevision.cpp
   DumpObjects/DumpUser.cpp
   Objects/IpV4User.cpp
+  Objects/IpV6User.cpp
+  Objects/NamedUser.cpp
   main.cpp
   DumpObjects/Offset.cpp
   Objects/Page.cpp
@@ -43,6 +46,7 @@
 set(SOURCES_files_ClInclude
   DumpException.h
   DumpObjects/DumpIpV4User.h
+  DumpObjects/DumpIpV6User.h
   DumpObjects/DumpNamedUser.h
   DumpObjects/DumpObjectKind.h
   DumpObjects/DumpPage.h
@@ -61,6 +65,8 @@
   Indexes/IndexNode.h
   DumpObjects/Offset.h
   Objects/IpV4User.h
+  Objects/IpV6User.h
+  Objects/NamedUser.h
   Objects/Page.h
   Objects/Revision.h
   Indexes/Iterators/IndexIterator.h
diff --git a/DumpObjects/DumpIpV6User.cpp b/DumpObjects/DumpIpV6User.cpp
new file mode 100644
index 0000000..fd8a280
--- /dev/null
+++ b/DumpObjects/DumpIpV6User.cpp
@@ -0,0 +1,27 @@
+#include "DumpIpV6User.h"
+
+void DumpIpV6User::WriteInternal()
+{
+    WriteValue(user->Address);
+}
+
+unique_ptr<DumpUser> DumpIpV6User::Read(istream &stream)
+{
+    auto address = DumpTraits<std::array<std::uint16_t, 8>>::Read(stream);
+
+    return unique_ptr<DumpUser>(new 
DumpIpV6User(std::make_shared<IpV6User>(address)));
+}
+
+DumpIpV6User::DumpIpV6User(shared_ptr<IpV6User> user)
+    : user(user)
+{}
+
+shared_ptr<User> DumpIpV6User::GetUser() const
+{
+    return user;
+}
+
+uint32_t DumpIpV6User::NewLength()
+{
+    return ValueSize(user->Address);
+}
\ No newline at end of file
diff --git a/DumpObjects/DumpIpV6User.h b/DumpObjects/DumpIpV6User.h
new file mode 100644
index 0000000..c99c6a8
--- /dev/null
+++ b/DumpObjects/DumpIpV6User.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "DumpUser.h"
+#include "../Objects/IpV6User.h"
+
+class DumpIpV6User : public DumpUser
+{
+protected:
+    virtual void WriteInternal();
+public:
+    static unique_ptr<DumpUser> Read(istream &stream);
+
+    shared_ptr<IpV6User> user;
+
+    DumpIpV6User(shared_ptr<IpV6User> user);
+
+    virtual shared_ptr<User> GetUser() const override;
+    virtual uint32_t NewLength() override;
+};
\ No newline at end of file
diff --git a/DumpObjects/DumpNamedUser.cpp b/DumpObjects/DumpNamedUser.cpp
index 70eb884..29bb8d5 100644
--- a/DumpObjects/DumpNamedUser.cpp
+++ b/DumpObjects/DumpNamedUser.cpp
@@ -1,4 +1,5 @@
 #include "DumpNamedUser.h"
+#include "../Objects/NamedUser.h"
 
 void DumpNamedUser::WriteInternal()
 {
@@ -8,13 +9,13 @@
 
 unique_ptr<DumpUser> DumpNamedUser::Read(istream &stream)
 {
-    int userId = DumpTraits<uint32_t>::Read(stream);
+    uint32_t userId = DumpTraits<uint32_t>::Read(stream);
     string userName = DumpTraits<string>::Read(stream);
 
-    return unique_ptr<DumpUser>(new DumpNamedUser(shared_ptr<User>(new 
User(userId, userName))));
+    return unique_ptr<DumpUser>(new 
DumpNamedUser(std::make_shared<NamedUser>(userId, userName)));
 }
 
-DumpNamedUser::DumpNamedUser(shared_ptr<User> user)
+DumpNamedUser::DumpNamedUser(shared_ptr<NamedUser> user)
     : user(user)
 {}
 
diff --git a/DumpObjects/DumpNamedUser.h b/DumpObjects/DumpNamedUser.h
index e547ae0..c349d24 100644
--- a/DumpObjects/DumpNamedUser.h
+++ b/DumpObjects/DumpNamedUser.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "DumpUser.h"
+#include "../Objects/NamedUser.h"
 
 class DumpNamedUser : public DumpUser
 {
@@ -9,9 +10,9 @@
 public:
     static unique_ptr<DumpUser> Read(istream &stream);
 
-    shared_ptr<User> user;
+    shared_ptr<NamedUser> user;
 
-    DumpNamedUser(shared_ptr<User> user);
+    DumpNamedUser(shared_ptr<NamedUser> user);
 
     virtual shared_ptr<User> GetUser() const override;
     virtual uint32_t NewLength() override;
diff --git a/DumpObjects/DumpTraits.h b/DumpObjects/DumpTraits.h
index a4c74a7..2bfae8f 100644
--- a/DumpObjects/DumpTraits.h
+++ b/DumpObjects/DumpTraits.h
@@ -5,6 +5,7 @@
 #include <memory>
 #include <string>
 #include <iostream>
+#include <array>
 #include <vector>
 #include "../DumpException.h"
 
@@ -231,7 +232,7 @@
         return result;
     }
 
-    static void Write(ostream &stream, const vector<T> value)
+    static void Write(ostream &stream, const vector<T> &value)
     {
         auto length = value.size();
 
@@ -246,7 +247,7 @@
         }
     }
 
-    static uint32_t DumpSize(const vector<T> value)
+    static uint32_t DumpSize(const vector<T> &value)
     {
         uint32_t size = DumpTraits<uint16_t>::DumpSize(value.size());
 
@@ -257,4 +258,41 @@
 
         return size;
     }
+};
+
+template<typename T, size_t N>
+class DumpTraits<std::array<T, N>>
+{
+public:
+    static std::array<T, N> Read(istream &stream)
+    {
+        std::array<T, N> result;
+
+        for (int i = 0; i < N; i++)
+        {
+            result[i] = DumpTraits<T>::Read(stream);
+        }
+
+        return result;
+    }
+
+    static void Write(ostream &stream, const std::array<T, N> &value)
+    {
+        for (T item : value)
+        {
+            DumpTraits<T>::Write(stream, item);
+        }
+    }
+
+    static uint32_t DumpSize(const std::array<T, N> &value)
+    {
+        uint32_t size = 0;
+
+        for (T item : value)
+        {
+            size += DumpTraits<T>::DumpSize(item);
+        }
+
+        return size;
+    }
 };
\ No newline at end of file
diff --git a/DumpObjects/DumpUser.cpp b/DumpObjects/DumpUser.cpp
index 847cc66..64aa042 100644
--- a/DumpObjects/DumpUser.cpp
+++ b/DumpObjects/DumpUser.cpp
@@ -1,19 +1,24 @@
-#include "DumpUser.h"
 #include "DumpIpV4User.h"
+#include "DumpIpV6User.h"
 #include "DumpNamedUser.h"
 
 using std::dynamic_pointer_cast;
 
 unique_ptr<DumpUser> DumpUser::Create(shared_ptr<User> user)
 {
-    DumpUser *result;
     auto ipV4User = dynamic_pointer_cast<IpV4User>(user);
     if (ipV4User != nullptr)
-        result = new DumpIpV4User(ipV4User);
-    else
-        result = new DumpNamedUser(user);
+        return unique_ptr<DumpUser>(new DumpIpV4User(ipV4User));
+    
+    auto ipV6User = dynamic_pointer_cast<IpV6User>(user);
+    if (ipV6User != nullptr)
+        return unique_ptr<DumpUser>(new DumpIpV6User(ipV6User));
 
-    return unique_ptr<DumpUser>(result);
+    auto namedUser = dynamic_pointer_cast<NamedUser>(user);
+    if (namedUser != nullptr)
+        return unique_ptr<DumpUser>(new DumpNamedUser(namedUser));
+
+    throw DumpException();
 }
 
 unique_ptr<DumpUser> DumpUser::Read(RevisionFlags flags, istream &stream)
diff --git a/Incremental dumps.vcxproj b/Incremental dumps.vcxproj
index efcf938..7bd5c80 100644
--- a/Incremental dumps.vcxproj
+++ b/Incremental dumps.vcxproj
@@ -83,11 +83,13 @@
     <ClCompile Include="Dump.cpp" />
     <ClCompile Include="DumpException.cpp" />
     <ClCompile Include="DumpObjects\DumpIpV4User.cpp" />
+    <ClCompile Include="DumpObjects\DumpIpV6User.cpp" />
     <ClCompile Include="DumpObjects\DumpNamedUser.cpp" />
     <ClCompile Include="DumpObjects\DumpObject.cpp" />
     <ClCompile Include="DumpObjects\FileHeader.cpp" />
     <ClInclude Include="DumpException.h" />
     <ClInclude Include="DumpObjects\DumpIpV4User.h" />
+    <ClInclude Include="DumpObjects\DumpIpV6User.h" />
     <ClInclude Include="DumpObjects\DumpNamedUser.h" />
     <ClInclude Include="DumpObjects\DumpObjectKind.h" />
     <ClInclude Include="DumpObjects\DumpPage.h" />
@@ -112,6 +114,8 @@
     <ClCompile Include="Objects\IpV4User.cpp" />
     <ClCompile Include="main.cpp" />
     <ClCompile Include="DumpObjects\Offset.cpp" />
+    <ClCompile Include="Objects\IpV6User.cpp" />
+    <ClCompile Include="Objects\NamedUser.cpp" />
     <ClCompile Include="Objects\Page.cpp" />
     <ClCompile Include="Objects\Revision.cpp" />
     <ClCompile Include="SevenZip.cpp" />
@@ -141,6 +145,8 @@
     <ClInclude Include="Indexes\IndexNode.h" />
     <ClInclude Include="DumpObjects\Offset.h" />
     <ClInclude Include="Objects\IpV4User.h" />
+    <ClInclude Include="Objects\IpV6User.h" />
+    <ClInclude Include="Objects\NamedUser.h" />
     <ClInclude Include="Objects\Page.h" />
     <ClInclude Include="Objects\Revision.h" />
     <ClInclude Include="Indexes\Iterators\IndexIterator.h" />
@@ -175,4 +181,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/Objects/IpV4User.cpp b/Objects/IpV4User.cpp
index b6af351..e32395f 100644
--- a/Objects/IpV4User.cpp
+++ b/Objects/IpV4User.cpp
@@ -7,9 +7,11 @@
 using std::stoi;
 using std::ostringstream;
 
-uint32_t IpV4User::ParseAddress(string address)
+uint32_t IpV4User::TryParseAddress(string address, bool &success)
 {
-    vector<string> stringParts = split(address, '.');
+    success = false;
+
+    std::vector<std::string> stringParts = split(address, '.');
     if (stringParts.size() != 4)
         return 0;
 
@@ -17,12 +19,25 @@
 
     for (int i = 0; i < 4; i++)
     {
-        int intPart = stoi(stringParts[i]);
-        if (intPart > 255 || intPart < 0)
+        bool success;
+        long intPart = tryParseLong(stringParts[i], success);
+        if (!success || intPart > 255 || intPart < 0)
             return 0;
         
         result |= (uint32_t)(uint8_t)intPart << (8 * i);
     }
+
+    success = true;
+    return result;
+}
+
+uint32_t IpV4User::ParseAddress(string address)
+{
+    bool success;
+    uint32_t result = TryParseAddress(address, success);
+
+    if (!success)
+        throw DumpException();
 
     return result;
 }
@@ -45,17 +60,11 @@
 
 IpV4User::IpV4User(string stringAddress)
     : User(0, stringAddress), Address(ParseAddress(stringAddress))
-{
-    if (Address == 0)
-        throw new DumpException();
-}
+{}
 
 IpV4User::IpV4User(string stringAddress, uint32_t parsedAddress)
     :  User(0, stringAddress), Address(parsedAddress)
-{
-    if (Address == 0)
-        throw new DumpException();
-}
+{}
 
 IpV4User::IpV4User(uint32_t parsedAddress)
     : User(0, AddressToString(parsedAddress)), Address(parsedAddress)
diff --git a/Objects/IpV4User.h b/Objects/IpV4User.h
index b7a234d..e9af543 100644
--- a/Objects/IpV4User.h
+++ b/Objects/IpV4User.h
@@ -5,6 +5,7 @@
 class IpV4User : public User
 {
 public:
+    static uint32_t TryParseAddress(string address, bool &success);
     static uint32_t ParseAddress(string address);
     static string AddressToString(uint32_t address);
 
diff --git a/Objects/IpV6User.cpp b/Objects/IpV6User.cpp
new file mode 100644
index 0000000..3d8c7bf
--- /dev/null
+++ b/Objects/IpV6User.cpp
@@ -0,0 +1,78 @@
+#include "IpV6User.h"
+#include <sstream>
+#include "../StringHelpers.h"
+#include "../DumpException.h"
+#include "Revision.h"
+
+using std::stoi;
+using std::ostringstream;
+
+std::array<uint16_t, 8> IpV6User::TryParseAddress(string address, bool 
&success)
+{
+    success = false;
+
+    std::vector<std::string> stringParts = split(address, ':');
+    if (stringParts.size() != 8)
+        return std::array<uint16_t, 8>();
+
+    std::array<uint16_t, 8> result;
+
+    for (int i = 0; i < 8; i++)
+    {
+        bool success;
+        long intPart = tryParseLong(stringParts[i], success, 16);
+        if (!success || intPart > 0xFFFF || intPart < 0)
+            return std::array<uint16_t, 8>();
+        
+        result[i] = intPart;
+    }
+
+    success = true;
+    return result;
+}
+
+std::array<uint16_t, 8> IpV6User::ParseAddress(string address)
+{
+    bool success;
+    auto result = TryParseAddress(address, success);
+
+    if (!success)
+        throw DumpException();
+
+    return result;
+}
+
+string IpV6User::AddressToString(std::array<uint16_t, 8> address)
+{
+    ostringstream s;
+
+    s << std::hex << std::uppercase;
+
+    for (int i = 0; i < 8; i++)
+    {
+        if (i != 0)
+            s << ':';
+
+        s << address[i];
+    }
+
+    return s.str();
+}
+
+IpV6User::IpV6User(string stringAddress)
+    : User(0, stringAddress), Address(ParseAddress(stringAddress))
+{
+}
+
+IpV6User::IpV6User(string stringAddress, std::array<uint16_t, 8> parsedAddress)
+    :  User(0, stringAddress), Address(parsedAddress)
+{}
+
+IpV6User::IpV6User(std::array<uint16_t, 8> parsedAddress)
+    : User(0, AddressToString(parsedAddress)), Address(parsedAddress)
+{}
+
+RevisionFlags IpV6User::UserKind() const
+{
+    return RevisionFlags::IpV6User;
+}
\ No newline at end of file
diff --git a/Objects/IpV6User.h b/Objects/IpV6User.h
new file mode 100644
index 0000000..38d2442
--- /dev/null
+++ b/Objects/IpV6User.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <array>
+#include "User.h"
+
+class IpV6User : public User
+{
+public:
+    static std::array<uint16_t, 8> TryParseAddress(string address, bool 
&success);
+    static std::array<uint16_t, 8> ParseAddress(string address);
+    static string AddressToString(std::array<uint16_t, 8> address);
+
+    IpV6User(string stringAddress);
+    IpV6User(string stringAddress, std::array<uint16_t, 8> parsedAddress);
+    IpV6User(std::array<uint16_t, 8> parsedAddress);
+
+    std::array<uint16_t, 8> Address;
+
+    virtual RevisionFlags UserKind() const override;
+};
\ No newline at end of file
diff --git a/Objects/NamedUser.cpp b/Objects/NamedUser.cpp
new file mode 100644
index 0000000..1e73426
--- /dev/null
+++ b/Objects/NamedUser.cpp
@@ -0,0 +1,11 @@
+#include "NamedUser.h"
+#include "Revision.h"
+
+NamedUser::NamedUser(std::uint32_t userId, string userName)
+    : User(userId, userName)
+{}
+
+RevisionFlags NamedUser::UserKind() const
+{
+    return RevisionFlags::NamedUser;
+}
\ No newline at end of file
diff --git a/Objects/NamedUser.h b/Objects/NamedUser.h
new file mode 100644
index 0000000..4127117
--- /dev/null
+++ b/Objects/NamedUser.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "User.h"
+
+class NamedUser : public User
+{
+public:
+    NamedUser(std::uint32_t userId, string userName);
+
+    virtual RevisionFlags UserKind() const override;
+};
\ No newline at end of file
diff --git a/Objects/Revision.h b/Objects/Revision.h
index 940a0ac..cf80de7 100644
--- a/Objects/Revision.h
+++ b/Objects/Revision.h
@@ -15,7 +15,8 @@
     Minor     = 0x01,
 
     NamedUser = 0x10,
-    IpV4User  = 0x20
+    IpV4User  = 0x20,
+    IpV6User  = 0x40
 };
 
 RevisionFlags operator |(RevisionFlags first, RevisionFlags second);
diff --git a/Objects/User.cpp b/Objects/User.cpp
index 83ef349..b7b6ce1 100644
--- a/Objects/User.cpp
+++ b/Objects/User.cpp
@@ -1,27 +1,50 @@
 #include "User.h"
 #include "IpV4User.h"
-#include "Revision.h"
+#include "IpV6User.h"
+#include "NamedUser.h"
+#include "../DumpException.h"
+
+unique_ptr<User> TryCreateFromIp(string ipAddress, bool &success)
+{
+    uint32_t ipV4 = IpV4User::TryParseAddress(ipAddress, success);
+    if (success)
+        return unique_ptr<User>(new IpV4User(ipAddress, ipV4));
+
+    auto ipV6 = IpV6User::TryParseAddress(ipAddress, success);
+    if (success)
+        return unique_ptr<User>(new IpV6User(ipAddress, ipV6));
+
+    success = false;
+    return nullptr;
+}
 
 unique_ptr<User> User::Create(uint32_t userId, string userName)
 {
-    User *result;
-    uint32_t ipV4 = IpV4User::ParseAddress(userName);
-    if (ipV4 != 0)
-        result = new IpV4User(userName, ipV4);
-    else
-        result = new User(userId, userName);
+    if (userId == 0)
+    {
+        bool success;
 
-    return unique_ptr<User>(result);
+        auto ipUser = TryCreateFromIp(userName, success);
+
+        if (success)
+            return ipUser;
+    }
+
+    return unique_ptr<User>(new NamedUser(userId, userName));
+}
+
+unique_ptr<User> User::CreateFromIp(string ipAddress)
+{
+    bool success;
+
+    auto ipUser = TryCreateFromIp(ipAddress, success);
+
+    if (!success)
+        throw DumpException();
+
+    return ipUser;
 }
 
 User::User(uint32_t userId, string userName)
     : UserId(userId), UserName(userName)
-{}
-
-RevisionFlags User::UserKind() const
-{
-    return RevisionFlags::NamedUser;
-}
-
-User::~User()
 {}
\ No newline at end of file
diff --git a/Objects/User.h b/Objects/User.h
index b91ce91..9729c23 100644
--- a/Objects/User.h
+++ b/Objects/User.h
@@ -10,17 +10,18 @@
 
 enum class RevisionFlags : uint8_t;
 
-// TODO: create class NamedUser?
 class User
 {
+protected:
+    User(uint32_t userId, string userName);
 public:
     static unique_ptr<User> Create(uint32_t userId, string userName);
-
-    User(uint32_t userId, string userName);
+    // creates IPv4 or IPv6 anonymous users
+    static unique_ptr<User> CreateFromIp(string ipAddress);
 
     uint32_t UserId;
     string UserName;
 
-    virtual RevisionFlags UserKind() const;
-    virtual ~User();
+    virtual RevisionFlags UserKind() const = 0;
+    virtual ~User() {}
 };
\ No newline at end of file
diff --git a/StringHelpers.cpp b/StringHelpers.cpp
index ddcedf4..d960735 100644
--- a/StringHelpers.cpp
+++ b/StringHelpers.cpp
@@ -1,17 +1,33 @@
 #include "StringHelpers.h"
 #include <sstream>
 
-using std::stringstream;
-
 // http://stackoverflow.com/a/236803/41071
-vector<string> split(const string &s, char delim)
+std::vector<std::string> split(const std::string &s, char delim)
 {
-    vector<string> elems;
-    stringstream ss(s);
-    string item;
+    std::vector<std::string> elems;
+    std::stringstream ss(s);
+    std::string item;
     while (getline(ss, item, delim))
     {
         elems.push_back(item);
     }
     return elems;
+}
+
+long tryParseLong(const std::string &s, bool &success, int radix)
+{
+    char* end;
+    const char* start = s.c_str();
+    long result = strtol(start, &end, radix);
+
+    if (errno == 0 && end != start && *end=='\0')
+    {
+        success = true;
+        return result;
+    }
+    else
+    {
+        success = false;
+        return 0;
+    }
 }
\ No newline at end of file
diff --git a/StringHelpers.h b/StringHelpers.h
index 472ad87..83b7bb5 100644
--- a/StringHelpers.h
+++ b/StringHelpers.h
@@ -3,7 +3,7 @@
 #include <string>
 #include <vector>
 
-using std::string;
-using std::vector;
+std::vector<std::string> split(const std::string &s, char delim);
 
-vector<string> split(const string &s, char delim);
\ No newline at end of file
+// makes sure the whole string is converted to an int
+long tryParseLong(const std::string &s, bool &success, int radix = 10);
\ No newline at end of file
diff --git a/XmlContributorProcessor.cpp b/XmlContributorProcessor.cpp
index 8651b23..f67f71c 100644
--- a/XmlContributorProcessor.cpp
+++ b/XmlContributorProcessor.cpp
@@ -1,7 +1,8 @@
 #include "XmlContributorProcessor.h"
 #include "XmlUtils.h"
 #include "Objects/Revision.h"
-#include "Objects/IpV4User.h"
+#include "Objects/User.h"
+#include "Objects/NamedUser.h"
 
 void XmlContributorProcessor::Handler(XML::Element &elem, void *userData)
 {
@@ -20,12 +21,12 @@
 
     auto revision = (Revision*)userData;
 
-    User* user;
+    std::shared_ptr<User> user;
 
     if (processor.ip != string())
-        user = new IpV4User(processor.ip);
+        user = User::CreateFromIp(processor.ip);
     else
-        user = new User(processor.id, processor.userName);
+        user = std::make_shared<NamedUser>(processor.id, processor.userName);
 
     revision->Contributor = shared_ptr<User>(user);
     revision->Flags |= user->UserKind();

-- 
To view, visit https://gerrit.wikimedia.org/r/75864
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I2af5ef6d84b7cf98d3fcfcbe8391dcbd976f51b5
Gerrit-PatchSet: 2
Gerrit-Project: operations/dumps/incremental
Gerrit-Branch: gsoc
Gerrit-Owner: Petr Onderka <[email protected]>
Gerrit-Reviewer: Petr Onderka <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to