Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/git/refs/head.py: 26%

98 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from git.config import GitConfigParser, SectionConstraint 

2from git.util import join_path 

3from git.exc import GitCommandError 

4 

5from .symbolic import SymbolicReference 

6from .reference import Reference 

7 

8# typinng --------------------------------------------------- 

9 

10from typing import Any, Sequence, Union, TYPE_CHECKING 

11 

12from git.types import PathLike, Commit_ish 

13 

14if TYPE_CHECKING: 14 ↛ 15line 14 didn't jump to line 15, because the condition on line 14 was never true

15 from git.repo import Repo 

16 from git.objects import Commit 

17 from git.refs import RemoteReference 

18 

19# ------------------------------------------------------------------- 

20 

21__all__ = ["HEAD", "Head"] 

22 

23 

24def strip_quotes(string: str) -> str: 

25 if string.startswith('"') and string.endswith('"'): 

26 return string[1:-1] 

27 return string 

28 

29 

30class HEAD(SymbolicReference): 

31 

32 """Special case of a Symbolic Reference as it represents the repository's 

33 HEAD reference.""" 

34 

35 _HEAD_NAME = "HEAD" 

36 _ORIG_HEAD_NAME = "ORIG_HEAD" 

37 __slots__ = () 

38 

39 def __init__(self, repo: "Repo", path: PathLike = _HEAD_NAME): 

40 if path != self._HEAD_NAME: 

41 raise ValueError("HEAD instance must point to %r, got %r" % (self._HEAD_NAME, path)) 

42 super(HEAD, self).__init__(repo, path) 

43 self.commit: "Commit" 

44 

45 def orig_head(self) -> SymbolicReference: 

46 """ 

47 :return: SymbolicReference pointing at the ORIG_HEAD, which is maintained 

48 to contain the previous value of HEAD""" 

49 return SymbolicReference(self.repo, self._ORIG_HEAD_NAME) 

50 

51 def reset( 

52 self, 

53 commit: Union[Commit_ish, SymbolicReference, str] = "HEAD", 

54 index: bool = True, 

55 working_tree: bool = False, 

56 paths: Union[PathLike, Sequence[PathLike], None] = None, 

57 **kwargs: Any, 

58 ) -> "HEAD": 

59 """Reset our HEAD to the given commit optionally synchronizing 

60 the index and working tree. The reference we refer to will be set to 

61 commit as well. 

62 

63 :param commit: 

64 Commit object, Reference Object or string identifying a revision we 

65 should reset HEAD to. 

66 

67 :param index: 

68 If True, the index will be set to match the given commit. Otherwise 

69 it will not be touched. 

70 

71 :param working_tree: 

72 If True, the working tree will be forcefully adjusted to match the given 

73 commit, possibly overwriting uncommitted changes without warning. 

74 If working_tree is True, index must be true as well 

75 

76 :param paths: 

77 Single path or list of paths relative to the git root directory 

78 that are to be reset. This allows to partially reset individual files. 

79 

80 :param kwargs: 

81 Additional arguments passed to git-reset. 

82 

83 :return: self""" 

84 mode: Union[str, None] 

85 mode = "--soft" 

86 if index: 

87 mode = "--mixed" 

88 

89 # it appears, some git-versions declare mixed and paths deprecated 

90 # see http://github.com/Byron/GitPython/issues#issue/2 

91 if paths: 

92 mode = None 

93 # END special case 

94 # END handle index 

95 

96 if working_tree: 

97 mode = "--hard" 

98 if not index: 

99 raise ValueError("Cannot reset the working tree if the index is not reset as well") 

100 

101 # END working tree handling 

102 

103 try: 

104 self.repo.git.reset(mode, commit, "--", paths, **kwargs) 

105 except GitCommandError as e: 

106 # git nowadays may use 1 as status to indicate there are still unstaged 

107 # modifications after the reset 

108 if e.status != 1: 

109 raise 

110 # END handle exception 

111 

112 return self 

113 

114 

115class Head(Reference): 

116 

117 """A Head is a named reference to a Commit. Every Head instance contains a name 

118 and a Commit object. 

119 

120 Examples:: 

121 

122 >>> repo = Repo("/path/to/repo") 

123 >>> head = repo.heads[0] 

124 

125 >>> head.name 

126 'master' 

127 

128 >>> head.commit 

129 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> 

130 

131 >>> head.commit.hexsha 

132 '1c09f116cbc2cb4100fb6935bb162daa4723f455'""" 

133 

134 _common_path_default = "refs/heads" 

135 k_config_remote = "remote" 

136 k_config_remote_ref = "merge" # branch to merge from remote 

137 

138 @classmethod 

139 def delete(cls, repo: "Repo", *heads: "Union[Head, str]", force: bool = False, **kwargs: Any) -> None: 

140 """Delete the given heads 

141 

142 :param force: 

143 If True, the heads will be deleted even if they are not yet merged into 

144 the main development stream. 

145 Default False""" 

146 flag = "-d" 

147 if force: 

148 flag = "-D" 

149 repo.git.branch(flag, *heads) 

150 

151 def set_tracking_branch(self, remote_reference: Union["RemoteReference", None]) -> "Head": 

152 """ 

153 Configure this branch to track the given remote reference. This will alter 

154 this branch's configuration accordingly. 

155 

156 :param remote_reference: The remote reference to track or None to untrack 

157 any references 

158 :return: self""" 

159 from .remote import RemoteReference 

160 

161 if remote_reference is not None and not isinstance(remote_reference, RemoteReference): 

162 raise ValueError("Incorrect parameter type: %r" % remote_reference) 

163 # END handle type 

164 

165 with self.config_writer() as writer: 

166 if remote_reference is None: 

167 writer.remove_option(self.k_config_remote) 

168 writer.remove_option(self.k_config_remote_ref) 

169 if len(writer.options()) == 0: 

170 writer.remove_section() 

171 else: 

172 writer.set_value(self.k_config_remote, remote_reference.remote_name) 

173 writer.set_value( 

174 self.k_config_remote_ref, 

175 Head.to_full_path(remote_reference.remote_head), 

176 ) 

177 

178 return self 

179 

180 def tracking_branch(self) -> Union["RemoteReference", None]: 

181 """ 

182 :return: The remote_reference we are tracking, or None if we are 

183 not a tracking branch""" 

184 from .remote import RemoteReference 

185 

186 reader = self.config_reader() 

187 if reader.has_option(self.k_config_remote) and reader.has_option(self.k_config_remote_ref): 

188 ref = Head( 

189 self.repo, 

190 Head.to_full_path(strip_quotes(reader.get_value(self.k_config_remote_ref))), 

191 ) 

192 remote_refpath = RemoteReference.to_full_path(join_path(reader.get_value(self.k_config_remote), ref.name)) 

193 return RemoteReference(self.repo, remote_refpath) 

194 # END handle have tracking branch 

195 

196 # we are not a tracking branch 

197 return None 

198 

199 def rename(self, new_path: PathLike, force: bool = False) -> "Head": 

200 """Rename self to a new path 

201 

202 :param new_path: 

203 Either a simple name or a path, i.e. new_name or features/new_name. 

204 The prefix refs/heads is implied 

205 

206 :param force: 

207 If True, the rename will succeed even if a head with the target name 

208 already exists. 

209 

210 :return: self 

211 :note: respects the ref log as git commands are used""" 

212 flag = "-m" 

213 if force: 

214 flag = "-M" 

215 

216 self.repo.git.branch(flag, self, new_path) 

217 self.path = "%s/%s" % (self._common_path_default, new_path) 

218 return self 

219 

220 def checkout(self, force: bool = False, **kwargs: Any) -> Union["HEAD", "Head"]: 

221 """Checkout this head by setting the HEAD to this reference, by updating the index 

222 to reflect the tree we point to and by updating the working tree to reflect 

223 the latest index. 

224 

225 The command will fail if changed working tree files would be overwritten. 

226 

227 :param force: 

228 If True, changes to the index and the working tree will be discarded. 

229 If False, GitCommandError will be raised in that situation. 

230 

231 :param kwargs: 

232 Additional keyword arguments to be passed to git checkout, i.e. 

233 b='new_branch' to create a new branch at the given spot. 

234 

235 :return: 

236 The active branch after the checkout operation, usually self unless 

237 a new branch has been created. 

238 If there is no active branch, as the HEAD is now detached, the HEAD 

239 reference will be returned instead. 

240 

241 :note: 

242 By default it is only allowed to checkout heads - everything else 

243 will leave the HEAD detached which is allowed and possible, but remains 

244 a special state that some tools might not be able to handle.""" 

245 kwargs["f"] = force 

246 if kwargs["f"] is False: 

247 kwargs.pop("f") 

248 

249 self.repo.git.checkout(self, **kwargs) 

250 if self.repo.head.is_detached: 

251 return self.repo.head 

252 else: 

253 return self.repo.active_branch 

254 

255 # { Configuration 

256 def _config_parser(self, read_only: bool) -> SectionConstraint[GitConfigParser]: 

257 if read_only: 

258 parser = self.repo.config_reader() 

259 else: 

260 parser = self.repo.config_writer() 

261 # END handle parser instance 

262 

263 return SectionConstraint(parser, 'branch "%s"' % self.name) 

264 

265 def config_reader(self) -> SectionConstraint[GitConfigParser]: 

266 """ 

267 :return: A configuration parser instance constrained to only read 

268 this instance's values""" 

269 return self._config_parser(read_only=True) 

270 

271 def config_writer(self) -> SectionConstraint[GitConfigParser]: 

272 """ 

273 :return: A configuration writer instance with read-and write access 

274 to options of this head""" 

275 return self._config_parser(read_only=False) 

276 

277 # } END configuration