Add linearize fallback for reverse sync and workflow guide (v1.3)
When josh-proxy rejects a reverse sync push due to unmappable merge commits, fall back to linearizing: cherry-pick regular commits individually, squash only the merge commits via cherry-pick -m 1. Also adds a recommended Git workflow section to the guide explaining where cross-branch merges should happen (monorepo) vs feature work (subrepo), and expands troubleshooting for the "josh rejected push" error with root cause analysis and prevention advice. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
106
lib/sync.sh
106
lib/sync.sh
@@ -284,37 +284,109 @@ reverse_sync() {
|
||||
local ts
|
||||
ts=$(date +%Y%m%d-%H%M%S)
|
||||
local staging_branch="auto-sync/subrepo-${subrepo_branch}-${ts}"
|
||||
local push_ref="HEAD"
|
||||
local linearized=false
|
||||
|
||||
if git push -o "base=${mono_branch}" "$(josh_auth_url)" "HEAD:refs/heads/${staging_branch}"; then
|
||||
log "INFO" "Pushed to staging branch via josh: ${staging_branch}"
|
||||
if ! git push -o "base=${mono_branch}" "$(josh_auth_url)" "${push_ref}:refs/heads/${staging_branch}" 2>/dev/null; then
|
||||
# Josh rejects pushes when the history contains merge commits whose
|
||||
# parents it cannot map (e.g., merges of staging/feature branches that
|
||||
# include auto-sync history). Fall back to linearizing: cherry-pick
|
||||
# regular commits as-is, squash only the problematic merge commits.
|
||||
# See ADR-011.
|
||||
log "WARN" "Direct push failed — linearizing history (cherry-pick + squash merges)"
|
||||
|
||||
# 6. Create PR on monorepo (NEVER direct push)
|
||||
local pr_body
|
||||
pr_body="## Subrepo changes
|
||||
local original_head
|
||||
original_head=$(git rev-parse HEAD)
|
||||
|
||||
git checkout -b "tmp-linearize-$$" "mono-filtered/${mono_branch}" 2>/dev/null \
|
||||
|| die "Failed to create linearize branch"
|
||||
|
||||
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 "")
|
||||
|
||||
local sha
|
||||
for sha in $commit_shas; do
|
||||
local parent_count
|
||||
parent_count=$(git cat-file -p "$sha" | grep -c "^parent ")
|
||||
|
||||
if [ "$parent_count" -le 1 ]; then
|
||||
# Regular commit — cherry-pick to preserve message and authorship
|
||||
if ! git cherry-pick "$sha" 2>/dev/null; then
|
||||
log "WARN" "Cherry-pick conflict on ${sha:0:7} — applying tree directly"
|
||||
git cherry-pick --abort 2>/dev/null
|
||||
# Fall back to applying the exact tree diff for this commit
|
||||
local msg author_name author_email author_date
|
||||
msg=$(git log -1 --format="%B" "$sha")
|
||||
author_name=$(git log -1 --format="%an" "$sha")
|
||||
author_email=$(git log -1 --format="%ae" "$sha")
|
||||
author_date=$(git log -1 --format="%aI" "$sha")
|
||||
git diff HEAD "$sha" | git apply --index 2>/dev/null \
|
||||
|| die "Failed to apply diff for ${sha:0:7}"
|
||||
GIT_AUTHOR_NAME="$author_name" GIT_AUTHOR_EMAIL="$author_email" \
|
||||
GIT_AUTHOR_DATE="$author_date" \
|
||||
git commit -m "$msg" || die "Failed to commit ${sha:0:7}"
|
||||
fi
|
||||
else
|
||||
# Merge commit — cherry-pick relative to first parent (squashes the merge)
|
||||
local msg author_name author_email author_date
|
||||
msg=$(git log -1 --format="%B" "$sha")
|
||||
author_name=$(git log -1 --format="%an" "$sha")
|
||||
author_email=$(git log -1 --format="%ae" "$sha")
|
||||
author_date=$(git log -1 --format="%aI" "$sha")
|
||||
|
||||
if ! git cherry-pick -m 1 "$sha" 2>/dev/null; then
|
||||
git cherry-pick --abort 2>/dev/null
|
||||
git diff HEAD "$sha" | git apply --index 2>/dev/null \
|
||||
|| die "Failed to apply merge diff for ${sha:0:7}"
|
||||
GIT_AUTHOR_NAME="$author_name" GIT_AUTHOR_EMAIL="$author_email" \
|
||||
GIT_AUTHOR_DATE="$author_date" \
|
||||
git commit -m "$msg" || die "Failed to commit merge ${sha:0:7}"
|
||||
fi
|
||||
log "INFO" "Squashed merge commit ${sha:0:7}: $(echo "$msg" | head -1)"
|
||||
fi
|
||||
done
|
||||
|
||||
push_ref="HEAD"
|
||||
linearized=true
|
||||
|
||||
git push -o "base=${mono_branch}" "$(josh_auth_url)" \
|
||||
"${push_ref}:refs/heads/${staging_branch}" \
|
||||
|| { log "ERROR" "Josh rejected linearized push — check josh-proxy logs"; echo "josh-rejected"; return; }
|
||||
fi
|
||||
|
||||
log "INFO" "Pushed to staging branch via josh: ${staging_branch}${linearized:+ (linearized)}"
|
||||
|
||||
# 6. Create PR on monorepo (NEVER direct push)
|
||||
local pr_body
|
||||
local linearize_note=""
|
||||
if [ "$linearized" = true ]; then
|
||||
linearize_note="
|
||||
> **Note:** Merge commits were squashed during sync because the subrepo
|
||||
> history contained merges that josh-proxy cannot map (see ADR-011).
|
||||
> Regular commits are preserved individually."
|
||||
fi
|
||||
pr_body="## Subrepo changes
|
||||
|
||||
New commits from subrepo \`${subrepo_branch}\`:
|
||||
|
||||
\`\`\`
|
||||
${human_commits}
|
||||
\`\`\`
|
||||
|
||||
${linearize_note}
|
||||
**Review checklist:**
|
||||
- [ ] Changes scoped to synced subfolder
|
||||
- [ ] No leaked credentials or environment-specific config
|
||||
- [ ] CI passes"
|
||||
|
||||
create_pr "${MONOREPO_API}" "${GITEA_TOKEN}" \
|
||||
"$mono_branch" "$staging_branch" \
|
||||
"[Subrepo Sync] ${subrepo_branch} → ${mono_branch}" \
|
||||
"$pr_body" \
|
||||
|| die "Failed to create PR on monorepo (check GITEA_TOKEN)"
|
||||
create_pr "${MONOREPO_API}" "${GITEA_TOKEN}" \
|
||||
"$mono_branch" "$staging_branch" \
|
||||
"[Subrepo Sync] ${subrepo_branch} → ${mono_branch}" \
|
||||
"$pr_body" \
|
||||
|| die "Failed to create PR on monorepo (check GITEA_TOKEN)"
|
||||
|
||||
log "INFO" "Reverse sync PR created on monorepo"
|
||||
echo "pr-created"
|
||||
else
|
||||
log "ERROR" "Josh rejected push — check josh-proxy logs"
|
||||
echo "josh-rejected"
|
||||
fi
|
||||
log "INFO" "Reverse sync PR created on monorepo"
|
||||
echo "pr-created"
|
||||
}
|
||||
|
||||
# ─── Initial Import: Subrepo → Monorepo (first time) ───────────────
|
||||
|
||||
Reference in New Issue
Block a user