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

96 statements  

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/ 

5 

6__all__ = ["Object", "IndexObject"] 

7 

8import os.path as osp 

9 

10import gitdb.typ as dbtyp 

11 

12from git.compat import force_text 

13from git.exc import WorkTreeRepositoryUnsupported 

14from git.util import LazyMixin, bin_to_hex, join_path_native, stream_copy 

15 

16from .util import get_object_type_by_name 

17 

18# typing ------------------------------------------------------------------ 

19 

20from typing import Any, TYPE_CHECKING, Union 

21 

22from git.types import AnyGitObject, GitObjectTypeString, PathLike, SupportsWrite 

23 

24if TYPE_CHECKING: 

25 from gitdb.base import OStream 

26 

27 from git.refs.reference import Reference 

28 from git.repo import Repo 

29 

30 from .blob import Blob 

31 from .submodule.base import Submodule 

32 from .tree import Tree 

33 

34IndexObjUnion = Union["Tree", "Blob", "Submodule"] 

35 

36# -------------------------------------------------------------------------- 

37 

38 

39class Object(LazyMixin): 

40 """Base class for classes representing git object types. 

41 

42 The following four leaf classes represent specific kinds of git objects: 

43 

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>` 

48 

49 See :manpage:`gitglossary(7)` on: 

50 

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 

57 

58 :note: 

59 See the :class:`~git.types.AnyGitObject` union type of the four leaf subclasses 

60 that represent actual git object types. 

61 

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.) 

67 

68 :note: 

69 This :class:`Object` class should not be confused with :class:`object` (the root 

70 of the class hierarchy in Python). 

71 """ 

72 

73 NULL_HEX_SHA = "0" * 40 

74 NULL_BIN_SHA = b"\0" * 20 

75 

76 TYPES = ( 

77 dbtyp.str_blob_type, 

78 dbtyp.str_tree_type, 

79 dbtyp.str_commit_type, 

80 dbtyp.str_tag_type, 

81 ) 

82 

83 __slots__ = ("repo", "binsha", "size") 

84 

85 type: Union[GitObjectTypeString, None] = None 

86 """String identifying (a concrete :class:`Object` subtype for) a git object type. 

87 

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. 

90 

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. 

96 

97 :note: 

98 See also :class:`~git.types.GitObjectTypeString`. 

99 """ 

100 

101 def __init__(self, repo: "Repo", binsha: bytes) -> None: 

102 """Initialize an object by identifying it by its binary sha. 

103 

104 All keyword arguments will be set on demand if ``None``. 

105 

106 :param repo: 

107 Repository this object is located in. 

108 

109 :param binsha: 

110 20 byte SHA1 

111 

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 ) 

123 

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. 

131 

132 :param id: 

133 :class:`~git.refs.reference.Reference`, rev-spec, or hexsha. 

134 

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)) 

140 

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 

146 

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 

158 

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) 

169 

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 

175 

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 

181 

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) 

185 

186 def __str__(self) -> str: 

187 """:return: String of our SHA1 as understood by all git commands""" 

188 return self.hexsha 

189 

190 def __repr__(self) -> str: 

191 """:return: String with pythonic representation of our object""" 

192 return '<git.%s "%s">' % (self.__class__.__name__, self.hexsha) 

193 

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") 

199 

200 @property 

201 def data_stream(self) -> "OStream": 

202 """ 

203 :return: 

204 File-object compatible stream to the uncompressed raw data of the object 

205 

206 :note: 

207 Returned streams must be read in order. 

208 """ 

209 return self.repo.odb.stream(self.binsha) 

210 

211 def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object": 

212 """Write our data directly to the given output stream. 

213 

214 :param ostream: 

215 File-object compatible stream object. 

216 

217 :return: 

218 self 

219 """ 

220 istream = self.repo.odb.stream(self.binsha) 

221 stream_copy(istream, ostream) 

222 return self 

223 

224 

225class IndexObject(Object): 

226 """Base for all objects that can be part of the index file. 

227 

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 """ 

233 

234 __slots__ = ("path", "mode") 

235 

236 # For compatibility with iterable lists. 

237 _id_attribute_ = "path" 

238 

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`. 

247 

248 :param repo: 

249 The :class:`~git.repo.base.Repo` we are located in. 

250 

251 :param binsha: 

252 20 byte sha1. 

253 

254 :param mode: 

255 The stat-compatible file mode as :class:`int`. 

256 Use the :mod:`stat` module to evaluate the information. 

257 

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``. 

261 

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 

271 

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) 

279 

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 

290 

291 @property 

292 def name(self) -> str: 

293 """:return: Name portion of the path, effectively being the basename""" 

294 return osp.basename(self.path) 

295 

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). 

302 

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")