diff --git a/svn_mirror/gitea.py b/svn_mirror/gitea.py index 19c27db..cf13f25 100644 --- a/svn_mirror/gitea.py +++ b/svn_mirror/gitea.py @@ -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")