Index: /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 3422)
+++ /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 3423)
@@ -7,4 +7,5 @@
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -40,5 +41,5 @@
      */
     private final Set<PrimitiveId> objectsWithChildrenToMerge;
-    private final Set<OsmPrimitive> deletedObjectsToUnlink;
+    private final Set<OsmPrimitive> objectsToDelete;
 
     /**
@@ -58,5 +59,5 @@
         mergedMap = new HashMap<PrimitiveId, PrimitiveId>();
         objectsWithChildrenToMerge = new HashSet<PrimitiveId>();
-        deletedObjectsToUnlink = new HashSet<OsmPrimitive>();
+        objectsToDelete = new HashSet<OsmPrimitive>();
     }
 
@@ -162,9 +163,59 @@
             }
         }
-        for (OsmPrimitive source: deletedObjectsToUnlink) {
-            OsmPrimitive target = getMergeTarget(source);
-            if (target == null)
-                throw new RuntimeException(tr("Missing merge target for object with id {0}", source.getUniqueId()));
-            targetDataSet.unlinkReferencesToPrimitive(target);
+
+        deleteMarkedObjects();
+    }
+
+    /**
+     * Deleted objects in objectsToDelete set and create conflicts for objects that cannot
+     * be deleted because they're referenced in the target dataset.
+     */
+    protected void deleteMarkedObjects() {
+        boolean flag;
+        do {
+            flag = false;
+            for (Iterator<OsmPrimitive> it = objectsToDelete.iterator();it.hasNext();) {
+                OsmPrimitive target = it.next();
+                OsmPrimitive source = sourceDataSet.getPrimitiveById(target.getPrimitiveId());
+                if (source == null)
+                    throw new RuntimeException(tr("Object of type {0} with id {1} was marked to be deleted, but it's missing in the source dataset",
+                            target.getType(), target.getUniqueId()));
+
+                List<OsmPrimitive> referrers = target.getReferrers();
+                if (referrers.isEmpty()) {
+                    target.setDeleted(true);
+                    target.mergeFrom(source);
+                    it.remove();
+                    flag = true;
+                } else {
+                    for (OsmPrimitive referrer : referrers) {
+                        // If one of object referrers isn't going to be deleted,
+                        // add a conflict and don't delete the object
+                        if (!objectsToDelete.contains(referrer)) {
+                            conflicts.add(target, source);
+                            it.remove();
+                            flag = true;
+                            break;
+                        }
+                    }
+                }
+
+            }
+        } while (flag);
+
+        if (!objectsToDelete.isEmpty()) {
+            // There are some more objects rest in the objectsToDelete set
+            // This can be because of cross-referenced relations.
+            for (OsmPrimitive osm: objectsToDelete) {
+                if (osm instanceof Way) {
+                    ((Way) osm).setNodes(null);
+                } else if (osm instanceof Relation) {
+                    ((Relation) osm).setMembers(null);
+                }
+            }
+            for (OsmPrimitive osm: objectsToDelete) {
+                osm.setDeleted(true);
+                osm.mergeFrom(sourceDataSet.getPrimitiveById(osm.getPrimitiveId()));
+            }
         }
     }
@@ -257,5 +308,11 @@
             // take. We take target.
             //
-        } else if (target.isDeleted() && ! source.isDeleted() && target.getVersion() == source.getVersion()) {
+        } else if (target.isVisible() != source.isVisible() && target.getVersion() == source.getVersion())
+            // Same version, but different "visible" attribute. It indicates a serious problem in datasets.
+            // For example, datasets can be fetched from different OSM servers or badly hand-modified.
+            // We shouldn't merge that datasets.
+            throw new DataIntegrityProblemException(tr("Conflict in 'visible' attribute for object of type {0} with id {1}",
+                    target.getType(), target.getId()));
+        else if (target.isDeleted() && ! source.isDeleted() && target.getVersion() == source.getVersion()) {
             // same version, but target is deleted. Assume target takes precedence
             // otherwise too many conflicts when refreshing from the server
@@ -269,22 +326,14 @@
                 }
             }
-        } else if (target.isDeleted() != source.isDeleted()) {
-            // differences in deleted state have to be resolved manually. This can
-            // happen if one layer is merged onto another layer
-            //
-            conflicts.add(target,source);
+        } else if (! target.isModified() && source.isDeleted()) {
+            // target not modified. We can assume that source is the most recent version,
+            // so mark it to be deleted.
+            //
+            objectsToDelete.add(target);
         } else if (! target.isModified() && source.isModified()) {
             // target not modified. We can assume that source is the most recent version.
-            // clone it into target. But check first, whether source is deleted. if so,
-            // make sure that target is not referenced any more in myDataSet. If it is there
-            // is a conflict
-            if (source.isDeleted()) {
-                if (!target.getReferrers().isEmpty()) {
-                    conflicts.add(target, source);
-                }
-            } else {
-                target.mergeFrom(source);
-                objectsWithChildrenToMerge.add(source.getPrimitiveId());
-            }
+            // clone it into target.
+            target.mergeFrom(source);
+            objectsWithChildrenToMerge.add(source.getPrimitiveId());
         } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
             // both not modified. Merge nevertheless.
@@ -303,4 +352,9 @@
                 target.setModified(false);
             }
+        } else if (source.isDeleted() != target.isDeleted()) {
+            // target is modified and deleted state differs.
+            // this have to be resolved manually.
+            //
+            conflicts.add(target,source);
         } else if (! target.hasEqualSemanticAttributes(source)) {
             // target is modified and is not semantically equal with source. Can't automatically
Index: /trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 3422)
+++ /trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 3423)
@@ -379,5 +379,5 @@
             } else if (action.equals("delete")) {
                 current.setDeleted(true);
-                current.setModified(true);
+                current.setModified(current.isVisible());
             } else if (action.equals("modify")) {
                 current.setModified(true);
