Extenote
← All Docs

Backup and Undo System

Every destructive operation in Extenote (tag renames, deletes, merges) creates a backup first. You can undo any operation with a single command, restoring your files to their previous state.

Note: Backup/undo is still lightly tested. Keep version control and verify restores before relying on it.

Step 1: creates a backup of specified files

Before modifying files, Extenote saves their current contents. The backup includes metadata about what operation was performed.

Test: creates a backup of specified files File: packages/cli/tests/backup.test.ts:36

it("creates a backup of specified files", async () => {
      // Create test files
      const file1 = path.join(tempDir, "file1.md");
      const file2 = path.join(tempDir, "file2.md");
      await fs.writeFile(file1, "content 1");
      await fs.writeFile(file2, "content 2");

      const backupId = await createBackup(tempDir, "test operation", [
        file1,
        file2,
      ]);

      expect(backupId).toBeDefined();
      expect(typeof backupId).toBe("string");

      const backups = await listBackups(tempDir);
      expect(backups.length).toBe(1);
      expect(backups[0].operation).toBe("test operation");
      expect(backups[0].files.length).toBe(2);
    });

Step 2: restores files from the last backup

Running “extenote undo” restores files from the most recent backup. The original content is written back, and the backup is consumed (removed).

Test: restores files from the last backup File: packages/cli/tests/backup.test.ts:94

it("restores files from the last backup", async () => {
      const file = path.join(tempDir, "test.md");
      await fs.writeFile(file, "original content");

      await createBackup(tempDir, "test", [file]);

      // Modify the file
      await fs.writeFile(file, "modified content");

      const result = await undoLastOperation(tempDir);

      expect(result.success).toBe(true);
      expect(result.filesRestored).toBe(1);

      const restored = await fs.readFile(file, "utf8");
      expect(restored).toBe("original content");
    });

Step 3: handles multiple backups correctly (LIFO order)

Multiple operations create stacked backups. Undo works in LIFO (last-in-first-out) order: each undo reverts one operation, and you can keep undoing to go further back.

Test: handles multiple backups correctly (LIFO order) File: packages/cli/tests/backup.test.ts:140

it("handles multiple backups correctly (LIFO order)", async () => {
      const file = path.join(tempDir, "test.md");

      // First backup
      await fs.writeFile(file, "version 1");
      await createBackup(tempDir, "first", [file]);

      // Second backup
      await fs.writeFile(file, "version 2");
      await createBackup(tempDir, "second", [file]);

      // Current state
      await fs.writeFile(file, "version 3");

      // Undo last (second) backup
      await undoLastOperation(tempDir);
      expect(await fs.readFile(file, "utf8")).toBe("version 2");

      // Undo first backup
      await undoLastOperation(tempDir);
      expect(await fs.readFile(file, "utf8")).toBe("version 1");
    });

This documentation is generated from test annotations. Edit the source test file to update.