Coverage for src/extratools_gittools/status/__init__.py: 13%

38 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-20 23:37 -0800

1import os 

2from typing import Any, Optional 

3 

4import sh 

5 

6 

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 

15 

16 return { 

17 "path": path, 

18 **parse_status(output), 

19 } 

20 

21 

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

23 oid: Optional[str] = None 

24 

25 head: Optional[str] = None 

26 upstream: Optional[str] = None 

27 

28 ahead: int = 0 

29 behind: int = 0 

30 

31 modified: list[str] = [] 

32 untracked: list[str] = [] 

33 

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]) 

57 

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 }