tooling: a merge driver for .beads/issues.jsonl
issues.jsonl is a passive export whose rows are ordered by insertion, so two sides that file issues collide in the same region even when the issues are disjoint. Git line-merges it and produces conflict markers INSIDE the JSONL, which breaks bd's auto-export silently -- observed today, where a stash-pop left `<<<<<<< Updated upstream` in the file and auto-export was skipping with "invalid character '<'" until the file was regenerated. The driver does no merge logic of its own, because bd already has the semantics: `bd import` skips any row whose updated_at is not strictly newer than the local one, creates ids it has not seen, and keeps the local row on a tie. So importing BOTH sides cannot move the database backwards, and the file is then regenerated from it. The database is the truth; the file is derived. Deliberately not reimplemented here: field-level reconciliation. `bd conflicts show/resolve` already does that against the Dolt working set, which is the real sync path -- a second copy against the text export would have to track bd's schema by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
@@ -0,0 +1 @@
|
||||
.beads/issues.jsonl merge=beads
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env sh
|
||||
# Git merge driver for .beads/issues.jsonl. Reasoning: context/code/tooling.md.
|
||||
set -eu
|
||||
|
||||
BASE="$1"; OURS="$2"; THEIRS="$3"; PATHNAME="${4:-.beads/issues.jsonl}"
|
||||
|
||||
say() { echo "beads-merge: $*" >&2; }
|
||||
|
||||
if ! command -v bd >/dev/null 2>&1; then
|
||||
say "bd is not on PATH; leaving $PATHNAME conflicted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Opt-in, because a merge driver that reaches the network fails in ways a merge
|
||||
# cannot report. Off by default; the export alone is enough for a git merge.
|
||||
if [ "${BEADS_MERGE_PULL:-0}" = "1" ]; then
|
||||
bd dolt pull "${BEADS_MERGE_REMOTE:-gh}" >&2 || say "dolt pull failed; continuing from local"
|
||||
fi
|
||||
|
||||
# Both sides, not just theirs: `bd import` skips any row whose updated_at is not
|
||||
# strictly newer than the local one, so neither call can move the database
|
||||
# backwards, and importing OURS restores a database that has lost rows.
|
||||
for side in "$OURS" "$THEIRS"; do
|
||||
[ -s "$side" ] || continue
|
||||
bd import "$side" >&2 || { say "import failed; leaving $PATHNAME conflicted"; exit 1; }
|
||||
done
|
||||
|
||||
bd export -o "$OURS" || { say "export failed; leaving $PATHNAME conflicted"; exit 1; }
|
||||
|
||||
if grep -qE '^(<<<<<<<|=======|>>>>>>>)' "$OURS"; then
|
||||
say "export produced conflict markers; refusing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
say "resolved $PATHNAME from the database ($(wc -l < "$OURS" | tr -d ' ') rows)"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user