gitea-github-sync: pin fetch refspec explicitly, stop silencing stderr

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEQNc4NfBST1m9NtCZVYa8
This commit is contained in:
Claude
2026-08-18 02:20:27 +00:00
parent e548f6526e
commit d33c4da775
+18 -7
View File
@@ -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"