diff options
-rw-r--r-- | .travis.yml | 3 | ||||
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | doc/source/changes.rst | 8 | ||||
-rw-r--r-- | git/cmd.py | 9 | ||||
-rw-r--r-- | git/index/fun.py | 3 | ||||
-rw-r--r-- | git/refs/tag.py | 3 | ||||
-rw-r--r-- | git/remote.py | 2 | ||||
-rw-r--r-- | git/repo/base.py | 32 | ||||
-rw-r--r-- | git/util.py | 2 | ||||
-rwxr-xr-x | setup.py | 2 | ||||
-rw-r--r-- | test/test_fun.py | 15 | ||||
-rw-r--r-- | test/test_repo.py | 38 |
13 files changed, 101 insertions, 19 deletions
diff --git a/.travis.yml b/.travis.yml index 8a171b4f..1bed8368 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,10 @@ # UNUSED, only for reference. If adjustments are needed, please see github actions language: python python: +<<<<<<< HEAD - "3.4" +======= +>>>>>>> b0f79c58ad919e90261d1e332df79a4ad0bc40de - "3.6" - "3.7" - "3.8" @@ -43,4 +43,5 @@ Contributors are: -Liam Beguin <liambeguin _at_ gmail.com> -Ram Rachum <ram _at_ rachum.com> -Alba Mendez <me _at_ alba.sh> +-Robert Westman <robert _at_ byteflux.io> Portions derived from other open source works and are clearly marked. @@ -1 +1 @@ -3.1.17 +3.1.18 diff --git a/doc/source/changes.rst b/doc/source/changes.rst index 68a94516..aabef802 100644 --- a/doc/source/changes.rst +++ b/doc/source/changes.rst @@ -2,6 +2,14 @@ Changelog ========= +3.1.18 +====== + +* drop support for python 3.5 to reduce maintenance burden on typing. Lower patch levels of python 3.5 would break, too. + +See the following for details: +https://github.com/gitpython-developers/gitpython/milestone/50?closed=1 + 3.1.17 ====== @@ -17,9 +17,7 @@ from subprocess import ( import subprocess import sys import threading -from collections import OrderedDict from textwrap import dedent -import warnings from git.compat import ( defenc, @@ -1004,13 +1002,6 @@ class Git(LazyMixin): def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any) -> List[str]: """Transforms Python style kwargs into git command line options.""" - # Python 3.6 preserves the order of kwargs and thus has a stable - # order. For older versions sort the kwargs by the key to get a stable - # order. - if sys.version_info[:2] < (3, 6): - kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0])) - warnings.warn("Python 3.5 support is deprecated and will be removed 2021-09-05.\n" + - "It does not preserve the order for key-word arguments and enforce lexical sorting instead.") args = [] for k, v in kwargs.items(): if isinstance(v, (list, tuple)): diff --git a/git/index/fun.py b/git/index/fun.py index 96d9b475..3fded347 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -11,6 +11,7 @@ from stat import ( S_ISDIR, S_IFMT, S_IFREG, + S_IXUSR, ) import subprocess @@ -115,7 +116,7 @@ def stat_mode_to_index_mode(mode: int) -> int: return S_IFLNK if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules return S_IFGITLINK - return S_IFREG | 0o644 | (mode & 0o111) # blobs with or without executable bit + return S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) # blobs with or without executable bit def write_cache(entries: Sequence[Union[BaseIndexEntry, 'IndexEntry']], stream: IO[bytes], diff --git a/git/refs/tag.py b/git/refs/tag.py index 8f88c522..4d84239e 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -18,7 +18,8 @@ class TagReference(Reference): print(tagref.tag.message)""" __slots__ = () - _common_path_default = "refs/tags" + _common_default = "tags" + _common_path_default = Reference._common_path_default + "/" + _common_default @property def commit(self): diff --git a/git/remote.py b/git/remote.py index e17f7bb8..6ea4b2a1 100644 --- a/git/remote.py +++ b/git/remote.py @@ -612,7 +612,7 @@ class Remote(LazyMixin, Iterable): # * [would prune] origin/new_branch token = " * [would prune] " if not line.startswith(token): - raise ValueError("Could not parse git-remote prune result: %r" % line) + continue ref_name = line.replace(token, "") # sometimes, paths start with a full ref name, like refs/tags/foo, see #260 if ref_name.startswith(Reference._common_path_default + '/'): diff --git a/git/repo/base.py b/git/repo/base.py index 2d2e915c..5abd4961 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -3,12 +3,13 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - import logging import os import re import warnings +from gitdb.exc import BadObject + from git.cmd import ( Git, handle_process_output @@ -402,7 +403,17 @@ class Repo(object): def tag(self, path: PathLike) -> TagReference: """:return: TagReference Object, reference pointing to a Commit or Tag :param path: path to the tag reference, i.e. 0.1.5 or tags/0.1.5 """ - return TagReference(self, path) + full_path = self._to_full_tag_path(path) + return TagReference(self, full_path) + + @staticmethod + def _to_full_tag_path(path): + if path.startswith(TagReference._common_path_default + '/'): + return path + if path.startswith(TagReference._common_default + '/'): + return Reference._common_path_default + '/' + path + else: + return TagReference._common_path_default + '/' + path def create_head(self, path: PathLike, commit: str = 'HEAD', force: bool = False, logmsg: Optional[str] = None @@ -608,6 +619,23 @@ class Repo(object): raise return True + def is_valid_object(self, sha: str, object_type: str = None) -> bool: + try: + complete_sha = self.odb.partial_to_complete_sha_hex(sha) + object_info = self.odb.info(complete_sha) + if object_type: + if object_info.type == object_type.encode(): + return True + else: + log.debug("Commit hash points to an object of type '%s'. Requested were objects of type '%s'", + object_info.type.decode(), object_type) + return False + else: + return True + except BadObject: + log.debug("Commit hash is invalid.") + return False + def _get_daemon_export(self) -> bool: if self.git_dir: filename = osp.join(self.git_dir, self.DAEMON_EXPORT_FILE) diff --git a/git/util.py b/git/util.py index 76aaee49..edbd5f1e 100644 --- a/git/util.py +++ b/git/util.py @@ -470,7 +470,7 @@ class RemoteProgress(object): line_str = line self._cur_line = line_str - if self.error_lines or self._cur_line.startswith(('error:', 'fatal:')): + if self._cur_line.startswith(('error:', 'fatal:')): self.error_lines.append(self._cur_line) return @@ -126,6 +126,6 @@ setup( "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.9" ] ) diff --git a/test/test_fun.py b/test/test_fun.py index a7fb8f8b..e3d07194 100644 --- a/test/test_fun.py +++ b/test/test_fun.py @@ -1,5 +1,5 @@ from io import BytesIO -from stat import S_IFDIR, S_IFREG, S_IFLNK +from stat import S_IFDIR, S_IFREG, S_IFLNK, S_IXUSR from os import stat import os.path as osp from unittest import SkipTest @@ -7,7 +7,8 @@ from unittest import SkipTest from git import Git from git.index import IndexFile from git.index.fun import ( - aggressive_tree_merge + aggressive_tree_merge, + stat_mode_to_index_mode, ) from git.objects.fun import ( traverse_tree_recursive, @@ -206,6 +207,16 @@ class TestFun(TestBase): assert_entries(aggressive_tree_merge(odb, trees), 2, True) # END handle ours, theirs + def test_stat_mode_to_index_mode(self): + modes = ( + 0o600, 0o611, 0o640, 0o641, 0o644, 0o650, 0o651, + 0o700, 0o711, 0o740, 0o744, 0o750, 0o751, 0o755, + ) + for mode in modes: + expected_mode = S_IFREG | (mode & S_IXUSR and 0o755 or 0o644) + assert stat_mode_to_index_mode(mode) == expected_mode + # END for each mode + def _assert_tree_entries(self, entries, num_trees): for entry in entries: assert len(entry) == num_trees diff --git a/test/test_repo.py b/test/test_repo.py index 8dc17833..8aced94d 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -414,6 +414,16 @@ class TestRepo(TestBase): def test_tag(self): assert self.rorepo.tag('refs/tags/0.1.5').commit + def test_tag_to_full_tag_path(self): + tags = ['0.1.5', 'tags/0.1.5', 'refs/tags/0.1.5'] + value_errors = [] + for tag in tags: + try: + self.rorepo.tag(tag) + except ValueError as valueError: + value_errors.append(valueError.args[0]) + self.assertEqual(value_errors, []) + def test_archive(self): tmpfile = tempfile.mktemp(suffix='archive-test') with open(tmpfile, 'wb') as stream: @@ -979,6 +989,34 @@ class TestRepo(TestBase): for i, j in itertools.permutations([c1, 'ffffff', ''], r=2): self.assertRaises(GitCommandError, repo.is_ancestor, i, j) + def test_is_valid_object(self): + repo = self.rorepo + commit_sha = 'f6aa8d1' + blob_sha = '1fbe3e4375' + tree_sha = '960b40fe36' + tag_sha = '42c2f60c43' + + # Check for valid objects + self.assertTrue(repo.is_valid_object(commit_sha)) + self.assertTrue(repo.is_valid_object(blob_sha)) + self.assertTrue(repo.is_valid_object(tree_sha)) + self.assertTrue(repo.is_valid_object(tag_sha)) + + # Check for valid objects of specific type + self.assertTrue(repo.is_valid_object(commit_sha, 'commit')) + self.assertTrue(repo.is_valid_object(blob_sha, 'blob')) + self.assertTrue(repo.is_valid_object(tree_sha, 'tree')) + self.assertTrue(repo.is_valid_object(tag_sha, 'tag')) + + # Check for invalid objects + self.assertFalse(repo.is_valid_object(b'1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a', 'blob')) + + # Check for invalid objects of specific type + self.assertFalse(repo.is_valid_object(commit_sha, 'blob')) + self.assertFalse(repo.is_valid_object(blob_sha, 'commit')) + self.assertFalse(repo.is_valid_object(tree_sha, 'commit')) + self.assertFalse(repo.is_valid_object(tag_sha, 'commit')) + @with_rw_directory def test_git_work_tree_dotgit(self, rw_dir): """Check that we find .git as a worktree file and find the worktree |