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
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
1# This module is part of GitPython and is released under the
2# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
4"""Some ref-based objects.
6Note the distinction between the :class:`HEAD` and :class:`Head` classes.
7"""
9__all__ = ["HEAD", "Head"]
11from git.config import GitConfigParser, SectionConstraint
12from git.exc import GitCommandError
13from git.util import join_path
15from .reference import Reference
16from .symbolic import SymbolicReference
18# typing ---------------------------------------------------
20from typing import Any, Sequence, TYPE_CHECKING, Union
22from git.types import Commit_ish, PathLike
24if TYPE_CHECKING:
25 from git.objects import Commit
26 from git.refs import RemoteReference
27 from git.repo import Repo
29# -------------------------------------------------------------------
32def strip_quotes(string: str) -> str:
33 if string.startswith('"') and string.endswith('"'):
34 return string[1:-1]
35 return string
38class HEAD(SymbolicReference):
39 """Special case of a :class:`~git.refs.symbolic.SymbolicReference` representing the
40 repository's HEAD reference."""
42 _HEAD_NAME = "HEAD"
43 _ORIG_HEAD_NAME = "ORIG_HEAD"
45 __slots__ = ()
47 # TODO: This can be removed once SymbolicReference.commit has static type hints.
48 commit: "Commit"
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)
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)
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.
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.
78 :param index:
79 If ``True``, the index will be set to match the given commit.
80 Otherwise it will not be touched.
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.
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.
91 :param kwargs:
92 Additional arguments passed to :manpage:`git-reset(1)`.
94 :return:
95 self
96 """
97 mode: Union[str, None]
98 mode = "--soft"
99 if index:
100 mode = "--mixed"
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
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")
114 # END working tree handling
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
125 return self
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.
132 Examples::
134 >>> repo = Repo("/path/to/repo")
135 >>> head = repo.heads[0]
137 >>> head.name
138 'master'
140 >>> head.commit
141 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
143 >>> head.commit.hexsha
144 '1c09f116cbc2cb4100fb6935bb162daa4723f455'
145 """
147 _common_path_default = "refs/heads"
148 k_config_remote = "remote"
149 k_config_remote_ref = "merge" # Branch to merge from remote.
151 @classmethod
152 def delete(cls, repo: "Repo", *heads: "Union[Head, str]", force: bool = False, **kwargs: Any) -> None:
153 """Delete the given heads.
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)
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.
168 :param remote_reference:
169 The remote reference to track or None to untrack any references.
171 :return:
172 self
173 """
174 from .remote import RemoteReference
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
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 )
193 return self
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
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
213 # We are not a tracking branch.
214 return None
216 def rename(self, new_path: PathLike, force: bool = False) -> "Head":
217 """Rename self to a new path.
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.
223 :param force:
224 If ``True``, the rename will succeed even if a head with the target name
225 already exists.
227 :return:
228 self
230 :note:
231 Respects the ref log, as git commands are used.
232 """
233 flag = "-m"
234 if force:
235 flag = "-M"
237 self.repo.git.branch(flag, self, new_path)
238 self.path = "%s/%s" % (self._common_path_default, new_path)
239 return self
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.
246 The command will fail if changed working tree files would be overwritten.
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.
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.
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.
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")
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
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
286 return SectionConstraint(parser, 'branch "%s"' % self.name)
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)
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)
304 # } END configuration