diff --git a/file.cpp b/file.cpp
index 82535cf..4a2f35c 100644
--- a/file.cpp
+++ b/file.cpp
@@ -6,6 +6,29 @@ static int StrStartsWith(char *str, char *start) {
     return memcmp(str, start, strlen(start)) == 0;
 }
 
+//-----------------------------------------------------------------------------
+// Clear and free all the dynamic memory associated with our currently-loaded
+// sketch. This does not leave the program in an acceptable state (with the
+// references created, and so on), so anyone calling this must fix that later.
+//-----------------------------------------------------------------------------
+void SolveSpace::ClearExisting(void) {
+    UndoClearStack(&redo);
+    UndoClearStack(&undo);
+
+    Group *g;
+    for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
+        g->Clear();
+    }
+
+    SK.constraint.Clear();
+    SK.request.Clear();
+    SK.group.Clear();
+    SK.style.Clear();
+
+    SK.entity.Clear();
+    SK.param.Clear();
+}
+
 hGroup SolveSpace::CreateDefaultDrawingGroup(void) {
     Group g;
     ZERO(&g);
@@ -24,16 +47,7 @@ hGroup SolveSpace::CreateDefaultDrawingGroup(void) {
 }
 
 void SolveSpace::NewFile(void) {
-    UndoClearStack(&redo);
-    UndoClearStack(&undo);
-
-    SK.constraint.Clear();
-    SK.request.Clear();
-    SK.group.Clear();
-    SK.style.Clear();
-
-    SK.entity.Clear();
-    SK.param.Clear();
+    ClearExisting();
 
     // Our initial group, that contains the references.
     Group g;
@@ -393,15 +407,8 @@ bool SolveSpace::LoadFromFile(char *filename) {
         return false;
     }
 
-    UndoClearStack(&redo);
-    UndoClearStack(&undo);
+    ClearExisting();
 
-    SK.constraint.Clear();
-    SK.request.Clear();
-    SK.group.Clear();
-    SK.entity.Clear();
-    SK.param.Clear();
-    SK.style.Clear();
     memset(&sv, 0, sizeof(sv));
     sv.g.scale = 1; // default is 1, not 0; so legacy files need this
 
diff --git a/group.cpp b/group.cpp
index c685807..2ed79ff 100644
--- a/group.cpp
+++ b/group.cpp
@@ -7,6 +7,27 @@ const hGroup Group::HGROUP_REFERENCES = { 1 };
 
 #define gs (SS.GW.gs)
 
+//-----------------------------------------------------------------------------
+// The group structure includes pointers to other dynamically-allocated
+// memory. This clears and frees them all.
+//-----------------------------------------------------------------------------
+void Group::Clear(void) {
+    polyLoops.Clear();
+    bezierLoops.Clear();
+    bezierOpens.Clear();
+    thisMesh.Clear();
+    runningMesh.Clear();
+    thisShell.Clear();
+    runningShell.Clear();
+    displayMesh.Clear();
+    displayEdges.Clear();
+    impMesh.Clear();
+    impShell.Clear();
+    impEntity.Clear();
+    // remap is the only one that doesn't get recreated when we regen
+    remap.Clear();
+}
+
 void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
     Param pa;
     memset(&pa, 0, sizeof(pa));
diff --git a/sketch.h b/sketch.h
index c02de88..17cac4d 100644
--- a/sketch.h
+++ b/sketch.h
@@ -189,6 +189,7 @@ public:
 
     void Activate(void);
     char *DescriptionString(void);
+    void Clear(void);
 
     static void AddParam(ParamList *param, hParam hp, double v);
     void Generate(EntityList *entity, ParamList *param);
diff --git a/solvespace.h b/solvespace.h
index 5f1eed2..c57d2cb 100644
--- a/solvespace.h
+++ b/solvespace.h
@@ -679,6 +679,7 @@ public:
     bool OkayToStartNewFile(void);
     hGroup CreateDefaultDrawingGroup(void);
     void UpdateWindowTitle(void);
+    void ClearExisting(void);
     void NewFile(void);
     bool SaveToFile(char *filename);
     bool LoadFromFile(char *filename);
diff --git a/undoredo.cpp b/undoredo.cpp
index ac76a00..bd30a4b 100644
--- a/undoredo.cpp
+++ b/undoredo.cpp
@@ -93,23 +93,10 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
 
     UndoState *ut = &(uk->d[uk->write]);
 
-    int i;
     // Free everything in the main copy of the program before replacing it
-    for(i = 0; i < SK.group.n; i++) {
-        Group *g = &(SK.group.elem[i]);
-        g->polyLoops.Clear();
-        g->bezierLoops.Clear();
-        g->bezierOpens.Clear();
-        g->thisMesh.Clear();
-        g->runningMesh.Clear();
-        g->thisShell.Clear();
-        g->runningShell.Clear();
-        g->displayMesh.Clear();
-        g->displayEdges.Clear();
-        g->remap.Clear();
-        g->impMesh.Clear();
-        g->impShell.Clear();
-        g->impEntity.Clear();
+    Group *g;
+    for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
+        g->Clear();
     }
     SK.group.Clear();
     SK.request.Clear();
diff --git a/wishlist.txt b/wishlist.txt
index 9415e51..bcb3d88 100644
--- a/wishlist.txt
+++ b/wishlist.txt
@@ -1,7 +1,6 @@
 replace show/hide links with icons
 add checked/unchecked checkbox and radio button
 fix bug with rotation in plane where green line stays displayed
-fix memory leak when switching between files
 area of sketch in a given workplane (easy, useful)
 lock point where dragged constraint
 remove toolbar icons for sketch in 3d, add View -> Align to Workplane