GitPython 3.1.46__tar.gz → 3.1.47__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.
Files changed (69) hide show
  1. {gitpython-3.1.46 → gitpython-3.1.47}/AUTHORS +1 -0
  2. {gitpython-3.1.46 → gitpython-3.1.47/GitPython.egg-info}/PKG-INFO +10 -10
  3. {gitpython-3.1.46 → gitpython-3.1.47}/GitPython.egg-info/requires.txt +1 -1
  4. {gitpython-3.1.46/GitPython.egg-info → gitpython-3.1.47}/PKG-INFO +10 -10
  5. {gitpython-3.1.46 → gitpython-3.1.47}/README.md +8 -8
  6. gitpython-3.1.47/VERSION +1 -0
  7. {gitpython-3.1.46 → gitpython-3.1.47}/doc/requirements.txt +1 -1
  8. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/changes.rst +9 -0
  9. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/tutorial.rst +6 -0
  10. {gitpython-3.1.46 → gitpython-3.1.47}/git/__init__.py +1 -1
  11. {gitpython-3.1.46 → gitpython-3.1.47}/git/cmd.py +39 -19
  12. {gitpython-3.1.46 → gitpython-3.1.47}/git/config.py +14 -4
  13. {gitpython-3.1.46 → gitpython-3.1.47}/git/index/base.py +2 -0
  14. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/commit.py +46 -8
  15. {gitpython-3.1.46 → gitpython-3.1.47}/git/repo/base.py +12 -4
  16. gitpython-3.1.46/VERSION +0 -1
  17. {gitpython-3.1.46 → gitpython-3.1.47}/CHANGES +0 -0
  18. {gitpython-3.1.46 → gitpython-3.1.47}/CONTRIBUTING.md +0 -0
  19. {gitpython-3.1.46 → gitpython-3.1.47}/GitPython.egg-info/SOURCES.txt +0 -0
  20. {gitpython-3.1.46 → gitpython-3.1.47}/GitPython.egg-info/dependency_links.txt +0 -0
  21. {gitpython-3.1.46 → gitpython-3.1.47}/GitPython.egg-info/not-zip-safe +0 -0
  22. {gitpython-3.1.46 → gitpython-3.1.47}/GitPython.egg-info/top_level.txt +0 -0
  23. {gitpython-3.1.46 → gitpython-3.1.47}/LICENSE +0 -0
  24. {gitpython-3.1.46 → gitpython-3.1.47}/MANIFEST.in +0 -0
  25. {gitpython-3.1.46 → gitpython-3.1.47}/doc/.gitignore +0 -0
  26. {gitpython-3.1.46 → gitpython-3.1.47}/doc/Makefile +0 -0
  27. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/conf.py +0 -0
  28. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/index.rst +0 -0
  29. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/intro.rst +0 -0
  30. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/quickstart.rst +0 -0
  31. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/reference.rst +0 -0
  32. {gitpython-3.1.46 → gitpython-3.1.47}/doc/source/roadmap.rst +0 -0
  33. {gitpython-3.1.46 → gitpython-3.1.47}/git/compat.py +0 -0
  34. {gitpython-3.1.46 → gitpython-3.1.47}/git/db.py +0 -0
  35. {gitpython-3.1.46 → gitpython-3.1.47}/git/diff.py +0 -0
  36. {gitpython-3.1.46 → gitpython-3.1.47}/git/exc.py +0 -0
  37. {gitpython-3.1.46 → gitpython-3.1.47}/git/index/__init__.py +0 -0
  38. {gitpython-3.1.46 → gitpython-3.1.47}/git/index/fun.py +0 -0
  39. {gitpython-3.1.46 → gitpython-3.1.47}/git/index/typ.py +0 -0
  40. {gitpython-3.1.46 → gitpython-3.1.47}/git/index/util.py +0 -0
  41. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/__init__.py +0 -0
  42. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/base.py +0 -0
  43. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/blob.py +0 -0
  44. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/fun.py +0 -0
  45. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/submodule/__init__.py +0 -0
  46. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/submodule/base.py +0 -0
  47. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/submodule/root.py +0 -0
  48. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/submodule/util.py +0 -0
  49. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/tag.py +0 -0
  50. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/tree.py +0 -0
  51. {gitpython-3.1.46 → gitpython-3.1.47}/git/objects/util.py +0 -0
  52. {gitpython-3.1.46 → gitpython-3.1.47}/git/py.typed +0 -0
  53. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/__init__.py +0 -0
  54. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/head.py +0 -0
  55. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/log.py +0 -0
  56. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/reference.py +0 -0
  57. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/remote.py +0 -0
  58. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/symbolic.py +0 -0
  59. {gitpython-3.1.46 → gitpython-3.1.47}/git/refs/tag.py +0 -0
  60. {gitpython-3.1.46 → gitpython-3.1.47}/git/remote.py +0 -0
  61. {gitpython-3.1.46 → gitpython-3.1.47}/git/repo/__init__.py +0 -0
  62. {gitpython-3.1.46 → gitpython-3.1.47}/git/repo/fun.py +0 -0
  63. {gitpython-3.1.46 → gitpython-3.1.47}/git/types.py +0 -0
  64. {gitpython-3.1.46 → gitpython-3.1.47}/git/util.py +0 -0
  65. {gitpython-3.1.46 → gitpython-3.1.47}/pyproject.toml +0 -0
  66. {gitpython-3.1.46 → gitpython-3.1.47}/requirements.txt +0 -0
  67. {gitpython-3.1.46 → gitpython-3.1.47}/setup.cfg +0 -0
  68. {gitpython-3.1.46 → gitpython-3.1.47}/setup.py +0 -0
  69. {gitpython-3.1.46 → gitpython-3.1.47}/test-requirements.txt +0 -0
@@ -56,5 +56,6 @@ Contributors are:
56
56
  -Ethan Lin <et.repositories _at_ gmail.com>
57
57
  -Jonas Scharpf <jonas.scharpf _at_ checkmk.com>
58
58
  -Gordon Marx
59
+ -Enji Cooper
59
60
 
60
61
  Portions derived from other open source works and are clearly marked.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.46
3
+ Version: 3.1.47
4
4
  Summary: GitPython is a Python library used to interact with Git repositories
5
5
  Home-page: https://github.com/gitpython-developers/GitPython
6
6
  Author: Sebastian Thiel, Michael Trier
@@ -41,7 +41,7 @@ Requires-Dist: pytest-mock; extra == "test"
41
41
  Requires-Dist: pytest-sugar; extra == "test"
42
42
  Requires-Dist: typing-extensions; python_version < "3.11" and extra == "test"
43
43
  Provides-Extra: doc
44
- Requires-Dist: sphinx<7.2,>=7.1.2; extra == "doc"
44
+ Requires-Dist: sphinx<8,>=7.4.7; extra == "doc"
45
45
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
46
46
  Requires-Dist: sphinx-autodoc-typehints; extra == "doc"
47
47
  Dynamic: author
@@ -105,7 +105,7 @@ by setting the `GIT_PYTHON_GIT_EXECUTABLE=<path/to/git>` environment variable.
105
105
  - Git (1.7.x or newer)
106
106
  - Python >= 3.7
107
107
 
108
- The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`.
108
+ The list of dependencies are listed in [`./requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/requirements.txt) and [`./test-requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/test-requirements.txt).
109
109
  The installer takes care of installing them for you.
110
110
 
111
111
  ### INSTALL
@@ -239,7 +239,7 @@ Style and formatting checks, and running tests on all the different supported Py
239
239
 
240
240
  #### Configuration files
241
241
 
242
- Specific tools are all configured in the `./pyproject.toml` file:
242
+ Specific tools are all configured in the [`./pyproject.toml`](https://github.com/gitpython-developers/GitPython/blob/main/pyproject.toml) file:
243
243
 
244
244
  - `pytest` (test runner)
245
245
  - `coverage.py` (code coverage)
@@ -248,9 +248,9 @@ Specific tools are all configured in the `./pyproject.toml` file:
248
248
 
249
249
  Orchestration tools:
250
250
 
251
- - Configuration for `pre-commit` is in the `./.pre-commit-config.yaml` file.
252
- - Configuration for `tox` is in `./tox.ini`.
253
- - Configuration for GitHub Actions (CI) is in files inside `./.github/workflows/`.
251
+ - Configuration for `pre-commit` is in the [`./.pre-commit-config.yaml`](https://github.com/gitpython-developers/GitPython/blob/main/.pre-commit-config.yaml) file.
252
+ - Configuration for `tox` is in [`./tox.ini`](https://github.com/gitpython-developers/GitPython/blob/main/tox.ini).
253
+ - Configuration for GitHub Actions (CI) is in files inside [`./.github/workflows/`](https://github.com/gitpython-developers/GitPython/tree/main/.github/workflows).
254
254
 
255
255
  ### Contributions
256
256
 
@@ -271,8 +271,8 @@ Please have a look at the [contributions file][contributing].
271
271
 
272
272
  ### How to make a new release
273
273
 
274
- 1. Update/verify the **version** in the `VERSION` file.
275
- 2. Update/verify that the `doc/source/changes.rst` changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
274
+ 1. Update/verify the **version** in the [`VERSION`](https://github.com/gitpython-developers/GitPython/blob/main/VERSION) file.
275
+ 2. Update/verify that the [`doc/source/changes.rst`](https://github.com/gitpython-developers/GitPython/blob/main/doc/source/changes.rst) changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
276
276
  3. Commit everything.
277
277
  4. Run `git tag -s <version>` to tag the version in Git.
278
278
  5. _Optionally_ create and activate a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment). (Then the next step can install `build` and `twine`.)
@@ -299,7 +299,7 @@ Please have a look at the [contributions file][contributing].
299
299
 
300
300
  [3-Clause BSD License](https://opensource.org/license/bsd-3-clause/), also known as the New BSD License. See the [LICENSE file][license].
301
301
 
302
- One file exclusively used for fuzz testing is subject to [a separate license, detailed here](./fuzzing/README.md#license).
302
+ One file exclusively used for fuzz testing is subject to [a separate license, detailed here](https://github.com/gitpython-developers/GitPython/blob/main/fuzzing/README.md#license).
303
303
  This file is not included in the wheel or sdist packages published by the maintainers of GitPython.
304
304
 
305
305
  [contributing]: https://github.com/gitpython-developers/GitPython/blob/main/CONTRIBUTING.md
@@ -4,7 +4,7 @@ gitdb<5,>=4.0.1
4
4
  typing-extensions>=3.10.0.2
5
5
 
6
6
  [doc]
7
- sphinx<7.2,>=7.1.2
7
+ sphinx<8,>=7.4.7
8
8
  sphinx_rtd_theme
9
9
  sphinx-autodoc-typehints
10
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.46
3
+ Version: 3.1.47
4
4
  Summary: GitPython is a Python library used to interact with Git repositories
5
5
  Home-page: https://github.com/gitpython-developers/GitPython
6
6
  Author: Sebastian Thiel, Michael Trier
@@ -41,7 +41,7 @@ Requires-Dist: pytest-mock; extra == "test"
41
41
  Requires-Dist: pytest-sugar; extra == "test"
42
42
  Requires-Dist: typing-extensions; python_version < "3.11" and extra == "test"
43
43
  Provides-Extra: doc
44
- Requires-Dist: sphinx<7.2,>=7.1.2; extra == "doc"
44
+ Requires-Dist: sphinx<8,>=7.4.7; extra == "doc"
45
45
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
46
46
  Requires-Dist: sphinx-autodoc-typehints; extra == "doc"
47
47
  Dynamic: author
@@ -105,7 +105,7 @@ by setting the `GIT_PYTHON_GIT_EXECUTABLE=<path/to/git>` environment variable.
105
105
  - Git (1.7.x or newer)
106
106
  - Python >= 3.7
107
107
 
108
- The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`.
108
+ The list of dependencies are listed in [`./requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/requirements.txt) and [`./test-requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/test-requirements.txt).
109
109
  The installer takes care of installing them for you.
110
110
 
111
111
  ### INSTALL
@@ -239,7 +239,7 @@ Style and formatting checks, and running tests on all the different supported Py
239
239
 
240
240
  #### Configuration files
241
241
 
242
- Specific tools are all configured in the `./pyproject.toml` file:
242
+ Specific tools are all configured in the [`./pyproject.toml`](https://github.com/gitpython-developers/GitPython/blob/main/pyproject.toml) file:
243
243
 
244
244
  - `pytest` (test runner)
245
245
  - `coverage.py` (code coverage)
@@ -248,9 +248,9 @@ Specific tools are all configured in the `./pyproject.toml` file:
248
248
 
249
249
  Orchestration tools:
250
250
 
251
- - Configuration for `pre-commit` is in the `./.pre-commit-config.yaml` file.
252
- - Configuration for `tox` is in `./tox.ini`.
253
- - Configuration for GitHub Actions (CI) is in files inside `./.github/workflows/`.
251
+ - Configuration for `pre-commit` is in the [`./.pre-commit-config.yaml`](https://github.com/gitpython-developers/GitPython/blob/main/.pre-commit-config.yaml) file.
252
+ - Configuration for `tox` is in [`./tox.ini`](https://github.com/gitpython-developers/GitPython/blob/main/tox.ini).
253
+ - Configuration for GitHub Actions (CI) is in files inside [`./.github/workflows/`](https://github.com/gitpython-developers/GitPython/tree/main/.github/workflows).
254
254
 
255
255
  ### Contributions
256
256
 
@@ -271,8 +271,8 @@ Please have a look at the [contributions file][contributing].
271
271
 
272
272
  ### How to make a new release
273
273
 
274
- 1. Update/verify the **version** in the `VERSION` file.
275
- 2. Update/verify that the `doc/source/changes.rst` changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
274
+ 1. Update/verify the **version** in the [`VERSION`](https://github.com/gitpython-developers/GitPython/blob/main/VERSION) file.
275
+ 2. Update/verify that the [`doc/source/changes.rst`](https://github.com/gitpython-developers/GitPython/blob/main/doc/source/changes.rst) changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
276
276
  3. Commit everything.
277
277
  4. Run `git tag -s <version>` to tag the version in Git.
278
278
  5. _Optionally_ create and activate a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment). (Then the next step can install `build` and `twine`.)
@@ -299,7 +299,7 @@ Please have a look at the [contributions file][contributing].
299
299
 
300
300
  [3-Clause BSD License](https://opensource.org/license/bsd-3-clause/), also known as the New BSD License. See the [LICENSE file][license].
301
301
 
302
- One file exclusively used for fuzz testing is subject to [a separate license, detailed here](./fuzzing/README.md#license).
302
+ One file exclusively used for fuzz testing is subject to [a separate license, detailed here](https://github.com/gitpython-developers/GitPython/blob/main/fuzzing/README.md#license).
303
303
  This file is not included in the wheel or sdist packages published by the maintainers of GitPython.
304
304
 
305
305
  [contributing]: https://github.com/gitpython-developers/GitPython/blob/main/CONTRIBUTING.md
@@ -46,7 +46,7 @@ by setting the `GIT_PYTHON_GIT_EXECUTABLE=<path/to/git>` environment variable.
46
46
  - Git (1.7.x or newer)
47
47
  - Python >= 3.7
48
48
 
49
- The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`.
49
+ The list of dependencies are listed in [`./requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/requirements.txt) and [`./test-requirements.txt`](https://github.com/gitpython-developers/GitPython/blob/main/test-requirements.txt).
50
50
  The installer takes care of installing them for you.
51
51
 
52
52
  ### INSTALL
@@ -180,7 +180,7 @@ Style and formatting checks, and running tests on all the different supported Py
180
180
 
181
181
  #### Configuration files
182
182
 
183
- Specific tools are all configured in the `./pyproject.toml` file:
183
+ Specific tools are all configured in the [`./pyproject.toml`](https://github.com/gitpython-developers/GitPython/blob/main/pyproject.toml) file:
184
184
 
185
185
  - `pytest` (test runner)
186
186
  - `coverage.py` (code coverage)
@@ -189,9 +189,9 @@ Specific tools are all configured in the `./pyproject.toml` file:
189
189
 
190
190
  Orchestration tools:
191
191
 
192
- - Configuration for `pre-commit` is in the `./.pre-commit-config.yaml` file.
193
- - Configuration for `tox` is in `./tox.ini`.
194
- - Configuration for GitHub Actions (CI) is in files inside `./.github/workflows/`.
192
+ - Configuration for `pre-commit` is in the [`./.pre-commit-config.yaml`](https://github.com/gitpython-developers/GitPython/blob/main/.pre-commit-config.yaml) file.
193
+ - Configuration for `tox` is in [`./tox.ini`](https://github.com/gitpython-developers/GitPython/blob/main/tox.ini).
194
+ - Configuration for GitHub Actions (CI) is in files inside [`./.github/workflows/`](https://github.com/gitpython-developers/GitPython/tree/main/.github/workflows).
195
195
 
196
196
  ### Contributions
197
197
 
@@ -212,8 +212,8 @@ Please have a look at the [contributions file][contributing].
212
212
 
213
213
  ### How to make a new release
214
214
 
215
- 1. Update/verify the **version** in the `VERSION` file.
216
- 2. Update/verify that the `doc/source/changes.rst` changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
215
+ 1. Update/verify the **version** in the [`VERSION`](https://github.com/gitpython-developers/GitPython/blob/main/VERSION) file.
216
+ 2. Update/verify that the [`doc/source/changes.rst`](https://github.com/gitpython-developers/GitPython/blob/main/doc/source/changes.rst) changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/<version>`
217
217
  3. Commit everything.
218
218
  4. Run `git tag -s <version>` to tag the version in Git.
219
219
  5. _Optionally_ create and activate a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment). (Then the next step can install `build` and `twine`.)
@@ -240,7 +240,7 @@ Please have a look at the [contributions file][contributing].
240
240
 
241
241
  [3-Clause BSD License](https://opensource.org/license/bsd-3-clause/), also known as the New BSD License. See the [LICENSE file][license].
242
242
 
243
- One file exclusively used for fuzz testing is subject to [a separate license, detailed here](./fuzzing/README.md#license).
243
+ One file exclusively used for fuzz testing is subject to [a separate license, detailed here](https://github.com/gitpython-developers/GitPython/blob/main/fuzzing/README.md#license).
244
244
  This file is not included in the wheel or sdist packages published by the maintainers of GitPython.
245
245
 
246
246
  [contributing]: https://github.com/gitpython-developers/GitPython/blob/main/CONTRIBUTING.md
@@ -0,0 +1 @@
1
+ 3.1.47
@@ -1,3 +1,3 @@
1
- sphinx >= 7.1.2, < 7.2
1
+ sphinx >= 7.4.7, < 8
2
2
  sphinx_rtd_theme
3
3
  sphinx-autodoc-typehints
@@ -2,6 +2,15 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ 3.1.47
6
+ ======
7
+
8
+ Address various security issues related to bypassing injection-protection
9
+ of unsafe Git flags.
10
+
11
+ See the following for all changes.
12
+ https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47
13
+
5
14
  3.1.46
6
15
  ======
7
16
 
@@ -513,6 +513,12 @@ The GitDB is a pure-python implementation of the git object database. It is the
513
513
 
514
514
  repo = Repo("path/to/repo", odbt=GitDB)
515
515
 
516
+ .. warning::
517
+ ``GitDB`` may fail or become extremely slow when traversing trees in
518
+ repositories with very large commits (thousands of changed files in a
519
+ single commit). If you encounter ``RecursionError`` or excessive
520
+ slowness during tree traversal, switch to ``GitCmdObjectDB`` instead.
521
+
516
522
 
517
523
  GitCmdObjectDB
518
524
  ==============
@@ -86,7 +86,7 @@ __all__ = [
86
86
  "to_hex_sha",
87
87
  ]
88
88
 
89
- __version__ = '3.1.46'
89
+ __version__ = '3.1.47'
90
90
 
91
91
  from typing import Any, List, Optional, Sequence, TYPE_CHECKING, Tuple, Union
92
92
 
@@ -368,8 +368,12 @@ class _AutoInterrupt:
368
368
  status = proc.wait() # Ensure the process goes away.
369
369
 
370
370
  self.status = self._status_code_if_terminate or status
371
- except OSError as ex:
372
- _logger.info("Ignored error after process had died: %r", ex)
371
+ except (OSError, AttributeError) as ex:
372
+ # On interpreter shutdown (notably on Windows), parts of the stdlib used by
373
+ # subprocess can already be torn down (e.g. `subprocess._winapi` becomes None),
374
+ # which can cause AttributeError during terminate(). In that case, we prefer
375
+ # to silently ignore to avoid noisy "Exception ignored in: __del__" messages.
376
+ _logger.info("Ignored error while terminating process: %r", ex)
373
377
  # END exception handling
374
378
 
375
379
  def __del__(self) -> None:
@@ -940,6 +944,21 @@ class Git(metaclass=_GitMeta):
940
944
  f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it."
941
945
  )
942
946
 
947
+ @classmethod
948
+ def _canonicalize_option_name(cls, option: str) -> str:
949
+ """Return the option name used for unsafe-option checks.
950
+
951
+ Examples:
952
+ ``"--upload-pack=/tmp/helper"`` -> ``"upload-pack"``
953
+ ``"upload_pack"`` -> ``"upload-pack"``
954
+ ``"--config core.filemode=false"`` -> ``"config"``
955
+ """
956
+ option_name = option.lstrip("-").split("=", 1)[0]
957
+ option_tokens = option_name.split(None, 1)
958
+ if not option_tokens:
959
+ return ""
960
+ return dashify(option_tokens[0])
961
+
943
962
  @classmethod
944
963
  def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None:
945
964
  """Check for unsafe options.
@@ -947,15 +966,12 @@ class Git(metaclass=_GitMeta):
947
966
  Some options that are passed to ``git <command>`` can be used to execute
948
967
  arbitrary commands. These are blocked by default.
949
968
  """
950
- # Options can be of the form `foo`, `--foo bar`, or `--foo=bar`, so we need to
951
- # check if they start with "--foo" or if they are equal to "foo".
952
- bare_unsafe_options = [option.lstrip("-") for option in unsafe_options]
969
+ # Options can be of the form `foo`, `--foo`, `--foo bar`, or `--foo=bar`.
970
+ canonical_unsafe_options = {cls._canonicalize_option_name(option): option for option in unsafe_options}
953
971
  for option in options:
954
- for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options):
955
- if option.startswith(unsafe_option) or option == bare_option:
956
- raise UnsafeOptionError(
957
- f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it."
958
- )
972
+ unsafe_option = canonical_unsafe_options.get(cls._canonicalize_option_name(option))
973
+ if unsafe_option is not None:
974
+ raise UnsafeOptionError(f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it.")
959
975
 
960
976
  AutoInterrupt: TypeAlias = _AutoInterrupt
961
977
 
@@ -1360,25 +1376,29 @@ class Git(metaclass=_GitMeta):
1360
1376
  if output_stream is None:
1361
1377
  stdout_value, stderr_value = communicate()
1362
1378
  # Strip trailing "\n".
1363
- if stdout_value.endswith(newline) and strip_newline_in_stdout: # type: ignore[arg-type]
1379
+ if stdout_value is not None and stdout_value.endswith(newline) and strip_newline_in_stdout: # type: ignore[arg-type]
1364
1380
  stdout_value = stdout_value[:-1]
1365
- if stderr_value.endswith(newline): # type: ignore[arg-type]
1381
+ if stderr_value is not None and stderr_value.endswith(newline): # type: ignore[arg-type]
1366
1382
  stderr_value = stderr_value[:-1]
1367
1383
 
1368
1384
  status = proc.returncode
1369
1385
  else:
1370
1386
  max_chunk_size = max_chunk_size if max_chunk_size and max_chunk_size > 0 else io.DEFAULT_BUFFER_SIZE
1371
- stream_copy(proc.stdout, output_stream, max_chunk_size)
1372
- stdout_value = proc.stdout.read()
1373
- stderr_value = proc.stderr.read()
1387
+ if proc.stdout is not None:
1388
+ stream_copy(proc.stdout, output_stream, max_chunk_size)
1389
+ stdout_value = proc.stdout.read()
1390
+ if proc.stderr is not None:
1391
+ stderr_value = proc.stderr.read()
1374
1392
  # Strip trailing "\n".
1375
- if stderr_value.endswith(newline): # type: ignore[arg-type]
1393
+ if stderr_value is not None and stderr_value.endswith(newline): # type: ignore[arg-type]
1376
1394
  stderr_value = stderr_value[:-1]
1377
1395
  status = proc.wait()
1378
1396
  # END stdout handling
1379
1397
  finally:
1380
- proc.stdout.close()
1381
- proc.stderr.close()
1398
+ if proc.stdout is not None:
1399
+ proc.stdout.close()
1400
+ if proc.stderr is not None:
1401
+ proc.stderr.close()
1382
1402
 
1383
1403
  if self.GIT_PYTHON_TRACE == "full":
1384
1404
  cmdstr = " ".join(redacted_command)
@@ -1568,7 +1588,7 @@ class Git(metaclass=_GitMeta):
1568
1588
 
1569
1589
  turns into::
1570
1590
 
1571
- git rev-list max-count 10 --header master
1591
+ git rev-list --max-count=10 --header=master
1572
1592
 
1573
1593
  :return:
1574
1594
  Same as :meth:`execute`. If no args are given, used :meth:`execute`'s
@@ -549,11 +549,21 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
549
549
  :return:
550
550
  The list of paths, where each path is a tuple of (option, value).
551
551
  """
552
+
553
+ def _all_items(section: str) -> List[Tuple[str, str]]:
554
+ """Return all (key, value) pairs for a section, including duplicate keys."""
555
+ return [
556
+ (key, value)
557
+ for key, values in self._sections[section].items_all()
558
+ if key != "__name__"
559
+ for value in values
560
+ ]
561
+
552
562
  paths = []
553
563
 
554
564
  for section in self.sections():
555
565
  if section == "include":
556
- paths += self.items(section)
566
+ paths += _all_items(section)
557
567
 
558
568
  match = CONDITIONAL_INCLUDE_REGEXP.search(section)
559
569
  if match is None or self._repo is None:
@@ -579,7 +589,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
579
589
  )
580
590
  if self._repo.git_dir:
581
591
  if fnmatch.fnmatchcase(os.fspath(self._repo.git_dir), value):
582
- paths += self.items(section)
592
+ paths += _all_items(section)
583
593
 
584
594
  elif keyword == "onbranch":
585
595
  try:
@@ -589,11 +599,11 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
589
599
  continue
590
600
 
591
601
  if fnmatch.fnmatchcase(branch_name, value):
592
- paths += self.items(section)
602
+ paths += _all_items(section)
593
603
  elif keyword == "hasconfig:remote.*.url":
594
604
  for remote in self._repo.remotes:
595
605
  if fnmatch.fnmatchcase(remote.url, value):
596
- paths += self.items(section)
606
+ paths += _all_items(section)
597
607
  break
598
608
  return paths
599
609
 
@@ -1133,6 +1133,7 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
1133
1133
  author_date: Union[datetime.datetime, str, None] = None,
1134
1134
  commit_date: Union[datetime.datetime, str, None] = None,
1135
1135
  skip_hooks: bool = False,
1136
+ trailers: Union[None, "Dict[str, str]", "List[Tuple[str, str]]"] = None,
1136
1137
  ) -> Commit:
1137
1138
  """Commit the current default index file, creating a
1138
1139
  :class:`~git.objects.commit.Commit` object.
@@ -1169,6 +1170,7 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable):
1169
1170
  committer=committer,
1170
1171
  author_date=author_date,
1171
1172
  commit_date=commit_date,
1173
+ trailers=trailers,
1172
1174
  )
1173
1175
  if not skip_hooks:
1174
1176
  run_commit_hook("post-commit", self)
@@ -450,14 +450,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
450
450
  :return:
451
451
  List containing key-value tuples of whitespace stripped trailer information.
452
452
  """
453
- cmd = ["git", "interpret-trailers", "--parse"]
454
- proc: Git.AutoInterrupt = self.repo.git.execute( # type: ignore[call-overload]
455
- cmd,
456
- as_process=True,
457
- istream=PIPE,
458
- )
459
- trailer: str = proc.communicate(str(self.message).encode())[0].decode("utf8")
460
- trailer = trailer.strip()
453
+ trailer = self._interpret_trailers(self.repo, self.message, ["--parse"], encoding=self.encoding).strip()
461
454
 
462
455
  if not trailer:
463
456
  return []
@@ -469,6 +462,27 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
469
462
 
470
463
  return trailer_list
471
464
 
465
+ @classmethod
466
+ def _interpret_trailers(
467
+ cls,
468
+ repo: "Repo",
469
+ message: Union[str, bytes],
470
+ trailer_args: Sequence[str],
471
+ encoding: str = default_encoding,
472
+ ) -> str:
473
+ message_bytes = message if isinstance(message, bytes) else message.encode(encoding, errors="strict")
474
+ cmd = [repo.git.GIT_PYTHON_GIT_EXECUTABLE, "interpret-trailers", *trailer_args]
475
+ proc: Git.AutoInterrupt = repo.git.execute( # type: ignore[call-overload]
476
+ cmd,
477
+ as_process=True,
478
+ istream=PIPE,
479
+ )
480
+ try:
481
+ stdout_bytes, _ = proc.communicate(message_bytes)
482
+ return stdout_bytes.decode(encoding, errors="strict")
483
+ finally:
484
+ finalize_process(proc)
485
+
472
486
  @property
473
487
  def trailers_dict(self) -> Dict[str, List[str]]:
474
488
  """Get the trailers of the message as a dictionary.
@@ -570,6 +584,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
570
584
  committer: Union[None, Actor] = None,
571
585
  author_date: Union[None, str, datetime.datetime] = None,
572
586
  commit_date: Union[None, str, datetime.datetime] = None,
587
+ trailers: Union[None, Dict[str, str], List[Tuple[str, str]]] = None,
573
588
  ) -> "Commit":
574
589
  """Commit the given tree, creating a :class:`Commit` object.
575
590
 
@@ -609,6 +624,14 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
609
624
  :param commit_date:
610
625
  The timestamp for the committer field.
611
626
 
627
+ :param trailers:
628
+ Optional trailer key-value pairs to append to the commit message.
629
+ Can be a dictionary mapping trailer keys to values, or a list of
630
+ ``(key, value)`` tuples (useful when the same key appears multiple
631
+ times, e.g. multiple ``Signed-off-by`` trailers). Trailers are
632
+ appended using ``git interpret-trailers``.
633
+ See :manpage:`git-interpret-trailers(1)`.
634
+
612
635
  :return:
613
636
  :class:`Commit` object representing the new commit.
614
637
 
@@ -678,6 +701,21 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable):
678
701
  tree = repo.tree(tree)
679
702
  # END tree conversion
680
703
 
704
+ # APPLY TRAILERS
705
+ if trailers:
706
+ trailer_args: List[str] = []
707
+ if isinstance(trailers, dict):
708
+ for key, val in trailers.items():
709
+ trailer_args.append("--trailer")
710
+ trailer_args.append(f"{key}: {val}")
711
+ else:
712
+ for key, val in trailers:
713
+ trailer_args.append("--trailer")
714
+ trailer_args.append(f"{key}: {val}")
715
+
716
+ message = cls._interpret_trailers(repo, str(message), trailer_args)
717
+ # END apply trailers
718
+
681
719
  # CREATE NEW COMMIT
682
720
  new_commit = cls(
683
721
  repo,
@@ -1042,11 +1042,19 @@ class Repo:
1042
1042
  :raise TypeError:
1043
1043
  If HEAD is detached.
1044
1044
 
1045
+ :raise ValueError:
1046
+ If HEAD points to the ``.invalid`` ref Git uses to mark refs as
1047
+ incompatible with older clients.
1048
+
1045
1049
  :return:
1046
1050
  :class:`~git.refs.head.Head` to the active branch
1047
1051
  """
1048
- # reveal_type(self.head.reference) # => Reference
1049
- return self.head.reference
1052
+ active_branch = self.head.reference
1053
+ if active_branch.name == ".invalid":
1054
+ raise ValueError(
1055
+ "HEAD points to 'refs/heads/.invalid', which Git uses to mark refs as incompatible with older clients"
1056
+ )
1057
+ return active_branch
1050
1058
 
1051
1059
  def blame_incremental(self, rev: str | HEAD | None, file: str, **kwargs: Any) -> Iterator["BlameEntry"]:
1052
1060
  """Iterator for blame information for the given file at the given revision.
@@ -1378,8 +1386,8 @@ class Repo:
1378
1386
  Git.check_unsafe_protocols(url)
1379
1387
  if not allow_unsafe_options:
1380
1388
  Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=cls.unsafe_git_clone_options)
1381
- if not allow_unsafe_options and multi_options:
1382
- Git.check_unsafe_options(options=multi_options, unsafe_options=cls.unsafe_git_clone_options)
1389
+ if not allow_unsafe_options and multi:
1390
+ Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)
1383
1391
 
1384
1392
  proc = git.clone(
1385
1393
  multi,
gitpython-3.1.46/VERSION DELETED
@@ -1 +0,0 @@
1
- 3.1.46
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