Files
devdash-cli/src/git/status.py
7000pctAUTO a2096f99f0
Some checks failed
CI / test (push) Has been cancelled
Initial upload: DevDash CLI with TUI dashboard
2026-02-01 06:52:50 +00:00

97 lines
2.5 KiB
Python

from dataclasses import dataclass
from typing import List, Optional
import subprocess
class GitStatusError(Exception):
pass
def _run_git_command(args: List[str], cwd: str = None) -> str:
try:
result = subprocess.run(
["git"] + args,
cwd=cwd,
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
raise GitStatusError(f"Git command failed: {e}")
def _get_current_branch(cwd: str = None) -> str:
try:
return _run_git_command(["rev-parse", "--abbrev-ref", "HEAD"], cwd)
except GitStatusError:
return "HEAD"
def _get_commit_hash(cwd: str = None) -> str:
return _run_git_command(["rev-parse", "SHORT"], cwd)
def _is_detached(cwd: str = None) -> bool:
branch = _get_current_branch(cwd)
return branch == "HEAD"
def _get_staged_count(cwd: str = None) -> int:
output = _run_git_command(["diff", "--staged", "--name-only"], cwd)
return len(output.splitlines()) if output else 0
def _get_unstaged_count(cwd: str = None) -> int:
output = _run_git_command(["diff", "--name-only"], cwd)
return len(output.splitlines()) if output else 0
def _get_untracked_count(cwd: str = None) -> int:
output = _run_git_command(["ls-files", "--others", "--exclude-standard"], cwd)
return len(output.splitlines()) if output else 0
@dataclass
class GitStatus:
branch: str
commit: str
staged: int = 0
unstaged: int = 0
untracked: int = 0
ahead: int = 0
behind: int = 0
is_detached: bool = False
is_clean: bool = True
def get_git_status(cwd: str = None) -> GitStatus:
try:
is_detached_head = _is_detached(cwd)
branch = "(detached)" if is_detached_head else _get_current_branch(cwd)
commit = _get_commit_hash(cwd)
staged = _get_staged_count(cwd)
unstaged = _get_unstaged_count(cwd)
untracked = _get_untracked_count(cwd)
is_clean = (staged == 0 and unstaged == 0 and untracked == 0)
return GitStatus(
branch=branch,
commit=commit,
staged=staged,
unstaged=unstaged,
untracked=untracked,
is_detached=is_detached_head,
is_clean=is_clean,
)
except GitStatusError:
raise GitStatusError("Failed to get git status")
def is_git_repo(cwd: str = None) -> bool:
try:
_run_git_command(["rev-parse", "--git-dir"], cwd)
return True
except GitStatusError:
return False