Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryAction.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryAction.java	(revision 28327)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryAction.java	(revision 28335)
@@ -46,10 +46,19 @@
         
         try {
-            ReplaceGeometryUtils.replaceWithNew(firstObject, secondObject);
+            ReplaceGeometryCommand replaceCommand =
+                    ReplaceGeometryUtils.buildReplaceWithNewCommand(firstObject, secondObject);
+            
+            // action was canceled
+            if (replaceCommand == null)
+                return;
+            
+            Main.main.undoRedo.add(replaceCommand);
         } catch (IllegalArgumentException ex) {
             JOptionPane.showMessageDialog(Main.parent,
                     ex.getMessage(), TITLE, JOptionPane.INFORMATION_MESSAGE);
+        } catch (ReplaceGeometryException ex) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    ex.getMessage(), TITLE, JOptionPane.INFORMATION_MESSAGE);
         }
-         
     }
 
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryCommand.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryCommand.java	(revision 28335)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryCommand.java	(revision 28335)
@@ -0,0 +1,32 @@
+package org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry;
+
+import java.util.Collection;
+import javax.swing.Icon;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Command to replace the geometry of one object with another.
+ * 
+ * @author joshdoe
+ */
+public class ReplaceGeometryCommand extends SequenceCommand {
+    private final String description;
+    
+    public ReplaceGeometryCommand(String description, Collection<Command> sequence) {
+        super(description, sequence);
+        this.description = description;
+    }
+
+    @Override
+    public String getDescriptionText() {
+        return description;
+    }
+    
+    @Override
+    public Icon getDescriptionIcon() {
+        return ImageProvider.get("dumbutils", "replacegeometry");
+    }
+    
+}
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryException.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryException.java	(revision 28335)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryException.java	(revision 28335)
@@ -0,0 +1,11 @@
+package org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry;
+
+/**
+ *
+ * @author joshdoe
+ */
+public class ReplaceGeometryException extends RuntimeException {
+    public ReplaceGeometryException(String message) {
+        super(message);
+    }
+}
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryUtils.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryUtils.java	(revision 28327)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/replacegeometry/ReplaceGeometryUtils.java	(revision 28335)
@@ -21,5 +21,4 @@
 public final class ReplaceGeometryUtils {
     private static final String TITLE = tr("Replace Geometry");
-    
     /**
      * Replace new or uploaded object with new object
@@ -29,13 +28,13 @@
      * @return 
      */
-    public static boolean replaceWithNew(OsmPrimitive firstObject, OsmPrimitive secondObject) {
+    public static ReplaceGeometryCommand buildReplaceWithNewCommand(OsmPrimitive firstObject, OsmPrimitive secondObject) {
         if (firstObject instanceof Node && secondObject instanceof Node) {
-            return replaceNodeWithNew((Node) firstObject, (Node) secondObject);
+            return buildReplaceNodeWithNewCommand((Node) firstObject, (Node) secondObject);
         } else if (firstObject instanceof Way && secondObject instanceof Way) {
-            return replaceWayWithNew(Arrays.asList((Way) firstObject, (Way) secondObject));
+            return buildReplaceWayWithNewCommand(Arrays.asList((Way) firstObject, (Way) secondObject));
         } else if (firstObject instanceof Node) {
-            return upgradeNode((Node) firstObject, secondObject);
+            return createUpgradeNodeCommand((Node) firstObject, secondObject);
         } else if (secondObject instanceof Node) {
-            return upgradeNode((Node) secondObject, firstObject);
+            return createUpgradeNodeCommand((Node) secondObject, firstObject);
         } else {
             throw new IllegalArgumentException(tr("This tool can only replace a node, upgrade a node to a way or a multipolygon, or replace a way with a way."));
@@ -51,14 +50,14 @@
      * @return 
      */
-    public static boolean replace(OsmPrimitive subjectObject, OsmPrimitive referenceSubject) {
+    public static ReplaceGeometryCommand buildReplaceCommand(OsmPrimitive subjectObject, OsmPrimitive referenceSubject) {
         if (subjectObject instanceof Node && referenceSubject instanceof Node) {
-            return replaceNode((Node) subjectObject, (Node) referenceSubject);
+            return buildReplaceNodeCommand((Node) subjectObject, (Node) referenceSubject);
         } else if (subjectObject instanceof Way && referenceSubject instanceof Way) {
-            return replaceWay((Way) subjectObject, (Way) referenceSubject);
+            return buildReplaceWayCommand((Way) subjectObject, (Way) referenceSubject);
         } else if (subjectObject instanceof Node) {
-            return upgradeNode((Node) subjectObject, referenceSubject);
+            return createUpgradeNodeCommand((Node) subjectObject, referenceSubject);
         } else if (referenceSubject instanceof Node) {
             // TODO: fix this illogical reversal?
-            return upgradeNode((Node) referenceSubject, subjectObject);
+            return createUpgradeNodeCommand((Node) referenceSubject, subjectObject);
         } else {
             throw new IllegalArgumentException(tr("This tool can only replace a node, upgrade a node to a way or a multipolygon, or replace a way with a way."));
@@ -72,13 +71,13 @@
      * @return 
      */
-    public static boolean replaceNodeWithNew(Node firstNode, Node secondNode) {
+    public static ReplaceGeometryCommand buildReplaceNodeWithNewCommand(Node firstNode, Node secondNode) {
         if (firstNode.isNew() && !secondNode.isNew())
-            return replaceNode(secondNode, firstNode);
+            return buildReplaceNodeCommand(secondNode, firstNode);
         else if (!firstNode.isNew() && secondNode.isNew())
-            return replaceNode(firstNode, secondNode);
+            return buildReplaceNodeCommand(firstNode, secondNode);
         else
             // both nodes are new OR uploaded, act like MergeNodes, moving first
             // node to second
-            return replaceNode(firstNode, secondNode);
+            return buildReplaceNodeCommand(firstNode, secondNode);
     }
     
@@ -90,9 +89,7 @@
      * @return
      */
-    public static boolean replaceNode(Node subjectNode, Node referenceNode) {
+    public static ReplaceGeometryCommand buildReplaceNodeCommand(Node subjectNode, Node referenceNode) {
         if (!OsmPrimitive.getFilteredList(subjectNode.getReferrers(), Way.class).isEmpty()) {
-            JOptionPane.showMessageDialog(Main.parent, tr("Node belongs to way(s), cannot replace."),
-                    TITLE, JOptionPane.INFORMATION_MESSAGE);
-            return false;
+            throw new ReplaceGeometryException(tr("Node belongs to way(s), cannot replace."));
         }
         // FIXME: handle different layers
@@ -100,8 +97,7 @@
         commands.add(MergeNodesAction.mergeNodes(Main.main.getEditLayer(), Arrays.asList(subjectNode, referenceNode), referenceNode));
 
-        Main.main.undoRedo.add(new SequenceCommand(
+        return new ReplaceGeometryCommand(
                 tr("Replace geometry for node {0}", subjectNode.getDisplayName(DefaultNameFormatter.getInstance())),
-                commands));
-        return true;
+                commands);
     }
     
@@ -112,15 +108,11 @@
      * @param referenceObject object with greater spatial quality
      */
-    public static boolean upgradeNode(Node subjectNode, OsmPrimitive referenceObject) {
+    public static ReplaceGeometryCommand createUpgradeNodeCommand(Node subjectNode, OsmPrimitive referenceObject) {
         if (!OsmPrimitive.getFilteredList(subjectNode.getReferrers(), Way.class).isEmpty()) {
-            JOptionPane.showMessageDialog(Main.parent, tr("Node belongs to way(s), cannot replace."),
-                    TITLE, JOptionPane.INFORMATION_MESSAGE);
-            return false;
+            throw new ReplaceGeometryException(tr("Node belongs to way(s), cannot replace."));
         }
 
         if (referenceObject instanceof Relation && !((Relation) referenceObject).isMultipolygon()) {
-            JOptionPane.showMessageDialog(Main.parent, tr("Relation is not a multipolygon, cannot be used as a replacement."),
-                    TITLE, JOptionPane.INFORMATION_MESSAGE);
-            return false;
+            throw new ReplaceGeometryException(tr("Relation is not a multipolygon, cannot be used as a replacement."));
         }
 
@@ -154,5 +146,5 @@
         if (tagResolutionCommands == null) {
             // user canceled tag merge dialog
-            return false;
+            return null;
         }
         commands.addAll(tagResolutionCommands);
@@ -187,11 +179,10 @@
         Main.main.getCurrentDataSet().setSelected(referenceObject);
 
-        Main.main.undoRedo.add(new SequenceCommand(
+        return new ReplaceGeometryCommand(
                 tr("Replace geometry for node {0}", subjectNode.getDisplayName(DefaultNameFormatter.getInstance())),
-                commands));
-        return true;
-    }
-    
-    public static boolean replaceWayWithNew(List<Way> selection) {
+                commands);
+    }
+    
+    public static ReplaceGeometryCommand buildReplaceWayWithNewCommand(List<Way> selection) {
         // determine which way will be replaced and which will provide the geometry
         boolean overrideNewCheck = false;
@@ -217,27 +208,20 @@
         
         if( !overrideNewCheck && (subjectWay.isNew() || !referenceWay.isNew()) ) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("Please select one way that exists in the database and one new way with correct geometry."),
-                    TITLE, JOptionPane.WARNING_MESSAGE);
-            return false;
-        }
-        return replaceWay(subjectWay, referenceWay);
-    }
-    
-    public static boolean replaceWay(Way subjectWay, Way referenceWay) {
+            throw new ReplaceGeometryException(
+                    tr("Please select one way that exists in the database and one new way with correct geometry."));
+        }
+        return buildReplaceWayCommand(subjectWay, referenceWay);
+    }
+    
+    public static ReplaceGeometryCommand buildReplaceWayCommand(Way subjectWay, Way referenceWay) {
 
         Area a = Main.main.getCurrentDataSet().getDataSourceArea();
         if (!isInArea(subjectWay, a) || !isInArea(referenceWay, a)) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("The ways must be entirely within the downloaded area."),
-                    TITLE, JOptionPane.WARNING_MESSAGE);
-            return false;
+            throw new ReplaceGeometryException(tr("The ways must be entirely within the downloaded area."));
         }
         
         if (hasImportantNode(referenceWay, subjectWay)) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("The way to be replaced cannot have any nodes with properties or relation memberships unless they belong to both ways."),
-                    TITLE, JOptionPane.WARNING_MESSAGE);
-            return false;
+            throw new ReplaceGeometryException(
+                    tr("The way to be replaced cannot have any nodes with properties or relation memberships unless they belong to both ways."));
         }
 
@@ -248,5 +232,5 @@
         if (tagResolutionCommands == null) {
             // user canceled tag merge dialog
-            return false;
+            return null;
         }
         commands.addAll(tagResolutionCommands);
@@ -357,8 +341,7 @@
 
         // Two items in undo stack: change original way and delete geometry way
-        Main.main.undoRedo.add(new SequenceCommand(
+        return new ReplaceGeometryCommand(
                 tr("Replace geometry for way {0}", subjectWay.getDisplayName(DefaultNameFormatter.getInstance())),
-                commands));
-        return true;
+                commands);
     }
 
