So where do you save the changes ?
The only way to save i.e. tracks would be to save the previous tracks array to a file or to memory.
On a large pcb you could be talking mega bytes of memory.
And that's just for tracks, what about text, components etc ?
Undo should only be saving a diff. If you're copying your full dataset for every mutation you're asking for trouble.
What if say someone removed 90% of the tracks that were 10 megabytes ?
Then they press undo, those 9 megabytes need to come from somewhere.
A simple single or double level undo wouldn't be too bad to do but an infinite one would seriously affect performance.

10 megabytes ? for tracks ?
let's look at this.
A segment is defined by a start pointpair and an end pointpair, a width, a layer and a netname. and a couple of flags ( like in selection )
Assuming 32 bit numbers (4 bytes per number ) (wasting storage like crazy cause you don't need allocation for billions of widths and netnames)
segment database example:
[startx : 4 bytes][ starty : 4 bytes][stopx : 4 bytes][stopy : 4 bytes][width index : 4 byte][layer index : 4 byte][flags : 4 byte][netname index : 4 byte] = 32 bytes
32 kilobyte gives you 1000 segments.
10 megabyte could hold 327680 segments ... i doubt you'll ever design board with that many segments.My largest one was 76000 tracks. and that was a 16 layer monster using blind and buried.
a pad or via could be 16 bytes (x and y each 4 bytes, 2 bytes to index the shape , 2 bytes in flags, 4 bytes to index net
A component would be similar
x and y location , a pointer to the symbol table , 2 bits for rotation , 2 bits for mirroring , a selection bit.
I'm starting to think your internal data structures are problematic.
PCB cad programs are largely a relational database. They use a database like system with tables. There is a table that holds tracks , one that holds via's, one that holds track widths, one that holds pad shapes , and so on.
Every entity on screen exists in one table. Finding things is running sort operations on fields in the tables and following links.
if your design has 1 million vias you do not need to allocate memory for 1 million different sizes. You only a 1 million entry table for the coordinates and a pointer to the via definition.
the via definition is a separate table that holds diameter and hole size ( and possibly start and stop layer if your software supports blind and buried or backdrilling , or another link to an advanced definition if you will do things like copper removal on unused layers, or have different annular rings per layer )
You could have a million via's but if they are all the same size that via table is only 1 record. not 1 million records. you don't need to store all that information per via.
The cad tools also store a link to the next connecting segment. As in: the start coordinate for this one coincides with the end coordinate of that one , and the end coordinate of this one coincides with the begin coordinate of yet another one. That way it is easy to parse the connectivity. You not have to analyse the entire table for interference checking, you just follow a linked list.
track 3904 : comesfrom -nothing- , beginx, beginy, endx,endy, goesto track 3905, width 45 , netname 703 , layer 27 , flags none
track 3905 : comesfrom 3904 , beginx , beginy, endx,endy, goesto track 3906, width 9 , netname 703 , layer 27 , flags inselection
track 3905 : comesfrom 3905 , beginx , beginy, endx,endy, goesto -nothing-, width 45 , netname 703 , layer 27 , flags none
the above would be a track made up from 3 segments, middle segment having a different width and being selected.
having the index pointers to the continuation segments available makes it easy to manipulate bendpoints. if i move a point i need to update the endpoint coordinate of the previous or next track and the begin point of the current segment. instant rubberbanding without running interference checking.
checking for missing connectivity (ratsnest display) is simply checking that everything connects to something. when a link in the database is missing : that's a broken connection.