scmrepo 1.4.0__tar.gz → 2.0.0__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-1.4.0 → scmrepo-2.0.0}/.cruft.json +1 -1
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.gitignore +2 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.pre-commit-config.yaml +6 -23
- {scmrepo-1.4.0/src/scmrepo.egg-info → scmrepo-2.0.0}/PKG-INFO +9 -21
- {scmrepo-1.4.0 → scmrepo-2.0.0}/noxfile.py +0 -1
- scmrepo-2.0.0/pyproject.toml +126 -0
- scmrepo-2.0.0/setup.cfg +4 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/base.py +5 -1
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/fs.py +98 -103
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/__init__.py +13 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/base.py +43 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/dulwich/__init__.py +67 -1
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/gitpython.py +56 -1
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/pygit2/__init__.py +201 -43
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/pygit2/callbacks.py +10 -1
- scmrepo-2.0.0/src/scmrepo/git/backend/pygit2/filter.py +65 -0
- scmrepo-2.0.0/src/scmrepo/git/config.py +35 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/credentials.py +2 -1
- scmrepo-2.0.0/src/scmrepo/git/lfs/__init__.py +8 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/client.py +218 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/exceptions.py +5 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/fetch.py +162 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/object.py +15 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/pointer.py +109 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/progress.py +61 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/smudge.py +51 -0
- scmrepo-2.0.0/src/scmrepo/git/lfs/storage.py +74 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/objects.py +3 -2
- {scmrepo-1.4.0 → scmrepo-2.0.0/src/scmrepo.egg-info}/PKG-INFO +9 -21
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo.egg-info/SOURCES.txt +12 -2
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo.egg-info/requires.txt +4 -18
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_credentials.py +6 -12
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_fs.py +9 -5
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_git.py +29 -0
- scmrepo-2.0.0/tests/test_lfs.py +76 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_pygit2.py +1 -1
- scmrepo-1.4.0/pyproject.toml +0 -108
- scmrepo-1.4.0/setup.cfg +0 -80
- scmrepo-1.4.0/src/scmrepo.egg-info/not-zip-safe +0 -1
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.coveragerc +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.gitattributes +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.github/dependabot.yml +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.github/workflows/release.yaml +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.github/workflows/tests.yaml +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/.github/workflows/update-template.yaml +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/CODE_OF_CONDUCT.rst +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/CONTRIBUTING.rst +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/LICENSE +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/README.rst +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/__init__.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/asyn.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/exceptions.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/__init__.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/dulwich/asyncssh_vendor.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/backend/dulwich/client.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/git/stash.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/noscm.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/progress.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/py.typed +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo/utils.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo.egg-info/dependency_links.txt +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/src/scmrepo.egg-info/top_level.txt +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/__init__.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/conftest.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/docker-compose.yml +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/git-init/git.sh +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_dulwich.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_noscm.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_scmrepo.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/test_stash.py +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/user.key +0 -0
- {scmrepo-1.4.0 → scmrepo-2.0.0}/tests/user.key.pub +0 -0
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
default_language_version:
|
|
2
2
|
python: python3
|
|
3
3
|
repos:
|
|
4
|
-
- repo: https://github.com/psf/black
|
|
5
|
-
rev: 23.9.1
|
|
6
|
-
hooks:
|
|
7
|
-
- id: black
|
|
8
4
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
5
|
rev: v4.4.0
|
|
10
6
|
hooks:
|
|
@@ -24,6 +20,12 @@ repos:
|
|
|
24
20
|
args: ['--fix=lf']
|
|
25
21
|
- id: sort-simple-yaml
|
|
26
22
|
- id: trailing-whitespace
|
|
23
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
24
|
+
rev: 'v0.1.5'
|
|
25
|
+
hooks:
|
|
26
|
+
- id: ruff
|
|
27
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
28
|
+
- id: ruff-format
|
|
27
29
|
- repo: https://github.com/codespell-project/codespell
|
|
28
30
|
rev: v2.2.5
|
|
29
31
|
hooks:
|
|
@@ -34,22 +36,3 @@ repos:
|
|
|
34
36
|
hooks:
|
|
35
37
|
- id: pyupgrade
|
|
36
38
|
args: [--py38-plus]
|
|
37
|
-
- repo: https://github.com/PyCQA/isort
|
|
38
|
-
rev: 5.12.0
|
|
39
|
-
hooks:
|
|
40
|
-
- id: isort
|
|
41
|
-
- repo: https://github.com/pycqa/flake8
|
|
42
|
-
rev: 6.1.0
|
|
43
|
-
hooks:
|
|
44
|
-
- id: flake8
|
|
45
|
-
additional_dependencies:
|
|
46
|
-
- flake8-bugbear==23.7.10
|
|
47
|
-
- flake8-comprehensions==3.14.0
|
|
48
|
-
- flake8-debugger==4.1.2
|
|
49
|
-
- flake8-string-format==0.3.0
|
|
50
|
-
- repo: https://github.com/pycqa/bandit
|
|
51
|
-
rev: 1.7.5
|
|
52
|
-
hooks:
|
|
53
|
-
- id: bandit
|
|
54
|
-
args: ["-c", "pyproject.toml"]
|
|
55
|
-
additional_dependencies: [".[toml]"]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scmrepo
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
5
|
-
|
|
6
|
-
Maintainer-email: support@dvc.org
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: scmrepo
|
|
5
|
+
Author-email: Iterative <support@dvc.org>
|
|
7
6
|
License: Apache-2.0
|
|
8
|
-
|
|
7
|
+
Project-URL: Issues, https://github.com/iterative/scmrepo/issues
|
|
8
|
+
Project-URL: Source, https://github.com/iterative/scmrepo
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.8
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -17,19 +17,20 @@ 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.13.
|
|
20
|
+
Requires-Dist: pygit2>=1.13.3
|
|
21
21
|
Requires-Dist: pygtrie>=2.3.2
|
|
22
22
|
Requires-Dist: fsspec>=2021.7.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
26
|
Requires-Dist: shortuuid>=0.5.0
|
|
27
|
+
Requires-Dist: dvc-objects<4,>=3
|
|
28
|
+
Requires-Dist: dvc-http>=2.29.0
|
|
27
29
|
Provides-Extra: tests
|
|
28
30
|
Requires-Dist: pytest==7.2.0; extra == "tests"
|
|
29
31
|
Requires-Dist: pytest-sugar==0.9.5; extra == "tests"
|
|
30
32
|
Requires-Dist: pytest-cov==3.0.0; extra == "tests"
|
|
31
33
|
Requires-Dist: pytest-mock==3.8.2; extra == "tests"
|
|
32
|
-
Requires-Dist: pylint==2.15.0; extra == "tests"
|
|
33
34
|
Requires-Dist: mypy==0.971; extra == "tests"
|
|
34
35
|
Requires-Dist: pytest-test-utils==0.0.8; extra == "tests"
|
|
35
36
|
Requires-Dist: pytest-asyncio==0.18.3; extra == "tests"
|
|
@@ -40,20 +41,7 @@ Requires-Dist: types-certifi==2021.10.8.3; extra == "tests"
|
|
|
40
41
|
Requires-Dist: types-mock==5.1.0.2; extra == "tests"
|
|
41
42
|
Requires-Dist: types-paramiko==3.3.0.0; extra == "tests"
|
|
42
43
|
Provides-Extra: dev
|
|
43
|
-
Requires-Dist:
|
|
44
|
-
Requires-Dist: pytest-sugar==0.9.5; extra == "dev"
|
|
45
|
-
Requires-Dist: pytest-cov==3.0.0; extra == "dev"
|
|
46
|
-
Requires-Dist: pytest-mock==3.8.2; extra == "dev"
|
|
47
|
-
Requires-Dist: pylint==2.15.0; extra == "dev"
|
|
48
|
-
Requires-Dist: mypy==0.971; extra == "dev"
|
|
49
|
-
Requires-Dist: pytest-test-utils==0.0.8; extra == "dev"
|
|
50
|
-
Requires-Dist: pytest-asyncio==0.18.3; extra == "dev"
|
|
51
|
-
Requires-Dist: pytest-docker==0.12.0; (python_version < "3.10" and implementation_name != "pypy") and extra == "dev"
|
|
52
|
-
Requires-Dist: mock==5.1.0; extra == "dev"
|
|
53
|
-
Requires-Dist: paramiko==3.3.1; extra == "dev"
|
|
54
|
-
Requires-Dist: types-certifi==2021.10.8.3; extra == "dev"
|
|
55
|
-
Requires-Dist: types-mock==5.1.0.2; extra == "dev"
|
|
56
|
-
Requires-Dist: types-paramiko==3.3.0.0; extra == "dev"
|
|
44
|
+
Requires-Dist: scmrepo[tests]; extra == "dev"
|
|
57
45
|
|
|
58
46
|
scmrepo
|
|
59
47
|
=======
|
|
@@ -29,7 +29,6 @@ def lint(session: nox.Session) -> None:
|
|
|
29
29
|
args = *(session.posargs or ("--show-diff-on-failure",)), "--all-files"
|
|
30
30
|
session.run("pre-commit", "run", *args)
|
|
31
31
|
session.run("python", "-m", "mypy")
|
|
32
|
-
session.run("python", "-m", "pylint", *locations)
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
@nox.session
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=48", "setuptools_scm[toml]>=6.3.1"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools_scm]
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "scmrepo"
|
|
9
|
+
description = "scmrepo"
|
|
10
|
+
readme = "README.rst"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
authors = [{ name = "Iterative", email = "support@dvc.org" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.8",
|
|
16
|
+
"Programming Language :: Python :: 3.9",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.8"
|
|
22
|
+
dynamic = ["version"]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"gitpython>3",
|
|
25
|
+
"dulwich>=0.21.6",
|
|
26
|
+
"pygit2>=1.13.3",
|
|
27
|
+
"pygtrie>=2.3.2",
|
|
28
|
+
"fsspec>=2021.7.0",
|
|
29
|
+
"pathspec>=0.9.0",
|
|
30
|
+
"asyncssh>=2.13.1,<3",
|
|
31
|
+
"funcy>=1.14",
|
|
32
|
+
"shortuuid>=0.5.0",
|
|
33
|
+
"dvc-objects>=3,<4",
|
|
34
|
+
"dvc-http>=2.29.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Issues = "https://github.com/iterative/scmrepo/issues"
|
|
39
|
+
Source = "https://github.com/iterative/scmrepo"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
tests = [
|
|
43
|
+
"pytest==7.2.0",
|
|
44
|
+
"pytest-sugar==0.9.5",
|
|
45
|
+
"pytest-cov==3.0.0",
|
|
46
|
+
"pytest-mock==3.8.2",
|
|
47
|
+
"mypy==0.971",
|
|
48
|
+
"pytest-test-utils==0.0.8",
|
|
49
|
+
"pytest-asyncio==0.18.3",
|
|
50
|
+
# https://github.com/docker/docker-py/issues/2902
|
|
51
|
+
"pytest-docker==0.12.0; python_version < '3.10' and implementation_name != 'pypy'",
|
|
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.3.0.0",
|
|
57
|
+
]
|
|
58
|
+
dev = [
|
|
59
|
+
"scmrepo[tests]",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.setuptools.package-data]
|
|
63
|
+
dvc_objects = ["py.typed"]
|
|
64
|
+
|
|
65
|
+
[tool.setuptools.packages.find]
|
|
66
|
+
where = ["src"]
|
|
67
|
+
namespaces = false
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
addopts = "-ra"
|
|
71
|
+
markers = [
|
|
72
|
+
"skip_git_backend: skip tests for given backend",
|
|
73
|
+
"slow: mark test as slow to run",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[tool.coverage.run]
|
|
77
|
+
branch = true
|
|
78
|
+
source = ["scmrepo", "tests"]
|
|
79
|
+
|
|
80
|
+
[tool.coverage.paths]
|
|
81
|
+
source = ["src", "*/site-packages"]
|
|
82
|
+
|
|
83
|
+
[tool.coverage.report]
|
|
84
|
+
show_missing = true
|
|
85
|
+
exclude_lines = [
|
|
86
|
+
"pragma: no cover",
|
|
87
|
+
"if __name__ == .__main__.:",
|
|
88
|
+
"if typing.TYPE_CHECKING:",
|
|
89
|
+
"if TYPE_CHECKING:",
|
|
90
|
+
"raise NotImplementedError",
|
|
91
|
+
"raise AssertionError",
|
|
92
|
+
"@overload",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[tool.mypy]
|
|
96
|
+
# Error output
|
|
97
|
+
show_column_numbers = true
|
|
98
|
+
show_error_codes = true
|
|
99
|
+
show_error_context = true
|
|
100
|
+
show_traceback = true
|
|
101
|
+
pretty = true
|
|
102
|
+
check_untyped_defs = false
|
|
103
|
+
# Warnings
|
|
104
|
+
warn_no_return = true
|
|
105
|
+
warn_redundant_casts = true
|
|
106
|
+
warn_unreachable = true
|
|
107
|
+
files = ["src", "tests"]
|
|
108
|
+
|
|
109
|
+
[[tool.mypy.overrides]]
|
|
110
|
+
module = [
|
|
111
|
+
"pygtrie",
|
|
112
|
+
"dvc_http.*",
|
|
113
|
+
"funcy",
|
|
114
|
+
"git",
|
|
115
|
+
"gitdb.*",
|
|
116
|
+
"fsspec.*",
|
|
117
|
+
"pathspec.patterns",
|
|
118
|
+
"asyncssh.*",
|
|
119
|
+
"pygit2.*",
|
|
120
|
+
"pytest_docker.plugin",
|
|
121
|
+
"urllib3.*",
|
|
122
|
+
]
|
|
123
|
+
ignore_missing_imports = true
|
|
124
|
+
|
|
125
|
+
[tool.codespell]
|
|
126
|
+
ignore-words-list = "cachable, keypair"
|
scmrepo-2.0.0/setup.cfg
ADDED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Manages source control systems (e.g. Git) in DVC."""
|
|
2
|
+
from contextlib import AbstractContextManager
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
class Base:
|
|
5
|
+
class Base(AbstractContextManager):
|
|
5
6
|
"""Base class for source control management driver implementations."""
|
|
6
7
|
|
|
7
8
|
def __init__(self, root_dir=None):
|
|
@@ -18,6 +19,9 @@ class Base:
|
|
|
18
19
|
class_name=type(self).__name__, directory=self.dir
|
|
19
20
|
)
|
|
20
21
|
|
|
22
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
23
|
+
self.close()
|
|
24
|
+
|
|
21
25
|
@property
|
|
22
26
|
def dir(self):
|
|
23
27
|
"""Path to a directory with SCM specific information."""
|
|
@@ -14,49 +14,79 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from scmrepo.git.objects import GitTrie
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
def bytesio_len(obj: "BytesIO") -> Optional[int]:
|
|
18
|
+
try:
|
|
19
|
+
offset = obj.tell()
|
|
20
|
+
length = obj.seek(0, os.SEEK_END)
|
|
21
|
+
obj.seek(offset)
|
|
22
|
+
except (AttributeError, OSError):
|
|
23
|
+
return None
|
|
24
|
+
return length
|
|
21
25
|
|
|
22
|
-
self.getcwd = getcwd or _getcwd
|
|
23
|
-
self.realpath = realpath or self.abspath
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
class GitFileSystem(AbstractFileSystem):
|
|
28
|
+
# pylint: disable=abstract-method
|
|
29
|
+
cachable = False
|
|
30
|
+
root_marker = "/"
|
|
27
31
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
path: str = None,
|
|
35
|
+
rev: str = None,
|
|
36
|
+
scm: "Git" = None,
|
|
37
|
+
trie: "GitTrie" = None,
|
|
38
|
+
rev_resolver: Callable[["Git", str], str] = None,
|
|
39
|
+
**kwargs,
|
|
40
|
+
):
|
|
41
|
+
from scmrepo.git import Git
|
|
42
|
+
from scmrepo.git.objects import GitTrie
|
|
43
|
+
|
|
44
|
+
super().__init__(**kwargs)
|
|
45
|
+
if not trie:
|
|
46
|
+
scm = scm or Git(path)
|
|
47
|
+
resolver = rev_resolver or Git.resolve_rev
|
|
48
|
+
resolved = resolver(scm, rev or "HEAD")
|
|
49
|
+
tree_obj = scm.pygit2.get_tree_obj(rev=resolved)
|
|
50
|
+
trie = GitTrie(tree_obj, resolved)
|
|
51
|
+
|
|
52
|
+
self.trie = trie
|
|
53
|
+
self.rev = self.trie.rev
|
|
31
54
|
|
|
32
|
-
|
|
55
|
+
def getcwd(self):
|
|
56
|
+
return self.root_marker
|
|
33
57
|
|
|
34
|
-
def
|
|
35
|
-
|
|
58
|
+
def chdir(self, path):
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def join(cls, *parts):
|
|
63
|
+
return posixpath.join(*parts)
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
@classmethod
|
|
66
|
+
def split(cls, path):
|
|
67
|
+
return posixpath.split(path)
|
|
39
68
|
|
|
40
69
|
def normpath(self, path):
|
|
41
|
-
return
|
|
70
|
+
return posixpath.normpath(path)
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
72
|
+
@classmethod
|
|
73
|
+
def isabs(cls, path):
|
|
74
|
+
return posixpath.isabs(path)
|
|
45
75
|
|
|
46
76
|
def abspath(self, path):
|
|
47
77
|
if not self.isabs(path):
|
|
48
78
|
path = self.join(self.getcwd(), path)
|
|
49
79
|
return self.normpath(path)
|
|
50
80
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def parts(self, path):
|
|
55
|
-
drive, path = self.flavour.splitdrive(path.rstrip(self.flavour.sep))
|
|
81
|
+
@classmethod
|
|
82
|
+
def commonprefix(cls, path):
|
|
83
|
+
return posixpath.commonprefix(path)
|
|
56
84
|
|
|
85
|
+
@classmethod
|
|
86
|
+
def parts(cls, path):
|
|
57
87
|
ret = []
|
|
58
88
|
while True:
|
|
59
|
-
path, part =
|
|
89
|
+
path, part = cls.split(path)
|
|
60
90
|
|
|
61
91
|
if part:
|
|
62
92
|
ret.append(part)
|
|
@@ -69,113 +99,77 @@ class Path:
|
|
|
69
99
|
|
|
70
100
|
ret.reverse()
|
|
71
101
|
|
|
72
|
-
if drive:
|
|
73
|
-
ret = [drive] + ret
|
|
74
|
-
|
|
75
102
|
return tuple(ret)
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
104
|
+
@classmethod
|
|
105
|
+
def parent(cls, path):
|
|
106
|
+
return posixpath.dirname(path)
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
@classmethod
|
|
109
|
+
def dirname(cls, path):
|
|
110
|
+
return cls.parent(path)
|
|
82
111
|
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
@classmethod
|
|
113
|
+
def parents(cls, path):
|
|
114
|
+
parts = cls.parts(path)
|
|
85
115
|
return tuple(
|
|
86
|
-
|
|
116
|
+
cls.join(*parts[:length]) for length in range(len(parts) - 1, 0, -1)
|
|
87
117
|
)
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
@classmethod
|
|
120
|
+
def name(cls, path):
|
|
121
|
+
return cls.parts(path)[-1]
|
|
91
122
|
|
|
92
|
-
|
|
93
|
-
|
|
123
|
+
@classmethod
|
|
124
|
+
def suffix(cls, path):
|
|
125
|
+
name = cls.name(path)
|
|
94
126
|
_, dot, suffix = name.partition(".")
|
|
95
127
|
return dot + suffix
|
|
96
128
|
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
@classmethod
|
|
130
|
+
def with_name(cls, path, name):
|
|
131
|
+
parts = list(cls.parts(path))
|
|
99
132
|
parts[-1] = name
|
|
100
|
-
return
|
|
133
|
+
return cls.join(*parts)
|
|
101
134
|
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
@classmethod
|
|
136
|
+
def with_suffix(cls, path, suffix):
|
|
137
|
+
parts = list(cls.parts(path))
|
|
104
138
|
real_path, _, _ = parts[-1].partition(".")
|
|
105
139
|
parts[-1] = real_path + suffix
|
|
106
|
-
return
|
|
140
|
+
return cls.join(*parts)
|
|
107
141
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
142
|
+
@classmethod
|
|
143
|
+
def isin(cls, left, right):
|
|
144
|
+
left_parts = cls.parts(left)
|
|
145
|
+
right_parts = cls.parts(right)
|
|
111
146
|
left_len = len(left_parts)
|
|
112
147
|
right_len = len(right_parts)
|
|
113
148
|
return left_len > right_len and left_parts[:right_len] == right_parts
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
@classmethod
|
|
151
|
+
def isin_or_eq(cls, left, right):
|
|
152
|
+
return left == right or cls.isin(left, right)
|
|
117
153
|
|
|
118
|
-
|
|
154
|
+
@classmethod
|
|
155
|
+
def overlaps(cls, left, right):
|
|
119
156
|
# pylint: disable=arguments-out-of-order
|
|
120
|
-
return
|
|
157
|
+
return cls.isin_or_eq(left, right) or cls.isin(right, left)
|
|
121
158
|
|
|
122
159
|
def relpath(self, path, start=None):
|
|
123
160
|
if start is None:
|
|
124
161
|
start = "."
|
|
125
|
-
return self.
|
|
162
|
+
return self.relpath(self.abspath(path), start=self.abspath(start))
|
|
126
163
|
|
|
127
164
|
def relparts(self, path, start=None):
|
|
128
165
|
return self.parts(self.relpath(path, start=start))
|
|
129
166
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def bytesio_len(obj: "BytesIO") -> Optional[int]:
|
|
135
|
-
try:
|
|
136
|
-
offset = obj.tell()
|
|
137
|
-
length = obj.seek(0, os.SEEK_END)
|
|
138
|
-
obj.seek(offset)
|
|
139
|
-
except (AttributeError, OSError):
|
|
140
|
-
return None
|
|
141
|
-
return length
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class GitFileSystem(AbstractFileSystem):
|
|
145
|
-
# pylint: disable=abstract-method
|
|
146
|
-
cachable = False
|
|
147
|
-
root_marker = "/"
|
|
148
|
-
|
|
149
|
-
def __init__(
|
|
150
|
-
self,
|
|
151
|
-
path: str = None,
|
|
152
|
-
rev: str = None,
|
|
153
|
-
scm: "Git" = None,
|
|
154
|
-
trie: "GitTrie" = None,
|
|
155
|
-
rev_resolver: Callable[["Git", str], str] = None,
|
|
156
|
-
**kwargs,
|
|
157
|
-
):
|
|
158
|
-
from scmrepo.git import Git
|
|
159
|
-
from scmrepo.git.objects import GitTrie
|
|
160
|
-
|
|
161
|
-
super().__init__(**kwargs)
|
|
162
|
-
if not trie:
|
|
163
|
-
scm = scm or Git(path)
|
|
164
|
-
resolver = rev_resolver or Git.resolve_rev
|
|
165
|
-
resolved = resolver(scm, rev or "HEAD")
|
|
166
|
-
tree_obj = scm.pygit2.get_tree_obj(rev=resolved)
|
|
167
|
-
trie = GitTrie(tree_obj, resolved)
|
|
168
|
-
|
|
169
|
-
self.trie = trie
|
|
170
|
-
self.rev = self.trie.rev
|
|
171
|
-
|
|
172
|
-
def _getcwd():
|
|
173
|
-
return self.root_marker
|
|
174
|
-
|
|
175
|
-
self.path = Path(self.sep, getcwd=_getcwd)
|
|
167
|
+
@classmethod
|
|
168
|
+
def as_posix(cls, path):
|
|
169
|
+
return path
|
|
176
170
|
|
|
177
171
|
def _get_key(self, path: str) -> Tuple[str, ...]:
|
|
178
|
-
path = self.
|
|
172
|
+
path = self.abspath(path)
|
|
179
173
|
if path == self.root_marker:
|
|
180
174
|
return ()
|
|
181
175
|
relparts = path.split(self.sep)
|
|
@@ -187,9 +181,10 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
187
181
|
self,
|
|
188
182
|
path: str,
|
|
189
183
|
mode: str = "rb",
|
|
190
|
-
block_size: int = None,
|
|
184
|
+
block_size: Optional[int] = None,
|
|
191
185
|
autocommit: bool = True,
|
|
192
|
-
cache_options: Dict = None,
|
|
186
|
+
cache_options: Optional[Dict] = None,
|
|
187
|
+
raw: bool = False,
|
|
193
188
|
**kwargs: Any,
|
|
194
189
|
) -> BinaryIO:
|
|
195
190
|
if mode != "rb":
|
|
@@ -197,7 +192,7 @@ class GitFileSystem(AbstractFileSystem):
|
|
|
197
192
|
|
|
198
193
|
key = self._get_key(path)
|
|
199
194
|
try:
|
|
200
|
-
obj = self.trie.open(key, mode=mode)
|
|
195
|
+
obj = self.trie.open(key, mode=mode, raw=raw)
|
|
201
196
|
obj.size = bytesio_len(obj)
|
|
202
197
|
return obj
|
|
203
198
|
except KeyError as exc:
|
|
@@ -171,6 +171,13 @@ class Git(Base):
|
|
|
171
171
|
def ignore_file(self):
|
|
172
172
|
return self.GITIGNORE
|
|
173
173
|
|
|
174
|
+
@cached_property
|
|
175
|
+
def lfs_storage(self):
|
|
176
|
+
from .lfs import LFSStorage
|
|
177
|
+
from .lfs.storage import get_storage_path
|
|
178
|
+
|
|
179
|
+
return LFSStorage(get_storage_path(self))
|
|
180
|
+
|
|
174
181
|
def _get_gitignore(self, path):
|
|
175
182
|
ignore_file_dir = os.path.dirname(path)
|
|
176
183
|
|
|
@@ -267,6 +274,8 @@ class Git(Base):
|
|
|
267
274
|
|
|
268
275
|
def close(self):
|
|
269
276
|
self.backends.close_initialized()
|
|
277
|
+
if "lfs_storage" in self.__dict__:
|
|
278
|
+
self.lfs_storage.close()
|
|
270
279
|
|
|
271
280
|
@property
|
|
272
281
|
def no_commits(self):
|
|
@@ -358,6 +367,7 @@ class Git(Base):
|
|
|
358
367
|
is_tracked = partialmethod(_backend_func, "is_tracked")
|
|
359
368
|
is_dirty = partialmethod(_backend_func, "is_dirty")
|
|
360
369
|
active_branch = partialmethod(_backend_func, "active_branch")
|
|
370
|
+
active_branch_remote = partialmethod(_backend_func, "active_branch_remote")
|
|
361
371
|
list_branches = partialmethod(_backend_func, "list_branches")
|
|
362
372
|
list_tags = partialmethod(_backend_func, "list_tags")
|
|
363
373
|
list_all_commits = partialmethod(_backend_func, "list_all_commits")
|
|
@@ -383,8 +393,11 @@ class Git(Base):
|
|
|
383
393
|
status = partialmethod(_backend_func, "status")
|
|
384
394
|
merge = partialmethod(_backend_func, "merge")
|
|
385
395
|
validate_git_remote = partialmethod(_backend_func, "validate_git_remote")
|
|
396
|
+
get_remote_url = partialmethod(_backend_func, "get_remote_url")
|
|
386
397
|
check_ref_format = partialmethod(_backend_func, "check_ref_format")
|
|
387
398
|
get_tag = partialmethod(_backend_func, "get_tag")
|
|
399
|
+
get_config = partialmethod(_backend_func, "get_config")
|
|
400
|
+
check_attr = partialmethod(_backend_func, "check_attr")
|
|
388
401
|
|
|
389
402
|
get_tree_obj = partialmethod(_backend_func, "get_tree_obj")
|
|
390
403
|
|
|
@@ -10,6 +10,7 @@ from ..objects import GitObject
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from scmrepo.progress import GitProgressEvent
|
|
12
12
|
|
|
13
|
+
from ..config import Config
|
|
13
14
|
from ..objects import GitCommit, GitTag
|
|
14
15
|
|
|
15
16
|
|
|
@@ -135,6 +136,10 @@ class BaseGitBackend(ABC):
|
|
|
135
136
|
def active_branch(self) -> str:
|
|
136
137
|
pass
|
|
137
138
|
|
|
139
|
+
@abstractmethod
|
|
140
|
+
def active_branch_remote(self) -> str:
|
|
141
|
+
"""Return the fetch remote name for the current branch."""
|
|
142
|
+
|
|
138
143
|
@abstractmethod
|
|
139
144
|
def list_branches(self) -> Iterable[str]:
|
|
140
145
|
pass
|
|
@@ -263,6 +268,9 @@ class BaseGitBackend(ABC):
|
|
|
263
268
|
returns True the local ref will be overwritten.
|
|
264
269
|
Callback will be of the form:
|
|
265
270
|
on_diverged(local_refname, remote_sha)
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Mapping of local_refname to sync status.
|
|
266
274
|
"""
|
|
267
275
|
|
|
268
276
|
@abstractmethod
|
|
@@ -394,6 +402,10 @@ class BaseGitBackend(ABC):
|
|
|
394
402
|
def validate_git_remote(self, url: str, **kwargs):
|
|
395
403
|
"""Verify that url is a valid git URL or remote name."""
|
|
396
404
|
|
|
405
|
+
@abstractmethod
|
|
406
|
+
def get_remote_url(self, remote: str) -> str:
|
|
407
|
+
"""Return URL for the specified remote."""
|
|
408
|
+
|
|
397
409
|
@abstractmethod
|
|
398
410
|
def check_ref_format(self, refname: str) -> bool:
|
|
399
411
|
"""Check if a reference name is well formed."""
|
|
@@ -410,3 +422,34 @@ class BaseGitBackend(ABC):
|
|
|
410
422
|
String SHA for the target object if the tag is a lightweight tag.
|
|
411
423
|
GitTag object if the tag is an annotated tag.
|
|
412
424
|
"""
|
|
425
|
+
|
|
426
|
+
@abstractmethod
|
|
427
|
+
def get_config(self, path: Optional[str] = None) -> "Config":
|
|
428
|
+
"""Return a Git config object.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
path: If set, a config object for the specified config file will be
|
|
432
|
+
returned. By default, the standard Git system/global/repo config
|
|
433
|
+
stack object will be returned.
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
@abstractmethod
|
|
437
|
+
def check_attr(
|
|
438
|
+
self,
|
|
439
|
+
path: str,
|
|
440
|
+
attr: str,
|
|
441
|
+
source: Optional[str] = None,
|
|
442
|
+
) -> Optional[Union[bool, str]]:
|
|
443
|
+
"""Return the value of the specified attribute for a pathname.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
path: Pathname to check.
|
|
447
|
+
attr: Attribute to check.
|
|
448
|
+
source: Optional tree-ish source to check.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
None when the attribute is not defined for the path (unspecified).
|
|
452
|
+
True when the attribute is defined as true (set).
|
|
453
|
+
False when the attribute is defined as false (unset).
|
|
454
|
+
The value of the attribute when a value has been assigned.
|
|
455
|
+
"""
|