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
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.refs import RemoteReference
26 from git.repo import Repo
28# -------------------------------------------------------------------
31def strip_quotes(string: str) -> str:
32 if string.startswith('"') and string.endswith('"'):
33 return string[1:-1]
34 return string
37class HEAD(SymbolicReference):
38 """Special case of a :class:`~git.refs.symbolic.SymbolicReference` representing the
39 repository's HEAD reference."""
41 _HEAD_NAME = "HEAD"
42 _ORIG_HEAD_NAME = "ORIG_HEAD"
44 __slots__ = ()
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)
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)
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.
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.
74 :param index:
75 If ``True``, the index will be set to match the given commit.
76 Otherwise it will not be touched.
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.
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.
87 :param kwargs:
88 Additional arguments passed to :manpage:`git-reset(1)`.
90 :return:
91 self
92 """
93 mode: Union[str, None]
94 mode = "--soft"
95 if index:
96 mode = "--mixed"
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
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")
110 # END working tree handling
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
121 return self
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.
128 Examples::
130 >>> repo = Repo("/path/to/repo")
131 >>> head = repo.heads[0]
133 >>> head.name
134 'master'
136 >>> head.commit
137 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
139 >>> head.commit.hexsha
140 '1c09f116cbc2cb4100fb6935bb162daa4723f455'
141 """
143 _common_path_default = "refs/heads"
144 k_config_remote = "remote"
145 k_config_remote_ref = "merge" # Branch to merge from remote.
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.
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)
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.
164 :param remote_reference:
165 The remote reference to track or None to untrack any references.
167 :return:
168 self
169 """
170 from .remote import RemoteReference
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
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 )
189 return self
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
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
209 # We are not a tracking branch.
210 return None
212 def rename(self, new_path: PathLike, force: bool = False) -> "Head":
213 """Rename self to a new path.
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.
219 :param force:
220 If ``True``, the rename will succeed even if a head with the target name
221 already exists.
223 :return:
224 self
226 :note:
227 Respects the ref log, as git commands are used.
228 """
229 flag = "-m"
230 if force:
231 flag = "-M"
233 self.repo.git.branch(flag, self, new_path)
234 self.path = "%s/%s" % (self._common_path_default, new_path)
235 return self
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.
242 The command will fail if changed working tree files would be overwritten.
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.
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.
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.
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")
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
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
282 return SectionConstraint(parser, 'branch "%s"' % self.name)
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)
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)
300 # } END configuration