From 67fa0d1a28335a39fb0cd5d802624b5c2799de01 Mon Sep 17 00:00:00 2001 From: claude-stand Date: Thu, 2 Jul 2026 19:12:15 +0300 Subject: [PATCH] fix(git-sync): a git-revert of a page delete restores the page (review warning) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A git-side revert of a delete commit re-adds the page's .md; the push classifier saw an add carrying a known pageId and emitted an UPDATE, writing the body to the still-trashed page. It stayed in Trash and the next pull re-deleted the file, so the revert was silently nullified (permanent vault<->Docmost divergence). In importPageMarkdown, if the target page is soft-deleted, restorePage() it first (restorePage was already in the client seam but never called), then apply the body — so a git revert actually brings the page back. Verified on stand: git rm -> page trashed; git revert -> deleted_at cleared (page restored). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../services/gitmost-datasource.service.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts b/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts index e9f3807f..867630dd 100644 --- a/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts +++ b/apps/server/src/integrations/git-sync/services/gitmost-datasource.service.ts @@ -222,7 +222,23 @@ export class GitmostDataSourceService { const currentPage = await this.pageRepo.findById(pageId, { includeContent: true, }); - if (baseMarkdown != null && fullMarkdown === baseMarkdown) { + // Revert-of-delete undelete (review warning). If the target page is currently + // SOFT-DELETED, this ingest is a git-revert that re-added the page's file + // (the push classifier saw an add carrying a known pageId -> UPDATE). Writing + // the body to a trashed page leaves it in Trash, and the next pull re-deletes + // the file — the git revert is silently nullified. Restore the page FIRST so + // the revert actually brings it back, then apply the body below. (restorePage + // stamps git-sync provenance so the loop-guard skips its own echo.) + if (currentPage?.deletedAt != null) { + await this.restorePage(ctx, pageId); + } + // Skip the early no-op return when we just restored (the page must still get + // its body write below); only short-circuit for a live, unchanged page. + if ( + currentPage?.deletedAt == null && + baseMarkdown != null && + fullMarkdown === baseMarkdown + ) { return { updatedAt: currentPage ? new Date(currentPage.updatedAt).toISOString()