fix: use git fetch from Gitea side to transfer objects + update refs without triggering pre-receive hook

This commit is contained in:
2026-06-24 13:47:50 +02:00
parent f8093bf648
commit 7ae008717f
+37 -35
View File
@@ -4,7 +4,7 @@ import logging
import os
import subprocess
from pathlib import Path
from typing import Dict, Optional
from typing import Optional
from .mirror import Mirror
@@ -52,10 +52,11 @@ def _git(*args: str, timeout: int = 300, check: bool = True) -> str:
def push_to_gitea(mirror: Mirror) -> bool:
"""Sync canonical refs to Gitea's bare repo via direct ref updates.
"""Sync canonical → Gitea via ``git fetch`` from Gitea's side.
Uses ``git update-ref`` to bypass Gitea's pre-receive hook, then
fires Gitea's ``post-receive`` hook manually so the UI refreshes.
``git fetch`` transfers objects + updates refs without triggering
Gitea's ``pre-receive`` hook, then fires ``post-receive`` manually
for the UI to refresh.
Returns True if anything changed.
"""
@@ -66,49 +67,50 @@ def push_to_gitea(mirror: Mirror) -> bool:
canonical = str(mirror.canonical_dir)
# Collect canonical refs
out = _git("--git-dir", canonical, "for-each-ref",
# Grab old refs so we can detect what changed
old_refs: Dict[str, str] = {}
out = _git("--git-dir", str(gitea_path), "for-each-ref",
"--format=%(objectname) %(refname)",
"refs/heads/", "refs/tags/",
timeout=30, check=False)
canonical_refs: Dict[str, str] = {}
for line in (out or "").splitlines():
line = line.strip()
if not line:
continue
obj_hash, ref = line.split(None, 1)
canonical_refs[ref] = obj_hash
h, r = line.split(None, 1)
old_refs[r] = h
# Gitea fetches from canonical — this transfers objects + refs,
# and DOES NOT trigger Gitea's pre-receive hook.
_git(
"--git-dir", str(gitea_path), "fetch", "--prune",
canonical,
"+refs/heads/*:refs/heads/*",
"+refs/tags/*:refs/tags/*",
timeout=300,
)
# Build the changelist for post-receive
changed: list = []
for ref, obj_hash in canonical_refs.items():
gitea_hash = _git("--git-dir", str(gitea_path), "rev-parse",
"--verify", "--quiet", ref,
timeout=15, check=False)
if obj_hash != gitea_hash:
if gitea_hash:
_git("--git-dir", str(gitea_path), "update-ref", ref,
obj_hash, gitea_hash, timeout=15)
else:
_git("--git-dir", str(gitea_path), "update-ref", ref,
obj_hash, timeout=15)
changed.append((gitea_hash or "0" * 40, obj_hash, ref))
logger.debug("Updated %s in Gitea", ref)
# Prune refs in Gitea not in canonical
gitea_out = _git("--git-dir", str(gitea_path), "for-each-ref",
"--format=%(objectname) %(refname)",
"refs/heads/", "refs/tags/",
timeout=30, check=False)
for line in (gitea_out or "").splitlines():
cur_refs: dict = {}
out = _git("--git-dir", str(gitea_path), "for-each-ref",
"--format=%(objectname) %(refname)",
"refs/heads/", "refs/tags/",
timeout=30, check=False)
for line in (out or "").splitlines():
line = line.strip()
if not line:
continue
obj_hash, ref = line.split(None, 1)
if ref not in canonical_refs:
_git("--git-dir", str(gitea_path), "update-ref", "-d", ref,
obj_hash, timeout=15)
changed.append((obj_hash, "0" * 40, ref))
logger.debug("Pruned %s from Gitea", ref)
h, r = line.split(None, 1)
cur_refs[r] = h
old = old_refs.get(r, "0" * 40)
if h != old:
changed.append((old, h, r))
# Detect deletions (refs present before fetch but gone now)
for r, old_h in old_refs.items():
if r not in cur_refs:
changed.append((old_h, "0" * 40, r))
if not changed:
logger.debug("No new commits to push to Gitea")