GitPython 3.1.51__py3-none-any.whl → 3.1.53__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.51'
89
+ __version__ = '3.1.53'
90
90
 
91
91
  from typing import Any, List, Optional, Sequence, TYPE_CHECKING, Tuple, Union
92
92
 
git/cmd.py CHANGED
@@ -903,29 +903,34 @@ class Git(metaclass=_GitMeta):
903
903
 
904
904
  @overload
905
905
  @classmethod
906
- def polish_url(cls, url: str, is_cygwin: Literal[False] = ...) -> str: ...
906
+ def polish_url(cls, url: str, is_cygwin: Literal[False] = ..., expand_vars: bool = ...) -> str: ...
907
907
 
908
908
  @overload
909
909
  @classmethod
910
- def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> str: ...
910
+ def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None, expand_vars: bool = True) -> str: ...
911
911
 
912
912
  @classmethod
913
- def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> PathLike:
913
+ def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None, expand_vars: bool = True) -> PathLike:
914
914
  """Remove any backslashes from URLs to be written in config files.
915
915
 
916
916
  Windows might create config files containing paths with backslashes, but git
917
917
  stops liking them as it will escape the backslashes. Hence we undo the escaping
918
918
  just to be sure.
919
+
920
+ :param expand_vars:
921
+ Expand environment variables and an initial ``~``. Disable this for values
922
+ obtained from an untrusted source, such as remote URLs.
919
923
  """
920
924
  if is_cygwin is None:
921
925
  is_cygwin = cls.is_cygwin()
922
926
 
923
927
  if is_cygwin:
924
- url = cygpath(url)
928
+ url = cygpath(url, expand_vars=expand_vars)
925
929
  else:
926
- url = os.path.expandvars(url)
927
- if url.startswith("~"):
928
- url = os.path.expanduser(url)
930
+ if expand_vars:
931
+ url = os.path.expandvars(url)
932
+ if url.startswith("~"):
933
+ url = os.path.expanduser(url)
929
934
  url = url.replace("\\\\", "\\").replace("\\", "/")
930
935
  return url
931
936
 
@@ -1353,7 +1358,7 @@ class Git(metaclass=_GitMeta):
1353
1358
 
1354
1359
  # Allow the user to have the command executed in their working dir.
1355
1360
  try:
1356
- cwd = self._working_dir or os.getcwd() # type: Union[None, str]
1361
+ cwd = self._working_dir or os.getcwd() # type: Optional[PathLike]
1357
1362
  if not os.access(str(cwd), os.X_OK):
1358
1363
  cwd = None
1359
1364
  except FileNotFoundError:
git/config.py CHANGED
@@ -897,6 +897,22 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
897
897
  def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None:
898
898
  if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name):
899
899
  raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label)
900
+ if label == "section" and isinstance(name, str):
901
+ in_quotes = False
902
+ escaped = False
903
+ for index, char in enumerate(name):
904
+ if escaped:
905
+ escaped = False
906
+ elif in_quotes and char == "\\":
907
+ escaped = True
908
+ elif char == '"':
909
+ if not in_quotes and (index == 0 or name[index - 1] not in " \t"):
910
+ raise ValueError("Git config quoted subsection names must begin after whitespace")
911
+ in_quotes = not in_quotes
912
+ elif char == "]" and not in_quotes:
913
+ raise ValueError("Git config section names must not contain an unquoted closing bracket")
914
+ if in_quotes:
915
+ raise ValueError("Git config section names must not contain an unterminated quote")
900
916
 
901
917
  @needs_values
902
918
  @set_dirty_and_flush_changes
git/index/fun.py CHANGED
@@ -64,6 +64,17 @@ def hook_path(name: str, git_dir: PathLike) -> str:
64
64
  return osp.join(git_dir, "hooks", name)
65
65
 
66
66
 
67
+ def _commit_hook_path(name: str, index: "IndexFile") -> str:
68
+ """:return: path to the named commit hook, respecting Git's core.hooksPath."""
69
+ with index.repo.config_reader() as config:
70
+ hooks_dir = config.get("core", "hooksPath", fallback="")
71
+
72
+ if not hooks_dir:
73
+ return hook_path(name, index.repo.git_dir)
74
+
75
+ return osp.abspath(osp.join(index.repo.working_dir, osp.expanduser(hooks_dir), name))
76
+
77
+
67
78
  def _has_file_extension(path: str) -> str:
68
79
  return osp.splitext(path)[1]
69
80
 
@@ -82,7 +93,7 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
82
93
 
83
94
  :raise git.exc.HookExecutionError:
84
95
  """
85
- hp = hook_path(name, index.repo.git_dir)
96
+ hp = _commit_hook_path(name, index)
86
97
  if not os.access(hp, os.X_OK):
87
98
  return
88
99
 
@@ -94,8 +105,14 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
94
105
  if sys.platform == "win32" and not _has_file_extension(hp):
95
106
  # Windows only uses extensions to determine how to open files
96
107
  # (doesn't understand shebangs). Try using bash to run the hook.
97
- relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
98
- cmd = ["bash.exe", relative_hp]
108
+ try:
109
+ bash_hp = osp.relpath(hp, index.repo.working_dir)
110
+ except ValueError:
111
+ # Different drives have no relative path on Windows. Git Bash accepts
112
+ # an absolute path in this form, although a relative path is preferable
113
+ # because it also works with the Windows Subsystem for Linux wrapper.
114
+ bash_hp = hp
115
+ cmd = ["bash.exe", Path(bash_hp).as_posix()]
99
116
 
100
117
  process = safer_popen(
101
118
  cmd + list(args),
@@ -49,6 +49,7 @@ from typing import (
49
49
  Callable,
50
50
  Dict,
51
51
  Iterator,
52
+ List,
52
53
  Mapping,
53
54
  Sequence,
54
55
  TYPE_CHECKING,
@@ -738,122 +739,132 @@ class Submodule(IndexObject, TraversableIterableObj):
738
739
  mrepo = None
739
740
  # END init mrepo
740
741
 
742
+ def fetch_remotes(module_repo: "Repo") -> None:
743
+ rmts = module_repo.remotes
744
+ len_rmts = len(rmts)
745
+ for i, remote in enumerate(rmts):
746
+ op = FETCH
747
+ if i == 0:
748
+ op |= BEGIN
749
+ # END handle start
750
+
751
+ progress.update(
752
+ op,
753
+ i,
754
+ len_rmts,
755
+ prefix + "Fetching remote %s of submodule %r" % (remote, self.name),
756
+ )
757
+ # ===============================
758
+ if not dry_run:
759
+ remote.fetch(progress=progress)
760
+ # END handle dry-run
761
+ # ===============================
762
+ if i == len_rmts - 1:
763
+ op |= END
764
+ # END handle end
765
+ progress.update(
766
+ op,
767
+ i,
768
+ len_rmts,
769
+ prefix + "Done fetching remote of submodule %r" % self.name,
770
+ )
771
+ # END fetch new data
772
+
741
773
  try:
742
774
  # ENSURE REPO IS PRESENT AND UP-TO-DATE
743
775
  #######################################
744
776
  try:
745
777
  mrepo = self.module()
746
- rmts = mrepo.remotes
747
- len_rmts = len(rmts)
748
- for i, remote in enumerate(rmts):
749
- op = FETCH
750
- if i == 0:
751
- op |= BEGIN
752
- # END handle start
753
-
754
- progress.update(
755
- op,
756
- i,
757
- len_rmts,
758
- prefix + "Fetching remote %s of submodule %r" % (remote, self.name),
759
- )
760
- # ===============================
761
- if not dry_run:
762
- remote.fetch(progress=progress)
763
- # END handle dry-run
764
- # ===============================
765
- if i == len_rmts - 1:
766
- op |= END
767
- # END handle end
768
- progress.update(
769
- op,
770
- i,
771
- len_rmts,
772
- prefix + "Done fetching remote of submodule %r" % self.name,
773
- )
774
- # END fetch new data
778
+ fetch_remotes(mrepo)
775
779
  except InvalidGitRepositoryError:
776
780
  mrepo = None
777
781
  if not init:
778
782
  return self
779
783
  # END early abort if init is not allowed
780
784
 
781
- # There is no git-repository yet - but delete empty paths.
782
785
  checkout_module_abspath = self.abspath
783
- if not dry_run and osp.isdir(checkout_module_abspath):
786
+ module_abspath = self._module_abspath(self.repo, self.path, self.name)
787
+
788
+ # ``git submodule deinit`` leaves the repository in
789
+ # ``.git/modules`` and empties the checkout. Reconnect that retained
790
+ # repository instead of trying to clone over it.
791
+ if not dry_run and osp.isdir(module_abspath):
784
792
  try:
785
- os.rmdir(checkout_module_abspath)
786
- except OSError as e:
787
- raise OSError(
788
- "Module directory at %r does already exist and is non-empty" % checkout_module_abspath
789
- ) from e
790
- # END handle OSError
791
- # END handle directory removal
792
-
793
- # Don't check it out at first - nonetheless it will create a local
794
- # branch according to the remote-HEAD if possible.
795
- progress.update(
796
- BEGIN | CLONE,
797
- 0,
798
- 1,
799
- prefix
800
- + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name),
801
- )
802
- if not dry_run:
803
- if self.url.startswith("."):
804
- url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url)
793
+ git.Repo(module_abspath)
794
+ except InvalidGitRepositoryError:
795
+ pass
805
796
  else:
806
- url = self.url
807
- mrepo = self._clone_repo(
808
- self.repo,
809
- url,
810
- self.path,
811
- self.name,
812
- n=True,
813
- env=env,
814
- multi_options=clone_multi_options,
815
- allow_unsafe_options=allow_unsafe_options,
816
- allow_unsafe_protocols=allow_unsafe_protocols,
797
+ if osp.lexists(checkout_module_abspath) and (
798
+ osp.islink(checkout_module_abspath)
799
+ or not osp.isdir(checkout_module_abspath)
800
+ or os.listdir(checkout_module_abspath)
801
+ ):
802
+ raise OSError(
803
+ "Module directory at %r does already exist and is non-empty" % checkout_module_abspath
804
+ )
805
+ os.makedirs(checkout_module_abspath, exist_ok=True)
806
+ self._write_git_file_and_module_config(checkout_module_abspath, module_abspath)
807
+ mrepo = git.Repo(checkout_module_abspath)
808
+ mrepo.head.reset(mrepo.head.commit, index=True, working_tree=True)
809
+ fetch_remotes(mrepo)
810
+ with self.repo.config_writer() as writer:
811
+ writer.set_value(sm_section(self.name), "url", self.url)
812
+
813
+ if mrepo is None:
814
+ # There is no git-repository yet - but delete empty paths.
815
+ if not dry_run and osp.isdir(checkout_module_abspath):
816
+ try:
817
+ os.rmdir(checkout_module_abspath)
818
+ except OSError as e:
819
+ raise OSError(
820
+ "Module directory at %r does already exist and is non-empty" % checkout_module_abspath
821
+ ) from e
822
+ # END handle directory removal
823
+
824
+ # Don't check it out at first - nonetheless it will create a local
825
+ # branch according to the remote-HEAD if possible.
826
+ progress.update(
827
+ BEGIN | CLONE,
828
+ 0,
829
+ 1,
830
+ prefix
831
+ + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name),
817
832
  )
818
- # END handle dry-run
819
- progress.update(
820
- END | CLONE,
821
- 0,
822
- 1,
823
- prefix + "Done cloning to %s" % checkout_module_abspath,
824
- )
825
-
826
- if not dry_run:
827
- # See whether we have a valid branch to check out.
828
- try:
829
- mrepo = cast("Repo", mrepo)
830
- # Find a remote which has our branch - we try to be flexible.
831
- remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name)
832
- local_branch = mkhead(mrepo, self.branch_path)
833
-
834
- # Have a valid branch, but no checkout - make sure we can figure
835
- # that out by marking the commit with a null_sha.
836
- local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA))
837
- # END initial checkout + branch creation
838
-
839
- # Make sure HEAD is not detached.
840
- mrepo.head.set_reference(
841
- local_branch,
842
- logmsg="submodule: attaching head to %s" % local_branch,
833
+ if not dry_run:
834
+ if self.url.startswith("."):
835
+ url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url)
836
+ else:
837
+ url = self.url
838
+ mrepo = self._clone_repo(
839
+ self.repo,
840
+ url,
841
+ self.path,
842
+ self.name,
843
+ n=True,
844
+ env=env,
845
+ multi_options=clone_multi_options,
846
+ allow_unsafe_options=allow_unsafe_options,
847
+ allow_unsafe_protocols=allow_unsafe_protocols,
843
848
  )
844
- mrepo.head.reference.set_tracking_branch(remote_branch)
845
- except (IndexError, InvalidGitRepositoryError):
846
- _logger.warning("Failed to checkout tracking branch %s", self.branch_path)
847
- # END handle tracking branch
848
-
849
- # NOTE: Have to write the repo config file as well, otherwise the
850
- # default implementation will be offended and not update the
851
- # repository. Maybe this is a good way to ensure it doesn't get into
852
- # our way, but we want to stay backwards compatible too... It's so
853
- # redundant!
854
- with self.repo.config_writer() as writer:
855
- writer.set_value(sm_section(self.name), "url", self.url)
856
- # END handle dry_run
849
+ progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath)
850
+
851
+ if not dry_run:
852
+ # See whether we have a valid branch to check out.
853
+ try:
854
+ mrepo = cast("Repo", mrepo)
855
+ remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name)
856
+ local_branch = mkhead(mrepo, self.branch_path)
857
+ local_branch.set_object(Object(mrepo, self.NULL_BIN_SHA))
858
+ mrepo.head.set_reference(
859
+ local_branch,
860
+ logmsg="submodule: attaching head to %s" % local_branch,
861
+ )
862
+ mrepo.head.reference.set_tracking_branch(remote_branch)
863
+ except (IndexError, InvalidGitRepositoryError):
864
+ _logger.warning("Failed to checkout tracking branch %s", self.branch_path)
865
+
866
+ with self.repo.config_writer() as writer:
867
+ writer.set_value(sm_section(self.name), "url", self.url)
857
868
  # END handle initialization
858
869
 
859
870
  # DETERMINE SHAS TO CHECK OUT
@@ -1267,6 +1278,36 @@ class Submodule(IndexObject, TraversableIterableObj):
1267
1278
 
1268
1279
  return self
1269
1280
 
1281
+ @unbare_repo
1282
+ def deinit(self, force: bool = False) -> "Submodule":
1283
+ """Run ``git submodule deinit`` on this submodule.
1284
+
1285
+ This is a thin wrapper around ``git submodule deinit <path>``,
1286
+ which unregisters the submodule (removes its entry from
1287
+ ``.git/config`` and empties the working-tree directory)
1288
+ without deleting the submodule from ``.gitmodules``
1289
+ or its checked-out repository under ``.git/modules/``.
1290
+ A subsequent :meth:`update` will re-initialize the
1291
+ submodule from the retained contents.
1292
+
1293
+ :param force:
1294
+ If ``True``, pass ``--force`` to ``git submodule deinit``. This
1295
+ allows deinitialization even when the submodule's working tree has
1296
+ local modifications that would otherwise block the command.
1297
+
1298
+ :return:
1299
+ self
1300
+
1301
+ :note:
1302
+ Doesn't work in bare repositories.
1303
+ """
1304
+ args: List[str] = []
1305
+ if force:
1306
+ args.append("--force")
1307
+ args.extend(["--", str(self.path)])
1308
+ self.repo.git.submodule("deinit", *args)
1309
+ return self
1310
+
1270
1311
  def set_parent_commit(self, commit: Union[Commit_ish, str, None], check: bool = True) -> "Submodule":
1271
1312
  """Set this instance to use the given commit whose tree is supposed to
1272
1313
  contain the ``.gitmodules`` blob.
git/repo/base.py CHANGED
@@ -1452,8 +1452,9 @@ class Repo:
1452
1452
  if multi_options:
1453
1453
  multi = shlex.split(" ".join(multi_options))
1454
1454
 
1455
+ clone_url = Git.polish_url(url, expand_vars=False)
1455
1456
  if not allow_unsafe_protocols:
1456
- Git.check_unsafe_protocols(url)
1457
+ Git.check_unsafe_protocols(clone_url)
1457
1458
  if not allow_unsafe_options:
1458
1459
  Git.check_unsafe_options(
1459
1460
  options=Git._option_candidates([], kwargs),
@@ -1465,7 +1466,7 @@ class Repo:
1465
1466
  proc = git.clone(
1466
1467
  multi,
1467
1468
  "--",
1468
- Git.polish_url(url),
1469
+ clone_url,
1469
1470
  clone_path,
1470
1471
  with_extended_output=True,
1471
1472
  as_process=True,
@@ -1505,7 +1506,7 @@ class Repo:
1505
1506
  # escape the backslashes. Hence we undo the escaping just to be sure.
1506
1507
  if repo.remotes:
1507
1508
  with repo.remotes[0].config_writer as writer:
1508
- writer.set_value("url", Git.polish_url(repo.remotes[0].url))
1509
+ writer.set_value("url", Git.polish_url(repo.remotes[0].url, expand_vars=False))
1509
1510
  # END handle remote repo
1510
1511
  return repo
1511
1512
 
git/util.py CHANGED
@@ -217,7 +217,7 @@ def rmtree(path: PathLike) -> None:
217
217
  couldn't be deleted are read-only. Windows will not remove them in that case.
218
218
  """
219
219
 
220
- def handler(function: Callable, path: PathLike, _excinfo: Any) -> None:
220
+ def handler(function: Callable[[str], Any], path: str, _excinfo: Any) -> None:
221
221
  """Callback for :func:`shutil.rmtree`.
222
222
 
223
223
  This works as either a ``onexc`` or ``onerror`` style callback.
@@ -382,13 +382,13 @@ def py_where(program: str, path: Optional[PathLike] = None) -> List[str]:
382
382
  return progs
383
383
 
384
384
 
385
- def _cygexpath(drive: Optional[str], path: str) -> str:
385
+ def _cygexpath(drive: Optional[str], path: str, expand_vars: bool = True) -> str:
386
386
  if osp.isabs(path) and not drive:
387
387
  # Invoked from `cygpath()` directly with `D:Apps\123`?
388
388
  # It's an error, leave it alone just slashes)
389
389
  p = path # convert to str if AnyPath given
390
390
  else:
391
- p = path and osp.normpath(osp.expandvars(osp.expanduser(path)))
391
+ p = path and osp.normpath(osp.expandvars(osp.expanduser(path)) if expand_vars else path)
392
392
  if osp.isabs(p):
393
393
  if drive:
394
394
  # Confusing, maybe a remote system should expand vars.
@@ -401,7 +401,7 @@ def _cygexpath(drive: Optional[str], path: str) -> str:
401
401
  return p_str.replace("\\", "/")
402
402
 
403
403
 
404
- _cygpath_parsers: Tuple[Tuple[Pattern[str], Callable, bool], ...] = (
404
+ _cygpath_parsers: Tuple[Tuple[Pattern[str], Callable[..., str], bool], ...] = (
405
405
  # See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
406
406
  # and: https://www.cygwin.com/cygwin-ug-net/using.html#unc-paths
407
407
  (
@@ -416,7 +416,7 @@ _cygpath_parsers: Tuple[Tuple[Pattern[str], Callable, bool], ...] = (
416
416
  )
417
417
 
418
418
 
419
- def cygpath(path: str) -> str:
419
+ def cygpath(path: str, expand_vars: bool = True) -> str:
420
420
  """Use :meth:`git.cmd.Git.polish_url` instead, that works on any environment."""
421
421
  path = os.fspath(path) # Ensure is str and not AnyPath.
422
422
  # Fix to use Paths when 3.5 dropped. Or to be just str if only for URLs?
@@ -424,12 +424,15 @@ def cygpath(path: str) -> str:
424
424
  for regex, parser, recurse in _cygpath_parsers:
425
425
  match = regex.match(path)
426
426
  if match:
427
- path = parser(*match.groups())
427
+ if parser is _cygexpath:
428
+ path = parser(*match.groups(), expand_vars=expand_vars)
429
+ else:
430
+ path = parser(*match.groups())
428
431
  if recurse:
429
- path = cygpath(path)
432
+ path = cygpath(path, expand_vars=expand_vars)
430
433
  break
431
434
  else:
432
- path = _cygexpath(None, path)
435
+ path = _cygexpath(None, path, expand_vars=expand_vars)
433
436
 
434
437
  return path
435
438
 
@@ -505,7 +508,7 @@ def get_user_id() -> str:
505
508
  return "%s@%s" % (getpass.getuser(), platform.node())
506
509
 
507
510
 
508
- def finalize_process(proc: Union[subprocess.Popen, "Git.AutoInterrupt"], **kwargs: Any) -> None:
511
+ def finalize_process(proc: Union["subprocess.Popen[Any]", "Git.AutoInterrupt"], **kwargs: Any) -> None:
509
512
  """Wait for the process (clone, fetch, pull or push) and handle its errors
510
513
  accordingly."""
511
514
  # TODO: No close proc-streams??
@@ -517,19 +520,21 @@ def expand_path(p: None, expand_vars: bool = ...) -> None: ...
517
520
 
518
521
 
519
522
  @overload
520
- def expand_path(p: PathLike, expand_vars: bool = ...) -> str:
523
+ def expand_path(p: PathLike, expand_vars: bool = ...) -> Optional[PathLike]:
521
524
  # TODO: Support for Python 3.5 has been dropped, so these overloads can be improved.
522
525
  ...
523
526
 
524
527
 
525
528
  def expand_path(p: Union[None, PathLike], expand_vars: bool = True) -> Optional[PathLike]:
526
- if isinstance(p, Path):
527
- return p.resolve()
529
+ if p is None:
530
+ return None
528
531
  try:
529
- p = osp.expanduser(p) # type: ignore[arg-type]
532
+ if isinstance(p, Path):
533
+ return p.resolve()
534
+ expanded_path = osp.expanduser(os.fspath(p))
530
535
  if expand_vars:
531
- p = osp.expandvars(p)
532
- return osp.normpath(osp.abspath(p))
536
+ expanded_path = osp.expandvars(expanded_path)
537
+ return osp.normpath(osp.abspath(expanded_path))
533
538
  except Exception:
534
539
  return None
535
540
 
@@ -764,7 +769,7 @@ class CallableRemoteProgress(RemoteProgress):
764
769
 
765
770
  __slots__ = ("_callable",)
766
771
 
767
- def __init__(self, fn: Callable) -> None:
772
+ def __init__(self, fn: Callable[..., Any]) -> None:
768
773
  self._callable = fn
769
774
  super().__init__()
770
775
 
@@ -843,7 +848,7 @@ class Actor:
843
848
  cls,
844
849
  env_name: str,
845
850
  env_email: str,
846
- config_reader: Union[None, "GitConfigParser", "SectionConstraint"] = None,
851
+ config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None,
847
852
  ) -> "Actor":
848
853
  actor = Actor("", "")
849
854
  user_id = None # We use this to avoid multiple calls to getpass.getuser().
@@ -879,7 +884,9 @@ class Actor:
879
884
  return actor
880
885
 
881
886
  @classmethod
882
- def committer(cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint"] = None) -> "Actor":
887
+ def committer(
888
+ cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None
889
+ ) -> "Actor":
883
890
  """
884
891
  :return:
885
892
  :class:`Actor` instance corresponding to the configured committer. It
@@ -894,7 +901,9 @@ class Actor:
894
901
  return cls._main_actor(cls.env_committer_name, cls.env_committer_email, config_reader)
895
902
 
896
903
  @classmethod
897
- def author(cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint"] = None) -> "Actor":
904
+ def author(
905
+ cls, config_reader: Union[None, "GitConfigParser", "SectionConstraint[GitConfigParser]"] = None
906
+ ) -> "Actor":
898
907
  """Same as :meth:`committer`, but defines the main author. It may be specified
899
908
  in the environment, but defaults to the committer."""
900
909
  return cls._main_actor(cls.env_author_name, cls.env_author_email, config_reader)
@@ -977,11 +986,11 @@ class IndexFileSHA1Writer:
977
986
 
978
987
  __slots__ = ("f", "sha1")
979
988
 
980
- def __init__(self, f: IO) -> None:
989
+ def __init__(self, f: IO[bytes]) -> None:
981
990
  self.f = f
982
991
  self.sha1 = make_sha(b"")
983
992
 
984
- def write(self, data: AnyStr) -> int:
993
+ def write(self, data: bytes) -> int:
985
994
  self.sha1.update(data)
986
995
  return self.f.write(data)
987
996
 
@@ -1178,6 +1187,7 @@ class IterableList(List[T_IterableObj]): # type: ignore[type-var]
1178
1187
  return super().__new__(cls)
1179
1188
 
1180
1189
  def __init__(self, id_attr: str, prefix: str = "") -> None:
1190
+ super().__init__()
1181
1191
  self._id_attr = id_attr
1182
1192
  self._prefix = prefix
1183
1193
 
@@ -1207,7 +1217,9 @@ class IterableList(List[T_IterableObj]): # type: ignore[type-var]
1207
1217
  # END for each item
1208
1218
  return list.__getattribute__(self, attr)
1209
1219
 
1210
- def __getitem__(self, index: Union[SupportsIndex, int, slice, str]) -> T_IterableObj: # type: ignore[override]
1220
+ def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
1221
+ self, index: Union[SupportsIndex, int, slice, str]
1222
+ ) -> T_IterableObj:
1211
1223
  if isinstance(index, int):
1212
1224
  return list.__getitem__(self, index)
1213
1225
  elif isinstance(index, slice):
@@ -1285,7 +1297,7 @@ class IterableObj(Protocol):
1285
1297
  :return:
1286
1298
  list(Item,...) list of item instances
1287
1299
  """
1288
- out_list: IterableList = IterableList(cls._id_attribute_)
1300
+ out_list: IterableList[T_IterableObj] = IterableList(cls._id_attribute_)
1289
1301
  out_list.extend(cls.iter_items(repo, *args, **kwargs))
1290
1302
  return out_list
1291
1303
 
@@ -1294,7 +1306,8 @@ class IterableClassWatcher(type):
1294
1306
  """Metaclass that issues :exc:`DeprecationWarning` when :class:`git.util.Iterable`
1295
1307
  is subclassed."""
1296
1308
 
1297
- def __init__(cls, name: str, bases: Tuple, clsdict: Dict) -> None:
1309
+ def __init__(cls, name: str, bases: Tuple[type, ...], clsdict: Dict[str, Any]) -> None:
1310
+ super().__init__(name, bases, clsdict)
1298
1311
  for base in bases:
1299
1312
  if type(base) is IterableClassWatcher:
1300
1313
  warnings.warn(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.51
3
+ Version: 3.1.53
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,17 +1,17 @@
1
- git/__init__.py,sha256=r3oBfjVUel4N6O4MuchUmPkCQzneGtQfvRrMetWXtWU,8899
2
- git/cmd.py,sha256=o5Mg1gNKFEPwa5DA0EiS0J4PCvSMZth2d5irYK1yPRw,74902
1
+ git/__init__.py,sha256=JfSdrpLoaZem4xHAf_iZARfzAGTDgpy7K60ptkzAFC8,8899
2
+ git/cmd.py,sha256=nIJlOcTeRoOPVtg5IO_EmhVRCejB8reAfukilRXR6Yo,75230
3
3
  git/compat.py,sha256=y1E6y6O2q5r8clSlr8ZNmuIWG9nmHuehQEsVsmBffs8,4526
4
- git/config.py,sha256=VWk1i09HN_nrghQc2iTm4yLpAkPwppHnEBU74XqZl1o,37533
4
+ git/config.py,sha256=aLDSuDiiB67W-OLktJpYVjSLwgIqWJtPYbRllNhQ7Gw,38406
5
5
  git/db.py,sha256=vIW9uWSbqu99zbuU2ZDmOhVOv1UPTmxrnqiCtRHCfjE,2368
6
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
9
  git/remote.py,sha256=rCbrMMjILsX08TGYai8wdxzHCHc4ba6vPt6tP9efOog,46881
10
10
  git/types.py,sha256=d3oDduUGjfoqffcUL_GOD-3qroF8m3r9ai6Xi9_FraM,10279
11
- git/util.py,sha256=cCbGpBpnQq1MtW_REe8ApsAGTzp9JgVCMUN8KFgnx14,44052
11
+ git/util.py,sha256=27MIkq6AhpjyAHF9r7UEA1pGYRAkxRMiZNo5IJGdJhU,44712
12
12
  git/index/__init__.py,sha256=i-Nqb8Lufp9aFbmxpQBORmmQnjEVVM1Pn58fsQkyGgQ,406
13
13
  git/index/base.py,sha256=6U-gbvhVa2YmW1v-JeT1TOW6wJHi1li5g9wume-iCmw,62260
14
- git/index/fun.py,sha256=v0WnKQxaAffMZxXXC7pQOiMvRRJfODhUNKDMruxKrN4,17110
14
+ git/index/fun.py,sha256=Cfrpq6GmhLXGVDDjYiXTN4f4bb35kvw6-cOGOBuJeGc,17862
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
@@ -23,7 +23,7 @@ git/objects/tag.py,sha256=jAGESnpmTEv-dLakPzheT5ILZFFArcItnXYqfxfDrgc,4441
23
23
  git/objects/tree.py,sha256=MmExcS59Iamb2cBqUvoHPdzgixabyvv5voHabossrl8,13898
24
24
  git/objects/util.py,sha256=Nlza4zLgdPmr_Yasyvvs6c1rKtW_wMxI6wDmQpQ3ufw,23846
25
25
  git/objects/submodule/__init__.py,sha256=6xySp767LVz3UylWgUalntS_nGXRuVzXxDuFAv_Wc2c,303
26
- git/objects/submodule/base.py,sha256=EWkbiJsFSpCt5zacoVrq-9JQFJWn4BWA7kdsXtjL5PU,64412
26
+ git/objects/submodule/base.py,sha256=NR8eP-dEr-U_EG8LahRRgJ2n2z1wZXY9V0w57pBbiIo,66328
27
27
  git/objects/submodule/root.py,sha256=5eTtYNHasqdPq6q0oDCPr7IaO6uAHL3b4DxMoiO2LhE,20246
28
28
  git/objects/submodule/util.py,sha256=sQqAYaiSJdFkZa9NlAuK_wTsMNiS-kkQnQjvIoJtc_o,3509
29
29
  git/refs/__init__.py,sha256=DWlJNnsx-4jM_E-VycbP-FZUdn6iWhjnH_uZ_pZXBro,509
@@ -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=yeW67EY983v8vAProm4kSmKcn_GoM8IycmYrV2RPLQg,63707
37
+ git/repo/base.py,sha256=1HFBIKTLLjmVQXhJlesWIXP90xoNM1RTVQF2eIwLkgs,63781
38
38
  git/repo/fun.py,sha256=PDFNkuTSGGoQc0EEEXjxHw2_KaKYvVjBZ9rrkwH5Q6Y,23462
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,,
39
+ gitpython-3.1.53.dist-info/licenses/AUTHORS,sha256=Iucx_CH3w7_4084yjM6C5BvKfLA8cVfYHWW2pVmpxSk,2410
40
+ gitpython-3.1.53.dist-info/licenses/LICENSE,sha256=hvyUwyGpr7wRUUcTURuv3tIl8lEA3MD3NQ6CvCMbi-s,1503
41
+ gitpython-3.1.53.dist-info/METADATA,sha256=IdYoD67td8TnivPczpNQpgODxRb9jAnqG-bA_RcL40g,14187
42
+ gitpython-3.1.53.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
43
+ gitpython-3.1.53.dist-info/top_level.txt,sha256=0hzDuIp8obv624V3GmbqsagBWkk8ohtGU-Bc1PmTT0o,4
44
+ gitpython-3.1.53.dist-info/RECORD,,