Coverage for src/gittools/__init__.py: 13%
38 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-20 21:58 -0800
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-20 21:58 -0800
1import os
2from typing import Any, Optional
4import sh
7def status(path: str = '.') -> Optional[dict[str, Any]]:
8 try:
9 output: str = str(sh.git(
10 "status", "-s", "-b", "--porcelain=2",
11 _cwd=os.path.expanduser(path)
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: Optional[str] = None
25 head: Optional[str] = None
26 upstream: Optional[str] = None
28 ahead: int = 0
29 behind: int = 0
31 modified: list[str] = []
32 untracked: list[str] = []
34 for line in output.rstrip('\n').splitlines():
35 if line.startswith('#'):
36 if line.startswith("# branch.oid "):
37 oid = line.rsplit(' ', 1)[1]
38 if line.startswith("# branch.head "):
39 branch = line.rsplit(' ', 1)[1]
40 if branch != "(detached)":
41 head = branch
42 elif line.startswith("# branch.upstream "):
43 branch = line.rsplit(' ', 1)[1]
44 if branch != "(detached)":
45 upstream = branch
46 elif line.startswith("# branch.ab "):
47 ahead, behind = [abs(int(x)) for x in line.rsplit(' ', 2)[1:]]
48 elif line.startswith('?'):
49 untracked.append(line.rsplit(' ', -1)[1])
50 elif not line.startswith('!'):
51 vals: list[str] = line.split(' ')
52 submodule: str = vals[2]
53 (
54 untracked if submodule[0] == 'S' and submodule[3] == 'U'
55 else modified
56 ).append(vals[-1])
58 return {
59 "oid": oid,
60 "branch": {
61 "head": head,
62 "upstream": upstream
63 },
64 "commits": {
65 "ahead": ahead,
66 "behind": behind
67 },
68 "files": {
69 "modified": modified,
70 "untracked": untracked
71 }
72 }