Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

99 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.objects import Commit 

26 from git.refs import RemoteReference 

27 from git.repo import Repo 

28 

29# ------------------------------------------------------------------- 

30 

31 

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

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

34 return string[1:-1] 

35 return string 

36 

37 

38class HEAD(SymbolicReference): 

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

40 repository's HEAD reference.""" 

41 

42 _HEAD_NAME = "HEAD" 

43 _ORIG_HEAD_NAME = "ORIG_HEAD" 

44 

45 __slots__ = () 

46 

47 # TODO: This can be removed once SymbolicReference.commit has static type hints. 

48 commit: "Commit" 

49 

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

51 if path != self._HEAD_NAME: 

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

53 super().__init__(repo, path) 

54 

55 def orig_head(self) -> SymbolicReference: 

56 """ 

57 :return: 

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

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

60 """ 

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

62 

63 def reset( 

64 self, 

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

66 index: bool = True, 

67 working_tree: bool = False, 

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

69 **kwargs: Any, 

70 ) -> "HEAD": 

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

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

73 

74 :param commit: 

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

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

77 

78 :param index: 

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

80 Otherwise it will not be touched. 

81 

82 :param working_tree: 

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

84 commit, possibly overwriting uncommitted changes without warning. 

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

86 

87 :param paths: 

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

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

90 

91 :param kwargs: 

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

93 

94 :return: 

95 self 

96 """ 

97 mode: Union[str, None] 

98 mode = "--soft" 

99 if index: 

100 mode = "--mixed" 

101 

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

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

104 if paths: 

105 mode = None 

106 # END special case 

107 # END handle index 

108 

109 if working_tree: 

110 mode = "--hard" 

111 if not index: 

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

113 

114 # END working tree handling 

115 

116 try: 

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

118 except GitCommandError as e: 

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

120 # modifications after the reset. 

121 if e.status != 1: 

122 raise 

123 # END handle exception 

124 

125 return self 

126 

127 

128class Head(Reference): 

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

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

131 

132 Examples:: 

133 

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

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

136 

137 >>> head.name 

138 'master' 

139 

140 >>> head.commit 

141 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> 

142 

143 >>> head.commit.hexsha 

144 '1c09f116cbc2cb4100fb6935bb162daa4723f455' 

145 """ 

146 

147 _common_path_default = "refs/heads" 

148 k_config_remote = "remote" 

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

150 

151 @classmethod 

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

153 """Delete the given heads. 

154 

155 :param force: 

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

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

158 """ 

159 flag = "-d" 

160 if force: 

161 flag = "-D" 

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

163 

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

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

166 alter this branch's configuration accordingly. 

167 

168 :param remote_reference: 

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

170 

171 :return: 

172 self 

173 """ 

174 from .remote import RemoteReference 

175 

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

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

178 # END handle type 

179 

180 with self.config_writer() as writer: 

181 if remote_reference is None: 

182 writer.remove_option(self.k_config_remote) 

183 writer.remove_option(self.k_config_remote_ref) 

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

185 writer.remove_section() 

186 else: 

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

188 writer.set_value( 

189 self.k_config_remote_ref, 

190 Head.to_full_path(remote_reference.remote_head), 

191 ) 

192 

193 return self 

194 

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

196 """ 

197 :return: 

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

199 branch. 

200 """ 

201 from .remote import RemoteReference 

202 

203 reader = self.config_reader() 

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

205 ref = Head( 

206 self.repo, 

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

208 ) 

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

210 return RemoteReference(self.repo, remote_refpath) 

211 # END handle have tracking branch 

212 

213 # We are not a tracking branch. 

214 return None 

215 

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

217 """Rename self to a new path. 

218 

219 :param new_path: 

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

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

222 

223 :param force: 

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

225 already exists. 

226 

227 :return: 

228 self 

229 

230 :note: 

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

232 """ 

233 flag = "-m" 

234 if force: 

235 flag = "-M" 

236 

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

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

239 return self 

240 

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

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

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

244 reflect the latest index. 

245 

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

247 

248 :param force: 

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

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

251 situation. 

252 

253 :param kwargs: 

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

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

256 

257 :return: 

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

259 branch has been created. 

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

261 reference will be returned instead. 

262 

263 :note: 

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

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

266 that some tools might not be able to handle. 

267 """ 

268 kwargs["f"] = force 

269 if kwargs["f"] is False: 

270 kwargs.pop("f") 

271 

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

273 if self.repo.head.is_detached: 

274 return self.repo.head 

275 else: 

276 return self.repo.active_branch 

277 

278 # { Configuration 

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

280 if read_only: 

281 parser = self.repo.config_reader() 

282 else: 

283 parser = self.repo.config_writer() 

284 # END handle parser instance 

285 

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

287 

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

289 """ 

290 :return: 

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

292 values. 

293 """ 

294 return self._config_parser(read_only=True) 

295 

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

297 """ 

298 :return: 

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

300 this head. 

301 """ 

302 return self._config_parser(read_only=False) 

303 

304 # } END configuration