Index: trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java	(revision 15851)
+++ trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java	(revision 15852)
@@ -183,4 +183,9 @@
                     Objects.equals(way, that.way);
         }
+
+        @Override
+        public String toString() {
+            return "w" + way.getUniqueId() + " " + way.getNodesCount() + " nodes";
+        }
     }
 
@@ -254,5 +259,5 @@
          */
         WayTraverser(Collection<WayInPolygon> ways) {
-            availableWays = new HashSet<>(ways);
+            availableWays = new LinkedHashSet<>(ways);
             lastWay = null;
         }
@@ -482,4 +487,5 @@
      */
     public void join(Collection<Way> ways) {
+        cmdsCount = 0;
         addedRelations.clear();
 
@@ -548,4 +554,6 @@
                 commitCommands(tr("Move tags from ways to relations"));
 
+                makeCommitsOneAction(marktr("Joined overlapping areas"));
+
                 if (result.polygons != null && ds != null) {
                     List<Way> allWays = new ArrayList<>();
@@ -569,5 +577,10 @@
             if (addUndoRedo) {
                 UndoRedoHandler.getInstance().undo();
-                UndoRedoHandler.getInstance().getRedoCommands().clear();
+                // add no-change commands to the stack to remove the half-done commands
+                Way w = ways.iterator().next();
+                cmds.add(new ChangeCommand(w, w));
+                cmds.add(new ChangeCommand(w, w));
+                commitCommands(tr("Reverting changes"));
+                UndoRedoHandler.getInstance().undo();
             }
         }
@@ -597,6 +610,7 @@
      * @return new area formed.
      * @throws UserCancelException if user cancels the operation
-     */
-    public JoinAreasResult joinAreas(List<Multipolygon> areas) throws UserCancelException {
+     * @since xxx : visibility changed from public to private
+     */
+    private JoinAreasResult joinAreas(List<Multipolygon> areas) throws UserCancelException {
 
         // see #11026 - Because <ways> is a dynamic filtered (on ways) of a filtered (on selected objects) collection,
@@ -705,6 +719,4 @@
             }
         }
-
-        makeCommitsOneAction(marktr("Joined overlapping areas"));
 
         if (warnAboutRelations) {
@@ -1008,5 +1020,31 @@
         }
 
+        revertDuplicateTwoNodeWays(result);
+
         return result;
+    }
+
+    /**
+     * Correct possible error in markWayInsideSide result when splitting a self-intersecting way.
+     * If we have two ways with the same two nodes and the same direction there must be a self intersection.
+     * Change the direction flag for the latter of the two ways. The result is that difference between the number
+     * of ways with insideToTheRight = {@code true} and those with insideToTheRight = {@code false}
+     * differs by 0 or 1, not more.
+     * <p>See #10511
+     * @param parts the parts of a single closed way
+     */
+    private static void revertDuplicateTwoNodeWays(List<WayInPolygon> parts) {
+        for (int i = 0; i < parts.size(); i++) {
+            WayInPolygon w1 = parts.get(i);
+            if (w1.way.getNodesCount() != 2)
+                continue;
+            for (int j = i + 1; j < parts.size(); j++) {
+                WayInPolygon w2 = parts.get(j);
+                if (w2.way.getNodesCount() == 2 && w1.insideToTheRight == w2.insideToTheRight
+                        && w1.way.firstNode() == w2.way.firstNode() && w1.way.lastNode() == w2.way.lastNode()) {
+                    w2.insideToTheRight = !w2.insideToTheRight;
+                }
+            }
+        }
     }
 
@@ -1162,5 +1200,4 @@
                 cleanMultigonWays.add(way);
         }
-
         WayTraverser traverser = new WayTraverser(cleanMultigonWays);
         List<AssembledPolygon> result = new ArrayList<>();
Index: trunk/test/unit/org/openstreetmap/josm/actions/JoinAreasActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/JoinAreasActionTest.java	(revision 15851)
+++ trunk/test/unit/org/openstreetmap/josm/actions/JoinAreasActionTest.java	(revision 15852)
@@ -15,5 +15,4 @@
 import java.util.Set;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
@@ -50,5 +49,5 @@
     @Rule
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection();
+    public JOSMTestRules test = new JOSMTestRules().main().projection().preferences();
 
     /**
@@ -58,5 +57,4 @@
      */
     @Test
-    @Ignore("disable this test, needs further working") // XXX
     public void testTicket10511() throws IOException, IllegalDataException {
         try (InputStream is = TestUtils.getRegressionDataStream(10511, "10511_mini.osm")) {
@@ -66,4 +64,6 @@
             try {
                 new JoinAreasAction(false).join(ds.getWays());
+                Collection<IPrimitive> found = SearchAction.searchAndReturn("type:way", SearchMode.replace);
+                assertEquals(1, found.size());
             } finally {
                 // Ensure we clean the place before leaving, even if test fails.
