Coverage for src/extratools_gittools/status.py: 86%
50 statements
« prev ^ index » next coverage.py v7.3.2, created at 2025-04-08 21:19 -0700
« prev ^ index » next coverage.py v7.3.2, created at 2025-04-08 21:19 -0700
1from pathlib import Path
2from typing import Any
4import sh
7def get_status(path: str = '.') -> dict[str, Any] | None:
8 try:
9 output: str = sh.git(
10 "status", "--short", "--branch", "--porcelain=2",
11 _cwd=Path(path).expanduser(),
12 )
13 except Exception:
14 return None
16 return {
17 "path": path,
18 **parse_status(output),
19 }
22def parse_status(output: str) -> dict[str, Any]:
23 oid: str | None = None
25 head: str | None = None
26 upstream: str | None = None
28 ahead: int = 0
29 behind: int = 0
31 staged: list[str] = []
32 unstaged: list[str] = []
33 untracked: list[str] = []
35 for line in output.rstrip('\n').splitlines():
36 vals: list[str] = line.split(' ')
38 match vals[0]:
39 case '#':
40 match vals[1]:
41 case "branch.oid":
42 oid = vals[2]
43 case "branch.head":
44 branch = vals[2]
45 if branch != "(detached)":
46 head = branch
47 case "branch.upstream":
48 branch = vals[2]
49 if branch != "(detached)":
50 upstream = branch
51 case "branch.ab":
52 ahead, behind = [abs(int(x)) for x in vals[2:]]
53 case '?':
54 untracked.append(vals[1])
55 case '!':
56 pass
57 case _:
58 path: str = vals[-1]
60 stage_flags: str = vals[1]
61 if stage_flags[0] != '.':
62 staged.append(path)
63 if stage_flags[1] != '.':
64 unstaged.append(path)
66 submodule_flags: str = vals[2]
67 if submodule_flags[0] == 'S' and submodule_flags[3] == 'U':
68 untracked.append(path)
70 return {
71 "oid": oid,
72 "branch": {
73 "head": head,
74 "upstream": upstream,
75 },
76 "commits": {
77 "ahead": ahead,
78 "behind": behind,
79 },
80 "files": {
81 "staged": staged,
82 "unstaged": unstaged,
83 "untracked": untracked,
84 },
85 }