diff --git a/GitMirror/config.example.ini b/GitMirror/config.example.ini index 0dd07996..475f19db 100644 --- a/GitMirror/config.example.ini +++ b/GitMirror/config.example.ini @@ -1,5 +1,8 @@ [gitea] BaseUrl = https://git.gvsds.com +# git 拉取地址(留空则同 BaseUrl);与 Gitea 同机部署时填 http://127.0.0.1:3000 +# 可绕开 frp 隧道,避免大仓库传输被中断(curl 92 / early EOF) +CloneBaseUrl = OrgName = GVSDS # 留空则从 SecretsIni 的 [gitea] API_TOKEN 读取 ApiToken = diff --git a/GitMirror/mirror.py b/GitMirror/mirror.py index 0180d567..36eea02e 100644 --- a/GitMirror/mirror.py +++ b/GitMirror/mirror.py @@ -23,6 +23,7 @@ import os import secrets as py_secrets import shutil import sqlite3 +import stat import subprocess import threading import urllib.parse @@ -48,6 +49,15 @@ def _Now() -> str: return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +def _ForceRemove(Func, Path: str, ExcInfo) -> None: + """shutil.rmtree 的回调:git 对象文件在 Windows 上是只读的,先去掉只读再删""" + try: + os.chmod(Path, stat.S_IWRITE) + Func(Path) + except OSError: + pass + + # --------------------------------------------------------------------------- # 配置 # --------------------------------------------------------------------------- @@ -61,6 +71,9 @@ class MirrorConfig: raise MirrorError(f'配置文件不存在:{Path}') self.Path = Path self.GiteaBaseUrl = (Parser.get('gitea', 'BaseUrl', fallback='https://git.gvsds.com') or '').rstrip('/') + # git 拉取地址可与 API 地址不同:同机部署时直连 127.0.0.1 可绕开 frp 隧道 + # (大仓库经隧道传输常被中断,表现为 curl 92 / early EOF / invalid index-pack) + self.GiteaCloneUrl = (Parser.get('gitea', 'CloneBaseUrl', fallback='') or self.GiteaBaseUrl).rstrip('/') self.GiteaOrg = Parser.get('gitea', 'OrgName', fallback='GVSDS') self.GiteaToken = (Parser.get('gitea', 'ApiToken', fallback='') or '').strip() self.SecretsIni = Parser.get('gitea', 'SecretsIni', fallback='') @@ -406,7 +419,7 @@ class MirrorManager: Private = bool(Repo.get('private', True)) Local = os.path.join(self.Cfg.ReposDir, f'{RepoName}.git') SourceUrl = self._AuthUrl( - f'{self.Cfg.GiteaBaseUrl}/{self.Cfg.GiteaOrg}/{RepoName}.git', 'oauth2', self.Cfg.GiteaToken) + f'{self.Cfg.GiteaCloneUrl}/{self.Cfg.GiteaOrg}/{RepoName}.git', 'oauth2', self.Cfg.GiteaToken) TargetUrl = self._AuthUrl( f'https://github.com/{self.Cfg.GithubOrg}/{GithubName}.git', GITHUB_AUTH_USER, self.Cfg.GithubToken) @@ -452,7 +465,7 @@ class MirrorManager: if not os.path.isdir(Local): return try: - shutil.rmtree(Local) + shutil.rmtree(Local, onerror=_ForceRemove) logger.info('已删除本地镜像:%s', RepoName) except OSError as Error: logger.warning('删除本地镜像失败 %s:%s', RepoName, Error)