Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java	(revision 31257)
@@ -8,6 +8,6 @@
 	public final LatLon latLon;
 	/** Direction of the picture */
-	protected final double ca;
-	private boolean isModified = false;
+	public final double ca;
+	public boolean isModified = false;
 	/** Temporal position of the picture until it is uplaoded */
 	public LatLon tempLatLon;
@@ -18,5 +18,5 @@
 	public LatLon movingLatLon;
 	/** Temporal direction of the picture until it is uplaoded */
-	protected double tempCa;
+	public double tempCa;
 	/**
 	 * When the object direction is being moved in the map, the temporal
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31257)
@@ -6,4 +6,5 @@
 import org.openstreetmap.josm.plugins.mapillary.cache.MapillaryCache;
 import org.openstreetmap.josm.plugins.mapillary.downloads.MapillaryDownloader;
+import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryHistoryDialog;
 import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryToggleDialog;
 import org.openstreetmap.josm.Main;
@@ -62,5 +63,7 @@
 
 	private List<Bounds> bounds;
-	private MapillaryToggleDialog tgd;
+
+	private MapillaryToggleDialog mtd;
+	private MapillaryHistoryDialog mhd;
 
 	private MouseAdapter mouseAdapter;
@@ -91,10 +94,17 @@
 			MapView.addLayerChangeListener(this);
 			Main.map.mapView.getEditLayer().data.addDataSetListener(this);
-			if (tgd == null) {
+			if (mtd == null) {
 				if (MapillaryToggleDialog.INSTANCE == null) {
-					tgd = MapillaryToggleDialog.getInstance();
-					Main.map.addToggleDialog(tgd, false);
+					mtd = MapillaryToggleDialog.getInstance();
+					Main.map.addToggleDialog(mtd, false);
 				} else
-					tgd = MapillaryToggleDialog.getInstance();
+					mtd = MapillaryToggleDialog.getInstance();
+			}
+			if (mhd == null) {
+				if (MapillaryHistoryDialog.INSTANCE == null) {
+					mhd = MapillaryHistoryDialog.getInstance();
+					Main.map.addToggleDialog(mhd, false);
+				} else
+					mhd = MapillaryHistoryDialog.getInstance();
 			}
 		}
@@ -109,5 +119,4 @@
 	}
 
-			
 	public synchronized static MapillaryLayer getInstance() {
 		if (MapillaryLayer.INSTANCE == null)
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandMoveImage.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandMoveImage.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandMoveImage.java	(revision 31257)
@@ -2,4 +2,5 @@
 
 import static org.openstreetmap.josm.tools.I18n.trn;
+
 import java.util.ArrayList;
 import java.util.List;
@@ -15,5 +16,4 @@
  */
 public class CommandMoveImage extends MapillaryCommand {
-	private List<MapillaryAbstractImage> images;
 	private double x;
 	private double y;
@@ -31,4 +31,5 @@
 			image.stopMoving();
 		}
+		checkModified();
 		Main.map.repaint();
 	}
@@ -40,4 +41,5 @@
 			image.stopMoving();
 		}
+		checkModified();
 		Main.map.repaint();
 	}
@@ -46,3 +48,11 @@
 		return trn("Moved {0} node", "Moved {0} nodes", images.size(), images.size());
 	}
+	
+	@Override
+	public void sum(MapillaryCommand command) {
+		if (command instanceof CommandMoveImage) {
+			this.x += ((CommandMoveImage) command).x;
+			this.y += ((CommandMoveImage) command).y;
+		}
+	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandTurnImage.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandTurnImage.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/CommandTurnImage.java	(revision 31257)
@@ -16,5 +16,4 @@
  */
 public class CommandTurnImage extends MapillaryCommand {
-	private List<MapillaryAbstractImage> images;
 	private double ca;
 
@@ -30,4 +29,5 @@
 			image.stopMoving();
 		}
+		checkModified();
 		Main.map.repaint();
 	}
@@ -39,9 +39,18 @@
 			image.stopMoving();
 		}
+		checkModified();
 		Main.map.repaint();
 	}
 
 	public String toString() {
-		return trn("Turned {0} node", "Moved {0} nodes", images.size(), images.size());
+		return trn("Turned {0} node", "Turned {0} nodes", this.images.size(),
+				this.images.size());
+	}
+
+	@Override
+	public void sum(MapillaryCommand command) {
+		if (command instanceof CommandTurnImage) {
+			this.ca += ((CommandTurnImage) command).ca;
+		}
 	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryCommand.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryCommand.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryCommand.java	(revision 31257)
@@ -1,3 +1,7 @@
 package org.openstreetmap.josm.plugins.mapillary.commands;
+
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
 
 /**
@@ -8,7 +12,15 @@
  */
 public abstract class MapillaryCommand {
+	protected List<MapillaryAbstractImage> images;
 
 	public abstract void undo();
 
 	public abstract void redo();
+	
+	public abstract void sum(MapillaryCommand command);
+	
+	public void checkModified() {
+		for (MapillaryAbstractImage image : images)
+			image.isModified = (image.tempLatLon == image.latLon || image.tempCa == image.ca);
+	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecord.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecord.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecord.java	(revision 31257)
@@ -5,4 +5,5 @@
 /**
  * History record system in order to let you undo commands
+ * 
  * @author nokutu
  *
@@ -10,4 +11,6 @@
 public class MapillaryRecord {
 	public static MapillaryRecord INSTANCE;
+
+	private ArrayList<MapillaryRecordListener> listeners;
 
 	public ArrayList<MapillaryCommand> commandList;
@@ -18,4 +21,5 @@
 		commandList = new ArrayList<>();
 		position = -1;
+		listeners = new ArrayList<>();
 	}
 
@@ -26,4 +30,12 @@
 	}
 
+	public void addListener(MapillaryRecordListener lis) {
+		this.listeners.add(lis);
+	}
+
+	public void removeListener(MapillaryRecordListener lis) {
+		this.listeners.remove(lis);
+	}
+
 	/**
 	 * Adds a new command to the list.
@@ -32,4 +44,12 @@
 	 */
 	public void addCommand(MapillaryCommand command) {
+		// Checks if it is a continuation of last command
+		if (position != -1
+				&& commandList.get(position).images.equals(command.images)
+				&& commandList.get(position).getClass() == command.getClass()) {
+			commandList.get(position).sum(command);
+			fireRecordChanged();
+			return;
+		}
 		commandList.add(position + 1, command);
 		position++;
@@ -37,4 +57,5 @@
 			commandList.remove(position + 1);
 		}
+		fireRecordChanged();
 	}
 
@@ -47,4 +68,5 @@
 		commandList.get(position).undo();
 		position--;
+		fireRecordChanged();
 	}
 
@@ -53,8 +75,15 @@
 	 */
 	public void redo() {
-		if (position >= commandList.size())
+		if (position + 1 >= commandList.size())
 			return;
+		position++;
 		commandList.get(position).redo();
-		position++;
+		fireRecordChanged();
+	}
+
+	private void fireRecordChanged() {
+		for (MapillaryRecordListener lis : listeners)
+			if (lis != null)
+				lis.recordChanged();
 	}
 }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecordListener.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecordListener.java	(revision 31257)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/commands/MapillaryRecordListener.java	(revision 31257)
@@ -0,0 +1,5 @@
+package org.openstreetmap.josm.plugins.mapillary.commands;
+
+public interface MapillaryRecordListener {
+	public void recordChanged();
+}
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java	(revision 31256)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java	(revision 31257)
@@ -3,11 +3,165 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+
+import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.plugins.mapillary.commands.MapillaryCommand;
+import org.openstreetmap.josm.plugins.mapillary.commands.MapillaryRecord;
+import org.openstreetmap.josm.plugins.mapillary.commands.MapillaryRecordListener;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
 
-public class MapillaryHistoryDialog extends ToggleDialog {
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public class MapillaryHistoryDialog extends ToggleDialog implements
+		MapillaryRecordListener {
+
+	public static MapillaryHistoryDialog INSTANCE;
+
+	private final DefaultTreeModel undoTreeModel = new DefaultTreeModel(
+			new DefaultMutableTreeNode());
+	private final DefaultTreeModel redoTreeModel = new DefaultTreeModel(
+			new DefaultMutableTreeNode());
+	private final JTree undoTree = new JTree(undoTreeModel);
+	private final JTree redoTree = new JTree(redoTreeModel);
+
+	private JSeparator separator = new JSeparator();
+	private Component spacer = Box.createRigidArea(new Dimension(0, 3));
+
+	private SideButton undoButton;
+	private SideButton redoButton;
 
 	public MapillaryHistoryDialog() {
-		super(tr("Mapillary history"), "mapillary.png",
+		super(tr("Mapillary history"), "mapillaryhistory.png",
 				tr("Open Mapillary history dialog"), null, 200);
+
+		MapillaryRecord.getInstance().addListener(this);
+
+		undoTree.expandRow(0);
+		undoTree.setShowsRootHandles(true);
+		undoTree.setRootVisible(false);
+		undoTree.setCellRenderer(new MapillaryCellRenderer());
+		redoTree.expandRow(0);
+		redoTree.setCellRenderer(new MapillaryCellRenderer());
+		redoTree.setShowsRootHandles(true);
+		redoTree.setRootVisible(false);
+
+		JPanel treesPanel = new JPanel(new GridBagLayout());
+		treesPanel.add(spacer, GBC.eol());
+		spacer.setVisible(false);
+		treesPanel.add(undoTree, GBC.eol().fill(GBC.HORIZONTAL));
+		separator.setVisible(false);
+		treesPanel.add(separator, GBC.eol().fill(GBC.HORIZONTAL));
+		treesPanel.add(redoTree, GBC.eol().fill(GBC.HORIZONTAL));
+		treesPanel.add(Box.createRigidArea(new Dimension(0, 0)), GBC.std()
+				.weight(0, 1));
+		treesPanel.setBackground(redoTree.getBackground());
+
+		undoButton = new SideButton(new UndoAction());
+		redoButton = new SideButton(new RedoAction());
+
+		createLayout(treesPanel, true,
+				Arrays.asList(new SideButton[] { undoButton, redoButton }));
+	}
+
+	public static MapillaryHistoryDialog getInstance() {
+		if (INSTANCE == null)
+			INSTANCE = new MapillaryHistoryDialog();
+		return INSTANCE;
+	}
+
+	private void buildTree() {
+		redoButton.setEnabled(true);
+		undoButton.setEnabled(true);
+		ArrayList<MapillaryCommand> commands = MapillaryRecord.getInstance().commandList;
+		int position = MapillaryRecord.getInstance().position;
+		ArrayList<MapillaryCommand> undoCommands = new ArrayList<>();
+		if (position >= 0)
+			undoCommands = new ArrayList<>(commands.subList(0, position + 1));
+		else
+			undoButton.setEnabled(false);
+		ArrayList<MapillaryCommand> redoCommands = new ArrayList<>();
+		if (commands.size() > 0 && position + 1 < commands.size())
+			redoCommands = new ArrayList<>(commands.subList(position + 1,
+					commands.size()));
+		else
+			redoButton.setEnabled(false);
+
+		DefaultMutableTreeNode redoRoot = new DefaultMutableTreeNode();
+		DefaultMutableTreeNode undoRoot = new DefaultMutableTreeNode();
+
+		for (MapillaryCommand command : undoCommands) {
+			if (command != null)
+				undoRoot.add(new DefaultMutableTreeNode(command.toString()));
+		}
+		for (MapillaryCommand command : redoCommands) {
+			if (command != null)
+				redoRoot.add(new DefaultMutableTreeNode(command.toString()));
+		}
+
+		separator
+				.setVisible(!undoCommands.isEmpty() || !redoCommands.isEmpty());
+		spacer.setVisible(undoCommands.isEmpty() && !redoCommands.isEmpty());
+
+		undoTreeModel.setRoot(undoRoot);
+		redoTreeModel.setRoot(redoRoot);
+	}
+
+	@Override
+	public void recordChanged() {
+		buildTree();
+	}
+
+	private class UndoAction extends AbstractAction {
+
+		public UndoAction() {
+			putValue(NAME, tr("Undo"));
+			putValue(SMALL_ICON, ImageProvider.get("undo"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent arg0) {
+			MapillaryRecord.getInstance().undo();
+		}
+
+	}
+
+	private class RedoAction extends AbstractAction {
+		public RedoAction() {
+			putValue(NAME, tr("Redo"));
+			putValue(SMALL_ICON, ImageProvider.get("redo"));
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent arg0) {
+			MapillaryRecord.getInstance().redo();
+		}
+
+	}
+
+	private static class MapillaryCellRenderer extends DefaultTreeCellRenderer {
+		@Override
+		public Component getTreeCellRendererComponent(JTree tree, Object value,
+				boolean sel, boolean expanded, boolean leaf, int row,
+				boolean hasFocus) {
+			super.getTreeCellRendererComponent(tree, value, sel, expanded,
+					leaf, row, hasFocus);
+			setIcon(ImageProvider.get("data/node.png"));
+			return this;
+		}
 	}
 }
