fix(git-sync): review #4404 batch — sanitize-title echo, per-space gate, move-echo, merge-agreement, fence-aware conflict scan, e2e asserts
Addresses reviewer comment #4404 (critical + blocking): - Critical #2: renamePage skips the echo where the incoming title equals sanitizeTitle(current title) — a Docmost title with FS-hostile chars (: / " |, newlines, double-space, >120) was pulled to a sanitized stem then written back, permanently corrupting the real title. (datasource) - Blocking #3: runOnce enforces per-space settings.gitSync.enabled (the event path bypassed opt-in; any edited space would git-init + export). (orchestrator) - Blocking #6: movePage no-ops the position-less same-parent echo that clobbered the user's chosen sibling order. (datasource) - Blocking #9: hasConflictMarkers is fence-aware — '<<<<<<< HEAD' inside a code block (git-tutorial page) no longer trips the all-or-nothing gate that froze the whole space's refs. (push.ts) - Blocking #11: three-way tryMergeRegion short-circuits when live==target (diff3 agreement) instead of logging a false 'same-block conflict resolved to git' — the echo noise that masked real data-loss signals. (three-way-merge) - Blocking #12/#13: e2e-advanced — drop the delete-cap block (no such feature; failed with a scary '(data loss!)'); non-member assert now expects 404 (existence not leaked), not 403. Verified on stand: sanitized-title rename preserves DB title (vault file sanitized); non-enabled space creates no vault; fenced conflict markers ingest without jamming; build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1159,15 +1159,47 @@ export function isPageFile(path: string): boolean {
|
||||
* `|||||||` and `=======`): the base text is neither side's current content, so
|
||||
* keeping it would inject obsolete lines AND leak a raw `|||||||` marker.
|
||||
*/
|
||||
const CONFLICT_BEGIN_RE = /^<{7}/m;
|
||||
const CONFLICT_END_RE = /^>{7}/m;
|
||||
const CONFLICT_BEGIN_LINE_RE = /^<{7}/;
|
||||
const CONFLICT_BASE_LINE_RE = /^\|{7}/;
|
||||
const CONFLICT_SEP_LINE_RE = /^={7}/;
|
||||
const CONFLICT_END_LINE_RE = /^>{7}/;
|
||||
|
||||
// A code-fence open/close line (``` or ~~~), matching markdown-to-prosemirror's
|
||||
// CODE_FENCE_RE. Used to make conflict-marker detection fence-aware.
|
||||
const CONFLICT_CODE_FENCE_RE = /^(\s*)(`{3,}|~{3,})/;
|
||||
|
||||
export function hasConflictMarkers(body: string): boolean {
|
||||
return CONFLICT_BEGIN_RE.test(body) && CONFLICT_END_RE.test(body);
|
||||
// Fence-aware scan (review #9). A page documenting git (a ```-fenced block that
|
||||
// literally contains `<<<<<<< HEAD` ... `>>>>>>>`) must NOT be flagged as a
|
||||
// conflict: with autoMergeConflicts OFF the all-or-nothing gate would then jam
|
||||
// the ENTIRE space's refs forever, re-failing the batch every cycle. Only count
|
||||
// begin/end marker lines that live OUTSIDE a fenced code block.
|
||||
let inFence = false;
|
||||
let fenceMarker = "";
|
||||
let sawBegin = false;
|
||||
for (const line of body.split("\n")) {
|
||||
const fence = line.match(CONFLICT_CODE_FENCE_RE);
|
||||
if (inFence) {
|
||||
// Close on a fence line of the same marker char, length >= the opener.
|
||||
if (
|
||||
fence &&
|
||||
fence[2][0] === fenceMarker[0] &&
|
||||
fence[2].length >= fenceMarker.length
|
||||
) {
|
||||
inFence = false;
|
||||
fenceMarker = "";
|
||||
}
|
||||
continue; // everything inside a fence is inert
|
||||
}
|
||||
if (fence) {
|
||||
inFence = true;
|
||||
fenceMarker = fence[2];
|
||||
continue;
|
||||
}
|
||||
if (CONFLICT_BEGIN_LINE_RE.test(line)) sawBegin = true;
|
||||
else if (sawBegin && CONFLICT_END_LINE_RE.test(line)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function stripConflictMarkers(body: string): string {
|
||||
|
||||
Reference in New Issue
Block a user