GitPython 3.1.50__py3-none-any.whl → 3.1.51__py3-none-any.whl

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.
git/__init__.py CHANGED
@@ -86,7 +86,7 @@ __all__ = [
86
86
  "to_hex_sha",
87
87
  ]
88
88
 
89
- __version__ = '3.1.50'
89
+ __version__ = '3.1.51'
90
90
 
91
91
  from typing import Any, List, Optional, Sequence, TYPE_CHECKING, Tuple, Union
92
92
 
git/cmd.py CHANGED
@@ -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
- """Check for unsafe options.
965
-
966
- Some options that are passed to ``git <command>`` can be used to execute
967
- arbitrary commands. These are blocked by default.
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
- unsafe_option = canonical_unsafe_options.get(cls._canonicalize_option_name(option))
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 argument list to execute.
1135
- It should be a sequence of program arguments, or a string. The
1136
- program to execute is the first item in the args sequence or string.
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[str]) -> List[str]:
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=master
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
git/config.py CHANGED
@@ -634,6 +634,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
634
634
  files_to_read = list(self._file_or_files)
635
635
  # END ensure we have a copy of the paths to handle
636
636
 
637
+ files_to_read = [osp.abspath(path) if isinstance(path, (str, os.PathLike)) else path for path in files_to_read]
638
+
637
639
  seen = set(files_to_read)
638
640
  num_read_include_files = 0
639
641
  while files_to_read:
git/diff.py CHANGED
@@ -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
- handle_process_output(proc, text_list.append, None, finalize_process, decode_streams=False)
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
- None,
772
- finalize_process,
786
+ stderr_list.append,
787
+ finalize_process_with_stderr,
773
788
  decode_streams=False,
774
789
  )
775
790
 
git/index/base.py CHANGED
@@ -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[ # type: ignore[override]
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.
git/objects/commit.py CHANGED
@@ -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
  """
git/remote.py CHANGED
@@ -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: Union[Callable[..., Any], RemoteProgress, None],
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: Union[RemoteProgress, None, "UpdateProgress"] = None,
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(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_fetch_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: Union[RemoteProgress, "UpdateProgress", None] = None,
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(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_pull_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: Union[RemoteProgress, "UpdateProgress", Callable[..., RemoteProgress], None] = None,
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(options=list(kwargs.keys()), unsafe_options=self.unsafe_git_push_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
  "--",
git/repo/base.py CHANGED
@@ -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."""
@@ -295,7 +309,8 @@ class Repo:
295
309
  sm_gitpath = find_worktree_git_dir(dotgit)
296
310
 
297
311
  if sm_gitpath is not None:
298
- git_dir = expand_path(sm_gitpath, expand_vars)
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))
299
314
  self._working_tree_dir = curpath
300
315
  break
301
316
 
@@ -774,6 +789,7 @@ class Repo:
774
789
  self,
775
790
  rev: Union[str, Commit, "SymbolicReference", None] = None,
776
791
  paths: Union[PathLike, Sequence[PathLike]] = "",
792
+ allow_unsafe_options: bool = False,
777
793
  **kwargs: Any,
778
794
  ) -> Iterator[Commit]:
779
795
  """An iterator of :class:`~git.objects.commit.Commit` objects representing the
@@ -791,6 +807,9 @@ class Repo:
791
807
  Arguments to be passed to :manpage:`git-rev-list(1)`.
792
808
  Common ones are ``max_count`` and ``skip``.
793
809
 
810
+ :param allow_unsafe_options:
811
+ Allow unsafe options in the revision argument, like ``--output``.
812
+
794
813
  :note:
795
814
  To receive only commits between two named revisions, use the
796
815
  ``"revA...revB"`` revision specifier.
@@ -801,7 +820,18 @@ class Repo:
801
820
  if rev is None:
802
821
  rev = self.head.commit
803
822
 
804
- return Commit.iter_items(self, rev, paths, **kwargs)
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
+ )
805
835
 
806
836
  def merge_base(self, *rev: TBD, **kwargs: Any) -> List[Commit]:
807
837
  R"""Find the closest common ancestor for the given revision
@@ -1078,7 +1108,9 @@ class Repo:
1078
1108
  )
1079
1109
  return active_branch
1080
1110
 
1081
- def blame_incremental(self, rev: str | HEAD | None, file: str, **kwargs: Any) -> Iterator["BlameEntry"]:
1111
+ def blame_incremental(
1112
+ self, rev: str | HEAD | None, file: str, allow_unsafe_options: bool = False, **kwargs: Any
1113
+ ) -> Iterator["BlameEntry"]:
1082
1114
  """Iterator for blame information for the given file at the given revision.
1083
1115
 
1084
1116
  Unlike :meth:`blame`, this does not return the actual file's contents, only a
@@ -1089,6 +1121,9 @@ class Repo:
1089
1121
  uncommitted changes. Otherwise, anything successfully parsed by
1090
1122
  :manpage:`git-rev-parse(1)` is a valid option.
1091
1123
 
1124
+ :param allow_unsafe_options:
1125
+ Allow unsafe options in revision argument, like ``--output``.
1126
+
1092
1127
  :return:
1093
1128
  Lazy iterator of :class:`BlameEntry` tuples, where the commit indicates the
1094
1129
  commit to blame for the line, and range indicates a span of line numbers in
@@ -1097,6 +1132,10 @@ class Repo:
1097
1132
  If you combine all line number ranges outputted by this command, you should get
1098
1133
  a continuous range spanning all line numbers in the file.
1099
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
+ )
1100
1139
 
1101
1140
  data: bytes = self.git.blame(rev, "--", file, p=True, incremental=True, stdout_as_string=False, **kwargs)
1102
1141
  commits: Dict[bytes, Commit] = {}
@@ -1175,7 +1214,8 @@ class Repo:
1175
1214
  rev: Union[str, HEAD, None],
1176
1215
  file: str,
1177
1216
  incremental: bool = False,
1178
- rev_opts: Optional[List[str]] = None,
1217
+ rev_opts: Optional[Sequence[str]] = None,
1218
+ allow_unsafe_options: bool = False,
1179
1219
  **kwargs: Any,
1180
1220
  ) -> List[List[Commit | List[str | bytes] | None]] | Iterator[BlameEntry] | None:
1181
1221
  """The blame information for the given file at the given revision.
@@ -1185,6 +1225,9 @@ class Repo:
1185
1225
  uncommitted changes. Otherwise, anything successfully parsed by
1186
1226
  :manpage:`git-rev-parse(1)` is a valid option.
1187
1227
 
1228
+ :param allow_unsafe_options:
1229
+ Allow unsafe options in revision argument, like ``--output``.
1230
+
1188
1231
  :return:
1189
1232
  list: [git.Commit, list: [<line>]]
1190
1233
 
@@ -1194,9 +1237,14 @@ class Repo:
1194
1237
  appearance.
1195
1238
  """
1196
1239
  if incremental:
1197
- return self.blame_incremental(rev, file, **kwargs)
1198
- rev_opts = rev_opts or []
1199
- data: bytes = self.git.blame(rev, *rev_opts, "--", file, p=True, stdout_as_string=False, **kwargs)
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)
1200
1248
  commits: Dict[str, Commit] = {}
1201
1249
  blames: List[List[Commit | List[str | bytes] | None]] = []
1202
1250
 
@@ -1407,7 +1455,10 @@ class Repo:
1407
1455
  if not allow_unsafe_protocols:
1408
1456
  Git.check_unsafe_protocols(url)
1409
1457
  if not allow_unsafe_options:
1410
- Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_options=cls.unsafe_git_clone_options)
1458
+ Git.check_unsafe_options(
1459
+ options=Git._option_candidates([], kwargs),
1460
+ unsafe_options=cls.unsafe_git_clone_options,
1461
+ )
1411
1462
  if not allow_unsafe_options and multi:
1412
1463
  Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)
1413
1464
 
@@ -1582,6 +1633,8 @@ class Repo:
1582
1633
  ostream: Union[TextIO, BinaryIO],
1583
1634
  treeish: Optional[str] = None,
1584
1635
  prefix: Optional[str] = None,
1636
+ allow_unsafe_options: bool = False,
1637
+ allow_unsafe_protocols: bool = False,
1585
1638
  **kwargs: Any,
1586
1639
  ) -> Repo:
1587
1640
  """Archive the tree at the given revision.
@@ -1604,6 +1657,12 @@ class Repo:
1604
1657
  repository-relative path to a directory or file to place into the archive,
1605
1658
  or a list or tuple of multiple paths.
1606
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
+
1607
1666
  :raise git.exc.GitCommandError:
1608
1667
  If something went wrong.
1609
1668
 
@@ -1614,6 +1673,14 @@ class Repo:
1614
1673
  treeish = self.head.commit
1615
1674
  if prefix and "prefix" not in kwargs:
1616
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
+ )
1617
1684
  kwargs["output_stream"] = ostream
1618
1685
  path = kwargs.pop("path", [])
1619
1686
  path = cast(Union[PathLike, List[PathLike], Tuple[PathLike, ...]], path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.50
3
+ Version: 3.1.51
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
@@ -1,23 +1,23 @@
1
- git/__init__.py,sha256=-XcbLWMd0hzWrSVPHoz9wDkN1bWkUznLYN6x5oCYGdQ,8899
2
- git/cmd.py,sha256=4Ly0NF9kt1JarTD0zpPv7a0Al5I2PaohRMIG0UGKLCw,68559
1
+ git/__init__.py,sha256=r3oBfjVUel4N6O4MuchUmPkCQzneGtQfvRrMetWXtWU,8899
2
+ git/cmd.py,sha256=o5Mg1gNKFEPwa5DA0EiS0J4PCvSMZth2d5irYK1yPRw,74902
3
3
  git/compat.py,sha256=y1E6y6O2q5r8clSlr8ZNmuIWG9nmHuehQEsVsmBffs8,4526
4
- git/config.py,sha256=tPAXhoQozL0Mr7x4gtoUlFGUkkT8LtO3Ocqsqz-7ZrU,37412
4
+ git/config.py,sha256=VWk1i09HN_nrghQc2iTm4yLpAkPwppHnEBU74XqZl1o,37533
5
5
  git/db.py,sha256=vIW9uWSbqu99zbuU2ZDmOhVOv1UPTmxrnqiCtRHCfjE,2368
6
- git/diff.py,sha256=xhOeMmFDmXOdpje2vA1mzJpmpBnyX-nrt-mp2RiVR1M,27172
6
+ git/diff.py,sha256=qkL8g0m7UoIB5TcY7X23DgEMAmBJ0ZpEVFdHR1pDKJU,27767
7
7
  git/exc.py,sha256=Gc7g1pHpn8OmTse30NHmJVsBJ2CYH8LxaR8y8UA3lIM,7119
8
8
  git/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- git/remote.py,sha256=pYn9dAlz-QwvNMWXD1M57pMPQitthOM86qTRK_cpTqU,46786
9
+ git/remote.py,sha256=rCbrMMjILsX08TGYai8wdxzHCHc4ba6vPt6tP9efOog,46881
10
10
  git/types.py,sha256=d3oDduUGjfoqffcUL_GOD-3qroF8m3r9ai6Xi9_FraM,10279
11
11
  git/util.py,sha256=cCbGpBpnQq1MtW_REe8ApsAGTzp9JgVCMUN8KFgnx14,44052
12
12
  git/index/__init__.py,sha256=i-Nqb8Lufp9aFbmxpQBORmmQnjEVVM1Pn58fsQkyGgQ,406
13
- git/index/base.py,sha256=PdOmwjSjVXdYJF31y1853QdYTV8qUVAgAK0YZw3eyEI,61233
13
+ git/index/base.py,sha256=6U-gbvhVa2YmW1v-JeT1TOW6wJHi1li5g9wume-iCmw,62260
14
14
  git/index/fun.py,sha256=v0WnKQxaAffMZxXXC7pQOiMvRRJfODhUNKDMruxKrN4,17110
15
15
  git/index/typ.py,sha256=ZjRWp5J79OwFQor1XXN4C6JmHTDclD9H-tKZtAeqyTI,7019
16
16
  git/index/util.py,sha256=f0BfE2JPkdypsB2Q_HJ8KU7MC45RRHywL_GWwxa_5hw,3676
17
17
  git/objects/__init__.py,sha256=O6ZL_olX7e5-8iIbKviRPkVSJxN37WA-EC0q9d48U5Y,637
18
18
  git/objects/base.py,sha256=5-p9uSGWvPxX9hidvkGH9tQOJoH2eaRggUbMr9VJiL0,10285
19
19
  git/objects/blob.py,sha256=G9PZMNc5CFRQFNnlquBRAM5-jZg8581lRcxkvARobSk,1231
20
- git/objects/commit.py,sha256=uD--bk2e-fNcnOEEPTutEHjx170Pt0ytJfe5zWKrFWc,32191
20
+ git/objects/commit.py,sha256=5Pn-lDmFqzp2HxXGDv7Vr1pwwkC18IcZxmbRIkEaR7k,32816
21
21
  git/objects/fun.py,sha256=B4jCqhAjm6Hl79GK58FPzW1H9K6Wc7Tx0rssyWmAcEE,8935
22
22
  git/objects/tag.py,sha256=jAGESnpmTEv-dLakPzheT5ILZFFArcItnXYqfxfDrgc,4441
23
23
  git/objects/tree.py,sha256=MmExcS59Iamb2cBqUvoHPdzgixabyvv5voHabossrl8,13898
@@ -34,11 +34,11 @@ git/refs/remote.py,sha256=WTlkJtpu895jJxbknDNOwL31tivQY_AHQrvroUoFBX4,2902
34
34
  git/refs/symbolic.py,sha256=7ScmZHwlqPDKX9bxVC5queSZXUgqEY7_J_Si6gDBSLw,36139
35
35
  git/refs/tag.py,sha256=AKrFrMhkNzX8e9x5WmDRtHovbN9zYQghuKoneVW-TJg,5002
36
36
  git/repo/__init__.py,sha256=CILSVH36fX_WxVFSjD9o1WF5LgsNedPiJvSngKZqfVU,210
37
- git/repo/base.py,sha256=EohoIREtbdpmUqVs7kfUgBIosEzZ29nMgiO9YMuAYDU,61165
37
+ git/repo/base.py,sha256=yeW67EY983v8vAProm4kSmKcn_GoM8IycmYrV2RPLQg,63707
38
38
  git/repo/fun.py,sha256=PDFNkuTSGGoQc0EEEXjxHw2_KaKYvVjBZ9rrkwH5Q6Y,23462
39
- gitpython-3.1.50.dist-info/licenses/AUTHORS,sha256=uyjC8YZ5vcAlx2nSJ44J8gE7wjA8nc69_zWYo_HKtVc,2360
40
- gitpython-3.1.50.dist-info/licenses/LICENSE,sha256=hvyUwyGpr7wRUUcTURuv3tIl8lEA3MD3NQ6CvCMbi-s,1503
41
- gitpython-3.1.50.dist-info/METADATA,sha256=3LhbhjxZJPBAeX0855vAyV3Eod7P7L0GlQB4qw6mNzY,14187
42
- gitpython-3.1.50.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
43
- gitpython-3.1.50.dist-info/top_level.txt,sha256=0hzDuIp8obv624V3GmbqsagBWkk8ohtGU-Bc1PmTT0o,4
44
- gitpython-3.1.50.dist-info/RECORD,,
39
+ gitpython-3.1.51.dist-info/licenses/AUTHORS,sha256=Iucx_CH3w7_4084yjM6C5BvKfLA8cVfYHWW2pVmpxSk,2410
40
+ gitpython-3.1.51.dist-info/licenses/LICENSE,sha256=hvyUwyGpr7wRUUcTURuv3tIl8lEA3MD3NQ6CvCMbi-s,1503
41
+ gitpython-3.1.51.dist-info/METADATA,sha256=J90-vOl4F__jrBR1sBzcxCffU24yTuuW8E_YnByR2Bk,14187
42
+ gitpython-3.1.51.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
43
+ gitpython-3.1.51.dist-info/top_level.txt,sha256=0hzDuIp8obv624V3GmbqsagBWkk8ohtGU-Bc1PmTT0o,4
44
+ gitpython-3.1.51.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (83.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -57,5 +57,6 @@ Contributors are:
57
57
  -Jonas Scharpf <jonas.scharpf _at_ checkmk.com>
58
58
  -Gordon Marx
59
59
  -Enji Cooper
60
+ -Harshita Yadav <harshitayadav504 _at_ gmail.com>
60
61
 
61
62
  Portions derived from other open source works and are clearly marked.