Coverage for src/extratools_gittools/status.py: 86%

50 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2025-04-10 19:59 -0700

1from pathlib import Path 

2from typing import Any 

3 

4import sh 

5 

6 

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 

15 

16 return { 

17 "path": path, 

18 **parse_status(output), 

19 } 

20 

21 

22def parse_status(output: str) -> dict[str, Any]: 

23 oid: str | None = None 

24 

25 head: str | None = None 

26 upstream: str | None = None 

27 

28 ahead: int = 0 

29 behind: int = 0 

30 

31 staged: list[str] = [] 

32 unstaged: list[str] = [] 

33 untracked: list[str] = [] 

34 

35 for line in output.rstrip('\n').splitlines(): 

36 vals: list[str] = line.split(' ') 

37 

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] 

59 

60 stage_flags: str = vals[1] 

61 if stage_flags[0] != '.': 

62 staged.append(path) 

63 if stage_flags[1] != '.': 

64 unstaged.append(path) 

65 

66 submodule_flags: str = vals[2] 

67 if submodule_flags[0] == 'S' and submodule_flags[3] == 'U': 

68 untracked.append(path) 

69 

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 }