| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 71b9545 commit ca55dbf
13 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,14 +77,6 @@ | |||
| 77 | 77 | "lineCount": 1 | |
| 78 | 78 | } | |
| 79 | 79 | }, | |
| 80 | - { | ||
| 81 | - "code": "reportAssignmentType", | ||
| 82 | - "range": { | ||
| 83 | - "startColumn": 38, | ||
| 84 | - "endColumn": 76, | ||
| 85 | - "lineCount": 1 | ||
| 86 | - } | ||
| 87 | - }, | ||
| 88 | 80 | { | |
| 89 | 81 | "code": "reportArgumentType", | |
| 90 | 82 | "range": { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | from git.cmd import Git, handle_process_output | |
| 13 | 13 | from git.compat import defenc | |
| 14 | + from git.objects.base import IndexObject | ||
| 14 | 15 | from git.objects.blob import Blob | |
| 15 | 16 | from git.objects.util import mode_str_to_int | |
| 16 | 17 | from git.util import finalize_process, hex_to_bin | |
@@ -35,7 +36,6 @@ | |||
| 35 | 36 | if TYPE_CHECKING: | |
| 36 | 37 | from subprocess import Popen | |
| 37 | 38 | ||
| 38 | - from git.objects.base import IndexObject | ||
| 39 | 39 | from git.objects.commit import Commit | |
| 40 | 40 | from git.objects.tree import Tree | |
| 41 | 41 | from git.repo.base import Repo | |
@@ -378,6 +378,10 @@ class Diff: | |||
| 378 | 378 | Diffs keep information about the changed blob objects, the file mode, renames, | |
| 379 | 379 | deletions and new files. | |
| 380 | 380 | ||
| 381 | + For submodule changes, ``a_blob`` and ``b_blob`` are | ||
| 382 | + :class:`~git.objects.base.IndexObject` instances whose SHAs refer to commits in | ||
| 383 | + the submodule repository. | ||
| 384 | + | ||
| 381 | 385 | There are a few cases where ``None`` has to be expected as member variable value: | |
| 382 | 386 | ||
| 383 | 387 | New File:: | |
@@ -481,17 +485,22 @@ def __init__( | |||
| 481 | 485 | repo = submodule.module() | |
| 482 | 486 | break | |
| 483 | 487 | ||
| 488 | + # Gitlinks reference commits; generic index objects preserve their path and mode. | ||
| 484 | 489 | self.a_blob: Union["IndexObject", None] | |
| 485 | 490 | if a_blob_id is None or a_blob_id == self.NULL_HEX_SHA: | |
| 486 | 491 | self.a_blob = None | |
| 487 | 492 | else: | |
| 488 | - self.a_blob = Blob(repo, hex_to_bin(a_blob_id), mode=self.a_mode, path=self.a_path) | ||
| 493 | + self.a_blob = (IndexObject if self.a_mode == 0o160000 else Blob)( | ||
| 494 | + repo, hex_to_bin(a_blob_id), mode=self.a_mode, path=self.a_path | ||
| 495 | + ) | ||
| 489 | 496 | ||
| 490 | 497 | self.b_blob: Union["IndexObject", None] | |
| 491 | 498 | if b_blob_id is None or b_blob_id == self.NULL_HEX_SHA: | |
| 492 | 499 | self.b_blob = None | |
| 493 | 500 | else: | |
| 494 | - self.b_blob = Blob(repo, hex_to_bin(b_blob_id), mode=self.b_mode, path=self.b_path) | ||
| 501 | + self.b_blob = (IndexObject if self.b_mode == 0o160000 else Blob)( | ||
| 502 | + repo, hex_to_bin(b_blob_id), mode=self.b_mode, path=self.b_path | ||
| 503 | + ) | ||
| 495 | 504 | ||
| 496 | 505 | self.new_file: bool = new_file | |
| 497 | 506 | self.deleted_file: bool = deleted_file | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -310,7 +310,7 @@ def merge_tree( | |||
| 310 | 310 | return self | |
| 311 | 311 | ||
| 312 | 312 | @classmethod | |
| 313 | - def new(cls, repo: "Repo", *tree_sha: Union[str, Tree]) -> "IndexFile": | ||
| 313 | + def new(cls, repo: "Repo", *tree_sha: Union[str, bytes, Tree]) -> "IndexFile": | ||
| 314 | 314 | """Merge the given treeish revisions into a new index which is returned. | |
| 315 | 315 | ||
| 316 | 316 | This method behaves like ``git-read-tree --aggressive`` when doing the merge. | |
@@ -326,7 +326,9 @@ def new(cls, repo: "Repo", *tree_sha: Union[str, Tree]) -> "IndexFile": | |||
| 326 | 326 | If you intend to write such a merged Index, supply an alternate | |
| 327 | 327 | ``file_path`` to its :meth:`write` method. | |
| 328 | 328 | """ | |
| 329 | - tree_sha_bytes: List[bytes] = [to_bin_sha(str(t)) for t in tree_sha] | ||
| 329 | + tree_sha_bytes: List[bytes] = [ | ||
| 330 | + to_bin_sha(t if isinstance(t, bytes) else str(t).encode("ascii")) for t in tree_sha | ||
| 331 | + ] | ||
| 330 | 332 | base_entries = aggressive_tree_merge(repo.odb, tree_sha_bytes) | |
| 331 | 333 | ||
| 332 | 334 | inst = cls(repo) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,7 +12,7 @@ | |||
| 12 | 12 | from git.exc import WorkTreeRepositoryUnsupported | |
| 13 | 13 | from git.util import LazyMixin, bin_to_hex, join_path_native, stream_copy | |
| 14 | 14 | ||
| 15 | - from .util import get_object_type_by_name | ||
| 15 | + from .util import _assert_object_type, get_object_type_by_name | ||
| 16 | 16 | ||
| 17 | 17 | # typing ------------------------------------------------------------------ | |
| 18 | 18 | ||
@@ -107,6 +107,10 @@ def __init__(self, repo: "Repo", binsha: bytes) -> None: | |||
| 107 | 107 | ||
| 108 | 108 | :param binsha: | |
| 109 | 109 | 20 byte SHA1 | |
| 110 | + | ||
| 111 | + :note: | ||
| 112 | + Object data is loaded lazily. If `binsha` refers to a different object | ||
| 113 | + type, accessing that data raises :exc:`ValueError`. | ||
| 110 | 114 | """ | |
| 111 | 115 | super().__init__() | |
| 112 | 116 | self.repo = repo | |
@@ -155,6 +159,7 @@ def _set_cache_(self, attr: str) -> None: | |||
| 155 | 159 | """Retrieve object information.""" | |
| 156 | 160 | if attr == "size": | |
| 157 | 161 | oinfo = self.repo.odb.info(self.binsha) | |
| 162 | + _assert_object_type(oinfo, self.type) | ||
| 158 | 163 | self.size = oinfo.size # type: int | |
| 159 | 164 | else: | |
| 160 | 165 | super()._set_cache_(attr) | |
@@ -198,7 +203,9 @@ def data_stream(self) -> "OStream": | |||
| 198 | 203 | :note: | |
| 199 | 204 | Returned streams must be read in order. | |
| 200 | 205 | """ | |
| 201 | - return self.repo.odb.stream(self.binsha) | ||
| 206 | + ostream = self.repo.odb.stream(self.binsha) | ||
| 207 | + _assert_object_type(ostream, self.type) | ||
| 208 | + return ostream | ||
| 202 | 209 | ||
| 203 | 210 | def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object": | |
| 204 | 211 | """Write our data directly to the given output stream. | |
@@ -209,7 +216,7 @@ def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object": | |||
| 209 | 216 | :return: | |
| 210 | 217 | self | |
| 211 | 218 | """ | |
| 212 | - istream = self.repo.odb.stream(self.binsha) | ||
| 219 | + istream = self.data_stream | ||
| 213 | 220 | stream_copy(istream, ostream) | |
| 214 | 221 | return self | |
| 215 | 222 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -242,7 +242,7 @@ def replace(self, **kwargs: Any) -> "Commit": | |||
| 242 | 242 | def _set_cache_(self, attr: str) -> None: | |
| 243 | 243 | if attr in Commit.__slots__: | |
| 244 | 244 | # Read the data in a chunk, its faster - then provide a file wrapper. | |
| 245 | - _binsha, _typename, self.size, stream = self.repo.odb.stream(self.binsha) | ||
| 245 | + _binsha, _typename, self.size, stream = self.data_stream | ||
| 246 | 246 | self._deserialize(BytesIO(stream.read())) | |
| 247 | 247 | else: | |
| 248 | 248 | super()._set_cache_(attr) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,6 +14,8 @@ | |||
| 14 | 14 | ||
| 15 | 15 | from git.compat import safe_decode, defenc | |
| 16 | 16 | ||
| 17 | + from .util import _assert_object_type | ||
| 18 | + | ||
| 17 | 19 | # typing ---------------------------------------------- | |
| 18 | 20 | ||
| 19 | 21 | from typing import ( | |
@@ -200,8 +202,10 @@ def traverse_trees_recursive( | |||
| 200 | 202 | if tree_sha is None: | |
| 201 | 203 | data: List[EntryTupOrNone] = [] | |
| 202 | 204 | else: | |
| 205 | + stream = odb.stream(tree_sha) | ||
| 206 | + _assert_object_type(stream, "tree") | ||
| 203 | 207 | # Make new list for typing as list invariant. | |
| 204 | - data = list(tree_entries_from_data(odb.stream(tree_sha).read())) | ||
| 208 | + data = list(tree_entries_from_data(stream.read())) | ||
| 205 | 209 | # END handle muted trees | |
| 206 | 210 | trees_data.append(data) | |
| 207 | 211 | # END for each sha to get data for | |
@@ -268,7 +272,9 @@ def traverse_tree_recursive(odb: "ObjectDBR", tree_sha: bytes, path_prefix: str) | |||
| 268 | 272 | Prefix to prepend to the front of all returned paths. | |
| 269 | 273 | """ | |
| 270 | 274 | entries = [] | |
| 271 | - data = tree_entries_from_data(odb.stream(tree_sha).read()) | ||
| 275 | + stream = odb.stream(tree_sha) | ||
| 276 | + _assert_object_type(stream, "tree") | ||
| 277 | + data = tree_entries_from_data(stream.read()) | ||
| 272 | 278 | ||
| 273 | 279 | # Unpacking/packing is faster than accessing individual items. | |
| 274 | 280 | for sha, mode, name in data: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -105,7 +105,7 @@ def __init__( | |||
| 105 | 105 | def _set_cache_(self, attr: str) -> None: | |
| 106 | 106 | """Cache all our attributes at once.""" | |
| 107 | 107 | if attr in TagObject.__slots__: | |
| 108 | - ostream = self.repo.odb.stream(self.binsha) | ||
| 108 | + ostream = self.data_stream | ||
| 109 | 109 | lines: List[str] = ostream.read().decode(defenc, "replace").splitlines() | |
| 110 | 110 | ||
| 111 | 111 | _obj, hexsha = lines[0].split(" ") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -208,7 +208,7 @@ def _get_intermediate_items( | |||
| 208 | 208 | def _set_cache_(self, attr: str) -> None: | |
| 209 | 209 | if attr == "_cache": | |
| 210 | 210 | # Set the data when we need it. | |
| 211 | - ostream = self.repo.odb.stream(self.binsha) | ||
| 211 | + ostream = self.data_stream | ||
| 212 | 212 | self._cache: List[TreeCacheTup] = tree_entries_from_data(ostream.read()) | |
| 213 | 213 | else: | |
| 214 | 214 | super()._set_cache_(attr) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,6 +28,8 @@ | |||
| 28 | 28 | import time | |
| 29 | 29 | import warnings | |
| 30 | 30 | ||
| 31 | + from gitdb.base import OStream | ||
| 32 | + | ||
| 31 | 33 | from git.util import Actor, IterableList, IterableObj | |
| 32 | 34 | ||
| 33 | 35 | # typing ------------------------------------------------------------ | |
@@ -54,6 +56,8 @@ | |||
| 54 | 56 | from io import BytesIO, StringIO | |
| 55 | 57 | from subprocess import Popen | |
| 56 | 58 | ||
| 59 | + from gitdb.base import OInfo | ||
| 60 | + | ||
| 57 | 61 | from git.types import Protocol, runtime_checkable | |
| 58 | 62 | ||
| 59 | 63 | from .blob import Blob | |
@@ -88,6 +92,19 @@ class TraverseNT(NamedTuple): | |||
| 88 | 92 | # { Functions | |
| 89 | 93 | ||
| 90 | 94 | ||
| 95 | + def _assert_object_type(oinfo: "OInfo", expected_type: Union[str, None]) -> None: | ||
| 96 | + """Reject mismatched object types before interpreting their data.""" | ||
| 97 | + if expected_type is None or oinfo.type == expected_type.encode("ascii"): | ||
| 98 | + return | ||
| 99 | + if isinstance(oinfo, OStream): | ||
| 100 | + # Drain the cat-file response even if the exception keeps the stream alive. | ||
| 101 | + while oinfo.read(8192): | ||
| 102 | + pass | ||
| 103 | + raise ValueError( | ||
| 104 | + "Object %s is a %s, not a %s" % (oinfo.hexsha.decode("ascii"), oinfo.type.decode("ascii"), expected_type) | ||
| 105 | + ) | ||
| 106 | + | ||
| 107 | + | ||
| 91 | 108 | def mode_str_to_int(modestr: Union[bytes, str]) -> int: | |
| 92 | 109 | """Convert mode bits from an octal mode string to an integer mode for git. | |
| 93 | 110 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ | |
| 5 | 5 | ||
| 6 | 6 | import gc | |
| 7 | + from io import BytesIO | ||
| 7 | 8 | import os | |
| 8 | 9 | import os.path as osp | |
| 9 | 10 | import sys | |
@@ -76,6 +77,21 @@ def test_base_object(self): | |||
| 76 | 77 | # Remove the file this way, instead of with a context manager or "finally", | |
| 77 | 78 | # so it is only removed on success, and we can inspect the file on failure. | |
| 78 | 79 | os.remove(tmpfile.name) | |
| 80 | + | ||
| 81 | + for wrong_type, attr in ((Blob, "data_stream"), (Tree, "_cache"), (Commit, "tree"), (TagObject, "object")): | ||
| 82 | + if wrong_type is obj_type: | ||
| 83 | + continue | ||
| 84 | + invalid = wrong_type(self.rorepo, binsha) | ||
| 85 | + for invalid_attr in ("size", attr): | ||
| 86 | + with self.assertRaisesRegex(ValueError, f"{hexsha}.*{typename}.*{wrong_type.type}"): | ||
| 87 | + getattr(invalid, invalid_attr) | ||
| 88 | + try: | ||
| 89 | + invalid.stream_data(BytesIO()) | ||
| 90 | + except ValueError: | ||
| 91 | + # The next read must work even while the exception retains the stream. | ||
| 92 | + self.assertEqual(item.data_stream.read(), data) | ||
| 93 | + else: | ||
| 94 | + self.fail("Expected ValueError for mismatched object type") | ||
| 79 | 95 | # END for each object type to create | |
| 80 | 96 | ||
| 81 | 97 | # Each has a unique sha. | |
| Back | FazBrowse Home | New Git URL |
0 commit comments