Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/git/objects/base.py: 65%
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# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
2#
3# This module is part of GitPython and is released under the
4# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
6__all__ = ["Object", "IndexObject"]
8import os.path as osp
10import gitdb.typ as dbtyp
12from git.compat import force_text
13from git.exc import WorkTreeRepositoryUnsupported
14from git.util import LazyMixin, bin_to_hex, join_path_native, stream_copy
16from .util import get_object_type_by_name
18# typing ------------------------------------------------------------------
20from typing import Any, TYPE_CHECKING, Union
22from git.types import AnyGitObject, GitObjectTypeString, PathLike, SupportsWrite
24if TYPE_CHECKING:
25 from gitdb.base import OStream
27 from git.refs.reference import Reference
28 from git.repo import Repo
30 from .blob import Blob
31 from .submodule.base import Submodule
32 from .tree import Tree
34IndexObjUnion = Union["Tree", "Blob", "Submodule"]
36# --------------------------------------------------------------------------
39class Object(LazyMixin):
40 """Base class for classes representing git object types.
42 The following four leaf classes represent specific kinds of git objects:
44 * :class:`Blob <git.objects.blob.Blob>`
45 * :class:`Tree <git.objects.tree.Tree>`
46 * :class:`Commit <git.objects.commit.Commit>`
47 * :class:`TagObject <git.objects.tag.TagObject>`
49 See :manpage:`gitglossary(7)` on:
51 * "object": https://git-scm.com/docs/gitglossary#def_object
52 * "object type": https://git-scm.com/docs/gitglossary#def_object_type
53 * "blob": https://git-scm.com/docs/gitglossary#def_blob_object
54 * "tree object": https://git-scm.com/docs/gitglossary#def_tree_object
55 * "commit object": https://git-scm.com/docs/gitglossary#def_commit_object
56 * "tag object": https://git-scm.com/docs/gitglossary#def_tag_object
58 :note:
59 See the :class:`~git.types.AnyGitObject` union type of the four leaf subclasses
60 that represent actual git object types.
62 :note:
63 :class:`~git.objects.submodule.base.Submodule` is defined under the hierarchy
64 rooted at this :class:`Object` class, even though submodules are not really a
65 type of git object. (This also applies to its
66 :class:`~git.objects.submodule.root.RootModule` subclass.)
68 :note:
69 This :class:`Object` class should not be confused with :class:`object` (the root
70 of the class hierarchy in Python).
71 """
73 NULL_HEX_SHA = "0" * 40
74 NULL_BIN_SHA = b"\0" * 20
76 TYPES = (
77 dbtyp.str_blob_type,
78 dbtyp.str_tree_type,
79 dbtyp.str_commit_type,
80 dbtyp.str_tag_type,
81 )
83 __slots__ = ("repo", "binsha", "size")
85 type: Union[GitObjectTypeString, None] = None
86 """String identifying (a concrete :class:`Object` subtype for) a git object type.
88 The subtypes that this may name correspond to the kinds of git objects that exist,
89 i.e., the objects that may be present in a git repository.
91 :note:
92 Most subclasses represent specific types of git objects and override this class
93 attribute accordingly. This attribute is ``None`` in the :class:`Object` base
94 class, as well as the :class:`IndexObject` intermediate subclass, but never
95 ``None`` in concrete leaf subclasses representing specific git object types.
97 :note:
98 See also :class:`~git.types.GitObjectTypeString`.
99 """
101 def __init__(self, repo: "Repo", binsha: bytes) -> None:
102 """Initialize an object by identifying it by its binary sha.
104 All keyword arguments will be set on demand if ``None``.
106 :param repo:
107 Repository this object is located in.
109 :param binsha:
110 20 byte SHA1
112 :note:
113 Object data is loaded lazily. Loading uncached :attr:`size` metadata
114 raises :exc:`ValueError` if `binsha` refers to a different object type.
115 """
116 super().__init__()
117 self.repo = repo
118 self.binsha = binsha
119 assert len(binsha) == 20, "Require 20 byte binary sha, got %r, len = %i" % (
120 binsha,
121 len(binsha),
122 )
124 @classmethod
125 def new(cls, repo: "Repo", id: Union[str, "Reference"]) -> AnyGitObject:
126 """
127 :return:
128 New :class:`Object` instance of a type appropriate to the object type behind
129 `id`. The id of the newly created object will be a binsha even though the
130 input id may have been a :class:`~git.refs.reference.Reference` or rev-spec.
132 :param id:
133 :class:`~git.refs.reference.Reference`, rev-spec, or hexsha.
135 :note:
136 This cannot be a ``__new__`` method as it would always call :meth:`__init__`
137 with the input id which is not necessarily a binsha.
138 """
139 return repo.rev_parse(str(id))
141 @classmethod
142 def new_from_sha(cls, repo: "Repo", sha1: bytes) -> AnyGitObject:
143 """
144 :return:
145 New object instance of a type appropriate to represent the given binary sha1
147 :param sha1:
148 20 byte binary sha1.
149 """
150 if sha1 == cls.NULL_BIN_SHA:
151 # The NULL binsha is always the root commit.
152 return get_object_type_by_name(b"commit")(repo, sha1)
153 # END handle special case
154 oinfo = repo.odb.info(sha1)
155 inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha)
156 inst.size = oinfo.size
157 return inst
159 def _set_cache_(self, attr: str) -> None:
160 """Retrieve object information."""
161 if attr == "size":
162 oinfo = self.repo.odb.info(self.binsha)
163 typename = force_text(oinfo.type, "ascii")
164 if self.type is not None and typename != self.type:
165 raise ValueError("Object %s is a %s, not a %s" % (self.hexsha, typename, self.type))
166 self.size = oinfo.size # type: int
167 else:
168 super()._set_cache_(attr)
170 def __eq__(self, other: Any) -> bool:
171 """:return: ``True`` if the objects have the same SHA1"""
172 if not hasattr(other, "binsha"):
173 return False
174 return self.binsha == other.binsha
176 def __ne__(self, other: Any) -> bool:
177 """:return: ``True`` if the objects do not have the same SHA1"""
178 if not hasattr(other, "binsha"):
179 return True
180 return self.binsha != other.binsha
182 def __hash__(self) -> int:
183 """:return: Hash of our id allowing objects to be used in dicts and sets"""
184 return hash(self.binsha)
186 def __str__(self) -> str:
187 """:return: String of our SHA1 as understood by all git commands"""
188 return self.hexsha
190 def __repr__(self) -> str:
191 """:return: String with pythonic representation of our object"""
192 return '<git.%s "%s">' % (self.__class__.__name__, self.hexsha)
194 @property
195 def hexsha(self) -> str:
196 """:return: 40 byte hex version of our 20 byte binary sha"""
197 # b2a_hex produces bytes.
198 return bin_to_hex(self.binsha).decode("ascii")
200 @property
201 def data_stream(self) -> "OStream":
202 """
203 :return:
204 File-object compatible stream to the uncompressed raw data of the object
206 :note:
207 Returned streams must be read in order.
208 """
209 return self.repo.odb.stream(self.binsha)
211 def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object":
212 """Write our data directly to the given output stream.
214 :param ostream:
215 File-object compatible stream object.
217 :return:
218 self
219 """
220 istream = self.repo.odb.stream(self.binsha)
221 stream_copy(istream, ostream)
222 return self
225class IndexObject(Object):
226 """Base for all objects that can be part of the index file.
228 The classes representing git object types that can be part of the index file are
229 :class:`~git.objects.tree.Tree` and :class:`~git.objects.blob.Blob`. In addition,
230 :class:`~git.objects.submodule.base.Submodule`, which is not really a git object
231 type but can be part of an index file, is also a subclass.
232 """
234 __slots__ = ("path", "mode")
236 # For compatibility with iterable lists.
237 _id_attribute_ = "path"
239 def __init__(
240 self,
241 repo: "Repo",
242 binsha: bytes,
243 mode: Union[None, int] = None,
244 path: Union[None, PathLike] = None,
245 ) -> None:
246 """Initialize a newly instanced :class:`IndexObject`.
248 :param repo:
249 The :class:`~git.repo.base.Repo` we are located in.
251 :param binsha:
252 20 byte sha1.
254 :param mode:
255 The stat-compatible file mode as :class:`int`.
256 Use the :mod:`stat` module to evaluate the information.
258 :param path:
259 The path to the file in the file system, relative to the git repository
260 root, like ``file.ext`` or ``folder/other.ext``.
262 :note:
263 Path may not be set if the index object has been created directly, as it
264 cannot be retrieved without knowing the parent tree.
265 """
266 super().__init__(repo, binsha)
267 if mode is not None:
268 self.mode = mode
269 if path is not None:
270 self.path = path
272 def __hash__(self) -> int:
273 """
274 :return:
275 Hash of our path as index items are uniquely identifiable by path, not by
276 their data!
277 """
278 return hash(self.path)
280 def _set_cache_(self, attr: str) -> None:
281 if attr in IndexObject.__slots__:
282 # They cannot be retrieved later on (not without searching for them).
283 raise AttributeError(
284 "Attribute '%s' unset: path and mode attributes must have been set during %s object creation"
285 % (attr, type(self).__name__)
286 )
287 else:
288 super()._set_cache_(attr)
289 # END handle slot attribute
291 @property
292 def name(self) -> str:
293 """:return: Name portion of the path, effectively being the basename"""
294 return osp.basename(self.path)
296 @property
297 def abspath(self) -> PathLike:
298 R"""
299 :return:
300 Absolute path to this index object in the file system (as opposed to the
301 :attr:`path` field which is a path relative to the git repository).
303 The returned path will be native to the system and contains ``\`` on
304 Windows.
305 """
306 if self.repo.working_tree_dir is not None:
307 return join_path_native(self.repo.working_tree_dir, self.path)
308 else:
309 raise WorkTreeRepositoryUnsupported("working_tree_dir was None or empty")