GitPython 3.1.49__tar.gz → 3.1.51__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.49 → gitpython-3.1.51}/AUTHORS +1 -0
- gitpython-3.1.51/CONTRIBUTING.md +46 -0
- {gitpython-3.1.49 → gitpython-3.1.51/GitPython.egg-info}/PKG-INFO +1 -1
- {gitpython-3.1.49/GitPython.egg-info → gitpython-3.1.51}/PKG-INFO +1 -1
- gitpython-3.1.51/VERSION +1 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/changes.rst +26 -1
- {gitpython-3.1.49 → gitpython-3.1.51}/git/__init__.py +1 -1
- {gitpython-3.1.49 → gitpython-3.1.51}/git/cmd.py +118 -10
- {gitpython-3.1.49 → gitpython-3.1.51}/git/config.py +18 -1
- {gitpython-3.1.49 → gitpython-3.1.51}/git/diff.py +19 -4
- {gitpython-3.1.49 → gitpython-3.1.51}/git/index/base.py +40 -3
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/commit.py +17 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/remote.py +19 -7
- {gitpython-3.1.49 → gitpython-3.1.51}/git/repo/base.py +97 -8
- gitpython-3.1.49/CONTRIBUTING.md +0 -15
- gitpython-3.1.49/VERSION +0 -1
- {gitpython-3.1.49 → gitpython-3.1.51}/CHANGES +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/GitPython.egg-info/SOURCES.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/GitPython.egg-info/dependency_links.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/GitPython.egg-info/not-zip-safe +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/GitPython.egg-info/requires.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/GitPython.egg-info/top_level.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/LICENSE +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/MANIFEST.in +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/README.md +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/.gitignore +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/Makefile +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/requirements.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/conf.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/index.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/intro.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/quickstart.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/reference.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/roadmap.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/doc/source/tutorial.rst +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/compat.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/db.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/exc.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/index/__init__.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/index/fun.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/index/typ.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/index/util.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/__init__.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/base.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/blob.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/fun.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/submodule/__init__.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/submodule/base.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/submodule/root.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/submodule/util.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/tag.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/tree.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/objects/util.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/py.typed +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/__init__.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/head.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/log.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/reference.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/remote.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/symbolic.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/refs/tag.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/repo/__init__.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/repo/fun.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/types.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/git/util.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/pyproject.toml +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/requirements.txt +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/setup.cfg +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/setup.py +0 -0
- {gitpython-3.1.49 → gitpython-3.1.51}/test-requirements.txt +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# How to contribute
|
|
2
|
+
|
|
3
|
+
The following is a short step-by-step rundown of what one typically would do to contribute.
|
|
4
|
+
|
|
5
|
+
- [Fork this project](https://github.com/gitpython-developers/GitPython/fork) on GitHub.
|
|
6
|
+
- For setting up the environment to run the self tests, please run `init-tests-after-clone.sh`.
|
|
7
|
+
- Please try to **write a test that fails unless the contribution is present.**
|
|
8
|
+
- Try to avoid massive commits and prefer to take small steps, with one commit for each.
|
|
9
|
+
- Feel free to add yourself to AUTHORS file.
|
|
10
|
+
- Create a pull request.
|
|
11
|
+
|
|
12
|
+
## Quality expectations
|
|
13
|
+
|
|
14
|
+
Contributions must be made with care and meet the quality bar of the surrounding code.
|
|
15
|
+
That means a change should not leave GitPython worse than it was before: it should be
|
|
16
|
+
readable, maintainable, tested where practical, documented and consistent with the
|
|
17
|
+
existing style and behavior.
|
|
18
|
+
|
|
19
|
+
A contribution that works only narrowly but lowers the quality of the
|
|
20
|
+
codebase may be declined. The maintainers may not always be able to provide
|
|
21
|
+
detailed feedback.
|
|
22
|
+
|
|
23
|
+
## AI-assisted contributions
|
|
24
|
+
|
|
25
|
+
If AI edits files for you, disclose it in the pull request description and commit
|
|
26
|
+
metadata. Prefer making the agent identity part of the commit, for example by using
|
|
27
|
+
an AI author such as `$agent $version <ai-agent@example.invalid>` or a co-author via
|
|
28
|
+
a `Co-authored-by: <agent-identity>` trailer.
|
|
29
|
+
|
|
30
|
+
Agents operating through a person's GitHub account must identify themselves. For
|
|
31
|
+
example, comments posted by an agent should say so directly with phrases like
|
|
32
|
+
`AI agent on behalf of <person>: ...`.
|
|
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.
|
|
42
|
+
|
|
43
|
+
## Fuzzing Test Specific Documentation
|
|
44
|
+
|
|
45
|
+
For details related to contributing to the fuzzing test suite and OSS-Fuzz integration, please
|
|
46
|
+
refer to the dedicated [fuzzing README](./fuzzing/README.md).
|
gitpython-3.1.51/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.1.51
|
|
@@ -2,11 +2,36 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
3.1.51
|
|
6
|
+
======
|
|
7
|
+
|
|
8
|
+
This is primarily a security release. It prevents additional argument-injection
|
|
9
|
+
paths that could allow execution of arbitrary commands or writing to arbitrary
|
|
10
|
+
files through unsafe Git options (GHSA-956x-8gvw-wg5v), and closes bypasses of
|
|
11
|
+
the existing protections using abbreviated long options or joined short options
|
|
12
|
+
(GHSA-2f96-g7mh-g2hx).
|
|
13
|
+
|
|
14
|
+
The release also improves support for relative worktree paths and diffs against
|
|
15
|
+
the empty tree, preserves stderr from failed diff processes, supports relative
|
|
16
|
+
configuration include paths, and includes assorted documentation, typing, test,
|
|
17
|
+
and CI improvements.
|
|
18
|
+
|
|
19
|
+
See the following for all changes.
|
|
20
|
+
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51
|
|
21
|
+
|
|
22
|
+
3.1.50
|
|
23
|
+
======
|
|
24
|
+
|
|
25
|
+
Save setting of configuration values, this time sections as well, as follow-up to 3.1.49.
|
|
26
|
+
|
|
27
|
+
See the following for all changes.
|
|
28
|
+
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.50
|
|
29
|
+
|
|
5
30
|
3.1.49
|
|
6
31
|
======
|
|
7
32
|
|
|
8
33
|
Save setting of configuration values,
|
|
9
|
-
which
|
|
34
|
+
which could be used to inject other more configuration.
|
|
10
35
|
|
|
11
36
|
Also more conforming `rev-parse` implementation.
|
|
12
37
|
|
|
@@ -649,6 +649,11 @@ class Git(metaclass=_GitMeta):
|
|
|
649
649
|
|
|
650
650
|
re_unsafe_protocol = re.compile(r"(.+)::.+")
|
|
651
651
|
|
|
652
|
+
unsafe_git_ls_remote_options = [
|
|
653
|
+
# This option allows arbitrary command execution in git-ls-remote.
|
|
654
|
+
"--upload-pack",
|
|
655
|
+
]
|
|
656
|
+
|
|
652
657
|
def __getstate__(self) -> Dict[str, Any]:
|
|
653
658
|
return slots_to_dict(self, exclude=self._excluded_)
|
|
654
659
|
|
|
@@ -961,17 +966,80 @@ class Git(metaclass=_GitMeta):
|
|
|
961
966
|
|
|
962
967
|
@classmethod
|
|
963
968
|
def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None:
|
|
964
|
-
"""
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
969
|
+
"""Raise :class:`~git.exc.UnsafeOptionError` for blocked option spellings.
|
|
970
|
+
|
|
971
|
+
In addition to exact matches, this rejects abbreviated long options accepted
|
|
972
|
+
by Git (for example, ``--upl`` for ``--upload-pack``) and unsafe short options
|
|
973
|
+
whose values are joined to the same token, including after clusterable flags
|
|
974
|
+
(for example, ``-uVALUE`` and ``-fuVALUE``).
|
|
975
|
+
|
|
976
|
+
A list containing only bare names is treated as normalized keyword arguments,
|
|
977
|
+
so multi-character names such as ``upload_p`` are checked as long-option
|
|
978
|
+
abbreviations. If any item starts with ``-``, the list is treated as tokenized
|
|
979
|
+
command-line input: bare items can be option values and are not checked as
|
|
980
|
+
abbreviations. Thus ``["--origin", "upload"]`` is allowed. Single-dash options
|
|
981
|
+
use short-option parsing rather than broad prefix matching, preserving safe
|
|
982
|
+
attached values such as ``-oupstream`` and ``-bcurrent``.
|
|
983
|
+
|
|
984
|
+
Some options passed to ``git <command>`` can execute arbitrary commands and
|
|
985
|
+
are therefore blocked by default unless the caller explicitly allows them.
|
|
968
986
|
"""
|
|
969
987
|
# Options can be of the form `foo`, `--foo`, `--foo bar`, or `--foo=bar`.
|
|
988
|
+
# Git accepts any unambiguous prefix of a long option, so an abbreviated
|
|
989
|
+
# spelling such as `--upl` for `--upload-pack` must be rejected too. An
|
|
990
|
+
# option is unsafe if its canonical name is a prefix of any blocked
|
|
991
|
+
# option's canonical name. Only long options and multi-character kwargs
|
|
992
|
+
# can be abbreviations; single-character short options remain exact-match
|
|
993
|
+
# only.
|
|
970
994
|
canonical_unsafe_options = {cls._canonicalize_option_name(option): option for option in unsafe_options}
|
|
995
|
+
unsafe_short_options = {
|
|
996
|
+
canonical: option
|
|
997
|
+
for canonical, option in canonical_unsafe_options.items()
|
|
998
|
+
if option.startswith("-") and not option.startswith("--") and len(canonical) == 1
|
|
999
|
+
}
|
|
1000
|
+
# These value-less Git flags can be clustered before another short option
|
|
1001
|
+
# (for example, ``-fuVALUE``). Stop at any other character because it may
|
|
1002
|
+
# begin an attached value, as ``o`` does in the safe option ``-oupstream``.
|
|
1003
|
+
clusterable_short_options = frozenset("46flnqsv")
|
|
1004
|
+
options_are_kwargs = all(not option.startswith("-") for option in options)
|
|
971
1005
|
for option in options:
|
|
972
|
-
|
|
1006
|
+
candidate = cls._canonicalize_option_name(option)
|
|
1007
|
+
if not candidate:
|
|
1008
|
+
continue
|
|
1009
|
+
unsafe_option = canonical_unsafe_options.get(candidate)
|
|
973
1010
|
if unsafe_option is not None:
|
|
974
1011
|
raise UnsafeOptionError(f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it.")
|
|
1012
|
+
option_token = option.split("=", 1)[0].split(None, 1)[0]
|
|
1013
|
+
if option_token.startswith("-") and not option_token.startswith("--"):
|
|
1014
|
+
for option_char in option_token[1:]:
|
|
1015
|
+
unsafe_option = unsafe_short_options.get(option_char)
|
|
1016
|
+
if unsafe_option is not None:
|
|
1017
|
+
raise UnsafeOptionError(
|
|
1018
|
+
f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it."
|
|
1019
|
+
)
|
|
1020
|
+
if option_char not in clusterable_short_options:
|
|
1021
|
+
break
|
|
1022
|
+
if not (option.startswith("--") or (options_are_kwargs and len(candidate) > 1)):
|
|
1023
|
+
continue
|
|
1024
|
+
for canonical, unsafe_option in canonical_unsafe_options.items():
|
|
1025
|
+
if canonical.startswith(candidate):
|
|
1026
|
+
raise UnsafeOptionError(
|
|
1027
|
+
f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it."
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
@classmethod
|
|
1031
|
+
def _option_candidates(cls, args: Sequence[Any] = (), kwargs: Optional[Mapping[str, Any]] = None) -> List[str]:
|
|
1032
|
+
"""Collect possible option spellings before command-line transformation."""
|
|
1033
|
+
options = [
|
|
1034
|
+
option for option in cls._unpack_args([arg for arg in args if arg is not None]) if option.startswith("-")
|
|
1035
|
+
]
|
|
1036
|
+
if kwargs:
|
|
1037
|
+
for key, value in kwargs.items():
|
|
1038
|
+
values = value if isinstance(value, (list, tuple)) else (value,)
|
|
1039
|
+
if any(value is True or (value is not False and value is not None) for value in values):
|
|
1040
|
+
key = str(key)
|
|
1041
|
+
options.append(f"-{key}" if len(key) == 1 else f"--{dashify(key)}")
|
|
1042
|
+
return options
|
|
975
1043
|
|
|
976
1044
|
AutoInterrupt: TypeAlias = _AutoInterrupt
|
|
977
1045
|
|
|
@@ -1030,6 +1098,22 @@ class Git(metaclass=_GitMeta):
|
|
|
1030
1098
|
|
|
1031
1099
|
self._persistent_git_options = self.transform_kwargs(split_single_char_options=True, **kwargs)
|
|
1032
1100
|
|
|
1101
|
+
def ls_remote(
|
|
1102
|
+
self,
|
|
1103
|
+
*args: Any,
|
|
1104
|
+
allow_unsafe_options: bool = False,
|
|
1105
|
+
**kwargs: Any,
|
|
1106
|
+
) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], "Git.AutoInterrupt"]:
|
|
1107
|
+
"""List references in a remote repository.
|
|
1108
|
+
|
|
1109
|
+
:param allow_unsafe_options:
|
|
1110
|
+
Allow unsafe options, like ``--upload-pack``.
|
|
1111
|
+
"""
|
|
1112
|
+
if not allow_unsafe_options:
|
|
1113
|
+
candidate_options = self._option_candidates(args, kwargs)
|
|
1114
|
+
Git.check_unsafe_options(options=candidate_options, unsafe_options=self.unsafe_git_ls_remote_options)
|
|
1115
|
+
return self._call_process("ls_remote", *args, **kwargs)
|
|
1116
|
+
|
|
1033
1117
|
@property
|
|
1034
1118
|
def working_dir(self) -> Union[None, PathLike]:
|
|
1035
1119
|
""":return: Git directory we are working on"""
|
|
@@ -1131,9 +1215,28 @@ class Git(metaclass=_GitMeta):
|
|
|
1131
1215
|
information (stdout).
|
|
1132
1216
|
|
|
1133
1217
|
:param command:
|
|
1134
|
-
The command
|
|
1135
|
-
|
|
1136
|
-
|
|
1218
|
+
The command to execute. A sequence of program arguments is recommended.
|
|
1219
|
+
A string is also accepted, but its meaning is strongly platform-dependent.
|
|
1220
|
+
|
|
1221
|
+
By default, a shell is not used. On Unix-like systems, a string is the whole
|
|
1222
|
+
program name (so ``"git log -n 1"`` raises :class:`GitCommandNotFound`). On
|
|
1223
|
+
Windows, the program parses the arguments itself, so multi-word strings can
|
|
1224
|
+
work but are not portable.
|
|
1225
|
+
|
|
1226
|
+
Avoid ``shell=True`` (and :attr:`Git.USE_SHELL`): this runs the command in
|
|
1227
|
+
a shell, which is generally unsafe. The shell interprets metacharacters
|
|
1228
|
+
such as ``;``, ``|``, ``&``, ``$(...)``, ``$VAR``, ``%VAR%``, and ``^``
|
|
1229
|
+
(depending on the platform) as syntax. Any untrusted text in the command
|
|
1230
|
+
can then execute arbitrary OS commands. See :attr:`Git.USE_SHELL`.
|
|
1231
|
+
|
|
1232
|
+
Producing a sequence automatically by :func:`shlex.split` and passing it
|
|
1233
|
+
as the command is far safer than ``shell=True``. But :func:`shlex.split`
|
|
1234
|
+
parses POSIX shell syntax on all systems, and the result is still unsafe
|
|
1235
|
+
for anything but *fixed, fully trusted* strings. Do not use it on strings
|
|
1236
|
+
built by interpolating values: whitespace or quoting in an untrusted value
|
|
1237
|
+
can still inject arguments. For input derived in any way from untrusted
|
|
1238
|
+
data, build the argument sequence yourself, while ensuring each argument
|
|
1239
|
+
is fully sanitized.
|
|
1137
1240
|
|
|
1138
1241
|
:param istream:
|
|
1139
1242
|
Standard input filehandle passed to :class:`subprocess.Popen`.
|
|
@@ -1201,6 +1304,11 @@ class Git(metaclass=_GitMeta):
|
|
|
1201
1304
|
needed (nor useful) to work around any known operating system specific
|
|
1202
1305
|
issues.
|
|
1203
1306
|
|
|
1307
|
+
On Unix-like systems, when migrating away from passing string commands with
|
|
1308
|
+
``shell=True``, :func:`shlex.split` may serve as a transitional step in rare
|
|
1309
|
+
cases, with extreme care. (Drop ``shell=True`` and pass the resulting
|
|
1310
|
+
sequence as the command.) See the `command` parameter above on the risks.
|
|
1311
|
+
|
|
1204
1312
|
:param env:
|
|
1205
1313
|
A dictionary of environment variables to be passed to
|
|
1206
1314
|
:class:`subprocess.Popen`.
|
|
@@ -1512,7 +1620,7 @@ class Git(metaclass=_GitMeta):
|
|
|
1512
1620
|
return args
|
|
1513
1621
|
|
|
1514
1622
|
@classmethod
|
|
1515
|
-
def _unpack_args(cls, arg_list: Sequence[
|
|
1623
|
+
def _unpack_args(cls, arg_list: Sequence[Any]) -> List[str]:
|
|
1516
1624
|
outlist = []
|
|
1517
1625
|
if isinstance(arg_list, (list, tuple)):
|
|
1518
1626
|
for arg in arg_list:
|
|
@@ -1588,7 +1696,7 @@ class Git(metaclass=_GitMeta):
|
|
|
1588
1696
|
|
|
1589
1697
|
turns into::
|
|
1590
1698
|
|
|
1591
|
-
git rev-list --max-count=10 --header
|
|
1699
|
+
git rev-list --max-count=10 --header master
|
|
1592
1700
|
|
|
1593
1701
|
:return:
|
|
1594
1702
|
Same as :meth:`execute`. If no args are given, used :meth:`execute`'s
|
|
@@ -72,6 +72,9 @@ CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbr
|
|
|
72
72
|
See: https://git-scm.com/docs/git-config#_conditional_includes
|
|
73
73
|
"""
|
|
74
74
|
|
|
75
|
+
UNSAFE_CONFIG_CHARS_RE = re.compile(r"[\r\n\x00]")
|
|
76
|
+
"""Characters that cannot be safely written in config names or values."""
|
|
77
|
+
|
|
75
78
|
|
|
76
79
|
class MetaParserBuilder(abc.ABCMeta): # noqa: B024
|
|
77
80
|
"""Utility class wrapping base-class methods into decorators that assure read-only
|
|
@@ -631,6 +634,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
631
634
|
files_to_read = list(self._file_or_files)
|
|
632
635
|
# END ensure we have a copy of the paths to handle
|
|
633
636
|
|
|
637
|
+
files_to_read = [osp.abspath(path) if isinstance(path, (str, os.PathLike)) else path for path in files_to_read]
|
|
638
|
+
|
|
634
639
|
seen = set(files_to_read)
|
|
635
640
|
num_read_include_files = 0
|
|
636
641
|
while files_to_read:
|
|
@@ -778,6 +783,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
778
783
|
|
|
779
784
|
def add_section(self, section: "cp._SectionName") -> None:
|
|
780
785
|
"""Assures added options will stay in order."""
|
|
786
|
+
self._assure_config_name_safe(section, "section")
|
|
781
787
|
return super().add_section(section)
|
|
782
788
|
|
|
783
789
|
@property
|
|
@@ -884,10 +890,14 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
884
890
|
|
|
885
891
|
def _value_to_string_safe(self, value: Union[str, bytes, int, float, bool]) -> str:
|
|
886
892
|
value_str = self._value_to_string(value)
|
|
887
|
-
if
|
|
893
|
+
if UNSAFE_CONFIG_CHARS_RE.search(value_str):
|
|
888
894
|
raise ValueError("Git config values must not contain CR, LF, or NUL")
|
|
889
895
|
return value_str
|
|
890
896
|
|
|
897
|
+
def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None:
|
|
898
|
+
if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name):
|
|
899
|
+
raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label)
|
|
900
|
+
|
|
891
901
|
@needs_values
|
|
892
902
|
@set_dirty_and_flush_changes
|
|
893
903
|
def set(
|
|
@@ -896,6 +906,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
896
906
|
option: str,
|
|
897
907
|
value: Union[str, bytes, int, float, bool, None] = None,
|
|
898
908
|
) -> None:
|
|
909
|
+
self._assure_config_name_safe(section, "section")
|
|
910
|
+
self._assure_config_name_safe(option, "option")
|
|
899
911
|
if value is not None:
|
|
900
912
|
value = self._value_to_string_safe(value)
|
|
901
913
|
return super().set(section, option, value)
|
|
@@ -920,6 +932,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
920
932
|
:return:
|
|
921
933
|
This instance
|
|
922
934
|
"""
|
|
935
|
+
self._assure_config_name_safe(section, "section")
|
|
936
|
+
self._assure_config_name_safe(option, "option")
|
|
923
937
|
value_str = self._value_to_string_safe(value)
|
|
924
938
|
if not self.has_section(section):
|
|
925
939
|
self.add_section(section)
|
|
@@ -948,6 +962,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
948
962
|
:return:
|
|
949
963
|
This instance
|
|
950
964
|
"""
|
|
965
|
+
self._assure_config_name_safe(section, "section")
|
|
966
|
+
self._assure_config_name_safe(option, "option")
|
|
951
967
|
value_str = self._value_to_string_safe(value)
|
|
952
968
|
if not self.has_section(section):
|
|
953
969
|
self.add_section(section)
|
|
@@ -968,6 +984,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
|
|
|
968
984
|
"""
|
|
969
985
|
if not self.has_section(section):
|
|
970
986
|
raise ValueError("Source section '%s' doesn't exist" % section)
|
|
987
|
+
self._assure_config_name_safe(new_name, "section")
|
|
971
988
|
if self.has_section(new_name):
|
|
972
989
|
raise ValueError("Destination section '%s' already exists" % new_name)
|
|
973
990
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# This module is part of GitPython and is released under the
|
|
4
4
|
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
|
|
5
5
|
|
|
6
|
-
__all__ = ["DiffConstants", "NULL_TREE", "INDEX", "Diffable", "DiffIndex", "Diff"]
|
|
6
|
+
__all__ = ["DiffConstants", "NULL_TREE", "NULL_TREE_SHA", "INDEX", "Diffable", "DiffIndex", "Diff"]
|
|
7
7
|
|
|
8
8
|
import enum
|
|
9
9
|
import re
|
|
@@ -84,6 +84,9 @@ This is an alias of :const:`DiffConstants.NULL_TREE`, which may also be accessed
|
|
|
84
84
|
:const:`git.NULL_TREE` and :const:`Diffable.NULL_TREE`.
|
|
85
85
|
"""
|
|
86
86
|
|
|
87
|
+
NULL_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
|
88
|
+
"""SHA of Git's canonical empty tree object."""
|
|
89
|
+
|
|
87
90
|
INDEX: Literal[DiffConstants.INDEX] = DiffConstants.INDEX
|
|
88
91
|
"""Stand-in indicating you want to diff against the index.
|
|
89
92
|
|
|
@@ -599,7 +602,14 @@ class Diff:
|
|
|
599
602
|
|
|
600
603
|
# FIXME: Here SLURPING raw, need to re-phrase header-regexes linewise.
|
|
601
604
|
text_list: List[bytes] = []
|
|
602
|
-
|
|
605
|
+
stderr_list: List[bytes] = []
|
|
606
|
+
|
|
607
|
+
def finalize_process_with_stderr(proc: Union["Popen", "Git.AutoInterrupt"]) -> None:
|
|
608
|
+
finalize_process(proc, stderr=b"".join(stderr_list))
|
|
609
|
+
|
|
610
|
+
handle_process_output(
|
|
611
|
+
proc, text_list.append, stderr_list.append, finalize_process_with_stderr, decode_streams=False
|
|
612
|
+
)
|
|
603
613
|
|
|
604
614
|
# For now, we have to bake the stream.
|
|
605
615
|
text = b"".join(text_list)
|
|
@@ -765,11 +775,16 @@ class Diff:
|
|
|
765
775
|
# :100644 100644 687099101... 37c5e30c8... M .gitignore
|
|
766
776
|
|
|
767
777
|
index: "DiffIndex" = DiffIndex()
|
|
778
|
+
stderr_list: List[bytes] = []
|
|
779
|
+
|
|
780
|
+
def finalize_process_with_stderr(proc: Union["Popen", "Git.AutoInterrupt"]) -> None:
|
|
781
|
+
finalize_process(proc, stderr=b"".join(stderr_list))
|
|
782
|
+
|
|
768
783
|
handle_process_output(
|
|
769
784
|
proc,
|
|
770
785
|
lambda byt: cls._handle_diff_line(byt, repo, index),
|
|
771
|
-
|
|
772
|
-
|
|
786
|
+
stderr_list.append,
|
|
787
|
+
finalize_process_with_stderr,
|
|
773
788
|
decode_streams=False,
|
|
774
789
|
)
|
|
775
790
|
|
|
@@ -1480,12 +1480,11 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1480
1480
|
|
|
1481
1481
|
return self
|
|
1482
1482
|
|
|
1483
|
-
# FIXME: This is documented to accept the same parameters as Diffable.diff, but this
|
|
1484
|
-
# does not handle NULL_TREE for `other`. (The suppressed mypy error is about this.)
|
|
1485
1483
|
def diff(
|
|
1486
1484
|
self,
|
|
1487
|
-
other: Union[
|
|
1485
|
+
other: Union[
|
|
1488
1486
|
Literal[git_diff.DiffConstants.INDEX],
|
|
1487
|
+
Literal[git_diff.DiffConstants.NULL_TREE],
|
|
1489
1488
|
"Tree",
|
|
1490
1489
|
"Commit",
|
|
1491
1490
|
str,
|
|
@@ -1512,6 +1511,44 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
|
|
|
1512
1511
|
if other is self.INDEX:
|
|
1513
1512
|
return git_diff.DiffIndex()
|
|
1514
1513
|
|
|
1514
|
+
if other == git_diff.NULL_TREE or other == git_diff.NULL_TREE_SHA:
|
|
1515
|
+
args: List[Union[PathLike, str]] = [
|
|
1516
|
+
"--cached",
|
|
1517
|
+
git_diff.NULL_TREE_SHA,
|
|
1518
|
+
"--abbrev=40",
|
|
1519
|
+
"--full-index",
|
|
1520
|
+
]
|
|
1521
|
+
|
|
1522
|
+
if not any(x in kwargs for x in ("find_renames", "no_renames", "M")):
|
|
1523
|
+
args.append("-M")
|
|
1524
|
+
|
|
1525
|
+
if create_patch:
|
|
1526
|
+
args.append("-p")
|
|
1527
|
+
args.append("--no-ext-diff")
|
|
1528
|
+
else:
|
|
1529
|
+
args.append("--raw")
|
|
1530
|
+
args.append("-z")
|
|
1531
|
+
|
|
1532
|
+
args.append("--no-color")
|
|
1533
|
+
|
|
1534
|
+
if paths is not None and not isinstance(paths, (tuple, list)):
|
|
1535
|
+
paths = [paths]
|
|
1536
|
+
|
|
1537
|
+
if paths:
|
|
1538
|
+
args.append("--")
|
|
1539
|
+
args.extend(paths)
|
|
1540
|
+
|
|
1541
|
+
kwargs["as_process"] = True
|
|
1542
|
+
proc = self.repo.git.diff(*args, **kwargs)
|
|
1543
|
+
|
|
1544
|
+
diff_method = (
|
|
1545
|
+
git_diff.Diff._index_from_patch_format if create_patch else git_diff.Diff._index_from_raw_format
|
|
1546
|
+
)
|
|
1547
|
+
index = diff_method(self.repo, proc)
|
|
1548
|
+
|
|
1549
|
+
proc.wait()
|
|
1550
|
+
return index
|
|
1551
|
+
|
|
1515
1552
|
# Index against anything but None is a reverse diff with the respective item.
|
|
1516
1553
|
# Handle existing -R flags properly.
|
|
1517
1554
|
# Transform strings to the object so that we can call diff on it.
|
|
@@ -86,6 +86,12 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
|
|
|
86
86
|
# INVARIANTS
|
|
87
87
|
default_encoding = "UTF-8"
|
|
88
88
|
|
|
89
|
+
# Options to :manpage:`git-rev-list(1)` that can overwrite files.
|
|
90
|
+
unsafe_git_rev_options = [
|
|
91
|
+
"--output",
|
|
92
|
+
"-o",
|
|
93
|
+
]
|
|
94
|
+
|
|
89
95
|
type: Literal["commit"] = "commit"
|
|
90
96
|
|
|
91
97
|
__slots__ = (
|
|
@@ -302,6 +308,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
|
|
|
302
308
|
repo: "Repo",
|
|
303
309
|
rev: Union[str, "Commit", "SymbolicReference"],
|
|
304
310
|
paths: Union[PathLike, Sequence[PathLike]] = "",
|
|
311
|
+
allow_unsafe_options: bool = False,
|
|
305
312
|
**kwargs: Any,
|
|
306
313
|
) -> Iterator["Commit"]:
|
|
307
314
|
R"""Find all commits matching the given criteria.
|
|
@@ -330,6 +337,11 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
|
|
|
330
337
|
raise ValueError("--pretty cannot be used as parsing expects single sha's only")
|
|
331
338
|
# END handle pretty
|
|
332
339
|
|
|
340
|
+
if not allow_unsafe_options:
|
|
341
|
+
Git.check_unsafe_options(
|
|
342
|
+
options=Git._option_candidates([rev], kwargs), unsafe_options=cls.unsafe_git_rev_options
|
|
343
|
+
)
|
|
344
|
+
|
|
333
345
|
# Use -- in all cases, to prevent possibility of ambiguous arguments.
|
|
334
346
|
# See https://github.com/gitpython-developers/GitPython/issues/264.
|
|
335
347
|
|
|
@@ -374,6 +386,11 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
|
|
|
374
386
|
"""Create a git stat from changes between this commit and its first parent
|
|
375
387
|
or from all changes done if this is the very first commit.
|
|
376
388
|
|
|
389
|
+
:note:
|
|
390
|
+
If this commit is at the boundary of a shallow clone, this will
|
|
391
|
+
raise :exc:`~git.exc.GitCommandError`, since the parent object
|
|
392
|
+
was never fetched and only exists as a reference on this commit.
|
|
393
|
+
|
|
377
394
|
:return:
|
|
378
395
|
:class:`Stats`
|
|
379
396
|
"""
|
|
@@ -517,6 +517,9 @@ class FetchInfo(IterableObj):
|
|
|
517
517
|
raise NotImplementedError
|
|
518
518
|
|
|
519
519
|
|
|
520
|
+
Progress = Union[RemoteProgress, "UpdateProgress", Callable[..., RemoteProgress], None]
|
|
521
|
+
|
|
522
|
+
|
|
520
523
|
class Remote(LazyMixin, IterableObj):
|
|
521
524
|
"""Provides easy read and write access to a git remote.
|
|
522
525
|
|
|
@@ -872,7 +875,7 @@ class Remote(LazyMixin, IterableObj):
|
|
|
872
875
|
def _get_fetch_info_from_stderr(
|
|
873
876
|
self,
|
|
874
877
|
proc: "Git.AutoInterrupt",
|
|
875
|
-
progress:
|
|
878
|
+
progress: Progress,
|
|
876
879
|
kill_after_timeout: Union[None, float] = None,
|
|
877
880
|
) -> IterableList["FetchInfo"]:
|
|
878
881
|
progress = to_progress_instance(progress)
|
|
@@ -1000,7 +1003,7 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1000
1003
|
def fetch(
|
|
1001
1004
|
self,
|
|
1002
1005
|
refspec: Union[str, List[str], None] = None,
|
|
1003
|
-
progress:
|
|
1006
|
+
progress: Progress = None,
|
|
1004
1007
|
verbose: bool = True,
|
|
1005
1008
|
kill_after_timeout: Union[None, float] = None,
|
|
1006
1009
|
allow_unsafe_protocols: bool = False,
|
|
@@ -1068,7 +1071,10 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1068
1071
|
Git.check_unsafe_protocols(ref)
|
|
1069
1072
|
|
|
1070
1073
|
if not allow_unsafe_options:
|
|
1071
|
-
Git.check_unsafe_options(
|
|
1074
|
+
Git.check_unsafe_options(
|
|
1075
|
+
options=Git._option_candidates([], kwargs),
|
|
1076
|
+
unsafe_options=self.unsafe_git_fetch_options,
|
|
1077
|
+
)
|
|
1072
1078
|
|
|
1073
1079
|
proc = self.repo.git.fetch(
|
|
1074
1080
|
"--", self, *args, as_process=True, with_stdout=False, universal_newlines=True, v=verbose, **kwargs
|
|
@@ -1081,7 +1087,7 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1081
1087
|
def pull(
|
|
1082
1088
|
self,
|
|
1083
1089
|
refspec: Union[str, List[str], None] = None,
|
|
1084
|
-
progress:
|
|
1090
|
+
progress: Progress = None,
|
|
1085
1091
|
kill_after_timeout: Union[None, float] = None,
|
|
1086
1092
|
allow_unsafe_protocols: bool = False,
|
|
1087
1093
|
allow_unsafe_options: bool = False,
|
|
@@ -1122,7 +1128,10 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1122
1128
|
Git.check_unsafe_protocols(ref)
|
|
1123
1129
|
|
|
1124
1130
|
if not allow_unsafe_options:
|
|
1125
|
-
Git.check_unsafe_options(
|
|
1131
|
+
Git.check_unsafe_options(
|
|
1132
|
+
options=Git._option_candidates([], kwargs),
|
|
1133
|
+
unsafe_options=self.unsafe_git_pull_options,
|
|
1134
|
+
)
|
|
1126
1135
|
|
|
1127
1136
|
proc = self.repo.git.pull(
|
|
1128
1137
|
"--", self, refspec, with_stdout=False, as_process=True, universal_newlines=True, v=True, **kwargs
|
|
@@ -1135,7 +1144,7 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1135
1144
|
def push(
|
|
1136
1145
|
self,
|
|
1137
1146
|
refspec: Union[str, List[str], None] = None,
|
|
1138
|
-
progress:
|
|
1147
|
+
progress: Progress = None,
|
|
1139
1148
|
kill_after_timeout: Union[None, float] = None,
|
|
1140
1149
|
allow_unsafe_protocols: bool = False,
|
|
1141
1150
|
allow_unsafe_options: bool = False,
|
|
@@ -1195,7 +1204,10 @@ class Remote(LazyMixin, IterableObj):
|
|
|
1195
1204
|
Git.check_unsafe_protocols(ref)
|
|
1196
1205
|
|
|
1197
1206
|
if not allow_unsafe_options:
|
|
1198
|
-
Git.check_unsafe_options(
|
|
1207
|
+
Git.check_unsafe_options(
|
|
1208
|
+
options=Git._option_candidates([], kwargs),
|
|
1209
|
+
unsafe_options=self.unsafe_git_push_options,
|
|
1210
|
+
)
|
|
1199
1211
|
|
|
1200
1212
|
proc = self.repo.git.push(
|
|
1201
1213
|
"--",
|
|
@@ -161,6 +161,20 @@ class Repo:
|
|
|
161
161
|
https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---configltkeygtltvaluegt
|
|
162
162
|
"""
|
|
163
163
|
|
|
164
|
+
unsafe_git_archive_options = [
|
|
165
|
+
# Allows arbitrary command execution through the remote git-upload-archive command.
|
|
166
|
+
"--exec",
|
|
167
|
+
# Writes output to a caller-controlled filesystem path.
|
|
168
|
+
"--output",
|
|
169
|
+
"-o",
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
unsafe_git_revision_options = [
|
|
173
|
+
# This option allows output to be written to arbitrary files before revision parsing.
|
|
174
|
+
"--output",
|
|
175
|
+
"-o",
|
|
176
|
+
]
|
|
177
|
+
|
|
164
178
|
# Invariants
|
|
165
179
|
config_level: ConfigLevels_Tup = ("system", "user", "global", "repository")
|
|
166
180
|
"""Represents the configuration level of a configuration file."""
|
|
@@ -242,6 +256,28 @@ class Repo:
|
|
|
242
256
|
# It's important to normalize the paths, as submodules will otherwise
|
|
243
257
|
# initialize their repo instances with paths that depend on path-portions
|
|
244
258
|
# that will not exist after being removed. It's just cleaner.
|
|
259
|
+
if (
|
|
260
|
+
osp.isfile(osp.join(curpath, "gitdir"))
|
|
261
|
+
and osp.isfile(osp.join(curpath, "commondir"))
|
|
262
|
+
and osp.isfile(osp.join(curpath, "HEAD"))
|
|
263
|
+
):
|
|
264
|
+
git_dir = curpath
|
|
265
|
+
|
|
266
|
+
if "GIT_WORK_TREE" in os.environ:
|
|
267
|
+
self._working_tree_dir = os.getenv("GIT_WORK_TREE")
|
|
268
|
+
else:
|
|
269
|
+
# Linked worktree administrative directories store the path to the
|
|
270
|
+
# worktree's .git file in their gitdir file (without "gitdir: " prefix).
|
|
271
|
+
with open(osp.join(git_dir, "gitdir")) as fp:
|
|
272
|
+
worktree_gitfile = fp.read().strip()
|
|
273
|
+
|
|
274
|
+
if not osp.isabs(worktree_gitfile):
|
|
275
|
+
worktree_gitfile = osp.normpath(osp.join(git_dir, worktree_gitfile))
|
|
276
|
+
|
|
277
|
+
self._working_tree_dir = osp.dirname(worktree_gitfile)
|
|
278
|
+
|
|
279
|
+
break
|
|
280
|
+
|
|
245
281
|
if is_git_dir(curpath):
|
|
246
282
|
git_dir = curpath
|
|
247
283
|
# from man git-config : core.worktree
|
|
@@ -273,7 +309,8 @@ class Repo:
|
|
|
273
309
|
sm_gitpath = find_worktree_git_dir(dotgit)
|
|
274
310
|
|
|
275
311
|
if sm_gitpath is not None:
|
|
276
|
-
|
|
312
|
+
# worktrees can use relative paths as of Git 2.48, so we join to curpath
|
|
313
|
+
git_dir = osp.normpath(osp.join(curpath, sm_gitpath))
|
|
277
314
|
self._working_tree_dir = curpath
|
|
278
315
|
break
|
|
279
316
|
|
|
@@ -752,6 +789,7 @@ class Repo:
|
|
|
752
789
|
self,
|
|
753
790
|
rev: Union[str, Commit, "SymbolicReference", None] = None,
|
|
754
791
|
paths: Union[PathLike, Sequence[PathLike]] = "",
|
|
792
|
+
allow_unsafe_options: bool = False,
|
|
755
793
|
**kwargs: Any,
|
|
756
794
|
) -> Iterator[Commit]:
|
|
757
795
|
"""An iterator of :class:`~git.objects.commit.Commit` objects representing the
|
|
@@ -769,6 +807,9 @@ class Repo:
|
|
|
769
807
|
Arguments to be passed to :manpage:`git-rev-list(1)`.
|
|
770
808
|
Common ones are ``max_count`` and ``skip``.
|
|
771
809
|
|
|
810
|
+
:param allow_unsafe_options:
|
|
811
|
+
Allow unsafe options in the revision argument, like ``--output``.
|
|
812
|
+
|
|
772
813
|
:note:
|
|
773
814
|
To receive only commits between two named revisions, use the
|
|
774
815
|
``"revA...revB"`` revision specifier.
|
|
@@ -779,7 +820,18 @@ class Repo:
|
|
|
779
820
|
if rev is None:
|
|
780
821
|
rev = self.head.commit
|
|
781
822
|
|
|
782
|
-
|
|
823
|
+
if not allow_unsafe_options:
|
|
824
|
+
Git.check_unsafe_options(
|
|
825
|
+
options=Git._option_candidates([rev], kwargs), unsafe_options=self.unsafe_git_revision_options
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
return Commit.iter_items(
|
|
829
|
+
self,
|
|
830
|
+
rev,
|
|
831
|
+
paths,
|
|
832
|
+
allow_unsafe_options=allow_unsafe_options,
|
|
833
|
+
**kwargs,
|
|
834
|
+
)
|
|
783
835
|
|
|
784
836
|
def merge_base(self, *rev: TBD, **kwargs: Any) -> List[Commit]:
|
|
785
837
|
R"""Find the closest common ancestor for the given revision
|
|
@@ -1056,7 +1108,9 @@ class Repo:
|
|
|
1056
1108
|
)
|
|
1057
1109
|
return active_branch
|
|
1058
1110
|
|
|
1059
|
-
def blame_incremental(
|
|
1111
|
+
def blame_incremental(
|
|
1112
|
+
self, rev: str | HEAD | None, file: str, allow_unsafe_options: bool = False, **kwargs: Any
|
|
1113
|
+
) -> Iterator["BlameEntry"]:
|
|
1060
1114
|
"""Iterator for blame information for the given file at the given revision.
|
|
1061
1115
|
|
|
1062
1116
|
Unlike :meth:`blame`, this does not return the actual file's contents, only a
|
|
@@ -1067,6 +1121,9 @@ class Repo:
|
|
|
1067
1121
|
uncommitted changes. Otherwise, anything successfully parsed by
|
|
1068
1122
|
:manpage:`git-rev-parse(1)` is a valid option.
|
|
1069
1123
|
|
|
1124
|
+
:param allow_unsafe_options:
|
|
1125
|
+
Allow unsafe options in revision argument, like ``--output``.
|
|
1126
|
+
|
|
1070
1127
|
:return:
|
|
1071
1128
|
Lazy iterator of :class:`BlameEntry` tuples, where the commit indicates the
|
|
1072
1129
|
commit to blame for the line, and range indicates a span of line numbers in
|
|
@@ -1075,6 +1132,10 @@ class Repo:
|
|
|
1075
1132
|
If you combine all line number ranges outputted by this command, you should get
|
|
1076
1133
|
a continuous range spanning all line numbers in the file.
|
|
1077
1134
|
"""
|
|
1135
|
+
if not allow_unsafe_options:
|
|
1136
|
+
Git.check_unsafe_options(
|
|
1137
|
+
options=Git._option_candidates([rev], kwargs), unsafe_options=self.unsafe_git_revision_options
|
|
1138
|
+
)
|
|
1078
1139
|
|
|
1079
1140
|
data: bytes = self.git.blame(rev, "--", file, p=True, incremental=True, stdout_as_string=False, **kwargs)
|
|
1080
1141
|
commits: Dict[bytes, Commit] = {}
|
|
@@ -1153,7 +1214,8 @@ class Repo:
|
|
|
1153
1214
|
rev: Union[str, HEAD, None],
|
|
1154
1215
|
file: str,
|
|
1155
1216
|
incremental: bool = False,
|
|
1156
|
-
rev_opts: Optional[
|
|
1217
|
+
rev_opts: Optional[Sequence[str]] = None,
|
|
1218
|
+
allow_unsafe_options: bool = False,
|
|
1157
1219
|
**kwargs: Any,
|
|
1158
1220
|
) -> List[List[Commit | List[str | bytes] | None]] | Iterator[BlameEntry] | None:
|
|
1159
1221
|
"""The blame information for the given file at the given revision.
|
|
@@ -1163,6 +1225,9 @@ class Repo:
|
|
|
1163
1225
|
uncommitted changes. Otherwise, anything successfully parsed by
|
|
1164
1226
|
:manpage:`git-rev-parse(1)` is a valid option.
|
|
1165
1227
|
|
|
1228
|
+
:param allow_unsafe_options:
|
|
1229
|
+
Allow unsafe options in revision argument, like ``--output``.
|
|
1230
|
+
|
|
1166
1231
|
:return:
|
|
1167
1232
|
list: [git.Commit, list: [<line>]]
|
|
1168
1233
|
|
|
@@ -1172,9 +1237,14 @@ class Repo:
|
|
|
1172
1237
|
appearance.
|
|
1173
1238
|
"""
|
|
1174
1239
|
if incremental:
|
|
1175
|
-
return self.blame_incremental(rev, file, **kwargs)
|
|
1176
|
-
|
|
1177
|
-
|
|
1240
|
+
return self.blame_incremental(rev, file, allow_unsafe_options=allow_unsafe_options, **kwargs)
|
|
1241
|
+
rev_opts_list = list(rev_opts or [])
|
|
1242
|
+
if not allow_unsafe_options:
|
|
1243
|
+
Git.check_unsafe_options(
|
|
1244
|
+
options=Git._option_candidates([rev, rev_opts_list], kwargs),
|
|
1245
|
+
unsafe_options=self.unsafe_git_revision_options,
|
|
1246
|
+
)
|
|
1247
|
+
data: bytes = self.git.blame(rev, *rev_opts_list, "--", file, p=True, stdout_as_string=False, **kwargs)
|
|
1178
1248
|
commits: Dict[str, Commit] = {}
|
|
1179
1249
|
blames: List[List[Commit | List[str | bytes] | None]] = []
|
|
1180
1250
|
|
|
@@ -1385,7 +1455,10 @@ class Repo:
|
|
|
1385
1455
|
if not allow_unsafe_protocols:
|
|
1386
1456
|
Git.check_unsafe_protocols(url)
|
|
1387
1457
|
if not allow_unsafe_options:
|
|
1388
|
-
Git.check_unsafe_options(
|
|
1458
|
+
Git.check_unsafe_options(
|
|
1459
|
+
options=Git._option_candidates([], kwargs),
|
|
1460
|
+
unsafe_options=cls.unsafe_git_clone_options,
|
|
1461
|
+
)
|
|
1389
1462
|
if not allow_unsafe_options and multi:
|
|
1390
1463
|
Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)
|
|
1391
1464
|
|
|
@@ -1560,6 +1633,8 @@ class Repo:
|
|
|
1560
1633
|
ostream: Union[TextIO, BinaryIO],
|
|
1561
1634
|
treeish: Optional[str] = None,
|
|
1562
1635
|
prefix: Optional[str] = None,
|
|
1636
|
+
allow_unsafe_options: bool = False,
|
|
1637
|
+
allow_unsafe_protocols: bool = False,
|
|
1563
1638
|
**kwargs: Any,
|
|
1564
1639
|
) -> Repo:
|
|
1565
1640
|
"""Archive the tree at the given revision.
|
|
@@ -1582,6 +1657,12 @@ class Repo:
|
|
|
1582
1657
|
repository-relative path to a directory or file to place into the archive,
|
|
1583
1658
|
or a list or tuple of multiple paths.
|
|
1584
1659
|
|
|
1660
|
+
:param allow_unsafe_options:
|
|
1661
|
+
Allow unsafe options, like ``--exec`` or ``--output``.
|
|
1662
|
+
|
|
1663
|
+
:param allow_unsafe_protocols:
|
|
1664
|
+
Allow unsafe protocols to be used in ``remote``, like ``ext``.
|
|
1665
|
+
|
|
1585
1666
|
:raise git.exc.GitCommandError:
|
|
1586
1667
|
If something went wrong.
|
|
1587
1668
|
|
|
@@ -1592,6 +1673,14 @@ class Repo:
|
|
|
1592
1673
|
treeish = self.head.commit
|
|
1593
1674
|
if prefix and "prefix" not in kwargs:
|
|
1594
1675
|
kwargs["prefix"] = prefix
|
|
1676
|
+
remote = kwargs.get("remote")
|
|
1677
|
+
if not allow_unsafe_protocols and remote is not None:
|
|
1678
|
+
Git.check_unsafe_protocols(str(remote))
|
|
1679
|
+
if not allow_unsafe_options:
|
|
1680
|
+
Git.check_unsafe_options(
|
|
1681
|
+
options=Git._option_candidates([], kwargs),
|
|
1682
|
+
unsafe_options=self.unsafe_git_archive_options,
|
|
1683
|
+
)
|
|
1595
1684
|
kwargs["output_stream"] = ostream
|
|
1596
1685
|
path = kwargs.pop("path", [])
|
|
1597
1686
|
path = cast(Union[PathLike, List[PathLike], Tuple[PathLike, ...]], path)
|
gitpython-3.1.49/CONTRIBUTING.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# How to contribute
|
|
2
|
-
|
|
3
|
-
The following is a short step-by-step rundown of what one typically would do to contribute.
|
|
4
|
-
|
|
5
|
-
- [Fork this project](https://github.com/gitpython-developers/GitPython/fork) on GitHub.
|
|
6
|
-
- For setting up the environment to run the self tests, please run `init-tests-after-clone.sh`.
|
|
7
|
-
- Please try to **write a test that fails unless the contribution is present.**
|
|
8
|
-
- Try to avoid massive commits and prefer to take small steps, with one commit for each.
|
|
9
|
-
- Feel free to add yourself to AUTHORS file.
|
|
10
|
-
- Create a pull request.
|
|
11
|
-
|
|
12
|
-
## Fuzzing Test Specific Documentation
|
|
13
|
-
|
|
14
|
-
For details related to contributing to the fuzzing test suite and OSS-Fuzz integration, please
|
|
15
|
-
refer to the dedicated [fuzzing README](./fuzzing/README.md).
|
gitpython-3.1.49/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.1.49
|
|
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
|
|
File without changes
|