Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/git/index/typ.py: 64%
74 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
1"""Module with additional types used by the index"""
3from binascii import b2a_hex
4from pathlib import Path
6from .util import pack, unpack
7from git.objects import Blob
10# typing ----------------------------------------------------------------------
12from typing import NamedTuple, Sequence, TYPE_CHECKING, Tuple, Union, cast, List
14from git.types import PathLike
16if TYPE_CHECKING: 16 ↛ 17line 16 didn't jump to line 17, because the condition on line 16 was never true
17 from git.repo import Repo
19StageType = int
21# ---------------------------------------------------------------------------------
23__all__ = ("BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType")
25# { Invariants
26CE_NAMEMASK = 0x0FFF
27CE_STAGEMASK = 0x3000
28CE_EXTENDED = 0x4000
29CE_VALID = 0x8000
30CE_STAGESHIFT = 12
32# } END invariants
35class BlobFilter(object):
37 """
38 Predicate to be used by iter_blobs allowing to filter only return blobs which
39 match the given list of directories or files.
41 The given paths are given relative to the repository.
42 """
44 __slots__ = "paths"
46 def __init__(self, paths: Sequence[PathLike]) -> None:
47 """
48 :param paths:
49 tuple or list of paths which are either pointing to directories or
50 to files relative to the current repository
51 """
52 self.paths = paths
54 def __call__(self, stage_blob: Tuple[StageType, Blob]) -> bool:
55 blob_pathlike: PathLike = stage_blob[1].path
56 blob_path: Path = blob_pathlike if isinstance(blob_pathlike, Path) else Path(blob_pathlike)
57 for pathlike in self.paths:
58 path: Path = pathlike if isinstance(pathlike, Path) else Path(pathlike)
59 # TODO: Change to use `PosixPath.is_relative_to` once Python 3.8 is no longer supported.
60 filter_parts: List[str] = path.parts
61 blob_parts: List[str] = blob_path.parts
62 if len(filter_parts) > len(blob_parts):
63 continue
64 if all(i == j for i, j in zip(filter_parts, blob_parts)):
65 return True
66 return False
69class BaseIndexEntryHelper(NamedTuple):
70 """Typed namedtuple to provide named attribute access for BaseIndexEntry.
71 Needed to allow overriding __new__ in child class to preserve backwards compat."""
73 mode: int
74 binsha: bytes
75 flags: int
76 path: PathLike
77 ctime_bytes: bytes = pack(">LL", 0, 0)
78 mtime_bytes: bytes = pack(">LL", 0, 0)
79 dev: int = 0
80 inode: int = 0
81 uid: int = 0
82 gid: int = 0
83 size: int = 0
86class BaseIndexEntry(BaseIndexEntryHelper):
88 """Small Brother of an index entry which can be created to describe changes
89 done to the index in which case plenty of additional information is not required.
91 As the first 4 data members match exactly to the IndexEntry type, methods
92 expecting a BaseIndexEntry can also handle full IndexEntries even if they
93 use numeric indices for performance reasons.
94 """
96 def __new__(
97 cls,
98 inp_tuple: Union[
99 Tuple[int, bytes, int, PathLike],
100 Tuple[int, bytes, int, PathLike, bytes, bytes, int, int, int, int, int],
101 ],
102 ) -> "BaseIndexEntry":
103 """Override __new__ to allow construction from a tuple for backwards compatibility"""
104 return super().__new__(cls, *inp_tuple)
106 def __str__(self) -> str:
107 return "%o %s %i\t%s" % (self.mode, self.hexsha, self.stage, self.path)
109 def __repr__(self) -> str:
110 return "(%o, %s, %i, %s)" % (self.mode, self.hexsha, self.stage, self.path)
112 @property
113 def hexsha(self) -> str:
114 """hex version of our sha"""
115 return b2a_hex(self.binsha).decode("ascii")
117 @property
118 def stage(self) -> int:
119 """Stage of the entry, either:
121 * 0 = default stage
122 * 1 = stage before a merge or common ancestor entry in case of a 3 way merge
123 * 2 = stage of entries from the 'left' side of the merge
124 * 3 = stage of entries from the right side of the merge
126 :note: For more information, see http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html
127 """
128 return (self.flags & CE_STAGEMASK) >> CE_STAGESHIFT
130 @classmethod
131 def from_blob(cls, blob: Blob, stage: int = 0) -> "BaseIndexEntry":
132 """:return: Fully equipped BaseIndexEntry at the given stage"""
133 return cls((blob.mode, blob.binsha, stage << CE_STAGESHIFT, blob.path))
135 def to_blob(self, repo: "Repo") -> Blob:
136 """:return: Blob using the information of this index entry"""
137 return Blob(repo, self.binsha, self.mode, self.path)
140class IndexEntry(BaseIndexEntry):
142 """Allows convenient access to IndexEntry data without completely unpacking it.
144 Attributes usully accessed often are cached in the tuple whereas others are
145 unpacked on demand.
147 See the properties for a mapping between names and tuple indices."""
149 @property
150 def ctime(self) -> Tuple[int, int]:
151 """
152 :return:
153 Tuple(int_time_seconds_since_epoch, int_nano_seconds) of the
154 file's creation time"""
155 return cast(Tuple[int, int], unpack(">LL", self.ctime_bytes))
157 @property
158 def mtime(self) -> Tuple[int, int]:
159 """See ctime property, but returns modification time"""
160 return cast(Tuple[int, int], unpack(">LL", self.mtime_bytes))
162 @classmethod
163 def from_base(cls, base: "BaseIndexEntry") -> "IndexEntry":
164 """
165 :return:
166 Minimal entry as created from the given BaseIndexEntry instance.
167 Missing values will be set to null-like values
169 :param base: Instance of type BaseIndexEntry"""
170 time = pack(">LL", 0, 0)
171 return IndexEntry((base.mode, base.binsha, base.flags, base.path, time, time, 0, 0, 0, 0, 0))
173 @classmethod
174 def from_blob(cls, blob: Blob, stage: int = 0) -> "IndexEntry":
175 """:return: Minimal entry resembling the given blob object"""
176 time = pack(">LL", 0, 0)
177 return IndexEntry(
178 (
179 blob.mode,
180 blob.binsha,
181 stage << CE_STAGESHIFT,
182 blob.path,
183 time,
184 time,
185 0,
186 0,
187 0,
188 0,
189 blob.size,
190 )
191 )