1. Patrick Mézard
  2. hgmbox

Commits

Patrick Mézard  committed 6b97968

Handle duplicated patches in a series

We had to refactor getgroups() to correctly handle message threading instead of
dealing with parent/child relationship only.

  • Participants
  • Parent commits f7672bd
  • Branches default

Comments (0)

Files changed (4)

File mbox.py

View file
         for c in findclusters(groups):
             yield c
 
-def makegroup(msgs):
+def makegroup(msgs, removedupes=False):
     """Return msgs sorted by index if they are a complete sequence"""
+    if not msgs:
+        return None
     msgs = sorted(msgs, key=lambda m: m.index)
     indexes = [m.index for m in msgs]
-    if indexes != list(xrange(msgs[0].count)):
-        # Maybe missing the intro message
-        if indexes != list(xrange(1, msgs[0].count)):
-            return None
+    if removedupes and len(set(indexes)) != len(indexes):
+        # There are duplicate patches, keep the most recent ones
+        dupes = {}
+        for m in msgs:
+            if m.index in dupes:
+                if m.date <= dupes[m.index].date:
+                    continue
+            dupes[m.index] = m
+        msgs = sorted(dupes.values(), key=lambda m: m.index)
+        indexes = [m.index for m in msgs]
+
+    if indexes == list(xrange(msgs[0].count)):
+        return msgs
+    # Maybe missing the intro message
+    if indexes == list(xrange(1, msgs[0].count)):
         msgs[:0] = [None]
-    return msgs
+        return msgs
+    return None
 
 def getgroups(patchmessages, datefn, orphans=False):
     """Yield groups found in patchmessages if accepted by datefn.
     Each group is a tuple with intro message (or None) and patch list.
     If orphans is set then groups are created for orphans too."""
-    pendings = {}
-    orphaneds = {}
+    children = {}
+    ids = {}
+    # Thread messages
     for m in patchmessages:
         if not datefn(m.date):
             continue
-        if m.count == 1:
-            yield None, [m]
+        ids[m.id] = m
+        if m.parentid:
+            children.setdefault(m.parentid, []).append(m.id)
+
+    # Handle single patches
+    for m in ids.values():
+        if m.count != 1:
             continue
-        msgid = m.parentid
-        if msgid is None:
-            orphaneds[m.id] = m
-        if m.index == 0:
-            msgid = m.id
-        if not msgid:
+        yield None, [m]
+        del ids[m.id]
+        if m.id in children:
+            del children[m.id]
+
+    # Handle patches series
+    for r in ids.values():
+        if r.index != 0:
             continue
-        pendings.setdefault(msgid, []).append(m)
-        msgs = pendings[msgid]
-        if len(msgs) != m.count:
+        series = []
+        pendings = [r]
+        while pendings:
+            m = pendings.pop()
+            series.append(m)
+            if m.id not in children:
+                continue
+            pendings.extend([ids[i] for i in children[m.id]
+                             if i in ids and ids[i].index != 0])
+        msgs = makegroup(series, True)
+        if not msgs:
             continue
-        msgs = makegroup(msgs)
-        if msgs is None:
-            continue
-        for m in msgs:
-            if m and m.id in orphaneds:
-                del orphaneds[m.id]
-        del pendings[msgid]
+        for m in series:
+            del ids[m.id]
+            if m.id in children:
+                del children[m.id]
         yield msgs[0], msgs[1:]
 
     # Try to find more groups using sender and date locality
-    for msgs in pendings.itervalues():
-        for m in msgs:
-            orphaneds[m.id] = m
-    for msgs in clustermessages(orphaneds.values()):
+    for msgs in clustermessages(ids.values()):
         msgs = makegroup(msgs)
         if msgs is None:
             continue
         for m in msgs:
             if m:
-                del orphaneds[m.id]
+                del ids[m.id]
         yield msgs[0], msgs[1:]
 
     # Return orphans missing their intro message as standalone messages
-    for m in orphaneds.values():
+    for m in ids.values():
         if m.count == 2 and m.index == 1:
             yield None, [m]
 
     if not orphans:
         return
     # Return orphaned messages as standalone groups
-    for m in orphaneds.values():
+    for m in ids.values():
         if m.count > 0 and m.index == 0:
             continue
         m.index, m.count = 0, 1

File test-mbox

View file
 n
 EOF
 
+echo % test duplicates
+hg mimport --mbox $TESTDIR/tests/testdupes.mbx <<EOF
+n
+n
+n
+EOF
+
 echo % test debugmbox help
 hg help debugmbox | \
     sed 's/use "hg -v help debugmbox" to show.*/use "hg -v help debugmbox" to show/'

File test-mbox.out

View file
 [PATCH 1 of 1 stable] templatefilters: use \uxxxx style escape for JSON string
 import this group? [Nydq?]  
 0 patches imported
+% test duplicates
+[PATCH 0 of 2] header
+    [PATCH 1 of 2] patch 1 of 2 replacement
+    [PATCH 2 of 2] patch 2 of 2
+import this group? [Nydq?]  
+0 patches imported
 % test debugmbox help
 hg debugmbox
 

File tests/testdupes.mbx

View file
+From MAILER-DAEMON Tue Nov 29 10:37:01 2011
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:13:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH 0 of 2] header
+Message-Id: <0ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+header text
+
+From MAILER-DAEMON Tue Nov 29 10:37:02 2011
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:14:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH 1 of 2] patch 1 of 2
+Message-Id: <1ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+In-Reply-To: <0ab3c4562bd574dbd782.1230599538@localhost>
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+patch1 content
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ /dev/null
+
+
+From MAILER-DAEMON Tue Nov 29 10:37:03 2011
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:15:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH 2 of 2] patch 2 of 2
+Message-Id: <2ab3c4562bd574dbd782.1230599538@localhost>
+Date: Tue, 16 Dec 2008 02:12:18 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+In-Reply-To: <0ab3c4562bd574dbd782.1230599538@localhost>
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+patch2 content
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ /dev/null
+
+
+From MAILER-DAEMON Tue Nov 29 10:37:04 2011
+Delivered-To: foobar@gmail.com
+Received: by 10.114.108.3 with SMTP id g3cs509927wac;
+        Mon, 29 Dec 2008 17:14:30 -0800 (PST)
+MIME-Version: 1.0
+Subject: [PATCH 1 of 2] patch 1 of 2 replacement
+Message-Id: <1ab3c4562bd574dbd782.1230599539@localhost>
+Date: Tue, 16 Dec 2008 02:12:19 +0100
+From: Bar Baz <barbaz@gmail.com>
+To: mercurial-devel@selenic.com
+In-Reply-To: <1ab3c4562bd574dbd782.1230599538@localhost>
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: mercurial-devel-bounces@selenic.com
+
+patch1 content replacement
+
+diff --git a/a b/a
+new file mode 100644
+--- /dev/null
++++ /dev/null
+
+