Coverage for src/features/git.py: 82.98%

47 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-08 13:49 +0000

1#!/usr/bin/env python3 

2 

3# Standard libraries 

4from pathlib import Path 

5from typing import List 

6 

7# Components 

8from ..system.commands import Commands 

9 

10# Git class 

11class Git: 

12 

13 # Constants 

14 BINARY: str = 'git' 

15 

16 # Commit exists 

17 @staticmethod 

18 def commit_exists(reference: str) -> bool: 

19 

20 # Check commit existance 

21 return bool( 

22 Commands.output(Git.BINARY, [ 

23 'rev-parse', 

24 '--quiet', 

25 '--verify', 

26 reference, 

27 ])) 

28 

29 # Diff 

30 @staticmethod 

31 def diff(files: List[str]) -> bool: 

32 

33 # Detect files differences 

34 return not Commands.run( 

35 Git.BINARY, 

36 [ 

37 'diff', 

38 '--quiet', 

39 ] + files, 

40 check=False, 

41 ) 

42 

43 # Hooks directory 

44 @staticmethod 

45 def hooks_dir() -> str: 

46 

47 # Result 

48 return Commands.output(Git.BINARY, [ 

49 'rev-parse', 

50 '--git-path', 

51 'hooks', 

52 ]) 

53 

54 # Hooks files 

55 @staticmethod 

56 def hooks_files() -> List[str]: 

57 

58 # Variables 

59 hooks_dir: Path = Path(Git.hooks_dir()) 

60 

61 # Result 

62 return [ 

63 hook.name for hook in Path(hooks_dir).iterdir() 

64 if hook.is_file() and '.' not in hook.name 

65 ] 

66 

67 # Intend 

68 @staticmethod 

69 def intend(files: List[str]) -> bool: 

70 

71 # Intend to add files 

72 return Commands.run( 

73 Git.BINARY, 

74 [ 

75 'add', 

76 '--intent-to-add', 

77 ] + files, 

78 check=False, 

79 ) 

80 

81 # Is repository 

82 @staticmethod 

83 def is_repository() -> bool: 

84 

85 # Check git repository 

86 return Commands.run( 

87 Git.BINARY, 

88 [ 

89 'rev-parse', 

90 '--is-inside-work-tree', 

91 ], 

92 check=False, 

93 ) 

94 

95 # Restore 

96 @staticmethod 

97 def restore(files: List[str]) -> bool: 

98 

99 # Restore files from the staging area 

100 return Commands.run(Git.BINARY, [ 

101 'checkout', 

102 '--quiet', 

103 '--', 

104 ] + files) 

105 

106 # Remotes 

107 @staticmethod 

108 def remotes() -> List[str]: 

109 

110 # Detect git repository 

111 if not Git.is_repository(): 

112 return [] 

113 

114 # List git remotes 

115 return [ 

116 remote.lstrip('*').strip() 

117 for remote in Commands.output(Git.BINARY, [ 

118 'remote', 

119 ]).splitlines() 

120 ] 

121 

122 # Staging empty 

123 @staticmethod 

124 def staging_empty() -> bool: 

125 

126 # Detect empty staging area 

127 return Commands.run( 

128 Git.BINARY, 

129 [ 

130 'diff', 

131 '--cached', 

132 '--quiet', 

133 ], 

134 check=False, 

135 ) 

136 

137 # Status 

138 @staticmethod 

139 def status(untracked: bool) -> bool: 

140 

141 # Show git status 

142 return Commands.run(Git.BINARY, [ 

143 'status', 

144 ] + ([ 

145 '--untracked-files', 

146 ] if untracked else [])) 

147 

148 # Update remote head 

149 @staticmethod 

150 def update_remote_head(remote: str) -> bool: 

151 

152 # Fetch remote branches 

153 Commands.run(Git.BINARY, [ 

154 'fetch', 

155 f'{remote}', 

156 ]) 

157 

158 # Update remote head 

159 return Commands.run(Git.BINARY, [ 

160 'remote', 

161 'set-head', 

162 f'{remote}', 

163 '-a', 

164 ]) 

165 

166 # URLs 

167 @staticmethod 

168 def urls() -> List[str]: 

169 

170 # Detect git repository 

171 if not Git.is_repository(): 

172 return [] 

173 

174 # List git remotes URLs 

175 return [ 

176 url.strip() for url in Commands.output(Git.BINARY, [ 

177 'ls-remote', 

178 '--get-url', 

179 ]).splitlines() 

180 ]