Index: src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java	(revision 14936)
+++ src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java	(working copy)
@@ -1,18 +1,21 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.osm;
+import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Logging;
 
 /**
- * A ChangesetDataSet holds the content of a changeset.
+ * A ChangesetDataSet holds the content of a changeset. Typically, a primitive is modified only once in a changeset,
+ * but if there are multiple modifications, the first and last are kept. Further intermediate versions are not kept.
  */
 public class ChangesetDataSet {
 
@@ -46,8 +49,8 @@
         HistoryOsmPrimitive getPrimitive();
     }
 
-    private final Map<PrimitiveId, HistoryOsmPrimitive> primitives = new HashMap<>();
-    private final Map<PrimitiveId, ChangesetModificationType> modificationTypes = new HashMap<>();
+    /** maps an id to either one {@link ChangesetDataSetEntry} or an array of {@link ChangesetDataSetEntry} */
+    private final Map<PrimitiveId, Object> entryMap = new HashMap<>();
 
     /**
      * Remembers a history primitive with the given modification type
@@ -56,12 +59,37 @@
      * @param cmt the modification type. Must not be null.
      * @throws IllegalArgumentException if primitive is null
      * @throws IllegalArgumentException if cmt is null
+     * @throws IllegalArgumentException if the same primitive was already stored with a higher or equal version
      */
     public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) {
         CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
         CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
-        primitives.put(primitive.getPrimitiveId(), primitive);
-        modificationTypes.put(primitive.getPrimitiveId(), cmt);
+        DefaultChangesetDataSetEntry csEntry = new DefaultChangesetDataSetEntry(cmt, primitive);
+        Object val = entryMap.get(primitive.getPrimitiveId());
+        ChangesetDataSetEntry[] entries;
+        if (val == null) {
+            entryMap.put(primitive.getPrimitiveId(), csEntry);
+            return;
+        }
+        if (val instanceof ChangesetDataSetEntry) {
+            entries = new ChangesetDataSetEntry[2];
+            entries[0] = (ChangesetDataSetEntry) val;
+            if (primitive.getVersion() <= entries[0].getPrimitive().getVersion()) {
+                throw new IllegalArgumentException(
+                        tr("Changeset {0}: Unexpected order of versions for {1}: v{2} is not higher than v{3}",
+                                String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
+                                primitive.getVersion(), entries[0].getPrimitive().getVersion()));
+            }
+        } else {
+            entries = (ChangesetDataSetEntry[]) val;
+        }
+        if (entries[1] != null) {
+            Logging.info("Changeset {0}: Change of {1} v{2} is replaced by version v{3}",
+                    String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
+                    entries[1].getPrimitive().getVersion(), primitive.getVersion());
+        }
+        entries[1] = csEntry;
+        entryMap.put(primitive.getPrimitiveId(), entries);
     }
 
     /**
@@ -71,24 +99,24 @@
      */
     public boolean contains(PrimitiveId id) {
         if (id == null) return false;
-        return primitives.containsKey(id);
+        return entryMap.containsKey(id);
     }
 
     /**
-     * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or
+     * Replies the last modification type for the object with id <code>id</code>. Replies null, if id is null or
      * if the object with id <code>id</code> isn't in the changeset content.
      *
      * @param id the id
-     * @return the modification type
+     * @return the last modification type or null
      */
     public ChangesetModificationType getModificationType(PrimitiveId id) {
-        if (!contains(id)) return null;
-        return modificationTypes.get(id);
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null ? e.getModificationType() : null;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was created in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
      * @return true if the primitive with id <code>id</code> was created in this
@@ -95,13 +123,13 @@
      * changeset.
      */
     public boolean isCreated(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.CREATED == getModificationType(id);
+        ChangesetDataSetEntry e = getFirstEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.CREATED;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was updated in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
      * @return true if the primitive with id <code>id</code> was updated in this
@@ -108,13 +136,13 @@
      * changeset.
      */
     public boolean isUpdated(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.UPDATED == getModificationType(id);
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.UPDATED;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was deleted in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
      * @return true if the primitive with id <code>id</code> was deleted in this
@@ -121,59 +149,103 @@
      * changeset.
      */
     public boolean isDeleted(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.DELETED == getModificationType(id);
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.DELETED;
     }
 
     /**
-     * Replies the set of primitives with a specific modification type
+     * Replies the number of primitives in the dataset.
      *
-     * @param cmt the modification type. Must not be null.
-     * @return the set of primitives
-     * @throws IllegalArgumentException if cmt is null
+     * @return the number of primitives in the dataset.
      */
-    public Set<HistoryOsmPrimitive> getPrimitivesByModificationType(ChangesetModificationType cmt) {
-        CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
-        return modificationTypes.entrySet().stream()
-                .filter(entry -> entry.getValue() == cmt)
-                .map(entry -> primitives.get(entry.getKey()))
-                .collect(Collectors.toSet());
+    public int size() {
+        return entryMap.size();
     }
 
     /**
-     * Replies the number of objects in the dataset
+     * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
+     * null, if there is no such primitive in the data set. If the primitive was modified
+     * multiple times, the last version is returned.
      *
-     * @return the number of objects in the dataset
+     * @param id the id
+     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
      */
-    public int size() {
-        return primitives.size();
+    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null ? e.getPrimitive() : null;
     }
 
     /**
-     * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
+     * @return an unmodifiable set of all primitives in this dataset.
+     * @since xxx
+     */
+    public Set<PrimitiveId> getIds() {
+        return Collections.unmodifiableSet(entryMap.keySet());
+    }
+
+    /**
+     * Replies the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
      * null, if there is no such primitive in the data set.
-     *
      * @param id the id
-     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
+     * @return the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
+     * @since xxx
      */
-    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
-        if (id == null) return null;
-        return primitives.get(id);
+    public ChangesetDataSetEntry getFirstEntry(PrimitiveId id) {
+        if (id == null)
+            return null;
+        Object val = entryMap.get(id);
+        if (val == null)
+            return null;
+        if (val instanceof ChangesetDataSetEntry[]) {
+            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+            return entries[0];
+        } else {
+            return (ChangesetDataSetEntry) val;
+        }
     }
 
     /**
-     * Returns an iterator over dataset entries.
-     * @return an iterator over dataset entries
+     * Replies the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
+     * null, if there is no such primitive in the data set.
+     * @param id the id
+     * @return the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
+     * @since xxx
      */
+    public ChangesetDataSetEntry getLastEntry(PrimitiveId id) {
+        if (id == null)
+            return null;
+        Object val = entryMap.get(id);
+        if (val == null)
+            return null;
+        if (val instanceof ChangesetDataSetEntry[]) {
+            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+            return entries[1];
+        } else {
+            return (ChangesetDataSetEntry) val;
+        }
+    }
+
+    /**
+     * Returns an iterator over dataset entries. The elements are returned in no particular order.
+     * @return an iterator over dataset entries. If a primitive was changed multiple times, only the last entry is returned.
+     */
     public Iterator<ChangesetDataSetEntry> iterator() {
         return new DefaultIterator();
     }
 
-    private static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
+    /**
+     * Class to keep one entry of a changeset: the combination of modification type and primitive.
+     */
+    public static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
         private final ChangesetModificationType modificationType;
         private final HistoryOsmPrimitive primitive;
 
-        DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
+        /**
+         * Construct new entry.
+         * @param modificationType the modification type
+         * @param primitive the primitive
+         */
+        public DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
             this.modificationType = modificationType;
             this.primitive = primitive;
         }
@@ -187,13 +259,18 @@
         public HistoryOsmPrimitive getPrimitive() {
             return primitive;
         }
+
+        @Override
+        public String toString() {
+            return modificationType.toString() + " " + primitive.toString();
+        }
     }
 
     private class DefaultIterator implements Iterator<ChangesetDataSetEntry> {
-        private final Iterator<Entry<PrimitiveId, ChangesetModificationType>> typeIterator;
+        private final Iterator<Entry<PrimitiveId, Object>> typeIterator;
 
         DefaultIterator() {
-            typeIterator = modificationTypes.entrySet().iterator();
+            typeIterator = entryMap.entrySet().iterator();
         }
 
         @Override
@@ -203,8 +280,17 @@
 
         @Override
         public ChangesetDataSetEntry next() {
-            Entry<PrimitiveId, ChangesetModificationType> next = typeIterator.next();
-            return new DefaultChangesetDataSetEntry(next.getValue(), primitives.get(next.getKey()));
+            Entry<PrimitiveId, Object> next = typeIterator.next();
+            // get last entry
+            Object val = next.getValue();
+            ChangesetDataSetEntry last;
+            if (val instanceof ChangesetDataSetEntry[]) {
+                ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+                last = entries[1];
+            } else {
+                last = (ChangesetDataSetEntry) val;
+            }
+            return new DefaultChangesetDataSetEntry(last.getModificationType(), last.getPrimitive());
         }
 
         @Override
Index: src/org/openstreetmap/josm/io/AbstractParser.java
===================================================================
--- src/org/openstreetmap/josm/io/AbstractParser.java	(revision 14936)
+++ src/org/openstreetmap/josm/io/AbstractParser.java	(working copy)
@@ -28,6 +28,8 @@
     /** the current primitive to be read */
     protected HistoryOsmPrimitive currentPrimitive;
     protected Locator locator;
+    /** if true, replace user information in input by anonymous user */
+    protected boolean useAnonymousUser;
 
     @Override
     public void setDocumentLocator(Locator locator) {
@@ -111,17 +113,20 @@
         long changesetId = changeset != null ? changeset : 0L;
         boolean visible = getMandatoryAttributeBoolean(atts, "visible");
 
-        Long uid = getAttributeLong(atts, "uid");
-        String userStr = atts.getValue("user");
-        User user;
-        if (userStr != null) {
-            if (uid != null) {
-                user = User.createOsmUser(uid, userStr);
-                user.setPreferredName(userStr);
-            } else {
-                user = User.createLocalUser(userStr);
+        User user = null;
+        if (!useAnonymousUser) {
+            Long uid = getAttributeLong(atts, "uid");
+            String userStr = atts.getValue("user");
+            if (userStr != null) {
+                if (uid != null) {
+                    user = User.createOsmUser(uid, userStr);
+                    user.setPreferredName(userStr);
+                } else {
+                    user = User.createLocalUser(userStr);
+                }
             }
-        } else {
+        }
+        if (user == null) {
             user = User.getAnonymous();
         }
 
Index: src/org/openstreetmap/josm/io/OsmChangesetContentParser.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmChangesetContentParser.java	(revision 14936)
+++ src/org/openstreetmap/josm/io/OsmChangesetContentParser.java	(working copy)
@@ -34,6 +34,9 @@
     private final ChangesetDataSet data = new ChangesetDataSet();
 
     private class Parser extends AbstractParser {
+        Parser(boolean useAnonymousUser) {
+            this.useAnonymousUser = useAnonymousUser;
+        }
 
         /** the current change modification type */
         private ChangesetDataSet.ChangesetModificationType currentModificationType;
@@ -120,7 +123,6 @@
      * @param source the input stream with the changeset content as XML document. Must not be null.
      * @throws IllegalArgumentException if source is {@code null}.
      */
-    @SuppressWarnings("resource")
     public OsmChangesetContentParser(InputStream source) {
         CheckParameterUtil.ensureParameterNotNull(source, "source");
         this.source = new InputSource(new InputStreamReader(source, StandardCharsets.UTF_8));
@@ -146,6 +148,19 @@
      * exceptions.
      */
     public ChangesetDataSet parse(ProgressMonitor progressMonitor) throws XmlParsingException {
+        return parse(progressMonitor, false);
+    }
+
+    /**
+     * Parses the content.
+     *
+     * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
+     * @param useAnonymousUser if true, replace all user information with the anonymous user
+     * @return the parsed data
+     * @throws XmlParsingException if something went wrong. Check for chained
+     * exceptions.
+     */
+    public ChangesetDataSet parse(ProgressMonitor progressMonitor, boolean useAnonymousUser) throws XmlParsingException {
         if (progressMonitor == null) {
             progressMonitor = NullProgressMonitor.INSTANCE;
         }
@@ -152,7 +167,7 @@
         try {
             progressMonitor.beginTask("");
             progressMonitor.indeterminateSubTask(tr("Parsing changeset content ..."));
-            XmlUtils.parseSafeSAX(source, new Parser());
+            XmlUtils.parseSafeSAX(source, new Parser(useAnonymousUser));
         } catch (XmlParsingException e) {
             throw e;
         } catch (ParserConfigurationException | SAXException | IOException e) {
@@ -171,6 +186,6 @@
      * exceptions.
      */
     public ChangesetDataSet parse() throws XmlParsingException {
-        return parse(null);
+        return parse(null, false);
     }
 }
Index: src/org/openstreetmap/josm/io/OsmServerChangesetReader.java
===================================================================
--- src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 14936)
+++ src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(working copy)
@@ -26,8 +26,26 @@
  *
  */
 public class OsmServerChangesetReader extends OsmServerReader {
+    final boolean useAnonymousUser;
 
     /**
+     * Constructs a new {@code OsmServerChangesetReader} with default settings.
+     */
+    public OsmServerChangesetReader() {
+        this(false);
+    }
+
+    /**
+     * Constructs a new {@code OsmServerChangesetReader}
+     * @param useAnonymousUser if true, replace all user information with the anonymous user
+     * @since xxx
+     */
+    public OsmServerChangesetReader(boolean useAnonymousUser) {
+        super();
+        this.useAnonymousUser = useAnonymousUser;
+    }
+
+    /**
      * don't use - not implemented!
      */
     @Override
@@ -198,7 +216,7 @@
                     return null;
                 monitor.setCustomText(tr("Downloading content for changeset {0} ...", id));
                 OsmChangesetContentParser parser = new OsmChangesetContentParser(in);
-                result = parser.parse(monitor.createSubTaskMonitor(1, true));
+                result = parser.parse(monitor.createSubTaskMonitor(1, true), useAnonymousUser);
             } catch (IOException e) {
                 Logging.warn(e);
             }
Index: test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java	(revision 14936)
+++ test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java	(working copy)
@@ -8,7 +8,6 @@
 
 import java.util.Date;
 import java.util.Iterator;
-import java.util.Set;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -17,7 +16,6 @@
 import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetDataSetEntry;
 import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType;
 import org.openstreetmap.josm.data.osm.history.HistoryNode;
-import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -36,42 +34,6 @@
     public JOSMTestRules test = new JOSMTestRules();
 
     /**
-     * Unit test of method {@link ChangesetDataSet#getPrimitivesByModificationType}.
-     */
-    @Test
-    public void testGetPrimitivesByModificationType() {
-        final ChangesetDataSet cds = new ChangesetDataSet();
-        // empty object, null parameter => IllegalArgumentException
-        try {
-            cds.getPrimitivesByModificationType(null);
-            fail("Should have thrown an IllegalArgumentException as we gave a null argument.");
-        } catch (IllegalArgumentException e) {
-            Logging.trace(e);
-            // Was expected
-        }
-
-        // empty object, a modification type => empty list
-        assertTrue(
-            "Empty data set should produce an empty list.",
-            cds.getPrimitivesByModificationType(
-                    ChangesetModificationType.CREATED).isEmpty()
-        );
-
-        // object with various items and modification types, fetch for CREATED
-        // => list containing only the CREATED item
-        HistoryNode prim1 = new HistoryNode(1, 1, true, User.getAnonymous(), 1, new Date(), LatLon.ZERO);
-        HistoryNode prim2 = new HistoryNode(2, 1, true, User.createLocalUser("test"), 1, new Date(), LatLon.NORTH_POLE);
-        HistoryNode prim3 = new HistoryNode(3, 1, true, User.getAnonymous(), 1, new Date(), LatLon.SOUTH_POLE);
-        cds.put(prim1, ChangesetModificationType.CREATED);
-        cds.put(prim2, ChangesetModificationType.DELETED);
-        cds.put(prim3, ChangesetModificationType.UPDATED);
-        Set<HistoryOsmPrimitive> result = cds.getPrimitivesByModificationType(
-                    ChangesetModificationType.CREATED);
-        assertEquals("We should have found only one item.", 1, result.size());
-        assertTrue("The item found is prim1.", result.contains(prim1));
-    }
-
-    /**
      * Unit test of method {@link ChangesetDataSet#iterator}.
      */
     @Test
