Index: /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 16211)
+++ /trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 16212)
@@ -211,5 +211,5 @@
             }
             for (Way w : copyFrom.getWays()) {
-                Way newWay = new Way(w);
+                Way newWay = new Way(w, false, false);
                 primMap.put(w, newWay);
                 List<Node> newNodes = new ArrayList<>();
@@ -224,12 +224,10 @@
             Collection<Relation> relations = copyFrom.getRelations();
             for (Relation r : relations) {
-                Relation newRelation = new Relation(r);
-                newRelation.setMembers(null);
+                Relation newRelation = new Relation(r, false, false);
                 primMap.put(r, newRelation);
                 addPrimitive(newRelation);
             }
             for (Relation r : relations) {
-                Relation newRelation = (Relation) primMap.get(r);
-                newRelation.setMembers(r.getMembers().stream()
+                ((Relation) primMap.get(r)).setMembers(r.getMembers().stream()
                         .map(rm -> new RelationMember(rm.getRole(), primMap.get(rm.getMember())))
                         .collect(Collectors.toList()));
Index: /trunk/src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 16211)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 16212)
@@ -221,10 +221,10 @@
 
     @Override
-    public void cloneFrom(OsmPrimitive osm) {
+    public void cloneFrom(OsmPrimitive osm, boolean copyChildren) {
         if (!(osm instanceof Node))
             throw new IllegalArgumentException("Not a node: " + osm);
         boolean locked = writeLock();
         try {
-            super.cloneFrom(osm);
+            super.cloneFrom(osm, copyChildren);
             setCoor(((Node) osm).getCoor());
         } finally {
Index: /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 16211)
+++ /trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 16212)
@@ -883,5 +883,16 @@
      * @param other other primitive
      */
-    public void cloneFrom(OsmPrimitive other) {
+    public final void cloneFrom(OsmPrimitive other) {
+        cloneFrom(other, true);
+    }
+
+    /**
+     * Get and write all attributes from the parameter. Does not fire any listener, so
+     * use this only in the data initializing phase
+     * @param other other primitive
+     * @param copyChildren whether to copy child primitives too
+     * @since 16212
+     */
+    protected void cloneFrom(OsmPrimitive other, boolean copyChildren) {
         // write lock is provided by subclasses
         if (id != other.id && dataSet != null)
Index: /trunk/src/org/openstreetmap/josm/data/osm/Relation.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 16211)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 16212)
@@ -199,11 +199,23 @@
      * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
      * If {@code false}, does nothing
-     */
-    public Relation(Relation clone, boolean clearMetadata) {
+     * @param copyMembers whether to copy relation members too
+     * @since 16212
+     */
+    public Relation(Relation clone, boolean clearMetadata, boolean copyMembers) {
         super(clone.getUniqueId(), true);
-        cloneFrom(clone);
+        cloneFrom(clone, copyMembers);
         if (clearMetadata) {
             clearOsmMetadata();
         }
+    }
+
+    /**
+     * Constructs an identical clone of the argument.
+     * @param clone The relation to clone
+     * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
+     * If {@code false}, does nothing
+     */
+    public Relation(Relation clone, boolean clearMetadata) {
+        this(clone, clearMetadata, true);
     }
 
@@ -237,12 +249,14 @@
 
     @Override
-    public void cloneFrom(OsmPrimitive osm) {
+    public void cloneFrom(OsmPrimitive osm, boolean copyMembers) {
         if (!(osm instanceof Relation))
             throw new IllegalArgumentException("Not a relation: " + osm);
         boolean locked = writeLock();
         try {
-            super.cloneFrom(osm);
-            // It's not necessary to clone members as RelationMember class is immutable
-            setMembers(((Relation) osm).getMembers());
+            super.cloneFrom(osm, copyMembers);
+            if (copyMembers) {
+                // It's not necessary to clone members as RelationMember class is immutable
+                setMembers(((Relation) osm).getMembers());
+            }
         } finally {
             writeUnlock(locked);
Index: /trunk/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 16211)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 16212)
@@ -204,12 +204,24 @@
      * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
      * If {@code false}, does nothing
-     * @since 2410
-     */
-    public Way(Way original, boolean clearMetadata) {
+     * @param copyNodes whether to copy nodes too
+     * @since 16212
+     */
+    public Way(Way original, boolean clearMetadata, boolean copyNodes) {
         super(original.getUniqueId(), true);
-        cloneFrom(original);
+        cloneFrom(original, copyNodes);
         if (clearMetadata) {
             clearOsmMetadata();
         }
+    }
+
+    /**
+     * Constructs a new {@code Way} from an existing {@code Way}.
+     * @param original The original {@code Way} to be identically cloned. Must not be null
+     * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
+     * If {@code false}, does nothing
+     * @since 2410
+     */
+    public Way(Way original, boolean clearMetadata) {
+        this(original, clearMetadata, true);
     }
 
@@ -286,12 +298,13 @@
 
     @Override
-    public void cloneFrom(OsmPrimitive osm) {
+    public void cloneFrom(OsmPrimitive osm, boolean copyNodes) {
         if (!(osm instanceof Way))
             throw new IllegalArgumentException("Not a way: " + osm);
         boolean locked = writeLock();
         try {
-            super.cloneFrom(osm);
-            Way otherWay = (Way) osm;
-            setNodes(otherWay.getNodes());
+            super.cloneFrom(osm, copyNodes);
+            if (copyNodes) {
+                setNodes(((Way) osm).getNodes());
+            }
         } finally {
             writeUnlock(locked);
Index: /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 16211)
+++ /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 16212)
@@ -316,3 +316,27 @@
         new DataSet().mergeFrom(ds);
     }
+
+    /**
+     * Checks that a read-only dataset can be cloned.
+     */
+    @Test
+    public void testCloneReadOnly() {
+        DataSet ds = new DataSet();
+        Node n1 = new Node(LatLon.SOUTH_POLE);
+        Node n2 = new Node(LatLon.NORTH_POLE);
+        ds.addPrimitive(n1);
+        ds.addPrimitive(n2);
+        Way w = new Way();
+        w.setNodes(Arrays.asList(n1, n2));
+        ds.addPrimitive(w);
+        Relation r = new Relation();
+        r.setMembers(Arrays.asList(new RelationMember(null, w)));
+        ds.addPrimitive(r);
+        ds.lock();
+
+        DataSet copy = new DataSet(ds);
+
+        assertEquals(4, copy.allPrimitives().size());
+        assertTrue(copy.isLocked());
+    }
 }
