scmrepo 2.0.4__tar.gz → 3.3.1__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.
Potentially problematic release.
This version of scmrepo might be problematic. Click here for more details.
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.github/workflows/release.yaml +2 -2
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.github/workflows/tests.yaml +3 -3
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.pre-commit-config.yaml +1 -1
- {scmrepo-2.0.4 → scmrepo-3.3.1}/CONTRIBUTING.rst +1 -1
- {scmrepo-2.0.4/src/scmrepo.egg-info → scmrepo-3.3.1}/PKG-INFO +21 -21
- {scmrepo-2.0.4 → scmrepo-3.3.1}/noxfile.py +2 -1
- {scmrepo-2.0.4 → scmrepo-3.3.1}/pyproject.toml +22 -23
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/asyn.py +4 -3
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/base.py +1 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/fs.py +6 -6
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/__init__.py +13 -14
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/base.py +5 -4
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/dulwich/__init__.py +33 -25
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py +14 -14
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/dulwich/client.py +5 -4
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/gitpython.py +9 -13
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/pygit2/__init__.py +27 -32
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/pygit2/callbacks.py +11 -6
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/pygit2/filter.py +2 -2
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/config.py +5 -4
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/credentials.py +10 -12
- scmrepo-3.3.1/src/scmrepo/git/lfs/client.py +308 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/fetch.py +9 -8
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/object.py +2 -2
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/pointer.py +2 -2
- scmrepo-3.3.1/src/scmrepo/git/lfs/progress.py +159 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/smudge.py +4 -1
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/storage.py +7 -4
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/objects.py +3 -2
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/noscm.py +3 -2
- scmrepo-3.3.1/src/scmrepo/urls.py +21 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1/src/scmrepo.egg-info}/PKG-INFO +21 -21
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo.egg-info/SOURCES.txt +2 -0
- scmrepo-3.3.1/src/scmrepo.egg-info/requires.txt +29 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/conftest.py +5 -4
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_dulwich.py +5 -5
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_git.py +6 -6
- scmrepo-3.3.1/tests/test_lfs.py +284 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_pygit2.py +2 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_scmrepo.py +1 -2
- scmrepo-3.3.1/tests/test_urls.py +31 -0
- scmrepo-2.0.4/src/scmrepo/git/lfs/client.py +0 -177
- scmrepo-2.0.4/src/scmrepo/git/lfs/progress.py +0 -61
- scmrepo-2.0.4/src/scmrepo.egg-info/requires.txt +0 -31
- scmrepo-2.0.4/tests/test_lfs.py +0 -76
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.coveragerc +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.cruft.json +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.gitattributes +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.github/dependabot.yml +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.github/workflows/update-template.yaml +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/.gitignore +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/CODE_OF_CONDUCT.rst +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/LICENSE +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/README.rst +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/setup.cfg +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/__init__.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/exceptions.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/backend/__init__.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/__init__.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/lfs/exceptions.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/git/stash.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/progress.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/py.typed +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo/utils.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo.egg-info/dependency_links.txt +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/src/scmrepo.egg-info/top_level.txt +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/__init__.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/docker-compose.yml +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/git-init/git.sh +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_credentials.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_fs.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_noscm.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/test_stash.py +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/user.key +0 -0
- {scmrepo-2.0.4 → scmrepo-3.3.1}/tests/user.key.pub +0 -0
|
@@ -17,12 +17,12 @@ jobs:
|
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
steps:
|
|
19
19
|
- name: Check out the repository
|
|
20
|
-
uses: actions/checkout@
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
21
|
with:
|
|
22
22
|
fetch-depth: 0
|
|
23
23
|
|
|
24
24
|
- name: Set up Python 3.10
|
|
25
|
-
uses: actions/setup-python@
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
26
|
with:
|
|
27
27
|
python-version: '3.10'
|
|
28
28
|
|
|
@@ -21,16 +21,16 @@ jobs:
|
|
|
21
21
|
fail-fast: false
|
|
22
22
|
matrix:
|
|
23
23
|
os: [ubuntu-20.04, windows-latest, macos-latest]
|
|
24
|
-
pyv: ['3.
|
|
24
|
+
pyv: ['3.9', '3.10', '3.11', '3.12']
|
|
25
25
|
|
|
26
26
|
steps:
|
|
27
27
|
- name: Check out the repository
|
|
28
|
-
uses: actions/checkout@
|
|
28
|
+
uses: actions/checkout@v4
|
|
29
29
|
with:
|
|
30
30
|
fetch-depth: 0
|
|
31
31
|
|
|
32
32
|
- name: Set up Python ${{ matrix.pyv }}
|
|
33
|
-
uses: actions/setup-python@
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
34
|
with:
|
|
35
35
|
python-version: ${{ matrix.pyv }}
|
|
36
36
|
|
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scmrepo
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Summary: scmrepo
|
|
5
5
|
Author-email: Iterative <support@dvc.org>
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Issues, https://github.com/iterative/scmrepo/issues
|
|
8
8
|
Project-URL: Source, https://github.com/iterative/scmrepo
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Development Status :: 4 - Beta
|
|
15
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
16
|
Description-Content-Type: text/x-rst
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: gitpython>3
|
|
19
19
|
Requires-Dist: dulwich>=0.21.6
|
|
20
|
-
Requires-Dist: pygit2>=1.
|
|
20
|
+
Requires-Dist: pygit2>=1.14.0
|
|
21
21
|
Requires-Dist: pygtrie>=2.3.2
|
|
22
|
-
Requires-Dist: fsspec>=
|
|
22
|
+
Requires-Dist: fsspec[tqdm]>=2024.2.0
|
|
23
23
|
Requires-Dist: pathspec>=0.9.0
|
|
24
24
|
Requires-Dist: asyncssh<3,>=2.13.1
|
|
25
25
|
Requires-Dist: funcy>=1.14
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist: dvc-http>=2.29.0
|
|
26
|
+
Requires-Dist: aiohttp-retry>=2.5.0
|
|
27
|
+
Requires-Dist: tqdm
|
|
29
28
|
Provides-Extra: tests
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist: pytest
|
|
33
|
-
Requires-Dist: pytest-
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist: pytest-
|
|
36
|
-
Requires-Dist: pytest-
|
|
37
|
-
Requires-Dist: pytest-
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist: paramiko==3.3.1; extra == "tests"
|
|
40
|
-
Requires-Dist: types-certifi==2021.10.8.3; extra == "tests"
|
|
41
|
-
Requires-Dist: types-mock==5.1.0.2; extra == "tests"
|
|
42
|
-
Requires-Dist: types-paramiko==3.4.0.20240120; extra == "tests"
|
|
29
|
+
Requires-Dist: aioresponses<0.8,>=0.7; extra == "tests"
|
|
30
|
+
Requires-Dist: paramiko<4,>=3.4.0; extra == "tests"
|
|
31
|
+
Requires-Dist: pytest<9,>=7; extra == "tests"
|
|
32
|
+
Requires-Dist: pytest-asyncio<1,>=0.23.2; extra == "tests"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "tests"
|
|
34
|
+
Requires-Dist: pytest-docker<4,>=1; extra == "tests"
|
|
35
|
+
Requires-Dist: pytest-mock; extra == "tests"
|
|
36
|
+
Requires-Dist: pytest-sugar; extra == "tests"
|
|
37
|
+
Requires-Dist: pytest-test-utils<0.2,>=0.1.0; extra == "tests"
|
|
43
38
|
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: mypy==1.9.0; extra == "dev"
|
|
44
40
|
Requires-Dist: scmrepo[tests]; extra == "dev"
|
|
41
|
+
Requires-Dist: types-certifi; extra == "dev"
|
|
42
|
+
Requires-Dist: types-mock; extra == "dev"
|
|
43
|
+
Requires-Dist: types-paramiko; extra == "dev"
|
|
44
|
+
Requires-Dist: types-tqdm; extra == "dev"
|
|
45
45
|
|
|
46
46
|
scmrepo
|
|
47
47
|
=======
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Automation using nox."""
|
|
2
|
+
|
|
2
3
|
import glob
|
|
3
4
|
import os
|
|
4
5
|
|
|
@@ -9,7 +10,7 @@ nox.options.sessions = "lint", "tests"
|
|
|
9
10
|
locations = "src", "tests"
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
@nox.session(python=["3.
|
|
13
|
+
@nox.session(python=["3.9", "3.10", "3.11", "3.12"])
|
|
13
14
|
def tests(session: nox.Session) -> None:
|
|
14
15
|
session.install(".[tests]")
|
|
15
16
|
session.run(
|
|
@@ -12,26 +12,25 @@ license = {text = "Apache-2.0"}
|
|
|
12
12
|
authors = [{ name = "Iterative", email = "support@dvc.org" }]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Programming Language :: Python :: 3",
|
|
15
|
-
"Programming Language :: Python :: 3.8",
|
|
16
15
|
"Programming Language :: Python :: 3.9",
|
|
17
16
|
"Programming Language :: Python :: 3.10",
|
|
18
17
|
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
19
|
"Development Status :: 4 - Beta",
|
|
20
20
|
]
|
|
21
|
-
requires-python = ">=3.
|
|
21
|
+
requires-python = ">=3.9"
|
|
22
22
|
dynamic = ["version"]
|
|
23
23
|
dependencies = [
|
|
24
24
|
"gitpython>3",
|
|
25
25
|
"dulwich>=0.21.6",
|
|
26
|
-
"pygit2>=1.
|
|
26
|
+
"pygit2>=1.14.0",
|
|
27
27
|
"pygtrie>=2.3.2",
|
|
28
|
-
"fsspec>=
|
|
28
|
+
"fsspec[tqdm]>=2024.2.0",
|
|
29
29
|
"pathspec>=0.9.0",
|
|
30
30
|
"asyncssh>=2.13.1,<3",
|
|
31
31
|
"funcy>=1.14",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"dvc-http>=2.29.0",
|
|
32
|
+
"aiohttp-retry>=2.5.0",
|
|
33
|
+
"tqdm",
|
|
35
34
|
]
|
|
36
35
|
|
|
37
36
|
[project.urls]
|
|
@@ -40,27 +39,27 @@ Source = "https://github.com/iterative/scmrepo"
|
|
|
40
39
|
|
|
41
40
|
[project.optional-dependencies]
|
|
42
41
|
tests = [
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"pytest
|
|
46
|
-
"pytest-
|
|
47
|
-
"
|
|
48
|
-
"pytest-
|
|
49
|
-
"pytest-
|
|
50
|
-
|
|
51
|
-
"pytest-
|
|
52
|
-
"mock==5.1.0",
|
|
53
|
-
"paramiko==3.3.1",
|
|
54
|
-
"types-certifi==2021.10.8.3",
|
|
55
|
-
"types-mock==5.1.0.2",
|
|
56
|
-
"types-paramiko==3.4.0.20240120",
|
|
42
|
+
"aioresponses>=0.7,<0.8",
|
|
43
|
+
"paramiko>=3.4.0,<4",
|
|
44
|
+
"pytest>=7,<9",
|
|
45
|
+
"pytest-asyncio>=0.23.2,<1",
|
|
46
|
+
"pytest-cov>=4.1.0",
|
|
47
|
+
"pytest-docker>=1,<4",
|
|
48
|
+
"pytest-mock",
|
|
49
|
+
"pytest-sugar",
|
|
50
|
+
"pytest-test-utils>=0.1.0,<0.2",
|
|
57
51
|
]
|
|
58
52
|
dev = [
|
|
53
|
+
"mypy==1.9.0",
|
|
59
54
|
"scmrepo[tests]",
|
|
55
|
+
"types-certifi",
|
|
56
|
+
"types-mock",
|
|
57
|
+
"types-paramiko",
|
|
58
|
+
"types-tqdm",
|
|
60
59
|
]
|
|
61
60
|
|
|
62
61
|
[tool.setuptools.package-data]
|
|
63
|
-
|
|
62
|
+
scmrepo = ["py.typed"]
|
|
64
63
|
|
|
65
64
|
[tool.setuptools.packages.find]
|
|
66
65
|
where = ["src"]
|
|
@@ -72,6 +71,7 @@ markers = [
|
|
|
72
71
|
"skip_git_backend: skip tests for given backend",
|
|
73
72
|
"slow: mark test as slow to run",
|
|
74
73
|
]
|
|
74
|
+
asyncio_mode = "auto"
|
|
75
75
|
|
|
76
76
|
[tool.coverage.run]
|
|
77
77
|
branch = true
|
|
@@ -109,7 +109,6 @@ files = ["src", "tests"]
|
|
|
109
109
|
[[tool.mypy.overrides]]
|
|
110
110
|
module = [
|
|
111
111
|
"pygtrie",
|
|
112
|
-
"dvc_http.*",
|
|
113
112
|
"funcy",
|
|
114
113
|
"git",
|
|
115
114
|
"gitdb.*",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""DVC re-implementation of fsspec's dedicated async event loop."""
|
|
2
|
+
|
|
2
3
|
import asyncio
|
|
3
4
|
import os
|
|
4
5
|
import threading
|
|
5
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Optional
|
|
6
7
|
|
|
7
8
|
from fsspec.asyn import ( # noqa: F401, pylint:disable=unused-import
|
|
8
9
|
_selector_policy,
|
|
@@ -11,9 +12,9 @@ from fsspec.asyn import ( # noqa: F401, pylint:disable=unused-import
|
|
|
11
12
|
)
|
|
12
13
|
|
|
13
14
|
# dedicated async IO thread
|
|
14
|
-
iothread:
|
|
15
|
+
iothread: list[Optional[threading.Thread]] = [None]
|
|
15
16
|
# global DVC event loop
|
|
16
|
-
default_loop:
|
|
17
|
+
default_loop: list[Optional[asyncio.AbstractEventLoop]] = [None]
|
|
17
18
|
lock = threading.Lock()
|
|
18
19
|
|
|
19
20
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import errno
|
|
2
2
|
import os
|
|
3
3
|
import posixpath
|
|
4
|
-
from typing import TYPE_CHECKING, Any, BinaryIO, Callable,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, Optional
|
|
5
5
|
|
|
6
6
|
from fsspec.callbacks import _DEFAULT_CALLBACK
|
|
7
7
|
from fsspec.spec import AbstractFileSystem
|
|
@@ -33,8 +33,8 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
33
33
|
self,
|
|
34
34
|
path: Optional[str] = None,
|
|
35
35
|
rev: Optional[str] = None,
|
|
36
|
-
scm: "Git" = None,
|
|
37
|
-
trie: "GitTrie" = None,
|
|
36
|
+
scm: Optional["Git"] = None,
|
|
37
|
+
trie: Optional["GitTrie"] = None,
|
|
38
38
|
rev_resolver: Optional[Callable[["Git", str], str]] = None,
|
|
39
39
|
**kwargs,
|
|
40
40
|
):
|
|
@@ -169,7 +169,7 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
169
169
|
def as_posix(cls, path):
|
|
170
170
|
return path
|
|
171
171
|
|
|
172
|
-
def _get_key(self, path: str) ->
|
|
172
|
+
def _get_key(self, path: str) -> tuple[str, ...]:
|
|
173
173
|
path = self.abspath(path)
|
|
174
174
|
if path == self.root_marker:
|
|
175
175
|
return ()
|
|
@@ -184,7 +184,7 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
184
184
|
mode: str = "rb",
|
|
185
185
|
block_size: Optional[int] = None,
|
|
186
186
|
autocommit: bool = True,
|
|
187
|
-
cache_options: Optional[
|
|
187
|
+
cache_options: Optional[dict] = None,
|
|
188
188
|
raw: bool = False,
|
|
189
189
|
**kwargs: Any,
|
|
190
190
|
) -> BinaryIO:
|
|
@@ -204,7 +204,7 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
204
204
|
errno.EISDIR, os.strerror(errno.EISDIR), path
|
|
205
205
|
) from exc
|
|
206
206
|
|
|
207
|
-
def info(self, path: str, **kwargs: Any) ->
|
|
207
|
+
def info(self, path: str, **kwargs: Any) -> dict[str, Any]:
|
|
208
208
|
key = self._get_key(path)
|
|
209
209
|
try:
|
|
210
210
|
# NOTE: to avoid wasting time computing object size, trie.info
|
|
@@ -5,18 +5,14 @@ import os
|
|
|
5
5
|
import re
|
|
6
6
|
import typing
|
|
7
7
|
from collections import OrderedDict
|
|
8
|
-
from collections.abc import Mapping
|
|
8
|
+
from collections.abc import Iterable, Mapping
|
|
9
9
|
from contextlib import contextmanager
|
|
10
10
|
from functools import partialmethod
|
|
11
11
|
from typing import (
|
|
12
12
|
TYPE_CHECKING,
|
|
13
13
|
Callable,
|
|
14
14
|
ClassVar,
|
|
15
|
-
Dict,
|
|
16
|
-
Iterable,
|
|
17
15
|
Optional,
|
|
18
|
-
Tuple,
|
|
19
|
-
Type,
|
|
20
16
|
Union,
|
|
21
17
|
)
|
|
22
18
|
|
|
@@ -44,14 +40,14 @@ if TYPE_CHECKING:
|
|
|
44
40
|
|
|
45
41
|
logger = logging.getLogger(__name__)
|
|
46
42
|
|
|
47
|
-
BackendCls =
|
|
43
|
+
BackendCls = type[BaseGitBackend]
|
|
48
44
|
|
|
49
45
|
|
|
50
46
|
_LOW_PRIO_BACKENDS = ("gitpython",)
|
|
51
47
|
|
|
52
48
|
|
|
53
49
|
class GitBackends(Mapping):
|
|
54
|
-
DEFAULT: ClassVar[
|
|
50
|
+
DEFAULT: ClassVar[dict[str, BackendCls]] = {
|
|
55
51
|
"dulwich": DulwichBackend,
|
|
56
52
|
"pygit2": Pygit2Backend,
|
|
57
53
|
"gitpython": GitPythonBackend,
|
|
@@ -72,7 +68,7 @@ class GitBackends(Mapping):
|
|
|
72
68
|
selected = selected or list(self.DEFAULT)
|
|
73
69
|
self.backends = OrderedDict((key, self.DEFAULT[key]) for key in selected)
|
|
74
70
|
|
|
75
|
-
self.initialized:
|
|
71
|
+
self.initialized: dict[str, BaseGitBackend] = {}
|
|
76
72
|
|
|
77
73
|
self.args = args
|
|
78
74
|
self.kwargs = kwargs
|
|
@@ -146,7 +142,7 @@ class Git(Base):
|
|
|
146
142
|
def clone(
|
|
147
143
|
cls,
|
|
148
144
|
url: str,
|
|
149
|
-
to_path: str,
|
|
145
|
+
to_path: Union[str, os.PathLike[str]],
|
|
150
146
|
rev: Optional[str] = None,
|
|
151
147
|
bare: bool = False,
|
|
152
148
|
mirror: bool = False,
|
|
@@ -169,7 +165,7 @@ class Git(Base):
|
|
|
169
165
|
return rev and cls.RE_HEXSHA.search(rev)
|
|
170
166
|
|
|
171
167
|
@classmethod
|
|
172
|
-
def split_ref_pattern(cls, ref: str) ->
|
|
168
|
+
def split_ref_pattern(cls, ref: str) -> tuple[str, str]:
|
|
173
169
|
name = cls.BAD_REF_CHARS_RE.split(ref, maxsplit=1)[0]
|
|
174
170
|
return name, ref[len(name) :]
|
|
175
171
|
|
|
@@ -323,13 +319,16 @@ class Git(Base):
|
|
|
323
319
|
|
|
324
320
|
@classmethod
|
|
325
321
|
def init(
|
|
326
|
-
cls,
|
|
322
|
+
cls,
|
|
323
|
+
path: Union[str, os.PathLike[str]],
|
|
324
|
+
bare: bool = False,
|
|
325
|
+
_backend: Optional[str] = None,
|
|
327
326
|
) -> "Git":
|
|
328
327
|
for name, backend in GitBackends.DEFAULT.items():
|
|
329
328
|
if _backend and name != _backend:
|
|
330
329
|
continue
|
|
331
330
|
try:
|
|
332
|
-
backend.init(path, bare=bare)
|
|
331
|
+
backend.init(os.fsdecode(path), bare=bare)
|
|
333
332
|
# TODO: reuse created object instead of initializing a new one.
|
|
334
333
|
return cls(path)
|
|
335
334
|
except NotImplementedError:
|
|
@@ -492,8 +491,8 @@ class Git(Base):
|
|
|
492
491
|
base: Optional[str] = None,
|
|
493
492
|
match: Optional[str] = None,
|
|
494
493
|
exclude: Optional[str] = None,
|
|
495
|
-
) ->
|
|
496
|
-
results:
|
|
494
|
+
) -> dict[str, Optional[str]]:
|
|
495
|
+
results: dict[str, Optional[str]] = {}
|
|
497
496
|
remained_revs = set()
|
|
498
497
|
if base == "refs/heads":
|
|
499
498
|
current_rev = self.get_rev()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
from collections.abc import Iterable, Mapping
|
|
3
4
|
from enum import Enum
|
|
4
|
-
from typing import TYPE_CHECKING, Callable,
|
|
5
|
+
from typing import TYPE_CHECKING, Callable, Optional, Union
|
|
5
6
|
|
|
6
7
|
from scmrepo.exceptions import SCMError
|
|
7
8
|
from scmrepo.git.objects import GitObject
|
|
@@ -46,7 +47,7 @@ class BaseGitBackend(ABC):
|
|
|
46
47
|
@abstractmethod
|
|
47
48
|
def clone(
|
|
48
49
|
url: str,
|
|
49
|
-
to_path: str,
|
|
50
|
+
to_path: Union[str, os.PathLike[str]],
|
|
50
51
|
shallow_branch: Optional[str] = None,
|
|
51
52
|
progress: Optional[Callable[["GitProgressEvent"], None]] = None,
|
|
52
53
|
bare: bool = False,
|
|
@@ -281,7 +282,7 @@ class BaseGitBackend(ABC):
|
|
|
281
282
|
ref: str,
|
|
282
283
|
message: Optional[str] = None,
|
|
283
284
|
include_untracked: bool = False,
|
|
284
|
-
) ->
|
|
285
|
+
) -> tuple[Optional[str], bool]:
|
|
285
286
|
"""Push a commit onto the specified stash.
|
|
286
287
|
|
|
287
288
|
Returns a tuple of the form (rev, need_reset) where need_reset
|
|
@@ -347,7 +348,7 @@ class BaseGitBackend(ABC):
|
|
|
347
348
|
@abstractmethod
|
|
348
349
|
def status(
|
|
349
350
|
self, ignored: bool = False, untracked_files: str = "all"
|
|
350
|
-
) ->
|
|
351
|
+
) -> tuple[Mapping[str, Iterable[str]], Iterable[str], Iterable[str]]:
|
|
351
352
|
"""Return tuple of (staged_files, unstaged_files, untracked_files).
|
|
352
353
|
|
|
353
354
|
staged_files will be a dict mapping status (add, delete, modify) to a
|
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import stat
|
|
7
|
+
from collections.abc import Iterable, Iterator, Mapping
|
|
7
8
|
from contextlib import closing
|
|
8
9
|
from functools import partial
|
|
9
10
|
from io import BytesIO, StringIO
|
|
@@ -11,13 +12,7 @@ from typing import (
|
|
|
11
12
|
TYPE_CHECKING,
|
|
12
13
|
Any,
|
|
13
14
|
Callable,
|
|
14
|
-
Dict,
|
|
15
|
-
Iterable,
|
|
16
|
-
Iterator,
|
|
17
|
-
List,
|
|
18
|
-
Mapping,
|
|
19
15
|
Optional,
|
|
20
|
-
Tuple,
|
|
21
16
|
Union,
|
|
22
17
|
)
|
|
23
18
|
|
|
@@ -151,18 +146,18 @@ class DulwichConfig(Config):
|
|
|
151
146
|
return self._config.encoding
|
|
152
147
|
return self._config.backends[0].encoding
|
|
153
148
|
|
|
154
|
-
def get(self, section:
|
|
149
|
+
def get(self, section: tuple[str, ...], name: str) -> str:
|
|
155
150
|
"""Return the specified setting as a string."""
|
|
156
151
|
return self._config.get(section, name).decode(self.encoding)
|
|
157
152
|
|
|
158
|
-
def get_bool(self, section:
|
|
153
|
+
def get_bool(self, section: tuple[str, ...], name: str) -> bool:
|
|
159
154
|
"""Return the specified setting as a boolean."""
|
|
160
155
|
value = self._config.get_boolean(section, name)
|
|
161
156
|
if value is None:
|
|
162
157
|
raise ValueError("setting is not a valid boolean")
|
|
163
158
|
return value
|
|
164
159
|
|
|
165
|
-
def get_multivar(self, section:
|
|
160
|
+
def get_multivar(self, section: tuple[str, ...], name: str) -> Iterator[str]:
|
|
166
161
|
"""Iterate over string values in the specified multivar setting."""
|
|
167
162
|
for value in self._config.get_multivar(section, name):
|
|
168
163
|
yield value.decode(self.encoding)
|
|
@@ -199,17 +194,17 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
199
194
|
except NotGitRepository as exc:
|
|
200
195
|
raise SCMError(f"{root_dir} is not a git repository") from exc
|
|
201
196
|
|
|
202
|
-
self._submodules:
|
|
197
|
+
self._submodules: dict[str, str] = self._find_submodules()
|
|
203
198
|
self._stashes: dict = {}
|
|
204
199
|
|
|
205
|
-
def _find_submodules(self) ->
|
|
200
|
+
def _find_submodules(self) -> dict[str, str]:
|
|
206
201
|
"""Return dict mapping submodule names to submodule paths.
|
|
207
202
|
|
|
208
203
|
Submodule paths will be relative to Git repo root.
|
|
209
204
|
"""
|
|
210
205
|
from dulwich.config import ConfigFile, parse_submodules
|
|
211
206
|
|
|
212
|
-
submodules:
|
|
207
|
+
submodules: dict[str, str] = {}
|
|
213
208
|
config_path = os.path.join(self.root_dir, ".gitmodules")
|
|
214
209
|
if os.path.isfile(config_path):
|
|
215
210
|
config = ConfigFile.from_path(config_path)
|
|
@@ -228,7 +223,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
228
223
|
def clone(
|
|
229
224
|
cls,
|
|
230
225
|
url: str,
|
|
231
|
-
to_path: str,
|
|
226
|
+
to_path: Union[str, os.PathLike[str]],
|
|
232
227
|
shallow_branch: Optional[str] = None,
|
|
233
228
|
progress: Optional[Callable[["GitProgressEvent"], None]] = None,
|
|
234
229
|
bare: bool = False,
|
|
@@ -270,7 +265,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
270
265
|
else:
|
|
271
266
|
cls._set_default_tracking_branch(repo)
|
|
272
267
|
except Exception as exc: # noqa: BLE001
|
|
273
|
-
raise CloneError(url, to_path) from exc
|
|
268
|
+
raise CloneError(url, os.fsdecode(to_path)) from exc
|
|
274
269
|
|
|
275
270
|
@staticmethod
|
|
276
271
|
def _set_default_tracking_branch(repo: "Repo"):
|
|
@@ -332,7 +327,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
332
327
|
self.repo.stage(list(self.repo.open_index()))
|
|
333
328
|
return
|
|
334
329
|
|
|
335
|
-
files:
|
|
330
|
+
files: list[bytes] = [
|
|
336
331
|
os.fsencode(fpath) for fpath in self._expand_paths(paths, force=force)
|
|
337
332
|
]
|
|
338
333
|
if update:
|
|
@@ -348,7 +343,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
348
343
|
else:
|
|
349
344
|
self.repo.stage(files)
|
|
350
345
|
|
|
351
|
-
def _expand_paths(self, paths:
|
|
346
|
+
def _expand_paths(self, paths: list[str], force: bool = False) -> Iterator[str]:
|
|
352
347
|
for path in paths:
|
|
353
348
|
if not os.path.isabs(path) and self._submodules:
|
|
354
349
|
# NOTE: If path is inside a submodule, Dulwich expects the
|
|
@@ -459,7 +454,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
459
454
|
return any(p == rel or p.startswith(rel_dir) for p in self.repo.open_index())
|
|
460
455
|
|
|
461
456
|
def is_dirty(self, untracked_files: bool = False) -> bool:
|
|
462
|
-
kwargs:
|
|
457
|
+
kwargs: dict[str, Any] = {} if untracked_files else {"untracked_files": "no"}
|
|
463
458
|
return any(self.status(**kwargs))
|
|
464
459
|
|
|
465
460
|
def active_branch(self) -> str:
|
|
@@ -707,9 +702,9 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
707
702
|
fetch_refs = []
|
|
708
703
|
|
|
709
704
|
def determine_wants(
|
|
710
|
-
remote_refs:
|
|
705
|
+
remote_refs: dict[bytes, bytes],
|
|
711
706
|
depth: Optional[int] = None, # pylint: disable=unused-argument
|
|
712
|
-
) ->
|
|
707
|
+
) -> list[bytes]:
|
|
713
708
|
fetch_refs.extend(
|
|
714
709
|
parse_reftuples(
|
|
715
710
|
DictRefsContainer(remote_refs),
|
|
@@ -782,7 +777,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
782
777
|
ref: str,
|
|
783
778
|
message: Optional[str] = None,
|
|
784
779
|
include_untracked: bool = False,
|
|
785
|
-
) ->
|
|
780
|
+
) -> tuple[Optional[str], bool]:
|
|
786
781
|
from dulwich.repo import InvalidUserIdentity
|
|
787
782
|
|
|
788
783
|
from scmrepo.git import Stash
|
|
@@ -836,8 +831,8 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
836
831
|
) -> Mapping[str, Optional[str]]:
|
|
837
832
|
if not base:
|
|
838
833
|
base = "refs/tags"
|
|
839
|
-
rev_mapping:
|
|
840
|
-
results:
|
|
834
|
+
rev_mapping: dict[str, Optional[str]] = {}
|
|
835
|
+
results: dict[str, Optional[str]] = {}
|
|
841
836
|
for ref in self.iter_refs(base=base):
|
|
842
837
|
if (match and not fnmatch.fnmatch(ref, match)) or (
|
|
843
838
|
exclude and fnmatch.fnmatch(ref, exclude)
|
|
@@ -847,7 +842,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
847
842
|
if revision and revision not in rev_mapping:
|
|
848
843
|
rev_mapping[revision] = ref
|
|
849
844
|
for rev in revs:
|
|
850
|
-
results[rev] = rev_mapping.get(rev
|
|
845
|
+
results[rev] = rev_mapping.get(rev)
|
|
851
846
|
return results
|
|
852
847
|
|
|
853
848
|
def diff(self, rev_a: str, rev_b: str, binary=False) -> str:
|
|
@@ -877,7 +872,7 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
877
872
|
|
|
878
873
|
def status(
|
|
879
874
|
self, ignored: bool = False, untracked_files: str = "all"
|
|
880
|
-
) ->
|
|
875
|
+
) -> tuple[Mapping[str, Iterable[str]], Iterable[str], Iterable[str]]:
|
|
881
876
|
from dulwich.porcelain import Error
|
|
882
877
|
from dulwich.porcelain import status as git_status
|
|
883
878
|
|
|
@@ -978,8 +973,21 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method
|
|
|
978
973
|
_IDENTITY_RE = re.compile(r"(?P<name>.+)\s+<(?P<email>.+)>")
|
|
979
974
|
|
|
980
975
|
|
|
981
|
-
def _parse_identity(identity: str) ->
|
|
976
|
+
def _parse_identity(identity: str) -> tuple[str, str]:
|
|
982
977
|
m = _IDENTITY_RE.match(identity)
|
|
983
978
|
if not m:
|
|
984
979
|
raise SCMError("Could not parse tagger identity '{identity}'")
|
|
985
980
|
return m.group("name"), m.group("email")
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
def ls_remote(url: str) -> dict[str, str]:
|
|
984
|
+
from dulwich import porcelain
|
|
985
|
+
from dulwich.client import HTTPUnauthorized
|
|
986
|
+
|
|
987
|
+
try:
|
|
988
|
+
refs = porcelain.ls_remote(url)
|
|
989
|
+
return {os.fsdecode(ref): sha.decode("ascii") for ref, sha in refs.items()}
|
|
990
|
+
except HTTPUnauthorized as exc:
|
|
991
|
+
raise AuthError(url) from exc
|
|
992
|
+
except Exception as exc: # noqa: BLE001
|
|
993
|
+
raise InvalidRemote(url) from exc
|