[PATCH 2/3] migrations: Don't fail if it's not possible to rehome a patch

Stephen Finucane stephen at that.guru
Thu Mar 5 04:26:45 AEDT 2020

Migration 0039 attempts to move patches that have ended up in an
arbitrary series due to race conditions into the correct series.
However, because we weren't previously considering versions when
choosing a series, this might not be possible resulting in the issues
seen in #340 [1]. If this happens, we could try rehome _those_ patches
but that's really complicated and requires bringing in a whole load of
parsing functionality from 'patchwork.parser' so we can e.g. rebuild the
list of references from the headers. Instead, print a big warning to the
admin with information about how they could fix the issue and then just
remove the offending references. This means we're still left with
orphaned series but these could be fixed manually, if necessary.

[1] https://github.com/getpatchwork/patchwork/issues/340

Signed-off-by: Stephen Finucane <stephen at that.guru>
Closes: #340
 .../0039_unique_series_references.py          | 35 +++++++++++++++++--
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/patchwork/migrations/0039_unique_series_references.py b/patchwork/migrations/0039_unique_series_references.py
index 99b10fcc..c8ac0dc6 100644
--- a/patchwork/migrations/0039_unique_series_references.py
+++ b/patchwork/migrations/0039_unique_series_references.py
@@ -1,6 +1,8 @@
 from django.db import connection, migrations, models
 from django.db.models import Count
+from django.db.utils import IntegrityError
 import django.db.models.deletion
+from django.utils import six
 def merge_duplicate_series(apps, schema_editor):
@@ -49,14 +51,41 @@ def merge_duplicate_series(apps, schema_editor):
             if series_ref == chosen_ref:
+            has_conflict = False
             # update the patches to point to our chosen series instead, on the
             # assumption that all other metadata is correct
             for patch in Patch.objects.filter(series=series_ref.series):
                 patch.series = chosen_ref.series
-                patch.save()
+                try:
+                    patch.save()
+                except IntegrityError as exc:
+                    has_conflict = True
+                    print(
+                        "We attempted to merge patch '%d' into the correct "
+                        "series, '%d', but it appears there is already a "
+                        "patch with the same number in this series. We have "
+                        "deleted the series references for the bad series, "
+                        "'%d', but you may wish to do further manual work to "
+                        "resolve this issue. For more information, refer to "
+                        "https://git.io/JvVvV. Error: %s" % (
+                            patch.id,
+                            chosen_ref.series.id,
+                            series_ref.series.id,
+                            six.text_type(exc),
+                        )
+                    )
-            # delete the other series (which will delete the series ref)
-            series_ref.series.delete()
+            if not has_conflict:
+                # assuming there has been no conflict and all patches have been
+                # moved to a new series, delete the other series (which will
+                # delete the series ref)
+                series_ref.series.delete()
+            else:
+                # otherwise just delete the series references and keep the old
+                # series for later cleanup
+                for series_ref in series_ref.series.references:
+                    series_ref.delete()
 def copy_project_field(apps, schema_editor):

More information about the Patchwork mailing list