From 11ae1e9a4808176c58015c71531439fa2a7da115 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 10 Aug 2026 18:46:37 +0300 Subject: [PATCH] gh-151365: Retry git fetch in CI workflows Fetching the history for computing the branch diff can fail with "fatal: shallow file has changed since we read it", a git 2.54 bug which is hit especially often for old pull requests, which have a lot of history to fetch. Retry the fetch up to 3 times, and annotate the run with a warning on every retry. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/reusable-check-html-ids.yml | 22 ++++++++++++++++--- .github/workflows/reusable-context.yml | 20 +++++++++++++++-- .github/workflows/reusable-docs.yml | 20 +++++++++++++++-- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reusable-check-html-ids.yml b/.github/workflows/reusable-check-html-ids.yml index 41ba1288be1ecf4..e245db8d184f902 100644 --- a/.github/workflows/reusable-check-html-ids.yml +++ b/.github/workflows/reusable-check-html-ids.yml @@ -23,17 +23,33 @@ jobs: - name: 'Find merge base' id: merge-base run: | + # Fetching can fail with "fatal: shallow file has changed since we + # read it", especially for old pull requests, which have a lot of + # history to fetch. Retry it several times (see gh-151365). + retry_fetch() { + for attempt in $(seq 5); do + if [ "${attempt}" -gt 1 ]; then + echo "::warning::git fetch failed, retrying (attempt ${attempt})" + sleep 3 + fi + if git fetch "$@"; then + return 0 + fi + done + return 1 + } + BASE="${{ github.event.pull_request.base.sha }}" HEAD="${{ github.event.pull_request.head.sha }}" - git fetch --depth=$((${{ github.event.pull_request.commits }} + 10)) --no-tags origin "$BASE" "$HEAD" + retry_fetch --depth=$((${{ github.event.pull_request.commits }} + 10)) --no-tags origin "$BASE" "$HEAD" if ! MERGE_BASE=$(git merge-base "$BASE" "$HEAD" 2>/dev/null); then - git fetch --deepen=1 --no-tags origin "$BASE" "$HEAD" + retry_fetch --deepen=1 --no-tags origin "$BASE" "$HEAD" OLDEST=$(git rev-list --reflog --max-parents=0 --reverse "${BASE}^" "${HEAD}^" | head -1) TIMESTAMP=$(git show --format=%at --no-patch "$OLDEST") - git fetch --shallow-since="$TIMESTAMP" --no-tags origin "$BASE" "$HEAD" + retry_fetch --shallow-since="$TIMESTAMP" --no-tags origin "$BASE" "$HEAD" MERGE_BASE=$(git merge-base "$BASE" "$HEAD") fi diff --git a/.github/workflows/reusable-context.yml b/.github/workflows/reusable-context.yml index c998cbff181dd12..76ebfba90fd16cd 100644 --- a/.github/workflows/reusable-context.yml +++ b/.github/workflows/reusable-context.yml @@ -96,8 +96,24 @@ jobs: run: | set -eux + # Fetching can fail with "fatal: shallow file has changed since we + # read it", especially for old pull requests, which have a lot of + # history to fetch. Retry it several times (see gh-151365). + retry_fetch() { + for attempt in $(seq 5); do + if [ "${attempt}" -gt 1 ]; then + echo "::warning::git fetch failed, retrying (attempt ${attempt})" + sleep 3 + fi + if git fetch "$@"; then + return 0 + fi + done + return 1 + } + # Fetch enough history to find a common ancestor commit (aka merge-base): - git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ + retry_fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ --no-tags --prune --no-recurse-submodules # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from): @@ -105,7 +121,7 @@ jobs: DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" ) # Get all commits since that commit date from the base branch (eg: main): - git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ + retry_fetch origin "${refspec_base}" --shallow-since="${DATE}" \ --no-tags --prune --no-recurse-submodules env: branch_pr: 'origin/${{ github.event.pull_request.head.ref }}' diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 199e0fd8d181f0b..f5fc73f26482be2 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -51,8 +51,24 @@ jobs: - name: 'Fetch commits to get branch diff' if: github.event_name == 'pull_request' run: | + # Fetching can fail with "fatal: shallow file has changed since we + # read it", especially for old pull requests, which have a lot of + # history to fetch. Retry it several times (see gh-151365). + retry_fetch() { + for attempt in $(seq 5); do + if [ "${attempt}" -gt 1 ]; then + echo "::warning::git fetch failed, retrying (attempt ${attempt})" + sleep 3 + fi + if git fetch "$@"; then + return 0 + fi + done + return 1 + } + # Fetch enough history to find a common ancestor commit (aka merge-base): - git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ + retry_fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ --no-tags --prune --no-recurse-submodules # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from): @@ -60,7 +76,7 @@ jobs: DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" ) # Get all commits since that commit date from the base branch (eg: master or main): - git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ + retry_fetch origin "${refspec_base}" --shallow-since="${DATE}" \ --no-tags --prune --no-recurse-submodules - name: 'Set up Python' uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0