[#7994] automatically merge multiple threads if that occurs
diff --git a/Allura/allura/model/artifact.py b/Allura/allura/model/artifact.py
index 1d4e230..67cbf3b 100644
--- a/Allura/allura/model/artifact.py
+++ b/Allura/allura/model/artifact.py
@@ -346,19 +346,38 @@
 
         """
         from .discuss import Thread
-        t = Thread.query.get(ref_id=self.index_id())
-        if t is None:
+        threads = Thread.query.find(dict(ref_id=self.index_id())).all()
+        if not threads:
             idx = self.index()
             t = Thread.new(
                 app_config_id=self.app_config_id,
                 discussion_id=self.app_config.discussion_id,
                 ref_id=idx['id'],
                 subject='%s discussion' % h.get_first(idx, 'title'))
+        elif len(threads) == 1:
+            t = threads[0]
+        else:
+            # there should not be multiple threads, we'll merge them
+            destination = threads.pop()
+            for thread in threads:
+                for post in thread.posts:
+                    post.thread_id = destination._id
+                    destination.num_replies += 1
+                    destination.last_post_date = max(destination.last_post_date, post.mod_date)
+                    session(post).flush(post)
+                    session(post).expunge(post)  # so thread.posts ref later in the code doesn't use stale posts
+                Thread.query.remove({'_id': thread._id})  # NOT thread.delete() since that would remove its posts too
+                thread.attachment_class().query.update({'thread_id': thread._id},
+                                                       {'$set': {'thread_id': destination._id}},
+                                                       multi=True)
+            t = destination
+
         parent_id = None
         if data:
             in_reply_to = data.get('in_reply_to', [])
             if in_reply_to:
                 parent_id = in_reply_to[0]
+
         return t, parent_id
 
     @LazyProperty
diff --git a/Allura/allura/tests/model/test_artifact.py b/Allura/allura/tests/model/test_artifact.py
index adbae4e..b7e9dc0 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -222,3 +222,27 @@
         assert_equal(c.project.last_updated, datetime(2014, 1, 1))
     finally:
         M.artifact_orm_session._get().skip_last_updated = False
+
+
+@with_setup(setUp, tearDown)
+def test_get_discussion_thread_dupe():
+    artif = WM.Page(title='TestSomeArtifact')
+    thr1 = artif.get_discussion_thread()[0]
+    thr1.post('thr1-post1')
+    thr1.post('thr1-post2')
+    thr2 = M.Thread.new(ref_id=thr1.ref_id)
+    thr2.post('thr2-post1')
+    thr2.post('thr2-post2')
+    thr2.post('thr2-post3')
+    thr3 = M.Thread.new(ref_id=thr1.ref_id)
+    thr3.post('thr3-post1')
+    thr4 = M.Thread.new(ref_id=thr1.ref_id)
+
+    thread_q = M.Thread.query.find(dict(ref_id=artif.index_id()))
+    assert_equal(thread_q.count(), 4)
+
+    thread = artif.get_discussion_thread()[0]  # force cleanup
+    threads = thread_q.all()
+    assert_equal(len(threads), 1)
+    assert_equal(len(thread.posts), 6)
+    assert not any(p.deleted for p in thread.posts)  # normal thread deletion propagates to children, make sure that doesn't happen