Skip to content

Commit 6f318f1

Browse files
committed
feat(git): run and clone
1 parent e242a15 commit 6f318f1

File tree

1 file changed

+289
-2
lines changed

1 file changed

+289
-2
lines changed

libvcs/cmd/git.py

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,293 @@
11
import pathlib
2+
from typing import Optional, Sequence, Union
3+
4+
from ..types import StrOrBytesPath, StrOrPath
5+
from .core import run
6+
7+
_CMD = Union[StrOrBytesPath, Sequence[StrOrBytesPath]]
28

39

410
class Git:
5-
def __init__(self, dir: pathlib.Path):
6-
self.dir: pathlib.Path = dir
11+
def __init__(self, dir: StrOrPath):
12+
"""Lite, typed, pythonic wrapper for git(1).
13+
14+
Parameters
15+
----------
16+
dir :
17+
Operates as PATH in the corresonding git subcommand.
18+
19+
Examples
20+
--------
21+
>>> Git(dir=tmp_path)
22+
<Git dir=...>
23+
"""
24+
#: Directory to check out
25+
self.dir: pathlib.Path
26+
if isinstance(dir, pathlib.Path):
27+
self.dir = dir
28+
else:
29+
self.dir = pathlib.Path(dir)
30+
31+
def __repr__(self):
32+
return f"<Git dir={self.dir}>"
33+
34+
def run(
35+
self,
36+
args: _CMD,
37+
# Print-and-exit flags
38+
version: Optional[bool] = None,
39+
help: Optional[bool] = None,
40+
html_path: Optional[bool] = None,
41+
man_path: Optional[bool] = None,
42+
info_path: Optional[bool] = None,
43+
# Normal flags
44+
cwd: Optional[StrOrBytesPath] = None,
45+
git_dir: Optional[StrOrBytesPath] = None,
46+
work_tree: Optional[StrOrBytesPath] = None,
47+
namespace: Optional[StrOrBytesPath] = None,
48+
super_prefix: Optional[StrOrBytesPath] = None,
49+
exec_path: Optional[StrOrBytesPath] = None,
50+
bare: Optional[bool] = None,
51+
no_replace_objects: Optional[bool] = None,
52+
literal_pathspecs: Optional[bool] = None,
53+
global_pathspecs: Optional[bool] = None,
54+
noglob_pathspecs: Optional[bool] = None,
55+
icase_pathspecs: Optional[bool] = None,
56+
no_optional_locks: Optional[bool] = None,
57+
config: Optional[str] = None,
58+
config_env: Optional[str] = None,
59+
**kwargs,
60+
):
61+
"""
62+
Passing None to a subcommand option, the flag won't be passed unless otherwise
63+
stated.
64+
65+
`git help` and `git help [cmd]`
66+
67+
Wraps git's `Options <https://git-scm.com/docs/git#_options>`_.
68+
69+
Parameters
70+
----------
71+
cwd : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
72+
``-C <path>``, Defaults to :attr:`~.cwd`
73+
git_dir : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
74+
``--git-dir <path>``
75+
work_tree : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
76+
``--work-tree <path>``
77+
namespace : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
78+
``--namespace <path>``
79+
super_prefix : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
80+
``--super-prefix <path>``
81+
exec_path : :attr:`libvcs.cmd.types.StrOrBytesPath`, optional
82+
``--exec-path=<path>``
83+
bare : bool
84+
``--bare``
85+
no_replace_objects : bool
86+
``--no-replace-objects``
87+
literal_pathspecs : bool
88+
``--literal-pathspecs``
89+
global_pathspecs : bool
90+
``--glob-pathspecs``
91+
noglob_pathspecs : bool
92+
``--noglob-pathspecs``
93+
icase_pathspecs : bool
94+
``--icase-pathspecs``
95+
no_optional_locks : bool
96+
``--no-optional-locks``
97+
version : bool
98+
``--version``
99+
html_path : bool
100+
``--html-path``
101+
man_path : bool
102+
``--man-path``
103+
info_path : bool
104+
``--info-path``
105+
help : bool
106+
``-h / --help``
107+
pager : bool
108+
``-p --pager``
109+
no_pager : bool
110+
``-P / --no-pager``
111+
config :
112+
``--config=<name>=<value>``
113+
config_env :
114+
``--config-env=<name>=<envvar>``
115+
116+
Examples
117+
--------
118+
>>> git = Git(dir=tmp_path)
119+
>>> git.run(['help']) # doctest: +NORMALIZE_WHITESPACE
120+
"usage: git [--version] [--help] [-C <path>]..."
121+
"""
122+
123+
if isinstance(args, Sequence):
124+
cli_args = ["git", *args]
125+
else:
126+
cli_args = ["git", args]
127+
128+
if "cwd" not in kwargs:
129+
kwargs["cwd"] = self.dir
130+
131+
#
132+
# Print-and-exit
133+
#
134+
if version is True:
135+
cli_args.append("--version")
136+
if help is True:
137+
cli_args.append("--help")
138+
if html_path is True:
139+
cli_args.append("--html-path")
140+
if man_path is True:
141+
cli_args.append("--man-path")
142+
if info_path is True:
143+
cli_args.append("--info-path")
144+
145+
#
146+
# Flags
147+
#
148+
if cwd is not None:
149+
cli_args.append(f"-C {cwd}")
150+
if git_dir is not None:
151+
cli_args.append(f"--git-dir {git_dir}")
152+
if work_tree is not None:
153+
cli_args.append(f"--work-tree {work_tree}")
154+
if namespace is not None:
155+
cli_args.append(f"--namespace {namespace}")
156+
if super_prefix is not None:
157+
cli_args.append(f"--super-prefix {super_prefix}")
158+
if exec_path is not None:
159+
cli_args.append(f"--exec-path {exec_path}")
160+
if bare is True:
161+
cli_args.append("--bare")
162+
if no_replace_objects is True:
163+
cli_args.append("--no-replace-objects")
164+
if literal_pathspecs is True:
165+
cli_args.append("--literal-pathspecs")
166+
if global_pathspecs is True:
167+
cli_args.append("--global-pathspecs")
168+
if noglob_pathspecs is True:
169+
cli_args.append("--noglob-pathspecs")
170+
if icase_pathspecs is True:
171+
cli_args.append("--icase-pathspecs")
172+
if no_optional_locks is True:
173+
cli_args.append("--no-optional-locks")
174+
175+
return run(cmd=cli_args, **kwargs)
176+
177+
def clone(
178+
self,
179+
url: str,
180+
separate_git_dir: Optional[StrOrBytesPath] = None,
181+
template: Optional[str] = None,
182+
filter: Optional[str] = None,
183+
depth: Optional[str] = None,
184+
branch: Optional[str] = None,
185+
origin: Optional[str] = None,
186+
upload_pack: Optional[str] = None,
187+
shallow_since: Optional[str] = None,
188+
shallow_exclude: Optional[str] = None,
189+
reference: Optional[str] = None,
190+
reference_if_able: Optional[str] = None,
191+
server_option: Optional[str] = None,
192+
jobs: Optional[str] = None,
193+
force: Optional[bool] = None,
194+
local: Optional[bool] = None,
195+
no_hardlinks: Optional[bool] = None,
196+
hardlinks: Optional[bool] = None,
197+
shared: Optional[bool] = None,
198+
progress: Optional[bool] = None,
199+
no_checkout: Optional[bool] = None,
200+
no_reject_shallow: Optional[bool] = None,
201+
reject_shallow: Optional[bool] = None,
202+
sparse: Optional[bool] = None,
203+
shallow_submodules: Optional[bool] = None,
204+
no_shallow_submodules: Optional[bool] = None,
205+
remote_submodules: Optional[bool] = None,
206+
no_remote_submodules: Optional[bool] = None,
207+
verbose: Optional[bool] = None,
208+
quiet: Optional[bool] = None,
209+
):
210+
"""Clone a working copy from an git repo.
211+
212+
Wraps `git clone <https://git-scm.com/docs/git-clone>`_.
213+
214+
Parameters
215+
----------
216+
url : str
217+
directory : str
218+
separate_git_dir : StrOrBytesPath
219+
Separate repository (.git/ ) from working tree
220+
force : bool, optional
221+
force operation to run
222+
223+
Examples
224+
--------
225+
>>> git = Git(dir=tmp_path)
226+
>>> git_remote_repo = create_git_remote_repo()
227+
>>> git.clone(url=f'file://{git_remote_repo}')
228+
''
229+
>>> git.dir.exists()
230+
True
231+
"""
232+
required_flags: list[str] = [url, str(self.dir)]
233+
local_flags: list[str] = []
234+
235+
if template is not None:
236+
local_flags.append(f"--template={template}")
237+
if separate_git_dir is not None:
238+
local_flags.append(f"--separate-git-dir={separate_git_dir}")
239+
if filter is not None:
240+
local_flags.append(f"--filter={filter}")
241+
if depth is not None:
242+
local_flags.append(f"--depth {depth}")
243+
if branch is not None:
244+
local_flags.append(f"--branch {branch}")
245+
if origin is not None:
246+
local_flags.append(f"--origin {origin}")
247+
if upload_pack is not None:
248+
local_flags.append(f"--upload-pack {upload_pack}")
249+
if shallow_since is not None:
250+
local_flags.append(f"--shallow-since={shallow_since}")
251+
if shallow_exclude is not None:
252+
local_flags.append(f"--shallow-exclude={shallow_exclude}")
253+
if reference is not None:
254+
local_flags.append(f"--reference {reference}")
255+
if reference_if_able is not None:
256+
local_flags.append(f"--reference {reference_if_able}")
257+
if server_option is not None:
258+
local_flags.append(f"--server-option={server_option}")
259+
if jobs is not None:
260+
local_flags.append(f"--jobs {jobs}")
261+
if local is True:
262+
local_flags.append("--local")
263+
if hardlinks is True:
264+
local_flags.append("--hardlinks")
265+
if no_hardlinks is True:
266+
local_flags.append("--no-hardlinks")
267+
if shared is True:
268+
local_flags.append("--shared")
269+
if quiet is True:
270+
local_flags.append("--quiet")
271+
if verbose is True:
272+
local_flags.append("--verbose")
273+
if progress is True:
274+
local_flags.append("--progress")
275+
if no_checkout is True:
276+
local_flags.append("--no-checkout")
277+
if no_reject_shallow is True:
278+
local_flags.append("--no-reject-shallow")
279+
if reject_shallow is True:
280+
local_flags.append("--reject-shallow")
281+
if sparse is True:
282+
local_flags.append("--sparse")
283+
if shallow_submodules is True:
284+
local_flags.append("--shallow-submodules")
285+
if no_shallow_submodules is True:
286+
local_flags.append("--no-shallow-submodules")
287+
if remote_submodules is True:
288+
local_flags.append("--remote-submodules")
289+
if no_remote_submodules is True:
290+
local_flags.append("--no-remote-submodules")
291+
return self.run(
292+
["clone", *local_flags, "--", *required_flags], check_returncode=False
293+
)

0 commit comments

Comments
 (0)