#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r"""git_pack.py —— 备份加速:把散落的 .git 目录各自打包成 zip(纯标准库)"""
import argparse
import sys
import zipfile
from pathlib import Path
def find_git_dirs(root: Path):
"""递归找 .git 目录;找到后不再进入其内部"""
found = []
for cur, dirs, files in __import__('os').walk(root):
curp = Path(cur)
for d in list(dirs):
if d == ".git":
found.append(curp / d)
dirs.remove(d)
return found
def dir_stats(p: Path):
n, size = 0, 0
for f in p.rglob("*"):
if f.is_file():
n += 1
size += f.stat().st_size
return n, size
def human(n):
for u in ("B", "KB", "MB", "GB"):
if n < 1024:
return f"{n:.0f}{u}" if u == "B" else f"{n:.1f}{u}"
n /= 1024
return f"{n:.1f}TB"
def do_pack(git_dir: Path, overwrite: bool) -> str:
out = git_dir.parent / (git_dir.parent.name + ".git.zip")
if out.exists() and not overwrite:
return f"跳过(已存在):{out.name}"
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for f in sorted(git_dir.rglob("*")):
if f.is_file():
z.write(f, f.relative_to(git_dir.parent).as_posix())
return f"已打包:{git_dir} -> {out.name}({human(out.stat().st_size)})"
def do_restore(zip_path: Path):
with zipfile.ZipFile(zip_path) as z:
z.extractall(zip_path.parent)
print(f"已还原:{zip_path.name} -> {zip_path.parent}\.git")
def main():
ap = argparse.ArgumentParser(description="把所有 .git 目录打包成 zip(备份加速)")
ap.add_argument("folder", nargs="?", help="要扫描的项目根文件夹")
ap.add_argument("--apply", action="store_true", help="真正打包(默认只预览)")
ap.add_argument("--overwrite", action="store_true", help="已存在的 zip 也重新打包")
ap.add_argument("--restore", help="把指定的 .git.zip 还原回 .git 目录")
args = ap.parse_args()
if args.restore:
do_restore(Path(args.restore))
return
root = Path(args.folder or input("请输入项目根文件夹路径:").strip().strip('"'))
if not root.is_dir():
print("文件夹不存在")
sys.exit(1)
gits = find_git_dirs(root)
if not gits:
print("没有找到任何 .git 目录。")
return
print(f"【预览】找到 {len(gits)} 个 .git 目录:")
for g in gits:
n, size = dir_stats(g)
print(f" {g} ({n} 个文件,{human(size)})")
if not args.apply:
print("未做任何改动;加 --apply 打包。")
return
for g in gits:
print(do_pack(g, args.overwrite))
print("完成。提示:在 FreeFileSync 排除过滤器里加上 .git\,备份就只会处理这些 zip 了。")
if __name__ == "__main__":
main()