Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/git/refs/head.py: 35%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

98 statements  

1# This module is part of GitPython and is released under the 

2# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ 

3 

4"""Some ref-based objects. 

5 

6Note the distinction between the :class:`HEAD` and :class:`Head` classes. 

7""" 

8 

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

10 

11from git.config import GitConfigParser, SectionConstraint 

12from git.exc import GitCommandError 

13from git.util import join_path 

14 

15from .reference import Reference 

16from .symbolic import SymbolicReference 

17 

18# typing --------------------------------------------------- 

19 

20from typing import Any, Sequence, TYPE_CHECKING, Union 

21 

22from git.types import Commit_ish, PathLike 

23 

24if TYPE_CHECKING: 

25 from git.refs import RemoteReference 

26 from git.repo import Repo 

27 

28# ------------------------------------------------------------------- 

29 

30 

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

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

33 return string[1:-1] 

34 return string 

35 

36 

37class HEAD(SymbolicReference): 

38 """Special case of a :class:`~git.refs.symbolic.SymbolicReference` representing the 

39 repository's HEAD reference.""" 

40 

41 _HEAD_NAME = "HEAD" 

42 _ORIG_HEAD_NAME = "ORIG_HEAD" 

43 

44 __slots__ = () 

45 

46 def __init__(self, repo: "Repo", path: PathLike = _HEAD_NAME) -> None: 

47 if path != self._HEAD_NAME: 

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

49 super().__init__(repo, path) 

50 

51 def orig_head(self) -> SymbolicReference: 

52 """ 

53 :return: 

54 :class:`~git.refs.symbolic.SymbolicReference` pointing at the ORIG_HEAD, 

55 which is maintained to contain the previous value of HEAD. 

56 """ 

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

58 

59 def reset( 

60 self, 

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

62 index: bool = True, 

63 working_tree: bool = False, 

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

65 **kwargs: Any, 

66 ) -> "HEAD": 

67 """Reset our HEAD to the given commit optionally synchronizing the index and 

68 working tree. The reference we refer to will be set to commit as well. 

69 

70 :param commit: 

71 :class:`~git.objects.commit.Commit`, :class:`~git.refs.reference.Reference`, 

72 or string identifying a revision we should reset HEAD to. 

73 

74 :param index: 

75 If ``True``, the index will be set to match the given commit. 

76 Otherwise it will not be touched. 

77 

78 :param working_tree: 

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

80 commit, possibly overwriting uncommitted changes without warning. 

81 If `working_tree` is ``True``, `index` must be ``True`` as well. 

82 

83 :param paths: 

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

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

86 

87 :param kwargs: 

88 Additional arguments passed to :manpage:`git-reset(1)`. 

89 

90 :return: 

91 self 

92 """ 

93 mode: Union[str, None] 

94 mode = "--soft" 

95 if index: 

96 mode = "--mixed" 

97 

98 # Explicit "--mixed" when passing paths is deprecated since git 1.5.4. 

99 # See https://github.com/gitpython-developers/GitPython/discussions/1876. 

100 if paths: 

101 mode = None 

102 # END special case 

103 # END handle index 

104 

105 if working_tree: 

106 mode = "--hard" 

107 if not index: 

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

109 

110 # END working tree handling 

111 

112 try: 

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

114 except GitCommandError as e: 

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

116 # modifications after the reset. 

117 if e.status != 1: 

118 raise 

119 # END handle exception 

120 

121 return self 

122 

123 

124class Head(Reference): 

125 """A Head is a named reference to a :class:`~git.objects.commit.Commit`. Every Head 

126 instance contains a name and a :class:`~git.objects.commit.Commit` object. 

127 

128 Examples:: 

129 

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

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

132 

133 >>> head.name 

134 'master' 

135 

136 >>> head.commit 

137 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> 

138 

139 >>> head.commit.hexsha 

140 '1c09f116cbc2cb4100fb6935bb162daa4723f455' 

141 """ 

142 

143 _common_path_default = "refs/heads" 

144 k_config_remote = "remote" 

145 k_config_remote_ref = "merge" # Branch to merge from remote. 

146 

147 @classmethod 

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

149 """Delete the given heads. 

150 

151 :param force: 

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

153 the main development stream. Default ``False``. 

154 """ 

155 flag = "-d" 

156 if force: 

157 flag = "-D" 

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

159 

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

161 """Configure this branch to track the given remote reference. This will 

162 alter this branch's configuration accordingly. 

163 

164 :param remote_reference: 

165 The remote reference to track or None to untrack any references. 

166 

167 :return: 

168 self 

169 """ 

170 from .remote import RemoteReference 

171 

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

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

174 # END handle type 

175 

176 with self.config_writer() as writer: 

177 if remote_reference is None: 

178 writer.remove_option(self.k_config_remote) 

179 writer.remove_option(self.k_config_remote_ref) 

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

181 writer.remove_section() 

182 else: 

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

184 writer.set_value( 

185 self.k_config_remote_ref, 

186 Head.to_full_path(remote_reference.remote_head), 

187 ) 

188 

189 return self 

190 

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

192 """ 

193 :return: 

194 The remote reference we are tracking, or ``None`` if we are not a tracking 

195 branch. 

196 """ 

197 from .remote import RemoteReference 

198 

199 reader = self.config_reader() 

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

201 ref = Head( 

202 self.repo, 

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

204 ) 

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

206 return RemoteReference(self.repo, remote_refpath) 

207 # END handle have tracking branch 

208 

209 # We are not a tracking branch. 

210 return None 

211 

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

213 """Rename self to a new path. 

214 

215 :param new_path: 

216 Either a simple name or a path, e.g. ``new_name`` or ``features/new_name``. 

217 The prefix ``refs/heads`` is implied. 

218 

219 :param force: 

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

221 already exists. 

222 

223 :return: 

224 self 

225 

226 :note: 

227 Respects the ref log, as git commands are used. 

228 """ 

229 flag = "-m" 

230 if force: 

231 flag = "-M" 

232 

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

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

235 return self 

236 

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

238 """Check out this head by setting the HEAD to this reference, by updating the 

239 index to reflect the tree we point to and by updating the working tree to 

240 reflect the latest index. 

241 

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

243 

244 :param force: 

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

246 If ``False``, :exc:`~git.exc.GitCommandError` will be raised in that 

247 situation. 

248 

249 :param kwargs: 

250 Additional keyword arguments to be passed to git checkout, e.g. 

251 ``b="new_branch"`` to create a new branch at the given spot. 

252 

253 :return: 

254 The active branch after the checkout operation, usually self unless a new 

255 branch has been created. 

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

257 reference will be returned instead. 

258 

259 :note: 

260 By default it is only allowed to checkout heads - everything else will leave 

261 the HEAD detached which is allowed and possible, but remains a special state 

262 that some tools might not be able to handle. 

263 """ 

264 kwargs["f"] = force 

265 if kwargs["f"] is False: 

266 kwargs.pop("f") 

267 

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

269 if self.repo.head.is_detached: 

270 return self.repo.head 

271 else: 

272 return self.repo.active_branch 

273 

274 # { Configuration 

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

276 if read_only: 

277 parser = self.repo.config_reader() 

278 else: 

279 parser = self.repo.config_writer() 

280 # END handle parser instance 

281 

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

283 

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

285 """ 

286 :return: 

287 A configuration parser instance constrained to only read this instance's 

288 values. 

289 """ 

290 return self._config_parser(read_only=True) 

291 

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

293 """ 

294 :return: 

295 A configuration writer instance with read-and write access to options of 

296 this head. 

297 """ 

298 return self._config_parser(read_only=False) 

299 

300 # } END configuration