From d33c4da775c0936774b7951b7cd0db5a4ccc5e8a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 02:20:27 +0000 Subject: [PATCH] gitea-github-sync: pin fetch refspec explicitly, stop silencing stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmed live: a repo's local bare mirror clone got stuck on a stale commit indefinitely even though every sync run reported [ok] — fetch never failed, it just wasn't updating refs/heads/* the way this script assumes. GitHub had the real current commit; the bare clone (and therefore what got pushed to Gitea) stayed frozen on an old one. `fetch --all --prune` trusts remote.origin.fetch as stored in the bare repo's own git config rather than asserting what that mapping actually is — if it drifted from the +refs/heads/*:refs/heads/* convention a fresh `git clone --bare` sets up (for whatever reason — this specific repo's local clone directory's history is unclear), fetch would "successfully" land new commits somewhere this script never reads (refs/remotes/origin/*) while refs/heads/* — the ref that actually gets mirrored — never moves. Deleting and re-cloning the affected repo's local directory fixed it immediately, consistent with a refspec-drift theory, though the exact original cause wasn't pinned down further. Now pins `+refs/heads/*:refs/heads/*` explicitly on every fetch instead of relying on `--all` plus whatever's configured, in both sync directions. Also stopped redirecting stderr to /dev/null on every clone/fetch/push call — a real auth or network failure now shows up in the log instead of a bare "Failed to X" with no reason, which is what made this bug take three rounds of manual ls-remote/rev-parse forensics across two machines to actually pin down. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YEQNc4NfBST1m9NtCZVYa8 --- vendor/ai-stack/gitea-github-sync.sh | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/vendor/ai-stack/gitea-github-sync.sh b/vendor/ai-stack/gitea-github-sync.sh index 2afd857..cd7f5b2 100755 --- a/vendor/ai-stack/gitea-github-sync.sh +++ b/vendor/ai-stack/gitea-github-sync.sh @@ -267,16 +267,25 @@ sync_github_to_gitea() { local repo_name="${full_name#*/}" local local_path="$WORK_DIR/$full_name" - # Clone or fetch from GitHub + # Clone or fetch from GitHub. Fetch pins the refspec explicitly + # (+refs/heads/*:refs/heads/*, the same mapping a fresh `git clone --bare` + # sets up) rather than trusting `--all` plus whatever remote.origin.fetch + # already happens to be configured — confirmed live: a bare clone whose + # refspec had drifted kept "successfully" fetching into a ref this script + # never reads (refs/remotes/origin/*), leaving refs/heads/* — and + # therefore what actually gets mirrored to Gitea — silently stuck on an + # old commit indefinitely, with no error at any step. Also no longer + # silencing stderr: a real auth/network failure should be visible in the + # log, not just "Failed to fetch" with no reason why. if [[ -d "$local_path" ]]; then info "Fetching $full_name from GitHub..." - git -C "$local_path" fetch --all --prune --quiet 2>/dev/null || { + git -C "$local_path" fetch origin '+refs/heads/*:refs/heads/*' --prune --quiet || { err "Failed to fetch $full_name"; return 1; } else info "Cloning $full_name from GitHub..." mkdir -p "$(dirname "$local_path")" local auth_url="${clone_url/https:\/\//https:\/\/$GITHUB_TOKEN@}" - git clone --bare --quiet "$auth_url" "$local_path" 2>/dev/null || { + git clone --bare --quiet "$auth_url" "$local_path" || { err "Failed to clone $full_name"; return 1; } fi @@ -295,7 +304,7 @@ sync_github_to_gitea() { gitea_push_url="${gitea_push_url/http:\/\//http:\/\/$GITEA_USER:$GITEA_TOKEN@}" gitea_push_url="$gitea_push_url/$GITEA_USER/$repo_name.git" - git -C "$local_path" push --mirror "$gitea_push_url" --quiet 2>/dev/null || { + git -C "$local_path" push --mirror "$gitea_push_url" --quiet || { err "Failed to push $full_name to Gitea"; return 1; } ok "GitHub → Gitea: $full_name" _log "PULL $full_name OK" @@ -311,14 +320,16 @@ sync_gitea_to_github() { local gitea_auth_url="${clone_url/https:\/\//https:\/\/$GITEA_USER:$GITEA_TOKEN@}" gitea_auth_url="${gitea_auth_url/http:\/\//http:\/\/$GITEA_USER:$GITEA_TOKEN@}" + # See the matching comment in sync_github_to_gitea() above — same + # explicit-refspec, visible-stderr fix, same reason. if [[ -d "$local_path" ]]; then info "Fetching $full_name from Gitea..." - git -C "$local_path" fetch --all --prune --quiet 2>/dev/null || { + git -C "$local_path" fetch origin '+refs/heads/*:refs/heads/*' --prune --quiet || { err "Failed to fetch $full_name from Gitea"; return 1; } else info "Cloning $full_name from Gitea..." mkdir -p "$(dirname "$local_path")" - git clone --bare --quiet "$gitea_auth_url" "$local_path" 2>/dev/null || { + git clone --bare --quiet "$gitea_auth_url" "$local_path" || { err "Failed to clone $full_name from Gitea"; return 1; } fi @@ -334,7 +345,7 @@ sync_gitea_to_github() { # Push to GitHub local github_push_url="https://$GITHUB_TOKEN@github.com/$GITHUB_USER/$repo_name.git" - git -C "$local_path" push --mirror "$github_push_url" --quiet 2>/dev/null || { + git -C "$local_path" push --mirror "$github_push_url" --quiet || { err "Failed to push $full_name to GitHub"; return 1; } ok "Gitea → GitHub: $full_name" _log "PUSH $full_name OK"