GitPython 3.1.59__tar.gz → 3.1.60__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.59 → gitpython-3.1.60/GitPython.egg-info}/PKG-INFO +1 -1
  2. {gitpython-3.1.59/GitPython.egg-info → gitpython-3.1.60}/PKG-INFO +1 -1
  3. gitpython-3.1.60/VERSION +1 -0
  4. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/changes.rst +15 -0
  5. {gitpython-3.1.59 → gitpython-3.1.60}/git/__init__.py +1 -1
  6. {gitpython-3.1.59 → gitpython-3.1.60}/git/config.py +23 -6
  7. {gitpython-3.1.59 → gitpython-3.1.60}/git/diff.py +31 -12
  8. {gitpython-3.1.59 → gitpython-3.1.60}/git/repo/base.py +64 -49
  9. {gitpython-3.1.59 → gitpython-3.1.60}/git/repo/fun.py +71 -39
  10. {gitpython-3.1.59 → gitpython-3.1.60}/git/util.py +8 -16
  11. gitpython-3.1.59/VERSION +0 -1
  12. {gitpython-3.1.59 → gitpython-3.1.60}/AUTHORS +0 -0
  13. {gitpython-3.1.59 → gitpython-3.1.60}/CHANGES +0 -0
  14. {gitpython-3.1.59 → gitpython-3.1.60}/CONTRIBUTING.md +0 -0
  15. {gitpython-3.1.59 → gitpython-3.1.60}/GitPython.egg-info/SOURCES.txt +0 -0
  16. {gitpython-3.1.59 → gitpython-3.1.60}/GitPython.egg-info/dependency_links.txt +0 -0
  17. {gitpython-3.1.59 → gitpython-3.1.60}/GitPython.egg-info/not-zip-safe +0 -0
  18. {gitpython-3.1.59 → gitpython-3.1.60}/GitPython.egg-info/requires.txt +0 -0
  19. {gitpython-3.1.59 → gitpython-3.1.60}/GitPython.egg-info/top_level.txt +0 -0
  20. {gitpython-3.1.59 → gitpython-3.1.60}/LICENSE +0 -0
  21. {gitpython-3.1.59 → gitpython-3.1.60}/MANIFEST.in +0 -0
  22. {gitpython-3.1.59 → gitpython-3.1.60}/README.md +0 -0
  23. {gitpython-3.1.59 → gitpython-3.1.60}/doc/.gitignore +0 -0
  24. {gitpython-3.1.59 → gitpython-3.1.60}/doc/Makefile +0 -0
  25. {gitpython-3.1.59 → gitpython-3.1.60}/doc/requirements.txt +0 -0
  26. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/conf.py +0 -0
  27. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/index.rst +0 -0
  28. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/intro.rst +0 -0
  29. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/quickstart.rst +0 -0
  30. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/reference.rst +0 -0
  31. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/roadmap.rst +0 -0
  32. {gitpython-3.1.59 → gitpython-3.1.60}/doc/source/tutorial.rst +0 -0
  33. {gitpython-3.1.59 → gitpython-3.1.60}/git/cmd.py +0 -0
  34. {gitpython-3.1.59 → gitpython-3.1.60}/git/compat.py +0 -0
  35. {gitpython-3.1.59 → gitpython-3.1.60}/git/db.py +0 -0
  36. {gitpython-3.1.59 → gitpython-3.1.60}/git/exc.py +0 -0
  37. {gitpython-3.1.59 → gitpython-3.1.60}/git/index/__init__.py +0 -0
  38. {gitpython-3.1.59 → gitpython-3.1.60}/git/index/base.py +0 -0
  39. {gitpython-3.1.59 → gitpython-3.1.60}/git/index/fun.py +0 -0
  40. {gitpython-3.1.59 → gitpython-3.1.60}/git/index/typ.py +0 -0
  41. {gitpython-3.1.59 → gitpython-3.1.60}/git/index/util.py +0 -0
  42. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/__init__.py +0 -0
  43. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/base.py +0 -0
  44. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/blob.py +0 -0
  45. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/commit.py +0 -0
  46. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/fun.py +0 -0
  47. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/submodule/__init__.py +0 -0
  48. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/submodule/base.py +0 -0
  49. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/submodule/root.py +0 -0
  50. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/submodule/util.py +0 -0
  51. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/tag.py +0 -0
  52. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/tree.py +0 -0
  53. {gitpython-3.1.59 → gitpython-3.1.60}/git/objects/util.py +0 -0
  54. {gitpython-3.1.59 → gitpython-3.1.60}/git/py.typed +0 -0
  55. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/__init__.py +0 -0
  56. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/head.py +0 -0
  57. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/log.py +0 -0
  58. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/reference.py +0 -0
  59. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/remote.py +0 -0
  60. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/symbolic.py +0 -0
  61. {gitpython-3.1.59 → gitpython-3.1.60}/git/refs/tag.py +0 -0
  62. {gitpython-3.1.59 → gitpython-3.1.60}/git/remote.py +0 -0
  63. {gitpython-3.1.59 → gitpython-3.1.60}/git/repo/__init__.py +0 -0
  64. {gitpython-3.1.59 → gitpython-3.1.60}/git/types.py +0 -0
  65. {gitpython-3.1.59 → gitpython-3.1.60}/pyproject.toml +0 -0
  66. {gitpython-3.1.59 → gitpython-3.1.60}/requirements.txt +0 -0
  67. {gitpython-3.1.59 → gitpython-3.1.60}/setup.cfg +0 -0
  68. {gitpython-3.1.59 → gitpython-3.1.60}/setup.py +0 -0
  69. {gitpython-3.1.59 → gitpython-3.1.60}/test-requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.59
3
+ Version: 3.1.60
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GitPython
3
- Version: 3.1.59
3
+ Version: 3.1.60
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
@@ -0,0 +1 @@
1
+ 3.1.60
@@ -2,6 +2,21 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ 3.1.60
6
+ ======
7
+
8
+ Security fixes for
9
+
10
+ * https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-g5vv-9gxw-82hx
11
+ * https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-whh4-5q6c-9v3x
12
+ * https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-239g-whfq-7xj9
13
+
14
+ If you can, also try and provide feedback on the upcoming v4 branch
15
+ https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
16
+
17
+ See the following for all changes.
18
+ https://github.com/gitpython-developers/GitPython/releases/tag/3.1.60
19
+
5
20
  3.1.59
6
21
  ======
7
22
 
@@ -86,7 +86,7 @@ __all__ = [
86
86
  "to_hex_sha",
87
87
  ]
88
88
 
89
- __version__ = '3.1.59'
89
+ __version__ = '3.1.60'
90
90
 
91
91
  from typing import Any, List, Optional, Sequence, TYPE_CHECKING, Tuple, Union
92
92
 
@@ -462,7 +462,8 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
462
462
  v = v[:-1]
463
463
  # END cut trailing escapes to prevent decode error
464
464
 
465
- return v.encode(defenc).decode("unicode_escape")
465
+ escapes = {"b": "\b", "n": "\n", "r": "\r", "t": "\t", '"': '"', "\\": "\\"}
466
+ return re.sub(r"\\(.)", lambda match: escapes.get(match.group(1), match.group(0)), v)
466
467
 
467
468
  # END string_decode
468
469
 
@@ -517,10 +518,12 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
517
518
  # Opens quoting and does not close: appears to start multi-line quoting.
518
519
  is_multi_line = True
519
520
  optval = string_decode(optval[1:])
520
- elif optval.find("\\", 1, -1) == -1 and optval.find('"', 1, -1) == -1:
521
- # Opens and closes quoting. Single line, and all we need is quote removal.
522
- optval = optval[1:-1]
523
- # TODO: Handle other quoted content, especially well-formed backslash escapes.
521
+ elif re.search(r'(?:^|[^\\])(?:\\\\)*"', optval[1:-1]):
522
+ # Preserve malformed values containing unescaped quotes.
523
+ pass
524
+ else:
525
+ # Opens and closes quoting.
526
+ optval = string_decode(optval[1:-1])
524
527
 
525
528
  # Preserves multiple values for duplicate optnames.
526
529
  cursect.add(optname, optval)
@@ -706,7 +709,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
706
709
 
707
710
  for v in values:
708
711
  value = self._value_to_string(v)
709
- if any(char in value for char in '\n\t\b\\"'):
712
+ if any(char in value for char in '\n\t\b\\"#;') or value[:1].isspace() or value[-1:].isspace():
710
713
  value = value.replace("\\", "\\\\").replace('"', '\\"')
711
714
  value = '"%s\\\n"' % value.replace("\n", "\\n").replace("\t", "\\t").replace("\b", "\\b")
712
715
  fp.write(("\t%s = %s\n" % (key, value)).encode(defenc))
@@ -768,6 +771,20 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder):
768
771
  return
769
772
  # END stop if we have include files
770
773
 
774
+ sections: List[_OMD] = [self._defaults]
775
+ section: _OMD
776
+ stored_section: _OMD
777
+ values: List[Any]
778
+ raw_value: Any
779
+ for _, stored_section in self._sections.items():
780
+ sections.append(stored_section)
781
+ for section in sections:
782
+ for key, values in section.items_all():
783
+ if key != "__name__":
784
+ for raw_value in values:
785
+ if "\r" in self._value_to_string(raw_value) or "\x00" in self._value_to_string(raw_value):
786
+ raise ValueError("Git config values must not contain CR or NUL")
787
+
771
788
  fp = self._file_or_files
772
789
 
773
790
  # We have a physical file on disk, so get a lock.
@@ -95,14 +95,35 @@ This is an alias of :const:`DiffConstants.INDEX`, which may also be accessed as
95
95
  :const:`git.INDEX` and :const:`Diffable.INDEX`, as well as :const:`Diffable.Index`.
96
96
  """
97
97
 
98
- _octal_byte_re = re.compile(rb"\\([0-9]{3})")
99
98
 
100
-
101
- def _octal_repl(matchobj: Match) -> bytes:
102
- value = matchobj.group(1)
103
- value = int(value, 8)
104
- value = bytes(bytearray((value,)))
105
- return value
99
+ def _unquote_path(path: bytes) -> bytes:
100
+ result = bytearray()
101
+ escapes = {
102
+ ord("a"): 7,
103
+ ord("b"): 8,
104
+ ord("f"): 12,
105
+ ord("n"): 10,
106
+ ord("r"): 13,
107
+ ord("t"): 9,
108
+ ord("v"): 11,
109
+ }
110
+ i = 0
111
+ while i < len(path):
112
+ if path[i] != ord("\\") or i + 1 == len(path):
113
+ result.append(path[i])
114
+ i += 1
115
+ continue
116
+ if path[i + 1] in b"0123" and i + 3 < len(path) and all(c in b"01234567" for c in path[i + 2 : i + 4]):
117
+ result.append(int(path[i + 1 : i + 4], 8))
118
+ i += 4
119
+ continue
120
+ escaped = path[i + 1]
121
+ if escaped in escapes or escaped in b'\\"':
122
+ result.append(escapes.get(escaped, escaped))
123
+ else:
124
+ result.extend(path[i : i + 2])
125
+ i += 2
126
+ return bytes(result)
106
127
 
107
128
 
108
129
  def decode_path(path: bytes, has_ab_prefix: bool = True) -> Optional[bytes]:
@@ -110,9 +131,7 @@ def decode_path(path: bytes, has_ab_prefix: bool = True) -> Optional[bytes]:
110
131
  return None
111
132
 
112
133
  if path.startswith(b'"') and path.endswith(b'"'):
113
- path = path[1:-1].replace(b"\\n", b"\n").replace(b"\\t", b"\t").replace(b'\\"', b'"').replace(b"\\\\", b"\\")
114
-
115
- path = _octal_byte_re.sub(_octal_repl, path)
134
+ path = _unquote_path(path[1:-1])
116
135
 
117
136
  if has_ab_prefix:
118
137
  assert path.startswith(b"a/") or path.startswith(b"b/")
@@ -220,8 +239,8 @@ class Diffable:
220
239
  to be read and diffed.
221
240
 
222
241
  :param allow_unsafe_options:
223
- If ``True``, allow options such as ``--output`` and ``-O`` that can write to
224
- or read from arbitrary filesystem paths.
242
+ If ``True``, allow options such as ``--output``, ``--no-index``, and ``-O``
243
+ that can write to or read from arbitrary filesystem paths.
225
244
 
226
245
  :param kwargs:
227
246
  Additional arguments passed to :manpage:`git-diff(1)`, such as ``R=True`` to
@@ -45,7 +45,6 @@ from git.util import (
45
45
 
46
46
  from .fun import (
47
47
  find_submodule_git_dir,
48
- find_worktree_git_dir,
49
48
  is_git_dir,
50
49
  rev_parse,
51
50
  touch,
@@ -209,6 +208,8 @@ class Repo:
209
208
  ]
210
209
 
211
210
  unsafe_git_diff_options = unsafe_git_revision_options + [
211
+ # Treats path operands as arbitrary filesystem paths.
212
+ "--no-index",
212
213
  # Reads caller-controlled order patterns from an arbitrary file.
213
214
  "-O",
214
215
  "--orderfile",
@@ -232,6 +233,10 @@ class Repo:
232
233
  ) -> None:
233
234
  R"""Create a new :class:`Repo` instance.
234
235
 
236
+ .. note::
237
+ Repositories using reftable may be opened, but GitPython's direct reference
238
+ access does not support reftable.
239
+
235
240
  :param path:
236
241
  The path to either the worktree directory or the .git directory itself::
237
242
 
@@ -266,7 +271,11 @@ class Repo:
266
271
  :class:`Repo`
267
272
  """
268
273
 
269
- epath = path or os.getenv("GIT_DIR")
274
+ git_dir_env = os.getenv("GIT_DIR")
275
+ object_dir_env = os.getenv("GIT_OBJECT_DIRECTORY")
276
+ if object_dir_env is not None:
277
+ object_dir_env = osp.abspath(object_dir_env)
278
+ epath = path or git_dir_env
270
279
  if not epath:
271
280
  epath = os.getcwd()
272
281
  epath = os.fspath(epath)
@@ -288,37 +297,48 @@ class Repo:
288
297
  raise NoSuchPathError(epath)
289
298
 
290
299
  # Walk up the path to find the `.git` dir.
291
- curpath = epath
292
- git_dir = None
300
+ curpath = os.fspath(epath) if epath is not None else ""
301
+ git_dir: Optional[str] = None
302
+ explicit_git_dir = not path and bool(git_dir_env)
293
303
  while curpath:
294
304
  # ABOUT osp.NORMPATH
295
305
  # It's important to normalize the paths, as submodules will otherwise
296
306
  # initialize their repo instances with paths that depend on path-portions
297
307
  # that will not exist after being removed. It's just cleaner.
298
- if (
299
- osp.isfile(osp.join(curpath, "gitdir"))
300
- and osp.isfile(osp.join(curpath, "commondir"))
301
- and osp.isfile(osp.join(curpath, "HEAD"))
302
- ):
303
- git_dir = curpath
304
-
305
- if "GIT_WORK_TREE" in os.environ:
306
- self._working_tree_dir = os.getenv("GIT_WORK_TREE")
307
- else:
308
- # Linked worktree administrative directories store the path to the
309
- # worktree's .git file in their gitdir file (without "gitdir: " prefix).
310
- with open(osp.join(git_dir, "gitdir")) as fp:
311
- worktree_gitfile = fp.read().strip()
308
+ if not explicit_git_dir:
309
+ dotgit = osp.join(curpath, ".git")
310
+ try:
311
+ sm_gitpath = find_submodule_git_dir(dotgit)
312
+ except OSError:
313
+ break
314
+ if sm_gitpath is not None:
315
+ # Worktrees can use relative paths as of Git 2.48, so join to curpath.
316
+ git_dir = osp.normpath(osp.join(curpath, os.fspath(sm_gitpath)))
317
+ self._working_tree_dir = curpath
318
+ break
319
+
320
+ # Like Git, do not fall back to a bare repository or parent directory when
321
+ # a non-directory .git entry exists but is not a valid gitfile.
322
+ if osp.exists(dotgit) and not osp.isdir(dotgit):
323
+ break
312
324
 
313
- if not osp.isabs(worktree_gitfile):
314
- worktree_gitfile = osp.normpath(osp.join(git_dir, worktree_gitfile))
325
+ if is_git_dir(curpath):
326
+ git_dir = curpath
327
+ if osp.isfile(osp.join(curpath, "gitdir")) and osp.isfile(osp.join(curpath, "commondir")):
328
+ if "GIT_WORK_TREE" in os.environ:
329
+ self._working_tree_dir = os.getenv("GIT_WORK_TREE")
330
+ else:
331
+ # Linked worktree administrative directories store the path to
332
+ # the worktree's .git file in gitdir (without a "gitdir: " prefix).
333
+ with open(osp.join(git_dir, "gitdir")) as fp:
334
+ worktree_gitfile = fp.read().strip()
315
335
 
316
- self._working_tree_dir = osp.dirname(worktree_gitfile)
336
+ if not osp.isabs(worktree_gitfile):
337
+ worktree_gitfile = osp.normpath(osp.join(git_dir, worktree_gitfile))
317
338
 
318
- break
339
+ self._working_tree_dir = osp.dirname(worktree_gitfile)
340
+ break
319
341
 
320
- if is_git_dir(curpath):
321
- git_dir = curpath
322
342
  # from man git-config : core.worktree
323
343
  # Set the path to the root of the working tree. If GIT_COMMON_DIR
324
344
  # environment variable is set, core.worktree is ignored and not used for
@@ -338,22 +358,7 @@ class Repo:
338
358
  self._working_tree_dir = os.getenv("GIT_WORK_TREE")
339
359
  break
340
360
 
341
- dotgit = osp.join(curpath, ".git")
342
- sm_gitpath = find_submodule_git_dir(dotgit)
343
- if sm_gitpath is not None:
344
- git_dir = osp.normpath(sm_gitpath)
345
-
346
- sm_gitpath = find_submodule_git_dir(dotgit)
347
- if sm_gitpath is None:
348
- sm_gitpath = find_worktree_git_dir(dotgit)
349
-
350
- if sm_gitpath is not None:
351
- # worktrees can use relative paths as of Git 2.48, so we join to curpath
352
- git_dir = osp.normpath(osp.join(curpath, sm_gitpath))
353
- self._working_tree_dir = curpath
354
- break
355
-
356
- if not search_parent_directories:
361
+ if explicit_git_dir or not search_parent_directories:
357
362
  break
358
363
  curpath, tail = osp.split(curpath)
359
364
  if not tail:
@@ -364,6 +369,16 @@ class Repo:
364
369
  raise InvalidGitRepositoryError(epath)
365
370
  self.git_dir = git_dir
366
371
 
372
+ common_dir_env = os.getenv("GIT_COMMON_DIR")
373
+ if common_dir_env is not None:
374
+ self._common_dir = osp.abspath(common_dir_env)
375
+ else:
376
+ try:
377
+ common_dir = os.fsdecode((Path(self.git_dir) / "commondir").read_bytes()).rstrip("\r\n")
378
+ self._common_dir = osp.join(self.git_dir, common_dir)
379
+ except OSError:
380
+ self._common_dir = ""
381
+
367
382
  self._bare = False
368
383
  try:
369
384
  self._bare = self.config_reader("repository").getboolean("core", "bare")
@@ -371,12 +386,6 @@ class Repo:
371
386
  # Let's not assume the option exists, although it should.
372
387
  pass
373
388
 
374
- try:
375
- common_dir = (Path(self.git_dir) / "commondir").read_text().splitlines()[0].strip()
376
- self._common_dir = osp.join(self.git_dir, common_dir)
377
- except OSError:
378
- self._common_dir = ""
379
-
380
389
  # Adjust the working directory in case we are actually bare - we didn't know
381
390
  # that in the first place.
382
391
  if self._bare:
@@ -385,9 +394,15 @@ class Repo:
385
394
 
386
395
  self.working_dir: PathLike = self._working_tree_dir or self.common_dir
387
396
  self.git = self.GitCommandWrapperType(self.working_dir)
397
+ if common_dir_env is not None:
398
+ self.git.update_environment(GIT_DIR=os.fspath(self.git_dir), GIT_COMMON_DIR=os.fspath(self.common_dir))
399
+ elif git_dir_env is not None:
400
+ self.git.update_environment(GIT_DIR=os.fspath(self.git_dir))
401
+ if object_dir_env is not None:
402
+ self.git.update_environment(GIT_OBJECT_DIRECTORY=object_dir_env)
388
403
 
389
404
  # Special handling, in special times.
390
- rootpath = osp.join(self.common_dir, "objects")
405
+ rootpath = object_dir_env if object_dir_env is not None else osp.join(self.common_dir, "objects")
391
406
  if issubclass(odbt, GitCmdObjectDB):
392
407
  self.odb = odbt(rootpath, self.git)
393
408
  else:
@@ -988,7 +1003,7 @@ class Repo:
988
1003
  :return:
989
1004
  List of strings being pathnames of alternates
990
1005
  """
991
- alternates_path = osp.join(self.common_dir, "objects", "info", "alternates")
1006
+ alternates_path = osp.join(self.odb.root_path(), "info", "alternates")
992
1007
 
993
1008
  if osp.exists(alternates_path):
994
1009
  with open(alternates_path, "rb") as f:
@@ -1009,7 +1024,7 @@ class Repo:
1009
1024
  The method does not check for the existence of the paths in `alts`, as the
1010
1025
  caller is responsible.
1011
1026
  """
1012
- alternates_path = osp.join(self.common_dir, "objects", "info", "alternates")
1027
+ alternates_path = osp.join(self.odb.root_path(), "info", "alternates")
1013
1028
  if not alts:
1014
1029
  if osp.isfile(alternates_path):
1015
1030
  os.remove(alternates_path)
@@ -60,22 +60,61 @@ def touch(filename: str) -> str:
60
60
  def is_git_dir(d: PathLike) -> bool:
61
61
  """This is taken from the git setup.c:is_git_directory function.
62
62
 
63
+ .. note::
64
+ This function recognizes repositories using reftable through their
65
+ compatibility files, but GitPython's direct reference access does not support
66
+ reftable.
67
+
63
68
  :raise git.exc.WorkTreeRepositoryUnsupported:
64
69
  If it sees a worktree directory. It's quite hacky to do that here, but at least
65
70
  clearly indicates that we don't support it. There is the unlikely danger to
66
71
  throw if we see directories which just look like a worktree dir, but are none.
67
72
  """
68
73
  if osp.isdir(d):
69
- if (osp.isdir(osp.join(d, "objects")) or "GIT_OBJECT_DIRECTORY" in os.environ) and osp.isdir(
70
- osp.join(d, "refs")
71
- ):
72
- headref = osp.join(d, "HEAD")
73
- return osp.isfile(headref) or (osp.islink(headref) and os.readlink(headref).startswith("refs"))
74
- elif (
75
- osp.isfile(osp.join(d, "gitdir"))
76
- and osp.isfile(osp.join(d, "commondir"))
77
- and osp.isfile(osp.join(d, "gitfile"))
78
- ):
74
+ headref = osp.join(d, "HEAD")
75
+ if osp.islink(headref):
76
+ try:
77
+ valid_head = os.readlink(headref).startswith("refs/")
78
+ except OSError:
79
+ valid_head = False
80
+ else:
81
+ try:
82
+ with open(headref, "rb") as fp:
83
+ head = fp.read(256)
84
+ except OSError:
85
+ valid_head = False
86
+ else:
87
+ valid_head = (head.startswith(b"ref:") and head[4:].lstrip().startswith(b"refs/")) or bool(
88
+ re.match(rb"(?:[0-9A-Fa-f]{64}|[0-9A-Fa-f]{40})", head)
89
+ )
90
+
91
+ common_dir = os.getenv("GIT_COMMON_DIR")
92
+ if common_dir == "":
93
+ return False
94
+ if common_dir is None:
95
+ common_dir_file = Path(d) / "commondir"
96
+ try:
97
+ common_dir = os.fsdecode(common_dir_file.read_bytes()).rstrip("\r\n")
98
+ except FileNotFoundError:
99
+ if osp.lexists(common_dir_file):
100
+ return False
101
+ common_dir = os.fspath(d)
102
+ except (OSError, UnicodeError):
103
+ return False
104
+ else:
105
+ if not common_dir:
106
+ return False
107
+ try:
108
+ common_dir = osp.realpath(osp.join(d, common_dir))
109
+ except (OSError, ValueError):
110
+ return False
111
+
112
+ object_dir = os.getenv("GIT_OBJECT_DIRECTORY")
113
+ if object_dir is None:
114
+ object_dir = osp.join(common_dir, "objects")
115
+ if valid_head and osp.isdir(object_dir) and osp.isdir(osp.join(common_dir, "refs")):
116
+ return True
117
+ if osp.isfile(osp.join(d, "gitdir")) and osp.isfile(osp.join(d, "commondir")) and osp.isfile(headref):
79
118
  raise WorkTreeRepositoryUnsupported(d)
80
119
  return False
81
120
 
@@ -84,19 +123,20 @@ def find_worktree_git_dir(dotgit: PathLike) -> Optional[str]:
84
123
  """Search for a gitdir for this worktree."""
85
124
  try:
86
125
  statbuf = os.stat(dotgit)
87
- except OSError:
126
+ except (FileNotFoundError, NotADirectoryError):
88
127
  return None
89
- if not stat.S_ISREG(statbuf.st_mode):
128
+ if not stat.S_ISREG(statbuf.st_mode) or statbuf.st_size > (1 << 20):
90
129
  return None
91
130
 
92
131
  try:
93
- lines = Path(dotgit).read_text().splitlines()
94
- for key, value in [line.strip().split(": ") for line in lines]:
95
- if key == "gitdir":
96
- return value
97
- except ValueError:
98
- pass
99
- return None
132
+ with open(dotgit, "rb") as fp:
133
+ content_bytes = fp.read(statbuf.st_size)
134
+ if len(content_bytes) != statbuf.st_size:
135
+ return None
136
+ content = os.fsdecode(content_bytes).rstrip("\r\n")
137
+ except (OSError, UnicodeError):
138
+ return None
139
+ return content[8:] if len(content) >= 9 and content.startswith("gitdir: ") else None
100
140
 
101
141
 
102
142
  def find_submodule_git_dir(d: PathLike) -> Optional[PathLike]:
@@ -104,26 +144,18 @@ def find_submodule_git_dir(d: PathLike) -> Optional[PathLike]:
104
144
  if is_git_dir(d):
105
145
  return d
106
146
 
107
- try:
108
- with open(d) as fp:
109
- content = fp.read().rstrip()
110
- except IOError:
111
- # It's probably not a file.
112
- pass
113
- else:
114
- if content.startswith("gitdir: "):
115
- path = content[8:]
116
-
117
- if Git.is_cygwin():
118
- # Cygwin creates submodules prefixed with `/cygdrive/...`.
119
- # Cygwin git understands Cygwin paths much better than Windows ones.
120
- # Also the Cygwin tests are assuming Cygwin paths.
121
- path = cygpath(path)
122
- if not osp.isabs(path):
123
- path = osp.normpath(osp.join(osp.dirname(d), path))
124
- return find_submodule_git_dir(path)
125
- # END handle exception
126
- return None
147
+ path = find_worktree_git_dir(d)
148
+ if path is None:
149
+ return None
150
+
151
+ if Git.is_cygwin():
152
+ # Cygwin creates submodules prefixed with `/cygdrive/...`.
153
+ # Cygwin git understands Cygwin paths much better than Windows ones.
154
+ # Also the Cygwin tests are assuming Cygwin paths.
155
+ path = cygpath(path)
156
+ if not osp.isabs(path):
157
+ path = osp.normpath(osp.join(osp.dirname(d), path))
158
+ return path if is_git_dir(path) else None
127
159
 
128
160
 
129
161
  def short_to_long(odb: "GitCmdObjectDB", hexsha: str) -> Optional[bytes]:
@@ -858,10 +858,6 @@ class Actor:
858
858
  committers and authors or anything with a name and an email as mentioned in the git
859
859
  log entries."""
860
860
 
861
- # PRECOMPILED REGEX
862
- name_only_regex = re.compile(r"<(.*)>")
863
- name_email_regex = re.compile(r"(.*) <(.*?)>")
864
-
865
861
  # ENVIRONMENT VARIABLES
866
862
  # These are read when creating new commits.
867
863
  env_author_name = "GIT_AUTHOR_NAME"
@@ -906,18 +902,14 @@ class Actor:
906
902
  :return:
907
903
  :class:`Actor`
908
904
  """
909
- m = cls.name_email_regex.search(string)
910
- if m:
911
- name, email = m.groups()
912
- return Actor(name, email)
913
- else:
914
- m = cls.name_only_regex.search(string)
915
- if m:
916
- return Actor(m.group(1), None)
917
- # Assume the best and use the whole string as name.
918
- return Actor(string, None)
919
- # END special case name
920
- # END handle name/email matching
905
+ line = string.partition("\n")[0]
906
+ left_bracket = line.find("<")
907
+ right_bracket = line.find(">", left_bracket + 1)
908
+ if left_bracket >= 0 and right_bracket >= 0:
909
+ return Actor(line[:left_bracket].rstrip(), line[left_bracket + 1 : right_bracket])
910
+
911
+ # Assume the best and use the whole string as name.
912
+ return Actor(string, None)
921
913
 
922
914
  @classmethod
923
915
  def _main_actor(
gitpython-3.1.59/VERSION DELETED
@@ -1 +0,0 @@
1
- 3.1.59
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes