95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from typing import Iterable
|
|
|
|
from . import output
|
|
|
|
|
|
class GitError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def _run_git(args: Iterable[str], *, capture_output: bool = False, check: bool = True) -> subprocess.CompletedProcess:
|
|
cmd = ["git", *args]
|
|
result = subprocess.run(
|
|
cmd,
|
|
text=True,
|
|
capture_output=capture_output,
|
|
check=False,
|
|
)
|
|
if check and result.returncode != 0:
|
|
stderr = result.stderr.strip() if result.stderr else ""
|
|
raise GitError(f"git {' '.join(args)} failed ({result.returncode}): {stderr}")
|
|
return result
|
|
|
|
|
|
def get_current_branch() -> str:
|
|
result = _run_git(["rev-parse", "--abbrev-ref", "HEAD"], capture_output=True)
|
|
return (result.stdout or "").strip()
|
|
|
|
|
|
def is_clean_working_tree() -> bool:
|
|
result = _run_git(["status", "--porcelain"], capture_output=True)
|
|
return (result.stdout or "").strip() == ""
|
|
|
|
|
|
def ensure_clean_working_tree() -> None:
|
|
if not is_clean_working_tree():
|
|
raise GitError(
|
|
"Working tree is niet clean. Commit of stash je wijzigingen voor je deze actie uitvoert."
|
|
)
|
|
|
|
|
|
def ensure_branch_exists(branch: str) -> None:
|
|
try:
|
|
_run_git(["show-ref", "--verify", f"refs/heads/{branch}"], capture_output=True)
|
|
except GitError as exc:
|
|
raise GitError(f"Branch '{branch}' bestaat niet lokaal.") from exc
|
|
|
|
|
|
def checkout_branch(branch: str) -> None:
|
|
_run_git(["checkout", branch])
|
|
|
|
|
|
def create_branch(branch: str, base: str) -> None:
|
|
_run_git(["checkout", base])
|
|
_run_git(["checkout", "-b", branch])
|
|
|
|
|
|
def fetch_remote(remote: str) -> None:
|
|
_run_git(["fetch", remote])
|
|
|
|
|
|
def ensure_not_behind_remote(branch: str, remote: str) -> None:
|
|
"""Controleer of branch niet achterloopt op remote.
|
|
|
|
We gebruiken `git rev-list --left-right --count local...remote` om
|
|
ahead/behind te bepalen.
|
|
"""
|
|
|
|
remote_ref = f"{remote}/{branch}"
|
|
try:
|
|
result = _run_git(
|
|
["rev-list", "--left-right", "--count", f"{branch}...{remote_ref}"],
|
|
capture_output=True,
|
|
)
|
|
except GitError:
|
|
# Geen tracking remote; in die gevallen doen we geen check.
|
|
return
|
|
|
|
output_str = (result.stdout or "").strip()
|
|
if not output_str:
|
|
return
|
|
|
|
ahead_str, behind_str = output_str.split()
|
|
ahead = int(ahead_str)
|
|
behind = int(behind_str)
|
|
|
|
if behind > 0:
|
|
raise GitError(
|
|
f"Branch '{branch}' loopt {behind} commit(s) achter op {remote_ref}. "
|
|
f"Doe eerst een 'git pull --ff-only' of werk te wijzigingen lokaal bij."
|
|
)
|
|
|