Fix reverse sync missing human commits when mono-filtered advances

--ancestry-path returns empty when mono-filtered/main has advanced
past the last forward sync (any monorepo commit causes josh to create
a new filtered SHA not in the subrepo's ancestry). Use git merge-base
to find the last connected point instead.

Also return skip-dirty (not skip) when trees differ but no human
commits found, preventing state from being updated on false skips
which would permanently lose unsynced commits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 14:07:20 +03:00
parent d6f334b861
commit b4eaa47ef6
3 changed files with 40 additions and 9 deletions

View File

@@ -226,7 +226,9 @@ ${BOT_TRAILER}: filter-change/${mono_branch}/$(date -u +%Y-%m-%dT%H:%M:%SZ)")
# ─── Reverse Sync: Subrepo → Monorepo ──────────────────────────────
#
# Always creates a PR on the monorepo — never pushes directly.
# Returns: skip | pr-created | josh-rejected
# Returns: skip | skip-dirty | pr-created | josh-rejected
# skip — trees identical, nothing to do
# skip-dirty — trees differ but no human commits found (don't update state)
reverse_sync() {
local mono_branch="$SYNC_BRANCH_MONO"
@@ -265,15 +267,27 @@ reverse_sync() {
fi
# 4. Find new human commits (excludes bot commits from forward sync)
# Uses --ancestry-path to restrict to the direct lineage and avoid
# leaking old history through reconciliation merge parents.
local human_commits
human_commits=$(git log --ancestry-path "mono-filtered/${mono_branch}..HEAD" \
--oneline --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
# Uses merge-base + --ancestry-path to restrict to the direct lineage.
# merge-base is needed because mono-filtered may have advanced past the
# last forward sync (any monorepo commit, even outside the subfolder,
# causes josh to create a new filtered commit). Using mono-filtered
# directly with --ancestry-path returns empty when it's not an ancestor
# of HEAD. merge-base always finds the last connected point.
local base human_commits
base=$(git merge-base "mono-filtered/${mono_branch}" HEAD 2>/dev/null || echo "")
if [ -n "$base" ]; then
human_commits=$(git log --ancestry-path "${base}..HEAD" \
--oneline --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
else
# No common ancestor (first sync or fully diverged) — skip ancestry-path
human_commits=$(git log "mono-filtered/${mono_branch}..HEAD" \
--oneline --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
fi
if [ -z "$human_commits" ]; then
log "INFO" "No new human commits in subrepo — nothing to sync"
echo "skip"
echo "skip-dirty"
return
fi
@@ -301,9 +315,16 @@ reverse_sync() {
git checkout -b "tmp-linearize-$$" "mono-filtered/${mono_branch}" 2>/dev/null \
|| die "Failed to create linearize branch"
# Reuse merge-base from step 4 (same reasoning: mono-filtered may have
# advanced past the last forward sync, breaking --ancestry-path).
local commit_shas
commit_shas=$(git log --ancestry-path "mono-filtered/${mono_branch}..${original_head}" \
--reverse --format="%H" --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
if [ -n "$base" ]; then
commit_shas=$(git log --ancestry-path "${base}..${original_head}" \
--reverse --format="%H" --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
else
commit_shas=$(git log "mono-filtered/${mono_branch}..${original_head}" \
--reverse --format="%H" --invert-grep --grep="^${BOT_TRAILER}:" 2>/dev/null || echo "")
fi
local sha
for sha in $commit_shas; do