How to recover to the specified version

I am using this code to recovery the y doc history version:

export function restoreFromHistory(version:number, docId: string) {
    if(ydoc){
        const snapshot = history[version];
        Y.applyUpdate(ydoc,snapshot);
        const txt = ydoc.getText(docId);
        console.log(txt.toString());
    }
}

it seems the update to specify version did not work. the online codemirror6 editor always show the latest version text. This is how to store the history version:

let history: Uint8Array[] = [];
var ydoc:Y.Doc;
export function saveHistory(docId: string){
    const snapshot = Y.encodeStateAsUpdate(ydoc);
    history.push(snapshot);
    const text = ydoc.getText(docId);
    console.log(text.toString());
}

Please see my reply to your other question here and read these existing posts:

1 Like

Hello @raine,

I’ve read all the posts you shared, but I still cannot figure out a way to revert changes given a yjs document and a certain snapshot. I understand that to keep using the same document, an update that reverse the changes must be calculated from a snapshot, and transacted to the document.

It doesn’t seem that any of the examples is able to revert an entire document, but only an item in the document.

Kindly can you provide sample code that can do that? In all of the posts you mentioned, there isn’t any official code that is recommended by the y.js team.

Here’s an example of how to create, encode, decode, and restore a snapshot. Hope this helps!

https://codesandbox.io/p/devbox/yjs-snapshot-mk3yc4

I believe the code you provided restores a snapshot, but doesn’t revert it into the same document (create new history).

By using the examples written in Is there a way to revert to specific version? - #6 by Himself65, I found that this code works:

export function revertChangesSinceSnapshot(doc: Doc, snapshot: Uint8Array) {
  const snap = Y.decodeSnapshot(snapshot);
  doc.gc = false;
  const tempdoc = Y.createDocFromSnapshot(doc, snap);

  const currentStateVector = Y.encodeStateVector(doc);
  const snapshotStateVector = Y.encodeStateVector(tempdoc);

  const changesSinceSnapshotUpdate = Y.encodeStateAsUpdate(doc, snapshotStateVector);

  const um = new Y.UndoManager(
    [...tempdoc.share.values()]
  );

  Y.applyUpdate(tempdoc, changesSinceSnapshotUpdate);
  um.undo();

  const revertChangesSinceSnapshotUpdate = Y.encodeStateAsUpdate(tempdoc, currentStateVector);
  Y.applyUpdate(doc, revertChangesSinceSnapshotUpdate, {
    user: {
      id: 'revert'
    }
  });
  doc.gc = true;
}

However I am unsure if handling doc.gc like this is the correct way when the document might be getting edited at the same time. There is quite limited information documented about “gc”. Is it fine to enable it and disable it as such? Or should this be kind of a permanent setting on the document when it’s first created? Would this ever cause snapshots to break or format to be invalid?

Ah, I understand now. You want a git revert style restoration that updates the doc with a diff back to the snapshot state.

It’s very clever actually. There is no risk of concurrent code running in between doc.gc = false and doc.gc = true since they are in a synchronous function. Javascript runs synchronous functions to the end before yielding to another task.

As for the robustness of this technique, it primarily depends on the behavior of UndoManager, which I am not personally familiar with. The technique reaches into the Doc and uses some YJS internals that are not part of its public API, so it may be fragile to YJS upgrades. But for the time being it could work perfectly fine. Perhaps @Himself65 can comment.

Yeah, I believe doc.gc will be used the next time a doc is updated. So It will be safe because browser is single thread environment.

But here is some trick in tempdoc.share.values() after months I have developed in CRDT is that maybe type of each value is not defined yet before you call getYX()

So there is a updated code you can check