9797 } >> "$GITHUB_OUTPUT"
9898 echo "upstream_count=${UPSTREAM_COUNT}" >> "$GITHUB_OUTPUT"
9999
100+ write_conflict_files_output() {
101+ {
102+ echo "conflict_files<<EOF"
103+ if [ "$#" -gt 0 ]; then
104+ printf '%s\n' "$@"
105+ fi
106+ echo "EOF"
107+ } >> "$GITHUB_OUTPUT"
108+ }
109+
110+ detect_conflict_files_from_merge_probe() {
111+ local start_ref probe_branch
112+ start_ref="$(git rev-parse --verify HEAD)"
113+ probe_branch="sync/probe-conflicts-${SAFE_TAG}-${GITHUB_RUN_ID:-$$}"
114+
115+ git checkout -B "${probe_branch}" "origin/main" >/dev/null 2>&1
116+ if git merge --no-ff --no-edit "${TAG_REF}" >/dev/null 2>&1; then
117+ git checkout -q "${start_ref}" >/dev/null 2>&1 || true
118+ git branch -D "${probe_branch}" >/dev/null 2>&1 || true
119+ return 10
120+ fi
121+
122+ mapfile -t PROBE_CONFLICT_FILES < <(git diff --name-only --diff-filter=U)
123+ git merge --abort >/dev/null 2>&1 || true
124+ git checkout -q "${start_ref}" >/dev/null 2>&1 || true
125+ git branch -D "${probe_branch}" >/dev/null 2>&1 || true
126+
127+ if [ "${#PROBE_CONFLICT_FILES[@]}" -eq 0 ]; then
128+ return 1
129+ fi
130+
131+ write_conflict_files_output "${PROBE_CONFLICT_FILES[@]}"
132+ }
133+
100134 filter_workflow_updates() {
101135 mapfile -t WORKFLOW_FILES < <(git diff --name-only origin/main -- .github/workflows)
102136 if [ "${#WORKFLOW_FILES[@]}" -eq 0 ]; then
@@ -121,6 +155,44 @@ jobs:
121155
122156 if git cat-file -e "origin/${BRANCH}:.upstream-sync-pending" 2>/dev/null; then
123157 echo "Branch still has .upstream-sync-pending marker; keeping conflict PR flow."
158+ set +e
159+ detect_conflict_files_from_merge_probe
160+ probe_status=$?
161+ set -e
162+
163+ if [ "${probe_status}" -eq 10 ]; then
164+ echo "No conflicts detected in probe; clearing stale marker and continuing sync."
165+ git checkout -B "${BRANCH}" "origin/${BRANCH}"
166+ git rm -f .upstream-sync-pending
167+ git commit -m "chore: clear stale upstream sync marker for ${TAG}"
168+
169+ if ! git merge-base --is-ancestor "${TAG_REF}" "HEAD"; then
170+ if ! git merge --no-ff --no-edit "${TAG_REF}"; then
171+ mapfile -t RECOVERY_CONFLICT_FILES < <(git diff --name-only --diff-filter=U)
172+ echo "::warning::Recovery merge failed despite clean probe; falling back to conflict flow."
173+ git merge --abort || true
174+ echo "needs_sync=false" >> "$GITHUB_OUTPUT"
175+ echo "merge_conflict=true" >> "$GITHUB_OUTPUT"
176+ if [ "${#RECOVERY_CONFLICT_FILES[@]}" -gt 0 ]; then
177+ write_conflict_files_output "${RECOVERY_CONFLICT_FILES[@]}"
178+ else
179+ write_conflict_files_output "(recovery merge failed unexpectedly)"
180+ fi
181+ exit 0
182+ fi
183+ fi
184+
185+ filter_workflow_updates
186+ git push -u origin "${BRANCH}"
187+
188+ echo "needs_sync=true" >> "$GITHUB_OUTPUT"
189+ echo "merge_conflict=false" >> "$GITHUB_OUTPUT"
190+ exit 0
191+ fi
192+
193+ if [ "${probe_status}" -ne 0 ]; then
194+ write_conflict_files_output "(conflict list unavailable: merge replay probe failed)"
195+ fi
124196 echo "needs_sync=false" >> "$GITHUB_OUTPUT"
125197 echo "merge_conflict=true" >> "$GITHUB_OUTPUT"
126198 exit 0
@@ -134,6 +206,9 @@ jobs:
134206 fi
135207
136208 echo "Branch exists but does not contain ${TAG}; leaving branch untouched."
209+ if ! detect_conflict_files_from_merge_probe; then
210+ write_conflict_files_output "(branch exists but does not contain upstream tag ${TAG})"
211+ fi
137212 echo "needs_sync=false" >> "$GITHUB_OUTPUT"
138213 echo "merge_conflict=true" >> "$GITHUB_OUTPUT"
139214 exit 0
@@ -163,29 +238,23 @@ jobs:
163238 git merge --abort || true
164239
165240 # Create a metadata commit so the branch differs from main
166- cat > .upstream-sync-pending <<MARKER
167- upstream_tag: ${TAG}
168- status: conflict
169- created: $(date -u +%Y-%m-%dT%H:%M:%SZ)
170-
171- This file was created by the upstream-release-sync workflow.
172- The merge of upstream tag ${TAG} into main produced conflicts.
173- Resolve them, delete this file, and push to this branch.
174- MARKER
241+ {
242+ echo " upstream_tag: ${TAG}"
243+ echo " status: conflict"
244+ echo " created: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
245+ echo
246+ echo " This file was created by the upstream-release-sync workflow."
247+ echo " The merge of upstream tag ${TAG} into main produced conflicts."
248+ echo " Resolve them, delete this file, and push to this branch."
249+ } > .upstream-sync-pending
175250 git add .upstream-sync-pending
176251 git commit -m "chore: mark pending upstream sync for ${TAG} (conflicts)"
177252
178253 git push -u origin "${BRANCH}"
179254
180255 echo "needs_sync=false" >> "$GITHUB_OUTPUT"
181256 echo "merge_conflict=true" >> "$GITHUB_OUTPUT"
182- {
183- echo "conflict_files<<EOF"
184- if [ "${#CONFLICT_FILES[@]}" -gt 0 ]; then
185- printf '%s\n' "${CONFLICT_FILES[@]}"
186- fi
187- echo "EOF"
188- } >> "$GITHUB_OUTPUT"
257+ write_conflict_files_output "${CONFLICT_FILES[@]}"
189258 exit 0
190259 fi
191260
0 commit comments