revisions: a save keeps what it replaces

Every write of a document's text keeps the text it replaces, in the same
transaction. Autosave writes every few seconds, so an ordinary save keeps
one at most every ten minutes; `:w!`, a save over the SFTP mount and a
restore always keep one, because each may undo something. A rename alone,
or a document that was empty, keeps nothing.

`gh` or `:history` lists the open document's earlier versions, the newest
first, and shows the highlighted one as a line diff against the text as it
is now. Enter brings it back, and what it replaces becomes a version of
its own, so a restore is undone the same way.

The daily sweep forgets versions older than WORKSPACE_REVISION_DAYS (90)
but never the newest 20 of a document, and keeps an upload as long as a
version still embeds it. Versions stay out of the export, the mount and
the readable mirror; a document in the trash shows none.
This commit is contained in:
nvrl committed 2026-10-20 18:40:50 +02:00
1 parent 8ffad6350f
commit fc2be66b0c
17 files changed
+887 -31

No files matched your search

+7
View File
@@ -24,6 +24,9 @@ pub struct Config {
pub vault_idle: std::time::Duration,
/// How many days a deleted node waits in the trash before it is purged.
pub trash_days: i64,
/// How many days a document's earlier versions are kept, past the newest
/// few that always are.
pub revision_days: i64,
pub backup: BackupConfig,
pub mail: MailConfig,
pub sftp: SftpConfig,
@@ -44,6 +47,7 @@ impl std::fmt::Debug for Config {
.field("secure_cookie", &self.secure_cookie)
.field("vault_idle", &self.vault_idle)
.field("trash_days", &self.trash_days)
.field("revision_days", &self.revision_days)
.field("backup", &self.backup)
.field("mail", &self.mail)
.field("sftp", &self.sftp)
@@ -293,6 +297,7 @@ impl Config {
// A day at least, so an undo is never raced by the sweep; ten years
// at most, as for the sessions.
let trash_days = number("TRASH_DAYS", 30, 1..=3650)? as i64;
let revision_days = number("REVISION_DAYS", 90, 1..=3650)? as i64;
let secure_cookie = flag("SECURE_COOKIE")?;
Ok(Self {
@@ -306,6 +311,7 @@ impl Config {
secure_cookie,
vault_idle,
trash_days,
revision_days,
backup: BackupConfig::from_env()?,
mail: MailConfig::from_env()?,
sftp: SftpConfig::from_env()?,
@@ -344,6 +350,7 @@ impl Config {
secure_cookie: false,
vault_idle: std::time::Duration::from_secs(60),
trash_days: 30,
revision_days: 90,
backup: BackupConfig {
device_name: "narl-diary".into(),
interval: None,