gitbolt 0.0.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitbolt/__init__.py +18 -0
- gitbolt/_internal_init.py +18 -0
- gitbolt/add.py +652 -0
- gitbolt/base.py +333 -0
- gitbolt/exceptions.py +46 -0
- gitbolt/git_subprocess/__init__.py +14 -0
- gitbolt/git_subprocess/_internal_init.py +9 -0
- gitbolt/git_subprocess/add.py +484 -0
- gitbolt/git_subprocess/base.py +436 -0
- gitbolt/git_subprocess/constants.py +13 -0
- gitbolt/git_subprocess/exceptions.py +110 -0
- gitbolt/git_subprocess/impl/__init__.py +6 -0
- gitbolt/git_subprocess/impl/simple.py +185 -0
- gitbolt/git_subprocess/ls_tree.py +384 -0
- gitbolt/git_subprocess/runner/__init__.py +8 -0
- gitbolt/git_subprocess/runner/base.py +64 -0
- gitbolt/git_subprocess/runner/simple_impl.py +89 -0
- gitbolt/git_subprocess/utils.py +179 -0
- gitbolt/ls_tree.py +155 -0
- gitbolt/models.py +686 -0
- gitbolt/py.typed +0 -0
- gitbolt/utils.py +179 -0
- gitbolt-0.0.0.dev1.dist-info/METADATA +308 -0
- gitbolt-0.0.0.dev1.dist-info/RECORD +27 -0
- gitbolt-0.0.0.dev1.dist-info/WHEEL +5 -0
- gitbolt-0.0.0.dev1.dist-info/licenses/LICENSE +201 -0
- gitbolt-0.0.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
A simple and straight-forward git command subprocess runner implementation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
from subprocess import CompletedProcess
|
|
12
|
+
from typing import overload, override, Any, Literal
|
|
13
|
+
|
|
14
|
+
from gitbolt.git_subprocess.constants import GIT_CMD
|
|
15
|
+
from gitbolt.git_subprocess.exceptions import GitCmdException
|
|
16
|
+
from gitbolt.git_subprocess.runner import GitCommandRunner
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SimpleGitCR(GitCommandRunner):
|
|
20
|
+
"""
|
|
21
|
+
Simple git command runner that simply runs everything `as-is` in a subprocess.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@overload
|
|
25
|
+
@override
|
|
26
|
+
def run_git_command(
|
|
27
|
+
self,
|
|
28
|
+
main_cmd_args: list[str],
|
|
29
|
+
subcommand_args: list[str],
|
|
30
|
+
*subprocess_run_args: Any,
|
|
31
|
+
_input: str,
|
|
32
|
+
text: Literal[True],
|
|
33
|
+
**subprocess_run_kwargs: Any,
|
|
34
|
+
) -> CompletedProcess[str]: ...
|
|
35
|
+
|
|
36
|
+
@overload
|
|
37
|
+
@override
|
|
38
|
+
def run_git_command(
|
|
39
|
+
self,
|
|
40
|
+
main_cmd_args: list[str],
|
|
41
|
+
subcommand_args: list[str],
|
|
42
|
+
*subprocess_run_args: Any,
|
|
43
|
+
_input: bytes,
|
|
44
|
+
text: Literal[False],
|
|
45
|
+
**subprocess_run_kwargs: Any,
|
|
46
|
+
) -> CompletedProcess[bytes]: ...
|
|
47
|
+
|
|
48
|
+
@overload
|
|
49
|
+
@override
|
|
50
|
+
def run_git_command(
|
|
51
|
+
self,
|
|
52
|
+
main_cmd_args: list[str],
|
|
53
|
+
subcommand_args: list[str],
|
|
54
|
+
*subprocess_run_args: Any,
|
|
55
|
+
text: Literal[True],
|
|
56
|
+
**subprocess_run_kwargs: Any,
|
|
57
|
+
) -> CompletedProcess[str]: ...
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
@override
|
|
61
|
+
def run_git_command(
|
|
62
|
+
self,
|
|
63
|
+
main_cmd_args: list[str],
|
|
64
|
+
subcommand_args: list[str],
|
|
65
|
+
*subprocess_run_args: Any,
|
|
66
|
+
text: Literal[False] = ...,
|
|
67
|
+
**subprocess_run_kwargs: Any,
|
|
68
|
+
) -> CompletedProcess[bytes]: ...
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
def run_git_command(
|
|
72
|
+
self,
|
|
73
|
+
main_cmd_args: list[str],
|
|
74
|
+
subcommand_args: list[str],
|
|
75
|
+
*subprocess_run_args: Any,
|
|
76
|
+
_input: str | bytes | None = None,
|
|
77
|
+
text: Literal[True, False] = False,
|
|
78
|
+
**subprocess_run_kwargs: Any,
|
|
79
|
+
) -> CompletedProcess[str] | CompletedProcess[bytes]:
|
|
80
|
+
try:
|
|
81
|
+
return subprocess.run(
|
|
82
|
+
[GIT_CMD, *main_cmd_args, *subcommand_args],
|
|
83
|
+
*subprocess_run_args,
|
|
84
|
+
input=_input,
|
|
85
|
+
text=text,
|
|
86
|
+
**subprocess_run_kwargs,
|
|
87
|
+
)
|
|
88
|
+
except subprocess.CalledProcessError as e:
|
|
89
|
+
raise GitCmdException(called_process_error=e, exit_code=e.returncode) from e
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Utility functions related to processors specific to git commands using subprocess.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from vt.utils.commons.commons.core_py import is_unset, not_none_not_unset, Unset
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def git_main_cmd_repeating_flag_args(
|
|
16
|
+
val: Sequence[Path] | Unset | None, cmd_flag: str
|
|
17
|
+
) -> list[str]:
|
|
18
|
+
"""
|
|
19
|
+
Returns a flattened list of repeating flags and values.
|
|
20
|
+
|
|
21
|
+
Used for flags like `-C` which may be repeated with multiple values.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
val: A list of values to apply with the flag.
|
|
25
|
+
cmd_flag: The flag to apply to each value.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A flattened list like ['-C', 'repo1', '-C', 'repo2']
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
>>> git_main_cmd_repeating_flag_args([Path("repo1"), Path("repo2")], "-C")
|
|
32
|
+
['-C', 'repo1', '-C', 'repo2']
|
|
33
|
+
|
|
34
|
+
>>> git_main_cmd_repeating_flag_args([], "-C")
|
|
35
|
+
[]
|
|
36
|
+
|
|
37
|
+
>>> from vt.utils.commons.commons.core_py import UNSET
|
|
38
|
+
>>> git_main_cmd_repeating_flag_args([Path("repo1"), UNSET, Path("repo2")], "-C")
|
|
39
|
+
['-C', 'repo1', '-C', 'repo2']
|
|
40
|
+
|
|
41
|
+
>>> git_main_cmd_repeating_flag_args(None, "-C")
|
|
42
|
+
[]
|
|
43
|
+
|
|
44
|
+
>>> git_main_cmd_repeating_flag_args(UNSET, "-C")
|
|
45
|
+
[]
|
|
46
|
+
"""
|
|
47
|
+
if not_none_not_unset(val):
|
|
48
|
+
return [
|
|
49
|
+
item
|
|
50
|
+
for entry in val
|
|
51
|
+
if not is_unset(entry)
|
|
52
|
+
for item in [cmd_flag, str(entry)]
|
|
53
|
+
]
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def git_main_cmd_dict_flag_args(
|
|
58
|
+
val: dict[str, str | bool | None | Unset] | None | Unset, cmd_flag: str
|
|
59
|
+
) -> list[str]:
|
|
60
|
+
"""
|
|
61
|
+
Converts a dictionary into flag pairs used by commands like `-c key=value`.
|
|
62
|
+
|
|
63
|
+
- `True` or `None` => `-c key`
|
|
64
|
+
- `False` => `-c key=`
|
|
65
|
+
- `UNSET` keys are skipped
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
val: Dictionary of key-value pairs.
|
|
69
|
+
cmd_flag: The flag to apply.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A list of CLI arguments.
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": "baz"}, "-c")
|
|
76
|
+
['-c', 'foo.bar=baz']
|
|
77
|
+
|
|
78
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": ""}, "-c")
|
|
79
|
+
['-c', 'foo.bar=']
|
|
80
|
+
|
|
81
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": True}, "-c")
|
|
82
|
+
['-c', 'foo.bar']
|
|
83
|
+
|
|
84
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": False}, "-c")
|
|
85
|
+
['-c', 'foo.bar=']
|
|
86
|
+
|
|
87
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": None}, "-c")
|
|
88
|
+
['-c', 'foo.bar']
|
|
89
|
+
|
|
90
|
+
>>> from vt.utils.commons.commons.core_py import UNSET
|
|
91
|
+
>>> git_main_cmd_dict_flag_args({"foo.bar": UNSET}, "-c")
|
|
92
|
+
[]
|
|
93
|
+
|
|
94
|
+
>>> git_main_cmd_dict_flag_args({
|
|
95
|
+
... "a.b": "x", "c.d": "", "e.f": True, "g.h": False, "i.j": None
|
|
96
|
+
... }, "-c")
|
|
97
|
+
['-c', 'a.b=x', '-c', 'c.d=', '-c', 'e.f', '-c', 'g.h=', '-c', 'i.j']
|
|
98
|
+
|
|
99
|
+
>>> git_main_cmd_dict_flag_args(None, "-c")
|
|
100
|
+
[]
|
|
101
|
+
|
|
102
|
+
>>> git_main_cmd_dict_flag_args(UNSET, "-c")
|
|
103
|
+
[]
|
|
104
|
+
|
|
105
|
+
>>> git_main_cmd_dict_flag_args({}, "-c")
|
|
106
|
+
[]
|
|
107
|
+
"""
|
|
108
|
+
if not_none_not_unset(val):
|
|
109
|
+
args = []
|
|
110
|
+
for k, v in val.items():
|
|
111
|
+
if is_unset(v):
|
|
112
|
+
continue
|
|
113
|
+
if v is True or v is None:
|
|
114
|
+
args += [cmd_flag, k]
|
|
115
|
+
elif v is False:
|
|
116
|
+
args += [cmd_flag, f"{k}="]
|
|
117
|
+
else:
|
|
118
|
+
args += [cmd_flag, f"{k}={v}"]
|
|
119
|
+
return args
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def git_main_cmd_simple_flag_args(val: bool | Unset | None, cmd_flag: str) -> list[str]:
|
|
124
|
+
"""
|
|
125
|
+
Returns a single flag if the value is set and truthy.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
val: A boolean-like value.
|
|
129
|
+
cmd_flag: The flag to apply.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
A list with just the flag, or empty.
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
>>> git_main_cmd_simple_flag_args(True, "--no-pager")
|
|
136
|
+
['--no-pager']
|
|
137
|
+
|
|
138
|
+
>>> git_main_cmd_simple_flag_args(False, "--no-pager")
|
|
139
|
+
[]
|
|
140
|
+
|
|
141
|
+
>>> git_main_cmd_simple_flag_args(None, "--no-pager")
|
|
142
|
+
[]
|
|
143
|
+
|
|
144
|
+
>>> from vt.utils.commons.commons.core_py import UNSET
|
|
145
|
+
>>> git_main_cmd_simple_flag_args(UNSET, "--no-pager")
|
|
146
|
+
[]
|
|
147
|
+
"""
|
|
148
|
+
if not_none_not_unset(val):
|
|
149
|
+
return [cmd_flag] if val else []
|
|
150
|
+
else:
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def git_main_cmd_pair_flag_args(val: Any, cmd_flag: str) -> list[str]:
|
|
155
|
+
"""
|
|
156
|
+
Returns a flag and value pair, or nothing if not set.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
val: The value to attach to the flag.
|
|
160
|
+
cmd_flag: The flag name.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A list like ['--exec-path', 'tmp'] or []
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
>>> git_main_cmd_pair_flag_args("foo", "--namespace")
|
|
167
|
+
['--namespace', 'foo']
|
|
168
|
+
|
|
169
|
+
>>> git_main_cmd_pair_flag_args(Path("repo"), "--namespace")
|
|
170
|
+
['--namespace', 'repo']
|
|
171
|
+
|
|
172
|
+
>>> git_main_cmd_pair_flag_args(None, "--namespace")
|
|
173
|
+
[]
|
|
174
|
+
|
|
175
|
+
>>> from vt.utils.commons.commons.core_py import UNSET
|
|
176
|
+
>>> git_main_cmd_pair_flag_args(UNSET, "--namespace")
|
|
177
|
+
[]
|
|
178
|
+
"""
|
|
179
|
+
return [cmd_flag, str(val)] if not_none_not_unset(val) else []
|
gitbolt/ls_tree.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Helper interfaces specific to ``git ls-tree`` subcommand.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import abstractmethod
|
|
9
|
+
from typing import Protocol, Unpack, override, Literal
|
|
10
|
+
|
|
11
|
+
from vt.utils.errors.error_specs import ERR_INVALID_USAGE
|
|
12
|
+
from vt.utils.errors.error_specs.utils import require_type, require_iterable
|
|
13
|
+
|
|
14
|
+
from gitbolt.exceptions import GitExitingException
|
|
15
|
+
from gitbolt.models import GitLsTreeOpts
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LsTreeArgsValidator(Protocol):
|
|
19
|
+
"""
|
|
20
|
+
The argument validator for ``git ls-tree`` subcommand.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def validate(self, tree_ish: str, **ls_tree_opts: Unpack[GitLsTreeOpts]) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Validate arguments passed to the ls-tree() method.
|
|
27
|
+
|
|
28
|
+
:param tree_ish: A tree-ish identifier (commit SHA, branch name, etc.).
|
|
29
|
+
:param ls_tree_opts: Keyword arguments mapping to supported options for ``git ls-tree``.
|
|
30
|
+
"""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class UtilLsTreeArgsValidator(LsTreeArgsValidator):
|
|
35
|
+
"""
|
|
36
|
+
Independent utility function sort of interface to perform ``ls_tree()`` arguments validation.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
def validate(self, tree_ish: str, **ls_tree_opts: Unpack[GitLsTreeOpts]) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Validate the inputs provided to the ``git ls-tree`` command.
|
|
43
|
+
|
|
44
|
+
This utility ensures type safety and logical correctness of arguments
|
|
45
|
+
passed to the ``git ls-tree`` subcommand.
|
|
46
|
+
|
|
47
|
+
This includes:
|
|
48
|
+
* Enforcing that ``tree_ish`` is a string.
|
|
49
|
+
* Checking that each supported boolean flag (like ``d``, ``r``, ``z``, etc.)
|
|
50
|
+
is a valid boolean if provided.
|
|
51
|
+
* Validating that ``abbrev`` is an integer between 0 and 40 (inclusive).
|
|
52
|
+
* Ensuring ``format_`` is a string if specified.
|
|
53
|
+
* Checking that ``path`` is a list of strings if specified.
|
|
54
|
+
|
|
55
|
+
All validations will raise a ``GitExitingException`` with a specific
|
|
56
|
+
exit code depending on the nature of the failure:
|
|
57
|
+
|
|
58
|
+
* ``TypeError`` leads to ``ERR_DATA_FORMAT_ERR``.
|
|
59
|
+
* ``ValueError`` leads to ``ERR_INVALID_USAGE``.
|
|
60
|
+
|
|
61
|
+
See: `git ls-tree documentation <https://git-scm.com/docs/git-ls-tree>`_.
|
|
62
|
+
|
|
63
|
+
:param tree_ish: A valid Git tree-ish identifier (e.g., branch name, commit hash).
|
|
64
|
+
:param ls_tree_opts: Keyword arguments that map to valid ls-tree options.
|
|
65
|
+
:raises GitExitingException: When validation fails.
|
|
66
|
+
|
|
67
|
+
Examples::
|
|
68
|
+
|
|
69
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD", d=True, abbrev=10)
|
|
70
|
+
>>> UtilLsTreeArgsValidator().validate("abc123", format_="%(objectname)", path=["src/", "README.md"])
|
|
71
|
+
>>> UtilLsTreeArgsValidator().validate("main", name_only=False, z=True)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Invalid Examples (will raise GitExitingException), printing these just for doctesting::
|
|
75
|
+
|
|
76
|
+
>>> UtilLsTreeArgsValidator().validate(42) # type: ignore[arg-type] # tree_ish expects str and int is provided
|
|
77
|
+
Traceback (most recent call last):
|
|
78
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'tree_ish' must be a string
|
|
79
|
+
|
|
80
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD", abbrev="abc") # type: ignore[arg-type] # abbrev expects int and str is provided
|
|
81
|
+
Traceback (most recent call last):
|
|
82
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'abbrev' must be an int
|
|
83
|
+
|
|
84
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD", abbrev=True) # type: ignore[arg-type] # abbrev expects int and bool is provided
|
|
85
|
+
Traceback (most recent call last):
|
|
86
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'abbrev' must be an int
|
|
87
|
+
|
|
88
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD", abbrev=100)
|
|
89
|
+
Traceback (most recent call last):
|
|
90
|
+
gitbolt.exceptions.GitExitingException: ValueError: abbrev must be between 0 and 40.
|
|
91
|
+
|
|
92
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD",
|
|
93
|
+
... path="src/") # type: ignore[arg-type] as path expects list[str] and str is provided.
|
|
94
|
+
Traceback (most recent call last):
|
|
95
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'path' must be a non-str iterable
|
|
96
|
+
|
|
97
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD",
|
|
98
|
+
... path=1) # type: ignore[arg-type] as path expects list[str] and str is provided.
|
|
99
|
+
Traceback (most recent call last):
|
|
100
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'path' must be a non-str iterable
|
|
101
|
+
|
|
102
|
+
>>> UtilLsTreeArgsValidator().validate("HEAD",
|
|
103
|
+
... z="yes") # type: ignore[arg-type] as z expects bool and str is provided.
|
|
104
|
+
Traceback (most recent call last):
|
|
105
|
+
gitbolt.exceptions.GitExitingException: TypeError: 'z' must be a boolean
|
|
106
|
+
"""
|
|
107
|
+
require_type(tree_ish, "tree_ish", str, GitExitingException)
|
|
108
|
+
|
|
109
|
+
bool_keys: list[
|
|
110
|
+
Literal[
|
|
111
|
+
"d",
|
|
112
|
+
"r",
|
|
113
|
+
"t",
|
|
114
|
+
"long",
|
|
115
|
+
"z",
|
|
116
|
+
"name_only",
|
|
117
|
+
"object_only",
|
|
118
|
+
"full_name",
|
|
119
|
+
"full_tree",
|
|
120
|
+
"name_status",
|
|
121
|
+
]
|
|
122
|
+
] = [
|
|
123
|
+
"d",
|
|
124
|
+
"r",
|
|
125
|
+
"t",
|
|
126
|
+
"long",
|
|
127
|
+
"z",
|
|
128
|
+
"name_only",
|
|
129
|
+
"object_only",
|
|
130
|
+
"full_name",
|
|
131
|
+
"full_tree",
|
|
132
|
+
"name_status",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
for key in bool_keys:
|
|
136
|
+
if key in ls_tree_opts:
|
|
137
|
+
the_key = ls_tree_opts[key]
|
|
138
|
+
require_type(the_key, key, bool, GitExitingException)
|
|
139
|
+
|
|
140
|
+
if "abbrev" in ls_tree_opts:
|
|
141
|
+
abbrev = ls_tree_opts["abbrev"]
|
|
142
|
+
require_type(abbrev, "abbrev", int, GitExitingException)
|
|
143
|
+
if not (0 <= abbrev <= 40):
|
|
144
|
+
errmsg = "abbrev must be between 0 and 40."
|
|
145
|
+
raise GitExitingException(
|
|
146
|
+
errmsg, exit_code=ERR_INVALID_USAGE
|
|
147
|
+
) from ValueError(errmsg)
|
|
148
|
+
|
|
149
|
+
if "format_" in ls_tree_opts:
|
|
150
|
+
format_ = ls_tree_opts["format_"]
|
|
151
|
+
require_type(format_, "format_", str, GitExitingException)
|
|
152
|
+
|
|
153
|
+
if "path" in ls_tree_opts:
|
|
154
|
+
path = ls_tree_opts["path"]
|
|
155
|
+
require_iterable(path, "path", str, list, GitExitingException)
|