GitPython 3.1.52__tar.gz → 3.1.54__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitpython-3.1.52 → gitpython-3.1.54}/CONTRIBUTING.md +8 -17
- {gitpython-3.1.52 → gitpython-3.1.54/GitPython.egg-info}/PKG-INFO +1 -1
- {gitpython-3.1.52/GitPython.egg-info → gitpython-3.1.54}/PKG-INFO +1 -1
- gitpython-3.1.54/VERSION +1 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/changes.rst +27 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/__init__.py +1 -1
- {gitpython-3.1.52 → gitpython-3.1.54}/git/cmd.py +8 -1
- {gitpython-3.1.52 → gitpython-3.1.54}/git/config.py +16 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/diff.py +12 -2
- {gitpython-3.1.52 → gitpython-3.1.54}/git/index/base.py +16 -2
- {gitpython-3.1.52 → gitpython-3.1.54}/git/index/fun.py +20 -3
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/submodule/base.py +141 -100
- {gitpython-3.1.52 → gitpython-3.1.54}/git/repo/base.py +5 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/util.py +28 -18
- {gitpython-3.1.52 → gitpython-3.1.54}/pyproject.toml +11 -0
- gitpython-3.1.52/VERSION +0 -1
- {gitpython-3.1.52 → gitpython-3.1.54}/AUTHORS +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/CHANGES +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/GitPython.egg-info/SOURCES.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/GitPython.egg-info/dependency_links.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/GitPython.egg-info/not-zip-safe +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/GitPython.egg-info/requires.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/GitPython.egg-info/top_level.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/LICENSE +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/MANIFEST.in +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/README.md +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/.gitignore +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/Makefile +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/requirements.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/conf.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/index.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/intro.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/quickstart.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/reference.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/roadmap.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/doc/source/tutorial.rst +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/compat.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/db.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/exc.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/index/__init__.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/index/typ.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/index/util.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/__init__.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/base.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/blob.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/commit.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/fun.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/submodule/__init__.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/submodule/root.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/submodule/util.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/tag.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/tree.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/objects/util.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/py.typed +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/__init__.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/head.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/log.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/reference.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/remote.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/symbolic.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/refs/tag.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/remote.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/repo/__init__.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/repo/fun.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/git/types.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/requirements.txt +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/setup.cfg +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/setup.py +0 -0
- {gitpython-3.1.52 → gitpython-3.1.54}/test-requirements.txt +0 -0
|
@@ -20,25 +20,16 @@ A contribution that works only narrowly but lowers the quality of the
|
|
|
20
20
|
codebase may be declined. The maintainers may not always be able to provide
|
|
21
21
|
detailed feedback.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Prevent agent impersonation
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
AI agents communicating through a person's account must identify themselves, for
|
|
26
|
+
example in issue or PR descriptions and comments. AI assistance that does not replace
|
|
27
|
+
the person as the speaker, such as proofreading or wording polish, does not require
|
|
28
|
+
identification.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Fully AI-generated comments on pull requests or issues must also be disclosed.
|
|
35
|
-
Undisclosed AI-generated comments may lead to the pull request or issue being closed.
|
|
36
|
-
|
|
37
|
-
AI-assisted proofreading or wording polish does not need disclosure, but it is still
|
|
38
|
-
courteous to mention it when the AI materially influenced the final text.
|
|
39
|
-
|
|
40
|
-
Automated or "full-auto" AI contributions without a human responsible for reviewing
|
|
41
|
-
and standing behind the work may be closed.
|
|
30
|
+
Attributing AI assistance in commit metadata, for example with a `Co-authored-by`
|
|
31
|
+
trailer, is welcome but not required. Code is reviewed the same way regardless of its
|
|
32
|
+
origin.
|
|
42
33
|
|
|
43
34
|
## Fuzzing Test Specific Documentation
|
|
44
35
|
|
gitpython-3.1.54/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.1.54
|
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
3.1.54
|
|
6
|
+
======
|
|
7
|
+
|
|
8
|
+
A security fix for
|
|
9
|
+
|
|
10
|
+
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-r9mr-m37c-5fr3
|
|
11
|
+
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-6p8h-3wgx-97gf
|
|
12
|
+
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-fjr4-x663-mwxc#event-857190
|
|
13
|
+
|
|
14
|
+
If you can, also try and provide feedback on the upcoming v4 branch
|
|
15
|
+
https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
|
|
16
|
+
|
|
17
|
+
See the following for all changes.
|
|
18
|
+
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.54
|
|
19
|
+
|
|
20
|
+
3.1.53
|
|
21
|
+
======
|
|
22
|
+
|
|
23
|
+
A security fix for
|
|
24
|
+
https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-3rp5-jjmw-4wv2
|
|
25
|
+
|
|
26
|
+
If you can, also try and provide feedback on the upcoming v4 branch
|
|
27
|
+
https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
|
|
28
|
+
|
|
29
|
+
See the following for all changes.
|
|
30
|
+
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.53
|
|
31
|
+
|
|
5
32
|
3.1.52
|
|
6
33
|
======
|
|
7
34
|
|
|
@@ -1039,11 +1039,18 @@ class Git(metaclass=_GitMeta):
|
|
|
1039
1039
|
option for option in cls._unpack_args([arg for arg in args if arg is not None]) if option.startswith("-")
|
|
1040
1040
|
]
|
|
1041
1041
|
if kwargs:
|
|
1042
|
+
split_single_char_options = kwargs.get("split_single_char_options", True)
|
|
1042
1043
|
for key, value in kwargs.items():
|
|
1043
1044
|
values = value if isinstance(value, (list, tuple)) else (value,)
|
|
1044
1045
|
if any(value is True or (value is not False and value is not None) for value in values):
|
|
1045
1046
|
key = str(key)
|
|
1046
1047
|
options.append(f"-{key}" if len(key) == 1 else f"--{dashify(key)}")
|
|
1048
|
+
if len(key) == 1 and split_single_char_options:
|
|
1049
|
+
options.extend(
|
|
1050
|
+
str(value)
|
|
1051
|
+
for value in values
|
|
1052
|
+
if value is not True and value not in (False, None) and str(value).startswith("-")
|
|
1053
|
+
)
|
|
1047
1054
|
return options
|
|
1048
1055
|
|
|
1049
1056
|
AutoInterrupt: TypeAlias = _AutoInterrupt
|
|
@@ -1358,7 +1365,7 @@ class Git(metaclass=_GitMeta):
|
|
|
1358
1365
|
|
|
1359
1366
|
# Allow the user to have the command executed in their working dir.
|
|
1360
1367
|
try:
|
|
1361
|
-
cwd = self._working_dir or os.getcwd() # type:
|
|
1368
|
+
cwd = self._working_dir or os.getcwd() # type: Optional[PathLike]
|
|
1362
1369
|
if not os.access(str(cwd), os.X_OK):
|
|
1363
1370
|
cwd = None
|
|
1364
1371
|
except FileNotFoundError:
|
|
@@ -897,6 +897,22 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
897
897
|
def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None:
|
|
898
898
|
if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name):
|
|
899
899
|
raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label)
|
|
900
|
+
if label == "section" and isinstance(name, str):
|
|
901
|
+
in_quotes = False
|
|
902
|
+
escaped = False
|
|
903
|
+
for index, char in enumerate(name):
|
|
904
|
+
if escaped:
|
|
905
|
+
escaped = False
|
|
906
|
+
elif in_quotes and char == "\\":
|
|
907
|
+
escaped = True
|
|
908
|
+
elif char == '"':
|
|
909
|
+
if not in_quotes and (index == 0 or name[index - 1] not in " \t"):
|
|
910
|
+
raise ValueError("Git config quoted subsection names must begin after whitespace")
|
|
911
|
+
in_quotes = not in_quotes
|
|
912
|
+
elif char == "]" and not in_quotes:
|
|
913
|
+
raise ValueError("Git config section names must not contain an unquoted closing bracket")
|
|
914
|
+
if in_quotes:
|
|
915
|
+
raise ValueError("Git config section names must not contain an unterminated quote")
|
|
900
916
|
|
|
901
917
|
@needs_values
|
|
902
918
|
@set_dirty_and_flush_changes
|
|
@@ -9,7 +9,7 @@ import enum
|
|
|
9
9
|
import re
|
|
10
10
|
import warnings
|
|
11
11
|
|
|
12
|
-
from git.cmd import handle_process_output
|
|
12
|
+
from git.cmd import Git, handle_process_output
|
|
13
13
|
from git.compat import defenc
|
|
14
14
|
from git.objects.blob import Blob
|
|
15
15
|
from git.objects.util import mode_str_to_int
|
|
@@ -35,7 +35,6 @@ from git.types import PathLike, Literal
|
|
|
35
35
|
if TYPE_CHECKING:
|
|
36
36
|
from subprocess import Popen
|
|
37
37
|
|
|
38
|
-
from git.cmd import Git
|
|
39
38
|
from git.objects.base import IndexObject
|
|
40
39
|
from git.objects.commit import Commit
|
|
41
40
|
from git.objects.tree import Tree
|
|
@@ -190,6 +189,7 @@ class Diffable:
|
|
|
190
189
|
other: Union[DiffConstants, "Tree", "Commit", str, None] = INDEX,
|
|
191
190
|
paths: Union[PathLike, List[PathLike], Tuple[PathLike, ...], None] = None,
|
|
192
191
|
create_patch: bool = False,
|
|
192
|
+
allow_unsafe_options: bool = False,
|
|
193
193
|
**kwargs: Any,
|
|
194
194
|
) -> "DiffIndex[Diff]":
|
|
195
195
|
"""Create diffs between two items being trees, trees and index or an index and
|
|
@@ -219,6 +219,10 @@ class Diffable:
|
|
|
219
219
|
applied makes the self to other. Patches are somewhat costly as blobs have
|
|
220
220
|
to be read and diffed.
|
|
221
221
|
|
|
222
|
+
:param allow_unsafe_options:
|
|
223
|
+
If ``True``, allow options such as ``--output`` that can write to arbitrary
|
|
224
|
+
filesystem paths.
|
|
225
|
+
|
|
222
226
|
:param kwargs:
|
|
223
227
|
Additional arguments passed to :manpage:`git-diff(1)`, such as ``R=True`` to
|
|
224
228
|
swap both sides of the diff.
|
|
@@ -231,6 +235,12 @@ class Diffable:
|
|
|
231
235
|
an instance of :class:`~git.objects.tree.Tree` or
|
|
232
236
|
:class:`~git.objects.commit.Commit`, or a git command error will occur.
|
|
233
237
|
"""
|
|
238
|
+
if not allow_unsafe_options:
|
|
239
|
+
Git.check_unsafe_options(
|
|
240
|
+
options=Git._option_candidates([other], kwargs),
|
|
241
|
+
unsafe_options=self.repo.unsafe_git_revision_options,
|
|
242
|
+
)
|
|
243
|
+
|
|
234
244
|
args: List[Union[PathLike, Diffable]] = []
|
|
235
245
|
args.append("--abbrev=40") # We need full shas.
|
|
236
246
|
args.append("--full-index") # Get full index paths, not only filenames.
|
|
@@ -23,6 +23,7 @@ from gitdb.base import IStream
|
|
|
23
23
|
from gitdb.db import MemoryDB
|
|
24
24
|
|
|
25
25
|
from git.compat import defenc, force_bytes
|
|
26
|
+
from git.cmd import Git
|
|
26
27
|
import git.diff as git_diff
|
|
27
28
|
from git.exc import CheckoutError, GitCommandError, GitError, InvalidGitRepositoryError
|
|
28
29
|
from git.objects import Blob, Commit, Object, Submodule, Tree
|
|
@@ -1492,6 +1493,7 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1492
1493
|
] = git_diff.INDEX,
|
|
1493
1494
|
paths: Union[PathLike, List[PathLike], Tuple[PathLike, ...], None] = None,
|
|
1494
1495
|
create_patch: bool = False,
|
|
1496
|
+
allow_unsafe_options: bool = False,
|
|
1495
1497
|
**kwargs: Any,
|
|
1496
1498
|
) -> git_diff.DiffIndex[git_diff.Diff]:
|
|
1497
1499
|
"""Diff this index against the working copy or a :class:`~git.objects.tree.Tree`
|
|
@@ -1504,6 +1506,12 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1504
1506
|
Will only work with indices that represent the default git index as they
|
|
1505
1507
|
have not been initialized with a stream.
|
|
1506
1508
|
"""
|
|
1509
|
+
if not allow_unsafe_options:
|
|
1510
|
+
Git.check_unsafe_options(
|
|
1511
|
+
options=Git._option_candidates([other], kwargs),
|
|
1512
|
+
unsafe_options=self.repo.unsafe_git_revision_options,
|
|
1513
|
+
)
|
|
1514
|
+
|
|
1507
1515
|
# Only run if we are the default repository index.
|
|
1508
1516
|
if self._file_path != self._index_path():
|
|
1509
1517
|
raise AssertionError("Cannot call %r on indices that do not represent the default git index" % self.diff())
|
|
@@ -1560,7 +1568,13 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1560
1568
|
# Invert the existing R flag.
|
|
1561
1569
|
cur_val = kwargs.get("R", False)
|
|
1562
1570
|
kwargs["R"] = not cur_val
|
|
1563
|
-
return other.diff(
|
|
1571
|
+
return other.diff(
|
|
1572
|
+
self.INDEX,
|
|
1573
|
+
paths,
|
|
1574
|
+
create_patch,
|
|
1575
|
+
allow_unsafe_options=allow_unsafe_options,
|
|
1576
|
+
**kwargs,
|
|
1577
|
+
)
|
|
1564
1578
|
# END diff against other item handling
|
|
1565
1579
|
|
|
1566
1580
|
# If other is not None here, something is wrong.
|
|
@@ -1568,4 +1582,4 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1568
1582
|
raise ValueError("other must be None, Diffable.INDEX, a Tree or Commit, was %r" % other)
|
|
1569
1583
|
|
|
1570
1584
|
# Diff against working copy - can be handled by superclass natively.
|
|
1571
|
-
return super().diff(other, paths, create_patch, **kwargs)
|
|
1585
|
+
return super().diff(other, paths, create_patch, allow_unsafe_options=allow_unsafe_options, **kwargs)
|
|
@@ -64,6 +64,17 @@ def hook_path(name: str, git_dir: PathLike) -> str:
|
|
|
64
64
|
return osp.join(git_dir, "hooks", name)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def _commit_hook_path(name: str, index: "IndexFile") -> str:
|
|
68
|
+
""":return: path to the named commit hook, respecting Git's core.hooksPath."""
|
|
69
|
+
with index.repo.config_reader() as config:
|
|
70
|
+
hooks_dir = config.get("core", "hooksPath", fallback="")
|
|
71
|
+
|
|
72
|
+
if not hooks_dir:
|
|
73
|
+
return hook_path(name, index.repo.git_dir)
|
|
74
|
+
|
|
75
|
+
return osp.abspath(osp.join(index.repo.working_dir, osp.expanduser(hooks_dir), name))
|
|
76
|
+
|
|
77
|
+
|
|
67
78
|
def _has_file_extension(path: str) -> str:
|
|
68
79
|
return osp.splitext(path)[1]
|
|
69
80
|
|
|
@@ -82,7 +93,7 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
|
|
|
82
93
|
|
|
83
94
|
:raise git.exc.HookExecutionError:
|
|
84
95
|
"""
|
|
85
|
-
hp =
|
|
96
|
+
hp = _commit_hook_path(name, index)
|
|
86
97
|
if not os.access(hp, os.X_OK):
|
|
87
98
|
return
|
|
88
99
|
|
|
@@ -94,8 +105,14 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
|
|
|
94
105
|
if sys.platform == "win32" and not _has_file_extension(hp):
|
|
95
106
|
# Windows only uses extensions to determine how to open files
|
|
96
107
|
# (doesn't understand shebangs). Try using bash to run the hook.
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
try:
|
|
109
|
+
bash_hp = osp.relpath(hp, index.repo.working_dir)
|
|
110
|
+
except ValueError:
|
|
111
|
+
# Different drives have no relative path on Windows. Git Bash accepts
|
|
112
|
+
# an absolute path in this form, although a relative path is preferable
|
|
113
|
+
# because it also works with the Windows Subsystem for Linux wrapper.
|
|
114
|
+
bash_hp = hp
|
|
115
|
+
cmd = ["bash.exe", Path(bash_hp).as_posix()]
|
|
99
116
|
|
|
100
117
|
process = safer_popen(
|
|
101
118
|
cmd + list(args),
|
|
@@ -49,6 +49,7 @@ from typing import (
|
|
|
49
49
|
Callable,
|
|
50
50
|
Dict,
|
|
51
51
|
Iterator,
|
|
52
|
+
List,
|
|
52
53
|
Mapping,
|
|
53
54
|
Sequence,
|
|
54
55
|
TYPE_CHECKING,
|
|
@@ -738,122 +739,132 @@ class Submodule(IndexObject, TraversableIterableObj):
|
|
|
738
739
|
mrepo = None
|
|
739
740
|
# END init mrepo
|
|
740
741
|
|
|
742
|
+
def fetch_remotes(module_repo: "Repo") -> None:
|
|
743
|
+
rmts = module_repo.remotes
|
|
744
|
+
len_rmts = len(rmts)
|
|
745
|
+
for i, remote in enumerate(rmts):
|
|
746
|
+
op = FETCH
|
|
747
|
+
if i == 0:
|
|
748
|
+
op |= BEGIN
|
|
749
|
+
# END handle start
|
|
750
|
+
|
|
751
|
+
progress.update(
|
|
752
|
+
op,
|
|
753
|
+
i,
|
|
754
|
+
len_rmts,
|
|
755
|
+
prefix + "Fetching remote %s of submodule %r" % (remote, self.name),
|
|
756
|
+
)
|
|
757
|
+
# ===============================
|
|
758
|
+
if not dry_run:
|
|
759
|
+
remote.fetch(progress=progress)
|
|
760
|
+
# END handle dry-run
|
|
761
|
+
# ===============================
|
|
762
|
+
if i == len_rmts - 1:
|
|
763
|
+
op |= END
|
|
764
|
+
# END handle end
|
|
765
|
+
progress.update(
|
|
766
|
+
op,
|
|
767
|
+
i,
|
|
768
|
+
len_rmts,
|
|
769
|
+
prefix + "Done fetching remote of submodule %r" % self.name,
|
|
770
|
+
)
|
|
771
|
+
# END fetch new data
|
|
772
|
+
|
|
741
773
|
try:
|
|
742
774
|
# ENSURE REPO IS PRESENT AND UP-TO-DATE
|
|
743
775
|
#######################################
|
|
744
776
|
try:
|
|
745
777
|
mrepo = self.module()
|
|
746
|
-
|
|
747
|
-
len_rmts = len(rmts)
|
|
748
|
-
for i, remote in enumerate(rmts):
|
|
749
|
-
op = FETCH
|
|
750
|
-
if i == 0:
|
|
751
|
-
op |= BEGIN
|
|
752
|
-
# END handle start
|
|
753
|
-
|
|
754
|
-
progress.update(
|
|
755
|
-
op,
|
|
756
|
-
i,
|
|
757
|
-
len_rmts,
|
|
758
|
-
prefix + "Fetching remote %s of submodule %r" % (remote, self.name),
|
|
759
|
-
)
|
|
760
|
-
# ===============================
|
|
761
|
-
if not dry_run:
|
|
762
|
-
remote.fetch(progress=progress)
|
|
763
|
-
# END handle dry-run
|
|
764
|
-
# ===============================
|
|
765
|
-
if i == len_rmts - 1:
|
|
766
|
-
op |= END
|
|
767
|
-
# END handle end
|
|
768
|
-
progress.update(
|
|
769
|
-
op,
|
|
770
|
-
i,
|
|
771
|
-
len_rmts,
|
|
772
|
-
prefix + "Done fetching remote of submodule %r" % self.name,
|
|
773
|
-
)
|
|
774
|
-
# END fetch new data
|
|
778
|
+
fetch_remotes(mrepo)
|
|
775
779
|
except InvalidGitRepositoryError:
|
|
776
780
|
mrepo = None
|
|
777
781
|
if not init:
|
|
778
782
|
return self
|
|
779
783
|
# END early abort if init is not allowed
|
|
780
784
|
|
|
781
|
-
# There is no git-repository yet - but delete empty paths.
|
|
782
785
|
checkout_module_abspath = self.abspath
|
|
783
|
-
|
|
786
|
+
module_abspath = self._module_abspath(self.repo, self.path, self.name)
|
|
787
|
+
|
|
788
|
+
# ``git submodule deinit`` leaves the repository in
|
|
789
|
+
# ``.git/modules`` and empties the checkout. Reconnect that retained
|
|
790
|
+
# repository instead of trying to clone over it.
|
|
791
|
+
if not dry_run and osp.isdir(module_abspath):
|
|
784
792
|
try:
|
|
785
|
-
|
|
786
|
-
except
|
|
787
|
-
|
|
788
|
-
"Module directory at %r does already exist and is non-empty" % checkout_module_abspath
|
|
789
|
-
) from e
|
|
790
|
-
# END handle OSError
|
|
791
|
-
# END handle directory removal
|
|
792
|
-
|
|
793
|
-
# Don't check it out at first - nonetheless it will create a local
|
|
794
|
-
# branch according to the remote-HEAD if possible.
|
|
795
|
-
progress.update(
|
|
796
|
-
BEGIN | CLONE,
|
|
797
|
-
0,
|
|
798
|
-
1,
|
|
799
|
-
prefix
|
|
800
|
-
+ "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name),
|
|
801
|
-
)
|
|
802
|
-
if not dry_run:
|
|
803
|
-
if self.url.startswith("."):
|
|
804
|
-
url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url)
|
|
793
|
+
git.Repo(module_abspath)
|
|
794
|
+
except InvalidGitRepositoryError:
|
|
795
|
+
pass
|
|
805
796
|
else:
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
797
|
+
if osp.lexists(checkout_module_abspath) and (
|
|
798
|
+
osp.islink(checkout_module_abspath)
|
|
799
|
+
or not osp.isdir(checkout_module_abspath)
|
|
800
|
+
or os.listdir(checkout_module_abspath)
|
|
801
|
+
):
|
|
802
|
+
raise OSError(
|
|
803
|
+
"Module directory at %r does already exist and is non-empty" % checkout_module_abspath
|
|
804
|
+
)
|
|
805
|
+
os.makedirs(checkout_module_abspath, exist_ok=True)
|
|
806
|
+
self._write_git_file_and_module_config(checkout_module_abspath, module_abspath)
|
|
807
|
+
mrepo = git.Repo(checkout_module_abspath)
|
|
808
|
+
mrepo.head.reset(mrepo.head.commit, index=True, working_tree=True)
|
|
809
|
+
fetch_remotes(mrepo)
|
|
810
|
+
with self.repo.config_writer() as writer:
|
|
811
|
+
writer.set_value(sm_section(self.name), "url", self.url)
|
|
812
|
+
|
|
813
|
+
if mrepo is None:
|
|
814
|
+
# There is no git-repository yet - but delete empty paths.
|
|
815
|
+
if not dry_run and osp.isdir(checkout_module_abspath):
|
|
816
|
+
try:
|
|
817
|
+
os.rmdir(checkout_module_abspath)
|
|
818
|
+
except OSError as e:
|
|
819
|
+
raise OSError(
|
|
820
|
+
"Module directory at %r does already exist and is non-empty" % checkout_module_abspath
|
|
821
|
+
) from e
|
|
822
|
+
# END handle directory removal
|
|
823
|
+
|
|
824
|
+
# Don't check it out at first - nonetheless it will create a local
|
|
825
|
+
# branch according to the remote-HEAD if possible.
|
|
826
|
+
progress.update(
|
|
827
|
+
BEGIN | CLONE,
|
|
828
|
+
0,
|
|
829
|
+
1,
|
|
830
|
+
prefix
|
|
831
|
+
+ "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name),
|
|
817
832
|
)
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
# Have a valid branch, but no checkout - make sure we can figure
|
|
835
|
-
# that out by marking the commit with a null_sha.
|
|
836
|
-
local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA))
|
|
837
|
-
# END initial checkout + branch creation
|
|
838
|
-
|
|
839
|
-
# Make sure HEAD is not detached.
|
|
840
|
-
mrepo.head.set_reference(
|
|
841
|
-
local_branch,
|
|
842
|
-
logmsg="submodule: attaching head to %s" % local_branch,
|
|
833
|
+
if not dry_run:
|
|
834
|
+
if self.url.startswith("."):
|
|
835
|
+
url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url)
|
|
836
|
+
else:
|
|
837
|
+
url = self.url
|
|
838
|
+
mrepo = self._clone_repo(
|
|
839
|
+
self.repo,
|
|
840
|
+
url,
|
|
841
|
+
self.path,
|
|
842
|
+
self.name,
|
|
843
|
+
n=True,
|
|
844
|
+
env=env,
|
|
845
|
+
multi_options=clone_multi_options,
|
|
846
|
+
allow_unsafe_options=allow_unsafe_options,
|
|
847
|
+
allow_unsafe_protocols=allow_unsafe_protocols,
|
|
843
848
|
)
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
849
|
+
progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath)
|
|
850
|
+
|
|
851
|
+
if not dry_run:
|
|
852
|
+
# See whether we have a valid branch to check out.
|
|
853
|
+
try:
|
|
854
|
+
mrepo = cast("Repo", mrepo)
|
|
855
|
+
remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name)
|
|
856
|
+
local_branch = mkhead(mrepo, self.branch_path)
|
|
857
|
+
local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA))
|
|
858
|
+
mrepo.head.set_reference(
|
|
859
|
+
local_branch,
|
|
860
|
+
logmsg="submodule: attaching head to %s" % local_branch,
|
|
861
|
+
)
|
|
862
|
+
mrepo.head.reference.set_tracking_branch(remote_branch)
|
|
863
|
+
except (IndexError, InvalidGitRepositoryError):
|
|
864
|
+
_logger.warning("Failed to checkout tracking branch %s", self.branch_path)
|
|
865
|
+
|
|
866
|
+
with self.repo.config_writer() as writer:
|
|
867
|
+
writer.set_value(sm_section(self.name), "url", self.url)
|
|
857
868
|
# END handle initialization
|
|
858
869
|
|
|
859
870
|
# DETERMINE SHAS TO CHECK OUT
|
|
@@ -1267,6 +1278,36 @@ class Submodule(IndexObject, TraversableIterableObj):
|
|
|
1267
1278
|
|
|
1268
1279
|
return self
|
|
1269
1280
|
|
|
1281
|
+
@unbare_repo
|
|
1282
|
+
def deinit(self, force: bool = False) -> "Submodule":
|
|
1283
|
+
"""Run ``git submodule deinit`` on this submodule.
|
|
1284
|
+
|
|
1285
|
+
This is a thin wrapper around ``git submodule deinit <path>``,
|
|
1286
|
+
which unregisters the submodule (removes its entry from
|
|
1287
|
+
``.git/config`` and empties the working-tree directory)
|
|
1288
|
+
without deleting the submodule from ``.gitmodules``
|
|
1289
|
+
or its checked-out repository under ``.git/modules/``.
|
|
1290
|
+
A subsequent :meth:`update` will re-initialize the
|
|
1291
|
+
submodule from the retained contents.
|
|
1292
|
+
|
|
1293
|
+
:param force:
|
|
1294
|
+
If ``True``, pass ``--force`` to ``git submodule deinit``. This
|
|
1295
|
+
allows deinitialization even when the submodule's working tree has
|
|
1296
|
+
local modifications that would otherwise block the command.
|
|
1297
|
+
|
|
1298
|
+
:return:
|
|
1299
|
+
self
|
|
1300
|
+
|
|
1301
|
+
:note:
|
|
1302
|
+
Doesn't work in bare repositories.
|
|
1303
|
+
"""
|
|
1304
|
+
args: List[str] = []
|
|
1305
|
+
if force:
|
|
1306
|
+
args.append("--force")
|
|
1307
|
+
args.extend(["--", str(self.path)])
|
|
1308
|
+
self.repo.git.submodule("deinit", *args)
|
|
1309
|
+
return self
|
|
1310
|
+
|
|
1270
1311
|
def set_parent_commit(self, commit: Union[Commit_ish, str, None], check: bool = True) -> "Submodule":
|
|
1271
1312
|
"""Set this instance to use the given commit whose tree is supposed to
|
|
1272
1313
|
contain the ``.gitmodules`` blob.
|
|
@@ -149,6 +149,8 @@ class Repo:
|
|
|
149
149
|
# Can override configuration variables that execute arbitrary commands:
|
|
150
150
|
"--config",
|
|
151
151
|
"-c",
|
|
152
|
+
# Can install hooks that execute during clone:
|
|
153
|
+
"--template",
|
|
152
154
|
]
|
|
153
155
|
"""Options to :manpage:`git-clone(1)` that allow arbitrary commands to be executed.
|
|
154
156
|
|
|
@@ -159,6 +161,9 @@ class Repo:
|
|
|
159
161
|
The ``--config``/``-c`` option allows users to override configuration variables like
|
|
160
162
|
``protocol.allow`` and ``core.gitProxy`` to execute arbitrary commands:
|
|
161
163
|
https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---configltkeygtltvaluegt
|
|
164
|
+
|
|
165
|
+
The ``--template`` option can install hooks that execute during clone:
|
|
166
|
+
https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---templatetemplate-directory
|
|
162
167
|
"""
|
|
163
168
|
|
|
164
169
|
unsafe_git_archive_options = [
|
|
@@ -217,7 +217,7 @@ def rmtree(path: PathLike) -> None:
|
|
|
217
217
|
couldn't be deleted are read-only. Windows will not remove them in that case.
|
|
218
218
|
"""
|
|
219
219
|
|
|
220
|
-
def handler(function: Callable, path:
|
|
220
|
+
def handler(function: Callable[[str], Any], path: str, _excinfo: Any) -> None:
|
|
221
221
|
"""Callback for :func:`shutil.rmtree`.
|
|
222
222
|
|
|
223
223
|
This works as either a ``onexc`` or ``onerror`` style callback.
|
|
@@ -401,7 +401,7 @@ def _cygexpath(drive: Optional[str], path: str, expand_vars: bool = True) -> str
|
|
|
401
401
|
return p_str.replace("\\", "/")
|
|
402
402
|
|
|
403
403
|
|
|
404
|
-
_cygpath_parsers: Tuple[Tuple[Pattern[str], Callable, bool], ...] = (
|
|
404
|
+
_cygpath_parsers: Tuple[Tuple[Pattern[str], Callable[..., str], bool], ...] = (
|
|
405
405
|
# See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
|
406
406
|
# and: https://www.cygwin.com/cygwin-ug-net/using.html#unc-paths
|
|
407
407
|
(
|
|
@@ -508,7 +508,7 @@ def get_user_id() -> str:
|
|
|
508
508
|
return "%s@%s" % (getpass.getuser(), platform.node())
|
|
509
509
|
|
|
510
510
|
|
|
511
|
-
def finalize_process(proc: Union[subprocess.Popen, "Git.AutoInterrupt"], **kwargs: Any) -> None:
|
|
511
|
+
def finalize_process(proc: Union["subprocess.Popen[Any]", "Git.AutoInterrupt"], **kwargs: Any) -> None:
|
|
512
512
|
"""Wait for the process (clone, fetch, pull or push) and handle its errors
|
|
513
513
|
accordingly."""
|
|
514
514
|
# TODO: No close proc-streams??
|
|
@@ -520,19 +520,21 @@ def expand_path(p: None, expand_vars: bool = ...) -> None: ...
|
|
|
520
520
|
|
|
521
521
|
|
|
522
522
|
@overload
|
|
523
|
-
def expand_path(p: PathLike, expand_vars: bool = ...) ->
|
|
523
|
+
def expand_path(p: PathLike, expand_vars: bool = ...) -> Optional[PathLike]:
|
|
524
524
|
# TODO: Support for Python 3.5 has been dropped, so these overloads can be improved.
|
|
525
525
|
...
|
|
526
526
|
|
|
527
527
|
|
|
528
528
|
def expand_path(p: Union[None, PathLike], expand_vars: bool = True) -> Optional[PathLike]:
|
|
529
|
-
if
|
|
530
|
-
return
|
|
529
|
+
if p is None:
|
|
530
|
+
return None
|
|
531
531
|
try:
|
|
532
|
-
|
|
532
|
+
if isinstance(p, Path):
|
|
533
|
+
return p.resolve()
|
|
534
|
+
expanded_path = osp.expanduser(os.fspath(p))
|
|
533
535
|
if expand_vars:
|
|
534
|
-
|
|
535
|
-
return osp.normpath(osp.abspath(
|
|
536
|
+
expanded_path = osp.expandvars(expanded_path)
|
|
537
|
+
return osp.normpath(osp.abspath(expanded_path))
|
|
536
538
|
except Exception:
|
|
537
539
|
return None
|
|
538
540
|
|
|
@@ -767,7 +769,7 @@ class CallableRemoteProgress(RemoteProgress):
|
|
|
767
769
|
|
|
768
770
|
__slots__ = ("_callable",)
|
|
769
771
|
|
|
770
|
-
def __init__(self, fn: Callable) -> None:
|
|
772
|
+
def __init__(self, fn: Callable[..., Any]) -> None:
|
|
771
773
|
self._callable = fn
|
|
772
774
|
super().__init__()
|
|
773
775
|
|
|
@@ -846,7 +848,7 @@ class Actor:
|
|
|
846
848
|
cls,
|
|
847
849
|
env_name: str,
|
|
848
850
|
env_email: str,
|
|
849
|
-
config_reader: Union[None, "GitConfigParser", "SectionConstraint"] = None,
|
|
851
|
+
config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None,
|
|
850
852
|
) -> "Actor":
|
|
851
853
|
actor = Actor("", "")
|
|
852
854
|
user_id = None # We use this to avoid multiple calls to getpass.getuser().
|
|
@@ -882,7 +884,9 @@ class Actor:
|
|
|
882
884
|
return actor
|
|
883
885
|
|
|
884
886
|
@classmethod
|
|
885
|
-
def committer(
|
|
887
|
+
def committer(
|
|
888
|
+
cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None
|
|
889
|
+
) -> "Actor":
|
|
886
890
|
"""
|
|
887
891
|
:return:
|
|
888
892
|
:class:`Actor` instance corresponding to the configured committer. It
|
|
@@ -897,7 +901,9 @@ class Actor:
|
|
|
897
901
|
return cls._main_actor(cls.env_committer_name, cls.env_committer_email, config_reader)
|
|
898
902
|
|
|
899
903
|
@classmethod
|
|
900
|
-
def author(
|
|
904
|
+
def author(
|
|
905
|
+
cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None
|
|
906
|
+
) -> "Actor":
|
|
901
907
|
"""Same as :meth:`committer`, but defines the main author. It may be specified
|
|
902
908
|
in the environment, but defaults to the committer."""
|
|
903
909
|
return cls._main_actor(cls.env_author_name, cls.env_author_email, config_reader)
|
|
@@ -980,11 +986,11 @@ class IndexFileSHA1Writer:
|
|
|
980
986
|
|
|
981
987
|
__slots__ = ("f", "sha1")
|
|
982
988
|
|
|
983
|
-
def __init__(self, f: IO) -> None:
|
|
989
|
+
def __init__(self, f: IO[bytes]) -> None:
|
|
984
990
|
self.f = f
|
|
985
991
|
self.sha1 = make_sha(b"")
|
|
986
992
|
|
|
987
|
-
def write(self, data:
|
|
993
|
+
def write(self, data: bytes) -> int:
|
|
988
994
|
self.sha1.update(data)
|
|
989
995
|
return self.f.write(data)
|
|
990
996
|
|
|
@@ -1181,6 +1187,7 @@ class IterableList(List[T_IterableObj]): # type: ignore[type-var]
|
|
|
1181
1187
|
return super().__new__(cls)
|
|
1182
1188
|
|
|
1183
1189
|
def __init__(self, id_attr: str, prefix: str = "") -> None:
|
|
1190
|
+
super().__init__()
|
|
1184
1191
|
self._id_attr = id_attr
|
|
1185
1192
|
self._prefix = prefix
|
|
1186
1193
|
|
|
@@ -1210,7 +1217,9 @@ class IterableList(List[T_IterableObj]): # type: ignore[type-var]
|
|
|
1210
1217
|
# END for each item
|
|
1211
1218
|
return list.__getattribute__(self, attr)
|
|
1212
1219
|
|
|
1213
|
-
def __getitem__(
|
|
1220
|
+
def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
|
|
1221
|
+
self, index: Union[SupportsIndex, int, slice, str]
|
|
1222
|
+
) -> T_IterableObj:
|
|
1214
1223
|
if isinstance(index, int):
|
|
1215
1224
|
return list.__getitem__(self, index)
|
|
1216
1225
|
elif isinstance(index, slice):
|
|
@@ -1288,7 +1297,7 @@ class IterableObj(Protocol):
|
|
|
1288
1297
|
:return:
|
|
1289
1298
|
list(Item,...) list of item instances
|
|
1290
1299
|
"""
|
|
1291
|
-
out_list: IterableList = IterableList(cls._id_attribute_)
|
|
1300
|
+
out_list: IterableList[T_IterableObj] = IterableList(cls._id_attribute_)
|
|
1292
1301
|
out_list.extend(cls.iter_items(repo, *args, **kwargs))
|
|
1293
1302
|
return out_list
|
|
1294
1303
|
|
|
@@ -1297,7 +1306,8 @@ class IterableClassWatcher(type):
|
|
|
1297
1306
|
"""Metaclass that issues :exc:`DeprecationWarning` when :class:`git.util.Iterable`
|
|
1298
1307
|
is subclassed."""
|
|
1299
1308
|
|
|
1300
|
-
def __init__(cls, name: str, bases: Tuple, clsdict: Dict) -> None:
|
|
1309
|
+
def __init__(cls, name: str, bases: Tuple[type, ...], clsdict: Dict[str, Any]) -> None:
|
|
1310
|
+
super().__init__(name, bases, clsdict)
|
|
1301
1311
|
for base in bases:
|
|
1302
1312
|
if type(base) is IterableClassWatcher:
|
|
1303
1313
|
warnings.warn(
|
|
@@ -33,6 +33,17 @@ exclude = ["^git/ext/gitdb"]
|
|
|
33
33
|
module = "gitdb.*"
|
|
34
34
|
ignore_missing_imports = true
|
|
35
35
|
|
|
36
|
+
[tool.basedpyright]
|
|
37
|
+
typeCheckingMode = "standard"
|
|
38
|
+
pythonVersion = "3.7"
|
|
39
|
+
extraPaths = [
|
|
40
|
+
"git/ext/gitdb",
|
|
41
|
+
"git/ext/gitdb/gitdb/ext/smmap",
|
|
42
|
+
]
|
|
43
|
+
exclude = [
|
|
44
|
+
"git/ext/gitdb",
|
|
45
|
+
]
|
|
46
|
+
|
|
36
47
|
[tool.coverage.run]
|
|
37
48
|
source = ["git"]
|
|
38
49
|
|
gitpython-3.1.52/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.1.52
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|