Import upstream version 3.1.27
Debian Janitor
2 years ago
43 | 43 | -Ram Rachum <ram _at_ rachum.com> |
44 | 44 | -Alba Mendez <me _at_ alba.sh> |
45 | 45 | -Robert Westman <robert _at_ byteflux.io> |
46 | -Hugo van Kemenade | |
46 | 47 | Portions derived from other open source works and are clearly marked. |
0 | 0 | Metadata-Version: 2.1 |
1 | 1 | Name: GitPython |
2 | Version: 3.1.24 | |
2 | Version: 3.1.27 | |
3 | 3 | Summary: GitPython is a python library used to interact with Git repositories |
4 | 4 | Home-page: https://github.com/gitpython-developers/GitPython |
5 | 5 | Author: Sebastian Thiel, Michael Trier |
6 | 6 | Author-email: byronimo@gmail.com, mtrier@gmail.com |
7 | 7 | License: BSD |
8 | Description: GitPython is a python library used to interact with Git repositories | |
9 | 8 | Platform: UNKNOWN |
10 | 9 | Classifier: Development Status :: 5 - Production/Stable |
11 | 10 | Classifier: Environment :: Console |
21 | 20 | Classifier: Programming Language :: Python :: 3.7 |
22 | 21 | Classifier: Programming Language :: Python :: 3.8 |
23 | 22 | Classifier: Programming Language :: Python :: 3.9 |
23 | Classifier: Programming Language :: Python :: 3.10 | |
24 | 24 | Requires-Python: >=3.7 |
25 | 25 | Description-Content-Type: text/markdown |
26 | License-File: LICENSE | |
27 | License-File: AUTHORS | |
28 | ||
29 | GitPython is a python library used to interact with Git repositories | |
30 |
0 | 0 | gitdb<5,>=4.0.1 |
1 | 1 | |
2 | [:python_version < "3.10"] | |
2 | [:python_version < "3.8"] | |
3 | 3 | typing-extensions>=3.7.4.3 |
0 | 0 | Metadata-Version: 2.1 |
1 | 1 | Name: GitPython |
2 | Version: 3.1.24 | |
2 | Version: 3.1.27 | |
3 | 3 | Summary: GitPython is a python library used to interact with Git repositories |
4 | 4 | Home-page: https://github.com/gitpython-developers/GitPython |
5 | 5 | Author: Sebastian Thiel, Michael Trier |
6 | 6 | Author-email: byronimo@gmail.com, mtrier@gmail.com |
7 | 7 | License: BSD |
8 | Description: GitPython is a python library used to interact with Git repositories | |
9 | 8 | Platform: UNKNOWN |
10 | 9 | Classifier: Development Status :: 5 - Production/Stable |
11 | 10 | Classifier: Environment :: Console |
21 | 20 | Classifier: Programming Language :: Python :: 3.7 |
22 | 21 | Classifier: Programming Language :: Python :: 3.8 |
23 | 22 | Classifier: Programming Language :: Python :: 3.9 |
23 | Classifier: Programming Language :: Python :: 3.10 | |
24 | 24 | Requires-Python: >=3.7 |
25 | 25 | Description-Content-Type: text/markdown |
26 | License-File: LICENSE | |
27 | License-File: AUTHORS | |
28 | ||
29 | GitPython is a python library used to interact with Git repositories | |
30 |
0 | 0 | ========= |
1 | 1 | Changelog |
2 | 2 | ========= |
3 | ||
4 | 3.1.28 | |
5 | ====== | |
6 | ||
7 | - Fix a vulenerability that could cause great slowdowns when encountering long remote path names | |
8 | when pulling/fetching. | |
9 | ||
10 | See the following for all changes. | |
11 | https://github.com/gitpython-developers/gitpython/milestone/58?closed=1 | |
12 | ||
13 | 3.1.27 | |
14 | ====== | |
15 | ||
16 | - Reduced startup time due to optimized imports. | |
17 | ||
18 | See the following for all changes. | |
19 | https://github.com/gitpython-developers/gitpython/milestone/57?closed=1 | |
20 | ||
21 | 3.1.26 | |
22 | ====== | |
23 | ||
24 | - Fixes a leaked file descriptor when reading the index, which would cause make writing a previously | |
25 | read index on windows impossible. | |
26 | See https://github.com/gitpython-developers/GitPython/issues/1395 for details. | |
27 | ||
28 | See the following for all changes. | |
29 | https://github.com/gitpython-developers/gitpython/milestone/56?closed=1 | |
30 | ||
31 | ||
32 | 3.1.25 | |
33 | ====== | |
34 | ||
35 | See the following for all changes. | |
36 | https://github.com/gitpython-developers/gitpython/milestone/55?closed=1 | |
3 | 37 | |
4 | 38 | |
5 | 39 | 3.1.24 |
7 | 7 | GitPython Tutorial |
8 | 8 | ================== |
9 | 9 | |
10 | GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, most of which explains a real-life usecase. | |
11 | ||
12 | All code presented here originated from `test_docs.py <https://github.com/gitpython-developers/GitPython/blob/main/test/test_docs.py>`_ to assure correctness. Knowing this should also allow you to more easily run the code for your own testing purposes, all you need is a developer installation of git-python. | |
10 | GitPython provides object model access to your git repository. This tutorial is composed of multiple sections, most of which explain a real-life use case. | |
11 | ||
12 | All code presented here originated from `test_docs.py <https://github.com/gitpython-developers/GitPython/blob/main/test/test_docs.py>`_ to assure correctness. Knowing this should also allow you to more easily run the code for your own testing purposes. All you need is a developer installation of git-python. | |
13 | 13 | |
14 | 14 | Meet the Repo type |
15 | 15 | ****************** |
13 | 13 | from typing import Optional |
14 | 14 | from git.types import PathLike |
15 | 15 | |
16 | __version__ = '3.1.24' | |
16 | __version__ = '3.1.27' | |
17 | 17 | |
18 | 18 | |
19 | 19 | #{ Initialization |
20 | 20 | def _init_externals() -> None: |
21 | 21 | """Initialize external projects by putting them into the path""" |
22 | if __version__ == '3.1.24' and 'PYOXIDIZER' not in os.environ: | |
22 | if __version__ == '3.1.27' and 'PYOXIDIZER' not in os.environ: | |
23 | 23 | sys.path.insert(1, osp.join(osp.dirname(__file__), 'ext', 'gitdb')) |
24 | 24 | |
25 | 25 | try: |
11 | 11 | from subprocess import ( |
12 | 12 | call, |
13 | 13 | Popen, |
14 | PIPE | |
14 | PIPE, | |
15 | DEVNULL | |
15 | 16 | ) |
16 | 17 | import subprocess |
17 | 18 | import threading |
872 | 873 | env=env, |
873 | 874 | cwd=cwd, |
874 | 875 | bufsize=-1, |
875 | stdin=istream, | |
876 | stdin=istream or DEVNULL, | |
876 | 877 | stderr=PIPE, |
877 | 878 | stdout=stdout_sink, |
878 | 879 | shell=shell is not None and shell or self.USE_SHELL, |
126 | 126 | |
127 | 127 | def _set_cache_(self, attr: str) -> None: |
128 | 128 | if attr == "entries": |
129 | # read the current index | |
130 | # try memory map for speed | |
131 | lfd = LockedFD(self._file_path) | |
132 | ok = False | |
133 | 129 | try: |
134 | fd = lfd.open(write=False, stream=False) | |
135 | ok = True | |
130 | fd = os.open(self._file_path, os.O_RDONLY) | |
136 | 131 | except OSError: |
137 | 132 | # in new repositories, there may be no index, which means we are empty |
138 | 133 | self.entries: Dict[Tuple[PathLike, StageType], IndexEntry] = {} |
139 | 134 | return None |
135 | # END exception handling | |
136 | ||
137 | try: | |
138 | stream = file_contents_ro(fd, stream=True, allow_mmap=True) | |
140 | 139 | finally: |
141 | if not ok: | |
142 | lfd.rollback() | |
143 | # END exception handling | |
144 | ||
145 | stream = file_contents_ro(fd, stream=True, allow_mmap=True) | |
146 | ||
147 | try: | |
148 | self._deserialize(stream) | |
149 | finally: | |
150 | lfd.rollback() | |
151 | # The handles will be closed on destruction | |
152 | # END read from default index on demand | |
140 | os.close(fd) | |
141 | ||
142 | self._deserialize(stream) | |
153 | 143 | else: |
154 | 144 | super(IndexFile, self)._set_cache_(attr) |
155 | 145 | |
983 | 973 | commit_date: Union[str, None] = None, |
984 | 974 | skip_hooks: bool = False) -> Commit: |
985 | 975 | """Commit the current default index file, creating a commit object. |
986 | For more information on the arguments, see tree.commit. | |
976 | For more information on the arguments, see Commit.create_from_tree(). | |
987 | 977 | |
988 | 978 | :note: If you have manually altered the .entries member of this instance, |
989 | 979 | don't forget to write() your changes to disk beforehand. |
2 | 2 | # NOTE: Autodoc hates it if this is a docstring |
3 | 3 | |
4 | 4 | from io import BytesIO |
5 | from pathlib import Path | |
5 | 6 | import os |
6 | 7 | from stat import ( |
7 | 8 | S_IFDIR, |
20 | 21 | force_text, |
21 | 22 | force_bytes, |
22 | 23 | is_posix, |
24 | is_win, | |
23 | 25 | safe_decode, |
24 | 26 | ) |
25 | 27 | from git.exc import ( |
73 | 75 | def hook_path(name: str, git_dir: PathLike) -> str: |
74 | 76 | """:return: path to the given named hook in the given git repository directory""" |
75 | 77 | return osp.join(git_dir, 'hooks', name) |
78 | ||
79 | ||
80 | def _has_file_extension(path): | |
81 | return osp.splitext(path)[1] | |
76 | 82 | |
77 | 83 | |
78 | 84 | def run_commit_hook(name: str, index: 'IndexFile', *args: str) -> None: |
88 | 94 | env = os.environ.copy() |
89 | 95 | env['GIT_INDEX_FILE'] = safe_decode(str(index.path)) |
90 | 96 | env['GIT_EDITOR'] = ':' |
97 | cmd = [hp] | |
91 | 98 | try: |
92 | cmd = subprocess.Popen([hp] + list(args), | |
99 | if is_win and not _has_file_extension(hp): | |
100 | # Windows only uses extensions to determine how to open files | |
101 | # (doesn't understand shebangs). Try using bash to run the hook. | |
102 | relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix() | |
103 | cmd = ["bash.exe", relative_hp] | |
104 | ||
105 | cmd = subprocess.Popen(cmd + list(args), | |
93 | 106 | env=env, |
94 | 107 | stdout=subprocess.PIPE, |
95 | 108 | stderr=subprocess.PIPE, |
3 | 3 | # This module is part of GitPython and is released under |
4 | 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php |
5 | 5 | import datetime |
6 | from subprocess import Popen | |
6 | from subprocess import Popen, PIPE | |
7 | 7 | from gitdb import IStream |
8 | 8 | from git.util import ( |
9 | 9 | hex_to_bin, |
12 | 12 | finalize_process |
13 | 13 | ) |
14 | 14 | from git.diff import Diffable |
15 | from git.cmd import Git | |
15 | 16 | |
16 | 17 | from .tree import Tree |
17 | 18 | from . import base |
38 | 39 | |
39 | 40 | # typing ------------------------------------------------------------------ |
40 | 41 | |
41 | from typing import Any, IO, Iterator, List, Sequence, Tuple, Union, TYPE_CHECKING, cast | |
42 | from typing import Any, IO, Iterator, List, Sequence, Tuple, Union, TYPE_CHECKING, cast, Dict | |
42 | 43 | |
43 | 44 | from git.types import PathLike, Literal |
44 | 45 | |
97 | 98 | :param binsha: 20 byte sha1 |
98 | 99 | :param parents: tuple( Commit, ... ) |
99 | 100 | is a tuple of commit ids or actual Commits |
100 | :param tree: Tree | |
101 | Tree object | |
101 | :param tree: Tree object | |
102 | 102 | :param author: Actor |
103 | 103 | is the author Actor object |
104 | 104 | :param authored_date: int_seconds_since_epoch |
314 | 314 | text = self.repo.git.diff(self.parents[0].hexsha, self.hexsha, '--', numstat=True) |
315 | 315 | return Stats._list_from_string(self.repo, text) |
316 | 316 | |
317 | @property | |
318 | def trailers(self) -> Dict: | |
319 | """Get the trailers of the message as dictionary | |
320 | ||
321 | Git messages can contain trailer information that are similar to RFC 822 | |
322 | e-mail headers (see: https://git-scm.com/docs/git-interpret-trailers). | |
323 | ||
324 | This funcions calls ``git interpret-trailers --parse`` onto the message | |
325 | to extract the trailer information. The key value pairs are stripped of | |
326 | leading and trailing whitespaces before they get saved into a dictionary. | |
327 | ||
328 | Valid message with trailer: | |
329 | ||
330 | .. code-block:: | |
331 | ||
332 | Subject line | |
333 | ||
334 | some body information | |
335 | ||
336 | another information | |
337 | ||
338 | key1: value1 | |
339 | key2 : value 2 with inner spaces | |
340 | ||
341 | dictionary will look like this: | |
342 | ||
343 | .. code-block:: | |
344 | ||
345 | { | |
346 | "key1": "value1", | |
347 | "key2": "value 2 with inner spaces" | |
348 | } | |
349 | ||
350 | :return: Dictionary containing whitespace stripped trailer information | |
351 | ||
352 | """ | |
353 | d = {} | |
354 | cmd = ['git', 'interpret-trailers', '--parse'] | |
355 | proc: Git.AutoInterrupt = self.repo.git.execute(cmd, as_process=True, istream=PIPE) # type: ignore | |
356 | trailer: str = proc.communicate(str(self.message).encode())[0].decode() | |
357 | if trailer.endswith('\n'): | |
358 | trailer = trailer[0:-1] | |
359 | if trailer != '': | |
360 | for line in trailer.split('\n'): | |
361 | key, value = line.split(':', 1) | |
362 | d[key.strip()] = value.strip() | |
363 | return d | |
364 | ||
317 | 365 | @ classmethod |
318 | 366 | def _iter_from_process_or_stream(cls, repo: 'Repo', proc_or_stream: Union[Popen, IO]) -> Iterator['Commit']: |
319 | 367 | """Parse out commit information into a list of Commit objects |
2 | 2 | import logging |
3 | 3 | import os |
4 | 4 | import stat |
5 | ||
6 | from unittest import SkipTest | |
7 | 5 | import uuid |
8 | 6 | |
9 | 7 | import git |
933 | 931 | rmtree(str(wtd)) |
934 | 932 | except Exception as ex: |
935 | 933 | if HIDE_WINDOWS_KNOWN_ERRORS: |
934 | from unittest import SkipTest | |
936 | 935 | raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex |
937 | 936 | raise |
938 | 937 | # END delete tree if possible |
944 | 943 | rmtree(git_dir) |
945 | 944 | except Exception as ex: |
946 | 945 | if HIDE_WINDOWS_KNOWN_ERRORS: |
946 | from unittest import SkipTest | |
947 | 947 | raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex |
948 | 948 | else: |
949 | 949 | raise |
128 | 128 | k_config_remote_ref = "merge" # branch to merge from remote |
129 | 129 | |
130 | 130 | @classmethod |
131 | def delete(cls, repo: 'Repo', *heads: 'Head', force: bool = False, **kwargs: Any) -> None: | |
131 | def delete(cls, repo: 'Repo', *heads: 'Union[Head, str]', force: bool = False, **kwargs: Any) -> None: | |
132 | 132 | """Delete the given heads |
133 | 133 | |
134 | 134 | :param force: |
36 | 36 | # super is Reference |
37 | 37 | return super(RemoteReference, cls).iter_items(repo, common_path) |
38 | 38 | |
39 | # The Head implementation of delete also accepts strs, but this | |
40 | # implementation does not. mypy doesn't have a way of representing | |
41 | # tightening the types of arguments in subclasses and recommends Any or | |
42 | # "type: ignore". (See https://github.com/python/typing/issues/241) | |
39 | 43 | @ classmethod |
40 | def delete(cls, repo: 'Repo', *refs: 'RemoteReference', **kwargs: Any) -> None: | |
44 | def delete(cls, repo: 'Repo', *refs: 'RemoteReference', # type: ignore | |
45 | **kwargs: Any) -> None: | |
41 | 46 | """Delete the given remote references |
42 | 47 | |
43 | 48 | :note: |
234 | 234 | raise NotImplementedError |
235 | 235 | |
236 | 236 | |
237 | class PushInfoList(IterableList[PushInfo]): | |
238 | def __new__(cls) -> 'PushInfoList': | |
239 | return cast(PushInfoList, IterableList.__new__(cls, 'push_infos')) | |
240 | ||
241 | def __init__(self) -> None: | |
242 | super().__init__('push_infos') | |
243 | self.error: Optional[Exception] = None | |
244 | ||
245 | def raise_if_error(self) -> None: | |
246 | """ | |
247 | Raise an exception if any ref failed to push. | |
248 | """ | |
249 | if self.error: | |
250 | raise self.error | |
251 | ||
252 | ||
237 | 253 | class FetchInfo(IterableObj, object): |
238 | 254 | |
239 | 255 | """ |
256 | 272 | NEW_TAG, NEW_HEAD, HEAD_UPTODATE, TAG_UPDATE, REJECTED, FORCED_UPDATE, \ |
257 | 273 | FAST_FORWARD, ERROR = [1 << x for x in range(8)] |
258 | 274 | |
259 | _re_fetch_result = re.compile(r'^\s*(.) (\[?[\w\s\.$@]+\]?)\s+(.+) -> ([^\s]+)( \(.*\)?$)?') | |
275 | _re_fetch_result = re.compile(r'^\s*(.) (\[[\w\s\.$@]+\]|[\w\.$@]+)\s+(.+) -> ([^\s]+)( \(.*\)?$)?') | |
260 | 276 | |
261 | 277 | _flag_map: Dict[flagKeyLiteral, int] = { |
262 | 278 | '!': ERROR, |
664 | 680 | return cls(repo, name) |
665 | 681 | |
666 | 682 | # add is an alias |
667 | add = create | |
683 | @ classmethod | |
684 | def add(cls, repo: 'Repo', name: str, url: str, **kwargs: Any) -> 'Remote': | |
685 | return cls.create(repo, name, url, **kwargs) | |
668 | 686 | |
669 | 687 | @ classmethod |
670 | 688 | def remove(cls, repo: 'Repo', name: str) -> str: |
771 | 789 | |
772 | 790 | def _get_push_info(self, proc: 'Git.AutoInterrupt', |
773 | 791 | progress: Union[Callable[..., Any], RemoteProgress, None], |
774 | kill_after_timeout: Union[None, float] = None) -> IterableList[PushInfo]: | |
792 | kill_after_timeout: Union[None, float] = None) -> PushInfoList: | |
775 | 793 | progress = to_progress_instance(progress) |
776 | 794 | |
777 | 795 | # read progress information from stderr |
779 | 797 | # read the lines manually as it will use carriage returns between the messages |
780 | 798 | # to override the previous one. This is why we read the bytes manually |
781 | 799 | progress_handler = progress.new_message_handler() |
782 | output: IterableList[PushInfo] = IterableList('push_infos') | |
800 | output: PushInfoList = PushInfoList() | |
783 | 801 | |
784 | 802 | def stdout_handler(line: str) -> None: |
785 | 803 | try: |
793 | 811 | stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or '' |
794 | 812 | try: |
795 | 813 | proc.wait(stderr=stderr_text) |
796 | except Exception: | |
814 | except Exception as e: | |
797 | 815 | # This is different than fetch (which fails if there is any std_err |
798 | 816 | # even if there is an output) |
799 | 817 | if not output: |
800 | 818 | raise |
801 | 819 | elif stderr_text: |
802 | 820 | log.warning("Error lines received while fetching: %s", stderr_text) |
821 | output.error = e | |
803 | 822 | |
804 | 823 | return output |
805 | 824 |
428 | 428 | :return: newly created Head Reference""" |
429 | 429 | return Head.create(self, path, commit, logmsg, force) |
430 | 430 | |
431 | def delete_head(self, *heads: 'Head', **kwargs: Any) -> None: | |
431 | def delete_head(self, *heads: 'Union[str, Head]', **kwargs: Any) -> None: | |
432 | 432 | """Delete the given heads |
433 | 433 | |
434 | 434 | :param kwargs: Additional keyword arguments to be passed to git-branch""" |
19 | 19 | import stat |
20 | 20 | from sys import maxsize |
21 | 21 | import time |
22 | from unittest import SkipTest | |
23 | 22 | from urllib.parse import urlsplit, urlunsplit |
24 | 23 | import warnings |
25 | 24 | |
129 | 128 | func(path) # Will scream if still not possible to delete. |
130 | 129 | except Exception as ex: |
131 | 130 | if HIDE_WINDOWS_KNOWN_ERRORS: |
131 | from unittest import SkipTest | |
132 | 132 | raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex |
133 | 133 | raise |
134 | 134 |
0 | 0 | gitdb>=4.0.1,<5 |
1 | typing-extensions>=3.7.4.3;python_version<"3.10" | |
1 | typing-extensions>=3.7.4.3;python_version<"3.8" |