Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/git/objects/base.py: 69%

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

90 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.exc import WorkTreeRepositoryUnsupported 

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

14 

15from .util import get_object_type_by_name 

16 

17# typing ------------------------------------------------------------------ 

18 

19from typing import Any, TYPE_CHECKING, Union 

20 

21from git.types import AnyGitObject, GitObjectTypeString, PathLike 

22 

23if TYPE_CHECKING: 

24 from gitdb.base import OStream 

25 

26 from git.refs.reference import Reference 

27 from git.repo import Repo 

28 

29 from .blob import Blob 

30 from .submodule.base import Submodule 

31 from .tree import Tree 

32 

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

34 

35# -------------------------------------------------------------------------- 

36 

37 

38class Object(LazyMixin): 

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

40 

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

42 

43 * :class:`Blob <git.objects.blob.Blob>` 

44 * :class:`Tree <git.objects.tree.Tree>` 

45 * :class:`Commit <git.objects.commit.Commit>` 

46 * :class:`TagObject <git.objects.tag.TagObject>` 

47 

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

49 

50 * "object": https://git-scm.com/docs/gitglossary#def_object 

51 * "object type": https://git-scm.com/docs/gitglossary#def_object_type 

52 * "blob": https://git-scm.com/docs/gitglossary#def_blob_object 

53 * "tree object": https://git-scm.com/docs/gitglossary#def_tree_object 

54 * "commit object": https://git-scm.com/docs/gitglossary#def_commit_object 

55 * "tag object": https://git-scm.com/docs/gitglossary#def_tag_object 

56 

57 :note: 

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

59 that represent actual git object types. 

60 

61 :note: 

62 :class:`~git.objects.submodule.base.Submodule` is defined under the hierarchy 

63 rooted at this :class:`Object` class, even though submodules are not really a 

64 type of git object. (This also applies to its 

65 :class:`~git.objects.submodule.root.RootModule` subclass.) 

66 

67 :note: 

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

69 of the class hierarchy in Python). 

70 """ 

71 

72 NULL_HEX_SHA = "0" * 40 

73 NULL_BIN_SHA = b"\0" * 20 

74 

75 TYPES = ( 

76 dbtyp.str_blob_type, 

77 dbtyp.str_tree_type, 

78 dbtyp.str_commit_type, 

79 dbtyp.str_tag_type, 

80 ) 

81 

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

83 

84 type: Union[GitObjectTypeString, None] = None 

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

86 

87 The subtypes that this may name correspond to the kinds of git objects that exist, 

88 i.e., the objects that may be present in a git repository. 

89 

90 :note: 

91 Most subclasses represent specific types of git objects and override this class 

92 attribute accordingly. This attribute is ``None`` in the :class:`Object` base 

93 class, as well as the :class:`IndexObject` intermediate subclass, but never 

94 ``None`` in concrete leaf subclasses representing specific git object types. 

95 

96 :note: 

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

98 """ 

99 

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

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

102 

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

104 

105 :param repo: 

106 Repository this object is located in. 

107 

108 :param binsha: 

109 20 byte SHA1 

110 """ 

111 super().__init__() 

112 self.repo = repo 

113 self.binsha = binsha 

114 assert len(binsha) == 20, "Require 20 byte binary sha, got %r, len = %i" % ( 

115 binsha, 

116 len(binsha), 

117 ) 

118 

119 @classmethod 

120 def new(cls, repo: "Repo", id: Union[str, "Reference"]) -> AnyGitObject: 

121 """ 

122 :return: 

123 New :class:`Object` instance of a type appropriate to the object type behind 

124 `id`. The id of the newly created object will be a binsha even though the 

125 input id may have been a `~git.refs.reference.Reference` or rev-spec. 

126 

127 :param id: 

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

129 

130 :note: 

131 This cannot be a ``__new__`` method as it would always call :meth:`__init__` 

132 with the input id which is not necessarily a binsha. 

133 """ 

134 return repo.rev_parse(str(id)) 

135 

136 @classmethod 

137 def new_from_sha(cls, repo: "Repo", sha1: bytes) -> AnyGitObject: 

138 """ 

139 :return: 

140 New object instance of a type appropriate to represent the given binary sha1 

141 

142 :param sha1: 

143 20 byte binary sha1. 

144 """ 

145 if sha1 == cls.NULL_BIN_SHA: 

146 # The NULL binsha is always the root commit. 

147 return get_object_type_by_name(b"commit")(repo, sha1) 

148 # END handle special case 

149 oinfo = repo.odb.info(sha1) 

150 inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha) 

151 inst.size = oinfo.size 

152 return inst 

153 

154 def _set_cache_(self, attr: str) -> None: 

155 """Retrieve object information.""" 

156 if attr == "size": 

157 oinfo = self.repo.odb.info(self.binsha) 

158 self.size = oinfo.size # type: int 

159 else: 

160 super()._set_cache_(attr) 

161 

162 def __eq__(self, other: Any) -> bool: 

163 """:return: ``True`` if the objects have the same SHA1""" 

164 if not hasattr(other, "binsha"): 

165 return False 

166 return self.binsha == other.binsha 

167 

168 def __ne__(self, other: Any) -> bool: 

169 """:return: ``True`` if the objects do not have the same SHA1""" 

170 if not hasattr(other, "binsha"): 

171 return True 

172 return self.binsha != other.binsha 

173 

174 def __hash__(self) -> int: 

175 """:return: Hash of our id allowing objects to be used in dicts and sets""" 

176 return hash(self.binsha) 

177 

178 def __str__(self) -> str: 

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

180 return self.hexsha 

181 

182 def __repr__(self) -> str: 

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

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

185 

186 @property 

187 def hexsha(self) -> str: 

188 """:return: 40 byte hex version of our 20 byte binary sha""" 

189 # b2a_hex produces bytes. 

190 return bin_to_hex(self.binsha).decode("ascii") 

191 

192 @property 

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

194 """ 

195 :return: 

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

197 

198 :note: 

199 Returned streams must be read in order. 

200 """ 

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

202 

203 def stream_data(self, ostream: "OStream") -> "Object": 

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

205 

206 :param ostream: 

207 File-object compatible stream object. 

208 

209 :return: 

210 self 

211 """ 

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

213 stream_copy(istream, ostream) 

214 return self 

215 

216 

217class IndexObject(Object): 

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

219 

220 The classes representing git object types that can be part of the index file are 

221 :class:`~git.objects.tree.Tree and :class:`~git.objects.blob.Blob`. In addition, 

222 :class:`~git.objects.submodule.base.Submodule`, which is not really a git object 

223 type but can be part of an index file, is also a subclass. 

224 """ 

225 

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

227 

228 # For compatibility with iterable lists. 

229 _id_attribute_ = "path" 

230 

231 def __init__( 

232 self, 

233 repo: "Repo", 

234 binsha: bytes, 

235 mode: Union[None, int] = None, 

236 path: Union[None, PathLike] = None, 

237 ) -> None: 

238 """Initialize a newly instanced :class:`IndexObject`. 

239 

240 :param repo: 

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

242 

243 :param binsha: 

244 20 byte sha1. 

245 

246 :param mode: 

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

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

249 

250 :param path: 

251 The path to the file in the file system, relative to the git repository 

252 root, like ``file.ext`` or ``folder/other.ext``. 

253 

254 :note: 

255 Path may not be set if the index object has been created directly, as it 

256 cannot be retrieved without knowing the parent tree. 

257 """ 

258 super().__init__(repo, binsha) 

259 if mode is not None: 

260 self.mode = mode 

261 if path is not None: 

262 self.path = path 

263 

264 def __hash__(self) -> int: 

265 """ 

266 :return: 

267 Hash of our path as index items are uniquely identifiable by path, not by 

268 their data! 

269 """ 

270 return hash(self.path) 

271 

272 def _set_cache_(self, attr: str) -> None: 

273 if attr in IndexObject.__slots__: 

274 # They cannot be retrieved later on (not without searching for them). 

275 raise AttributeError( 

276 "Attribute '%s' unset: path and mode attributes must have been set during %s object creation" 

277 % (attr, type(self).__name__) 

278 ) 

279 else: 

280 super()._set_cache_(attr) 

281 # END handle slot attribute 

282 

283 @property 

284 def name(self) -> str: 

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

286 return osp.basename(self.path) 

287 

288 @property 

289 def abspath(self) -> PathLike: 

290 R""" 

291 :return: 

292 Absolute path to this index object in the file system (as opposed to the 

293 :attr:`path` field which is a path relative to the git repository). 

294 

295 The returned path will be native to the system and contains ``\`` on 

296 Windows. 

297 """ 

298 if self.repo.working_tree_dir is not None: 

299 return join_path_native(self.repo.working_tree_dir, self.path) 

300 else: 

301 raise WorkTreeRepositoryUnsupported("working_tree_dir was None or empty")