You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.5KB

  1. # base.py
  2. # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
  3. #
  4. # This module is part of GitPython and is released under
  5. # the BSD License: http://www.opensource.org/licenses/bsd-license.php
  6. from git.util import LazyMixin, join_path_native, stream_copy, bin_to_hex
  7. import gitdb.typ as dbtyp
  8. import os.path as osp
  9. from .util import get_object_type_by_name
  10. _assertion_msg_format = "Created object %r whose python type %r disagrees with the acutal git object type %r"
  11. __all__ = ("Object", "IndexObject")
  12. class Object(LazyMixin):
  13. """Implements an Object which may be Blobs, Trees, Commits and Tags"""
  14. NULL_HEX_SHA = '0' * 40
  15. NULL_BIN_SHA = b'\0' * 20
  16. TYPES = (dbtyp.str_blob_type, dbtyp.str_tree_type, dbtyp.str_commit_type, dbtyp.str_tag_type)
  17. __slots__ = ("repo", "binsha", "size")
  18. type = None # to be set by subclass
  19. def __init__(self, repo, binsha):
  20. """Initialize an object by identifying it by its binary sha.
  21. All keyword arguments will be set on demand if None.
  22. :param repo: repository this object is located in
  23. :param binsha: 20 byte SHA1"""
  24. super(Object, self).__init__()
  25. self.repo = repo
  26. self.binsha = binsha
  27. assert len(binsha) == 20, "Require 20 byte binary sha, got %r, len = %i" % (binsha, len(binsha))
  28. @classmethod
  29. def new(cls, repo, id): # @ReservedAssignment
  30. """
  31. :return: New Object instance of a type appropriate to the object type behind
  32. id. The id of the newly created object will be a binsha even though
  33. the input id may have been a Reference or Rev-Spec
  34. :param id: reference, rev-spec, or hexsha
  35. :note: This cannot be a __new__ method as it would always call __init__
  36. with the input id which is not necessarily a binsha."""
  37. return repo.rev_parse(str(id))
  38. @classmethod
  39. def new_from_sha(cls, repo, sha1):
  40. """
  41. :return: new object instance of a type appropriate to represent the given
  42. binary sha1
  43. :param sha1: 20 byte binary sha1"""
  44. if sha1 == cls.NULL_BIN_SHA:
  45. # the NULL binsha is always the root commit
  46. return get_object_type_by_name(b'commit')(repo, sha1)
  47. # END handle special case
  48. oinfo = repo.odb.info(sha1)
  49. inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha)
  50. inst.size = oinfo.size
  51. return inst
  52. def _set_cache_(self, attr):
  53. """Retrieve object information"""
  54. if attr == "size":
  55. oinfo = self.repo.odb.info(self.binsha)
  56. self.size = oinfo.size
  57. # assert oinfo.type == self.type, _assertion_msg_format % (self.binsha, oinfo.type, self.type)
  58. else:
  59. super(Object, self)._set_cache_(attr)
  60. def __eq__(self, other):
  61. """:return: True if the objects have the same SHA1"""
  62. if not hasattr(other, 'binsha'):
  63. return False
  64. return self.binsha == other.binsha
  65. def __ne__(self, other):
  66. """:return: True if the objects do not have the same SHA1 """
  67. if not hasattr(other, 'binsha'):
  68. return True
  69. return self.binsha != other.binsha
  70. def __hash__(self):
  71. """:return: Hash of our id allowing objects to be used in dicts and sets"""
  72. return hash(self.binsha)
  73. def __str__(self):
  74. """:return: string of our SHA1 as understood by all git commands"""
  75. return self.hexsha
  76. def __repr__(self):
  77. """:return: string with pythonic representation of our object"""
  78. return '<git.%s "%s">' % (self.__class__.__name__, self.hexsha)
  79. @property
  80. def hexsha(self):
  81. """:return: 40 byte hex version of our 20 byte binary sha"""
  82. # b2a_hex produces bytes
  83. return bin_to_hex(self.binsha).decode('ascii')
  84. @property
  85. def data_stream(self):
  86. """ :return: File Object compatible stream to the uncompressed raw data of the object
  87. :note: returned streams must be read in order"""
  88. return self.repo.odb.stream(self.binsha)
  89. def stream_data(self, ostream):
  90. """Writes our data directly to the given output stream
  91. :param ostream: File object compatible stream object.
  92. :return: self"""
  93. istream = self.repo.odb.stream(self.binsha)
  94. stream_copy(istream, ostream)
  95. return self
  96. class IndexObject(Object):
  97. """Base for all objects that can be part of the index file , namely Tree, Blob and
  98. SubModule objects"""
  99. __slots__ = ("path", "mode")
  100. # for compatibility with iterable lists
  101. _id_attribute_ = 'path'
  102. def __init__(self, repo, binsha, mode=None, path=None):
  103. """Initialize a newly instanced IndexObject
  104. :param repo: is the Repo we are located in
  105. :param binsha: 20 byte sha1
  106. :param mode:
  107. is the stat compatible file mode as int, use the stat module
  108. to evaluate the information
  109. :param path:
  110. is the path to the file in the file system, relative to the git repository root, i.e.
  111. file.ext or folder/other.ext
  112. :note:
  113. Path may not be set of the index object has been created directly as it cannot
  114. be retrieved without knowing the parent tree."""
  115. super(IndexObject, self).__init__(repo, binsha)
  116. if mode is not None:
  117. self.mode = mode
  118. if path is not None:
  119. self.path = path
  120. def __hash__(self):
  121. """
  122. :return:
  123. Hash of our path as index items are uniquely identifiable by path, not
  124. by their data !"""
  125. return hash(self.path)
  126. def _set_cache_(self, attr):
  127. if attr in IndexObject.__slots__:
  128. # they cannot be retrieved lateron ( not without searching for them )
  129. raise AttributeError(
  130. "Attribute '%s' unset: path and mode attributes must have been set during %s object creation"
  131. % (attr, type(self).__name__))
  132. else:
  133. super(IndexObject, self)._set_cache_(attr)
  134. # END handle slot attribute
  135. @property
  136. def name(self):
  137. """:return: Name portion of the path, effectively being the basename"""
  138. return osp.basename(self.path)
  139. @property
  140. def abspath(self):
  141. """
  142. :return:
  143. Absolute path to this index object in the file system ( as opposed to the
  144. .path field which is a path relative to the git repository ).
  145. The returned path will be native to the system and contains '\' on windows. """
  146. return join_path_native(self.repo.working_tree_dir, self.path)