From a2096f99f086e39c41260ca1569d2b37ed191bb2 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 06:52:50 +0000 Subject: [PATCH] Initial upload: DevDash CLI with TUI dashboard --- src/git/status.py | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/git/status.py diff --git a/src/git/status.py b/src/git/status.py new file mode 100644 index 0000000..d597e93 --- /dev/null +++ b/src/git/status.py @@ -0,0 +1,96 @@ +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