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
« 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
5from .symbolic import SymbolicReference
6from .reference import Reference
8# typinng ---------------------------------------------------
10from typing import Any, Sequence, Union, TYPE_CHECKING
12from git.types import PathLike, Commit_ish
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
19# -------------------------------------------------------------------
21__all__ = ["HEAD", "Head"]
24def strip_quotes(string: str) -> str:
25 if string.startswith('"') and string.endswith('"'):
26 return string[1:-1]
27 return string
30class HEAD(SymbolicReference):
32 """Special case of a Symbolic Reference as it represents the repository's
33 HEAD reference."""
35 _HEAD_NAME = "HEAD"
36 _ORIG_HEAD_NAME = "ORIG_HEAD"
37 __slots__ = ()
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"
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)
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.
63 :param commit:
64 Commit object, Reference Object or string identifying a revision we
65 should reset HEAD to.
67 :param index:
68 If True, the index will be set to match the given commit. Otherwise
69 it will not be touched.
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
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.
80 :param kwargs:
81 Additional arguments passed to git-reset.
83 :return: self"""
84 mode: Union[str, None]
85 mode = "--soft"
86 if index:
87 mode = "--mixed"
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
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")
101 # END working tree handling
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
112 return self
115class Head(Reference):
117 """A Head is a named reference to a Commit. Every Head instance contains a name
118 and a Commit object.
120 Examples::
122 >>> repo = Repo("/path/to/repo")
123 >>> head = repo.heads[0]
125 >>> head.name
126 'master'
128 >>> head.commit
129 <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
131 >>> head.commit.hexsha
132 '1c09f116cbc2cb4100fb6935bb162daa4723f455'"""
134 _common_path_default = "refs/heads"
135 k_config_remote = "remote"
136 k_config_remote_ref = "merge" # branch to merge from remote
138 @classmethod
139 def delete(cls, repo: "Repo", *heads: "Union[Head, str]", force: bool = False, **kwargs: Any) -> None:
140 """Delete the given heads
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)
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.
156 :param remote_reference: The remote reference to track or None to untrack
157 any references
158 :return: self"""
159 from .remote import RemoteReference
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
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 )
178 return self
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
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
196 # we are not a tracking branch
197 return None
199 def rename(self, new_path: PathLike, force: bool = False) -> "Head":
200 """Rename self to a new path
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
206 :param force:
207 If True, the rename will succeed even if a head with the target name
208 already exists.
210 :return: self
211 :note: respects the ref log as git commands are used"""
212 flag = "-m"
213 if force:
214 flag = "-M"
216 self.repo.git.branch(flag, self, new_path)
217 self.path = "%s/%s" % (self._common_path_default, new_path)
218 return self
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.
225 The command will fail if changed working tree files would be overwritten.
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.
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.
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.
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")
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
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
263 return SectionConstraint(parser, 'branch "%s"' % self.name)
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)
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)
277 # } END configuration