init
This commit is contained in:
53
backend/repo.py
Normal file
53
backend/repo.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""git-Helfer (nackte CLI, keine Bibliothek): Anker-Stände und Diff fürs Re-Scannen."""
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _git(wurzel: Path, *args: str) -> str:
|
||||
res = subprocess.run(["git", "-C", str(wurzel), *args],
|
||||
capture_output=True, text=True, timeout=30)
|
||||
if res.returncode != 0:
|
||||
raise RuntimeError(f"git {' '.join(args)}: {res.stderr.strip() or res.stdout.strip()}")
|
||||
return res.stdout
|
||||
|
||||
|
||||
def ist_repo(wurzel: Path) -> bool:
|
||||
try:
|
||||
_git(wurzel, "rev-parse", "--git-dir")
|
||||
return True
|
||||
except (RuntimeError, FileNotFoundError):
|
||||
return False
|
||||
|
||||
|
||||
def head_hash(wurzel: Path) -> str:
|
||||
return _git(wurzel, "rev-parse", "--short=12", "HEAD").strip()
|
||||
|
||||
|
||||
def working_tree_sauber(wurzel: Path) -> bool:
|
||||
"""False bei uncommitteten Änderungen (inkl. untracked) — Scan/Board arbeiten nur
|
||||
auf sauberem Tree, sonst vermischen sich Stände. AUSNAHME `.planer/`: die eigenen
|
||||
Artefakte (z. B. aus einem Teillauf) dürfen den Folgelauf nicht blockieren."""
|
||||
for zeile in _git(wurzel, "status", "--porcelain").splitlines():
|
||||
pfad = zeile[3:].split(" -> ")[-1].strip().strip('"')
|
||||
if not pfad.startswith(".planer/"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def geaenderte_dateien(wurzel: Path, alt: str, neu: str = "HEAD") -> tuple[set[str], set[str]]:
|
||||
"""(geändert_oder_neu, gelöscht) zwischen `alt` und `neu`, `.planer/` ausgefiltert.
|
||||
Renames (R) = alt gelöscht + neu geändert (v1: kein Anker-Matching über Renames)."""
|
||||
geaendert: set[str] = set()
|
||||
geloescht: set[str] = set()
|
||||
for zeile in _git(wurzel, "diff", "--name-status", f"{alt}..{neu}").splitlines():
|
||||
teile = zeile.split("\t")
|
||||
status = teile[0]
|
||||
if status.startswith("R") and len(teile) == 3:
|
||||
geloescht.add(teile[1])
|
||||
geaendert.add(teile[2])
|
||||
elif status == "D":
|
||||
geloescht.add(teile[1])
|
||||
elif len(teile) >= 2:
|
||||
geaendert.add(teile[1])
|
||||
kein_planer = lambda s: not s.startswith(".planer/") # noqa: E731
|
||||
return set(filter(kein_planer, geaendert)), set(filter(kein_planer, geloescht))
|
||||
Reference in New Issue
Block a user