fastgit 0.1.0__tar.gz → 0.1.2__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.
- fastgit-0.1.2/PKG-INFO +104 -0
- fastgit-0.1.2/README.md +84 -0
- fastgit-0.1.2/fastgit/__init__.py +9 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit/_modidx.py +8 -0
- fastgit-0.1.2/fastgit/core.py +109 -0
- fastgit-0.1.2/fastgit.egg-info/PKG-INFO +104 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/pyproject.toml +1 -0
- fastgit-0.1.0/PKG-INFO +0 -75
- fastgit-0.1.0/README.md +0 -55
- fastgit-0.1.0/fastgit/__init__.py +0 -3
- fastgit-0.1.0/fastgit/core.py +0 -73
- fastgit-0.1.0/fastgit.egg-info/PKG-INFO +0 -75
- {fastgit-0.1.0 → fastgit-0.1.2}/LICENSE +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/MANIFEST.in +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit.egg-info/SOURCES.txt +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit.egg-info/dependency_links.txt +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit.egg-info/entry_points.txt +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit.egg-info/requires.txt +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/fastgit.egg-info/top_level.txt +0 -0
- {fastgit-0.1.0 → fastgit-0.1.2}/setup.cfg +0 -0
fastgit-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastgit
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Use git from python, fast
|
|
5
|
+
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/fastgit
|
|
8
|
+
Project-URL: Documentation, https://AnswerDotAI.github.io/fastgit
|
|
9
|
+
Keywords: nbdev,jupyter,notebook,python
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: fastcore>=1.12.27
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# fastgit
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
+
|
|
26
|
+
`fastgit` is a thin Python wrapper for the `git` CLI: one [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) object whose attribute calls run git commands, so if you know git, you already know fastgit. There is no reimplementation of git internals and no object model to learn – commands return git’s own output as (subclassed) `str`s.
|
|
27
|
+
|
|
28
|
+
It is designed for interactive use and automation alike: errors print tersely by default (like git itself) or raise on request, exit codes that mean “no” rather than “failed” are returned normally, and passing `sync=False` gives an async client for servers and concurrent code.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Installation
|
|
33
|
+
|
|
34
|
+
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
35
|
+
|
|
36
|
+
``` sh
|
|
37
|
+
$ pip install fastgit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### How to use
|
|
41
|
+
|
|
42
|
+
Create a [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) for any directory; every command runs with that directory as its working tree. Method names map to git subcommands, and results come back as stripped strings:
|
|
43
|
+
|
|
44
|
+
``` python
|
|
45
|
+
import shutil, tempfile
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
``` python
|
|
49
|
+
td = tempfile.mkdtemp()
|
|
50
|
+
g = Git(td)
|
|
51
|
+
g.init(b='main')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
'Initialized empty Git repository in /private/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/tmp8zyj76q7/.git/'
|
|
55
|
+
|
|
56
|
+
``` python
|
|
57
|
+
(g.d/'.gitignore').mk_write('*.bak')
|
|
58
|
+
g.add('.gitignore')
|
|
59
|
+
g.commit(m='add .gitignore')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
'[main (root-commit) 5113ce1] add .gitignore\n 1 file changed, 1 insertion(+)\n create mode 100644 .gitignore'
|
|
63
|
+
|
|
64
|
+
Keyword arguments become options: single-letter names are short options (`n=1` → `-n 1`), longer names long options with underscores turned into dashes, and `True` passes the bare flag:
|
|
65
|
+
|
|
66
|
+
``` python
|
|
67
|
+
g.log(n=1, oneline=True)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
'5113ce1 add .gitignore'
|
|
71
|
+
|
|
72
|
+
You can also pass path arguments after `--` using the `__` parameter:
|
|
73
|
+
|
|
74
|
+
``` python
|
|
75
|
+
g.log('--oneline', __=['.gitignore'])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
'5113ce1 add .gitignore'
|
|
79
|
+
|
|
80
|
+
Frequent queries are properties:
|
|
81
|
+
|
|
82
|
+
``` python
|
|
83
|
+
g.current_branch, g.commits
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
('main', ['5113ce1 add .gitignore'])
|
|
87
|
+
|
|
88
|
+
A failed command prints git’s message and returns `None`; pass `raise_exc=True` (per call or at init) to raise instead. Where git uses exit 1 to mean “no” rather than “error” – like `grep` finding nothing – the output is returned as usual, with the code on `.returncode`:
|
|
89
|
+
|
|
90
|
+
``` python
|
|
91
|
+
res = g.grep('missing')
|
|
92
|
+
res.returncode
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
1
|
|
96
|
+
|
|
97
|
+
Pass `sync=False` for an async client: the same commands and properties, each returning an awaitable, so a server never blocks its event loop on git:
|
|
98
|
+
|
|
99
|
+
``` python
|
|
100
|
+
ag = Git(td, sync=False)
|
|
101
|
+
await ag.last_commit
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
'add .gitignore'
|
fastgit-0.1.2/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# fastgit
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
|
+
|
|
6
|
+
`fastgit` is a thin Python wrapper for the `git` CLI: one [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) object whose attribute calls run git commands, so if you know git, you already know fastgit. There is no reimplementation of git internals and no object model to learn – commands return git’s own output as (subclassed) `str`s.
|
|
7
|
+
|
|
8
|
+
It is designed for interactive use and automation alike: errors print tersely by default (like git itself) or raise on request, exit codes that mean “no” rather than “failed” are returned normally, and passing `sync=False` gives an async client for servers and concurrent code.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
### Installation
|
|
13
|
+
|
|
14
|
+
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
15
|
+
|
|
16
|
+
``` sh
|
|
17
|
+
$ pip install fastgit
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### How to use
|
|
21
|
+
|
|
22
|
+
Create a [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) for any directory; every command runs with that directory as its working tree. Method names map to git subcommands, and results come back as stripped strings:
|
|
23
|
+
|
|
24
|
+
``` python
|
|
25
|
+
import shutil, tempfile
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
``` python
|
|
29
|
+
td = tempfile.mkdtemp()
|
|
30
|
+
g = Git(td)
|
|
31
|
+
g.init(b='main')
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
'Initialized empty Git repository in /private/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/tmp8zyj76q7/.git/'
|
|
35
|
+
|
|
36
|
+
``` python
|
|
37
|
+
(g.d/'.gitignore').mk_write('*.bak')
|
|
38
|
+
g.add('.gitignore')
|
|
39
|
+
g.commit(m='add .gitignore')
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
'[main (root-commit) 5113ce1] add .gitignore\n 1 file changed, 1 insertion(+)\n create mode 100644 .gitignore'
|
|
43
|
+
|
|
44
|
+
Keyword arguments become options: single-letter names are short options (`n=1` → `-n 1`), longer names long options with underscores turned into dashes, and `True` passes the bare flag:
|
|
45
|
+
|
|
46
|
+
``` python
|
|
47
|
+
g.log(n=1, oneline=True)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
'5113ce1 add .gitignore'
|
|
51
|
+
|
|
52
|
+
You can also pass path arguments after `--` using the `__` parameter:
|
|
53
|
+
|
|
54
|
+
``` python
|
|
55
|
+
g.log('--oneline', __=['.gitignore'])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
'5113ce1 add .gitignore'
|
|
59
|
+
|
|
60
|
+
Frequent queries are properties:
|
|
61
|
+
|
|
62
|
+
``` python
|
|
63
|
+
g.current_branch, g.commits
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
('main', ['5113ce1 add .gitignore'])
|
|
67
|
+
|
|
68
|
+
A failed command prints git’s message and returns `None`; pass `raise_exc=True` (per call or at init) to raise instead. Where git uses exit 1 to mean “no” rather than “error” – like `grep` finding nothing – the output is returned as usual, with the code on `.returncode`:
|
|
69
|
+
|
|
70
|
+
``` python
|
|
71
|
+
res = g.grep('missing')
|
|
72
|
+
res.returncode
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
1
|
|
76
|
+
|
|
77
|
+
Pass `sync=False` for an async client: the same commands and properties, each returning an awaitable, so a server never blocks its event loop on git:
|
|
78
|
+
|
|
79
|
+
``` python
|
|
80
|
+
ag = Git(td, sync=False)
|
|
81
|
+
await ag.last_commit
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
'add .gitignore'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Use git from python, fast
|
|
2
|
+
|
|
3
|
+
`fastgit` is a thin Python wrapper for the `git` CLI: one `Git` object whose attribute calls run git commands, so if you know git, you already know fastgit. There is no reimplementation of git internals and no object model to learn -- commands return git's own output as (subclassed) `str`s.
|
|
4
|
+
|
|
5
|
+
It is designed for interactive use and automation alike: errors print tersely by default (like git itself) or raise on request, exit codes that mean "no" rather than "failed" are returned normally, and passing `sync=False` gives an async client for servers and concurrent code."""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.2"
|
|
8
|
+
|
|
9
|
+
from .core import *
|
|
@@ -9,11 +9,19 @@ d = { 'settings': { 'branch': 'main',
|
|
|
9
9
|
'fastgit.core.Git.__call__': ('core.html#git.__call__', 'fastgit/core.py'),
|
|
10
10
|
'fastgit.core.Git.__getattr__': ('core.html#git.__getattr__', 'fastgit/core.py'),
|
|
11
11
|
'fastgit.core.Git.__init__': ('core.html#git.__init__', 'fastgit/core.py'),
|
|
12
|
+
'fastgit.core.Git._arun': ('core.html#git._arun', 'fastgit/core.py'),
|
|
13
|
+
'fastgit.core.Git._err': ('core.html#git._err', 'fastgit/core.py'),
|
|
14
|
+
'fastgit.core.Git._run': ('core.html#git._run', 'fastgit/core.py'),
|
|
12
15
|
'fastgit.core.Git.commits': ('core.html#git.commits', 'fastgit/core.py'),
|
|
13
16
|
'fastgit.core.Git.current_branch': ('core.html#git.current_branch', 'fastgit/core.py'),
|
|
14
17
|
'fastgit.core.Git.exists': ('core.html#git.exists', 'fastgit/core.py'),
|
|
15
18
|
'fastgit.core.Git.head_sha': ('core.html#git.head_sha', 'fastgit/core.py'),
|
|
16
19
|
'fastgit.core.Git.last_commit': ('core.html#git.last_commit', 'fastgit/core.py'),
|
|
17
20
|
'fastgit.core.Git.top': ('core.html#git.top', 'fastgit/core.py'),
|
|
21
|
+
'fastgit.core.GitRes': ('core.html#gitres', 'fastgit/core.py'),
|
|
22
|
+
'fastgit.core.GitRes.__new__': ('core.html#gitres.__new__', 'fastgit/core.py'),
|
|
23
|
+
'fastgit.core._gitcmd': ('core.html#_gitcmd', 'fastgit/core.py'),
|
|
24
|
+
'fastgit.core._gitres': ('core.html#_gitres', 'fastgit/core.py'),
|
|
25
|
+
'fastgit.core.acallgit': ('core.html#acallgit', 'fastgit/core.py'),
|
|
18
26
|
'fastgit.core.callgit': ('core.html#callgit', 'fastgit/core.py'),
|
|
19
27
|
'fastgit.core.get_top': ('core.html#get_top', 'fastgit/core.py')}}}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Implementation of fastgit
|
|
2
|
+
|
|
3
|
+
Docs: https://AnswerDotAI.github.io/fastgit/core.html.md"""
|
|
4
|
+
|
|
5
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
6
|
+
|
|
7
|
+
# %% auto #0
|
|
8
|
+
__all__ = ['GitRes', 'callgit', 'get_top', 'Git', 'acallgit']
|
|
9
|
+
|
|
10
|
+
# %% ../nbs/00_core.ipynb #c63a3ea7
|
|
11
|
+
from fastcore.utils import *
|
|
12
|
+
from fastcore.aio import then
|
|
13
|
+
import subprocess,asyncio
|
|
14
|
+
from subprocess import CalledProcessError
|
|
15
|
+
|
|
16
|
+
# %% ../nbs/00_core.ipynb #880c6a46
|
|
17
|
+
_ok1 = {'merge-tree','merge-base','grep','check-ignore','diff','diff-index','diff-files','diff-tree','show-ref','config','rev-parse'}
|
|
18
|
+
|
|
19
|
+
class GitRes(str):
|
|
20
|
+
"Git output `str`, carrying the command's `returncode`"
|
|
21
|
+
def __new__(cls, s, returncode=0):
|
|
22
|
+
self = super().__new__(cls, s)
|
|
23
|
+
self.returncode = returncode
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
# %% ../nbs/00_core.ipynb #e4c0a290
|
|
27
|
+
def _gitcmd(path, *args, uname=None, pre=None):
|
|
28
|
+
assert not (uname and pre), "Pass `uname` or `pre`, not both"
|
|
29
|
+
if uname:
|
|
30
|
+
warn("`uname` is deprecated; pass e.g `pre=['/usr/bin/sudo','-u',uname]` instead", DeprecationWarning, stacklevel=3)
|
|
31
|
+
pre = ['/usr/bin/sudo', '-u', uname]
|
|
32
|
+
cmd = ['git', '-C', str(Path(path).expanduser().resolve())] + list(args)
|
|
33
|
+
return [*pre, *cmd] if pre else cmd
|
|
34
|
+
|
|
35
|
+
def _gitres(cmd, args, out, err, returncode, ok_exit):
|
|
36
|
+
if ok_exit is None: ok_exit = 1 if args and args[0] in _ok1 else 0
|
|
37
|
+
if returncode not in (0, *listify(ok_exit)): raise CalledProcessError(returncode, cmd, out, err)
|
|
38
|
+
return GitRes((out + err).strip(), returncode)
|
|
39
|
+
|
|
40
|
+
def callgit(path, *args, uname=None, pre=None, ok_exit=None):
|
|
41
|
+
"Run git in `path`, returning stripped stdout+stderr as a `GitRes`; `ok_exit` adds accepted exit codes (default: 1 for `_ok1` commands)"
|
|
42
|
+
cmd = _gitcmd(path, *args, uname=uname, pre=pre)
|
|
43
|
+
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
44
|
+
return _gitres(cmd, args, r.stdout, r.stderr, r.returncode, ok_exit)
|
|
45
|
+
|
|
46
|
+
# %% ../nbs/00_core.ipynb #4c0df6c6
|
|
47
|
+
def get_top(folder):
|
|
48
|
+
try: return callgit(folder, 'rev-parse', '--show-toplevel')
|
|
49
|
+
except CalledProcessError: return None
|
|
50
|
+
|
|
51
|
+
# %% ../nbs/00_core.ipynb #97f78839
|
|
52
|
+
class Git:
|
|
53
|
+
"Run git commands in dir `d`; `sync=False` makes every command return an awaitable"
|
|
54
|
+
def __init__(self, d, pre=None, raise_exc=False, sync=True): self.d,self.pre,self.raise_exc,self._sync = Path(d).expanduser(),pre,raise_exc,sync
|
|
55
|
+
|
|
56
|
+
def __call__(self, cmd, *args, mute_errors=False, raise_exc=None, ok_exit=None, **kwargs):
|
|
57
|
+
paths = [str(p) for p in listify(kwargs.pop('__', None) or [])]
|
|
58
|
+
args = listify(args)
|
|
59
|
+
kwargs = {k:v for k,v in kwargs.items() if v is not False}
|
|
60
|
+
args += concat((f'-{k}',str(v)) for k,v in kwargs.items() if len(k)==1 and v is not True)
|
|
61
|
+
args += [f'-{k}' for k,v in kwargs.items() if len(k)==1 and v is True]
|
|
62
|
+
args += [f'--{k.replace("_","-")}={v}' for k,v in kwargs.items() if len(k)>1 and v is not True]
|
|
63
|
+
args += [f'--{k.replace("_","-")}' for k,v in kwargs.items() if len(k)>1 and v is True]
|
|
64
|
+
if paths: args += ['--'] + paths
|
|
65
|
+
f = self._run if self._sync else self._arun
|
|
66
|
+
return f(cmd, args, mute_errors, ifnone(raise_exc, self.raise_exc), ok_exit)
|
|
67
|
+
|
|
68
|
+
def _run(self, cmd, args, mute_errors, raise_exc, ok_exit):
|
|
69
|
+
try: return callgit(self.d, cmd, *args, pre=self.pre, ok_exit=ok_exit)
|
|
70
|
+
except CalledProcessError as e: self._err(e, cmd, args, mute_errors, raise_exc)
|
|
71
|
+
|
|
72
|
+
async def _arun(self, cmd, args, mute_errors, raise_exc, ok_exit):
|
|
73
|
+
try: return await acallgit(self.d, cmd, *args, pre=self.pre, ok_exit=ok_exit)
|
|
74
|
+
except CalledProcessError as e: self._err(e, cmd, args, mute_errors, raise_exc)
|
|
75
|
+
|
|
76
|
+
def _err(self, e, cmd, args, mute_errors, raise_exc):
|
|
77
|
+
if raise_exc: raise e
|
|
78
|
+
if not mute_errors: print(f"ERROR: git {' '.join(map(str, [cmd, *args]))}: {e.stderr.strip()}")
|
|
79
|
+
|
|
80
|
+
def __getattr__(self, nm):
|
|
81
|
+
if nm.startswith('_'): raise AttributeError(nm)
|
|
82
|
+
return partial(self, nm.replace('_','-'))
|
|
83
|
+
|
|
84
|
+
def top(self): return self.rev_parse('--show-toplevel', mute_errors=True, raise_exc=False)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def exists(self): return then(self.top(), lambda r: r is not None)
|
|
88
|
+
@property
|
|
89
|
+
def head_sha(self): return self.rev_parse('HEAD', mute_errors=True, raise_exc=False)
|
|
90
|
+
|
|
91
|
+
# %% ../nbs/00_core.ipynb #b688e74b
|
|
92
|
+
@patch(as_prop=True)
|
|
93
|
+
def last_commit(self:Git): return self.log('-1', pretty='format:%s')
|
|
94
|
+
|
|
95
|
+
# %% ../nbs/00_core.ipynb #fdf315b8
|
|
96
|
+
@patch(as_prop=True)
|
|
97
|
+
def commits(self:Git): return then(self.log('--oneline', mute_errors=True, raise_exc=False), lambda r: (r or '').splitlines())
|
|
98
|
+
|
|
99
|
+
# %% ../nbs/00_core.ipynb #712ca55b
|
|
100
|
+
@patch(as_prop=True)
|
|
101
|
+
def current_branch(self:Git): return self.branch('--show-current')
|
|
102
|
+
|
|
103
|
+
# %% ../nbs/00_core.ipynb #b69ecf1c
|
|
104
|
+
async def acallgit(path, *args, uname=None, pre=None, ok_exit=None):
|
|
105
|
+
"Async `callgit`: identical contract, run in an `asyncio` subprocess"
|
|
106
|
+
cmd = _gitcmd(path, *args, uname=uname, pre=pre)
|
|
107
|
+
p = await asyncio.create_subprocess_exec(*cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
108
|
+
out,err = await p.communicate()
|
|
109
|
+
return _gitres(cmd, args, out.decode(), err.decode(), p.returncode, ok_exit)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastgit
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Use git from python, fast
|
|
5
|
+
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/fastgit
|
|
8
|
+
Project-URL: Documentation, https://AnswerDotAI.github.io/fastgit
|
|
9
|
+
Keywords: nbdev,jupyter,notebook,python
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: fastcore>=1.12.27
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# fastgit
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
+
|
|
26
|
+
`fastgit` is a thin Python wrapper for the `git` CLI: one [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) object whose attribute calls run git commands, so if you know git, you already know fastgit. There is no reimplementation of git internals and no object model to learn – commands return git’s own output as (subclassed) `str`s.
|
|
27
|
+
|
|
28
|
+
It is designed for interactive use and automation alike: errors print tersely by default (like git itself) or raise on request, exit codes that mean “no” rather than “failed” are returned normally, and passing `sync=False` gives an async client for servers and concurrent code.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Installation
|
|
33
|
+
|
|
34
|
+
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
35
|
+
|
|
36
|
+
``` sh
|
|
37
|
+
$ pip install fastgit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### How to use
|
|
41
|
+
|
|
42
|
+
Create a [`Git`](https://AnswerDotAI.github.io/fastgit/core.html#git) for any directory; every command runs with that directory as its working tree. Method names map to git subcommands, and results come back as stripped strings:
|
|
43
|
+
|
|
44
|
+
``` python
|
|
45
|
+
import shutil, tempfile
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
``` python
|
|
49
|
+
td = tempfile.mkdtemp()
|
|
50
|
+
g = Git(td)
|
|
51
|
+
g.init(b='main')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
'Initialized empty Git repository in /private/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/tmp8zyj76q7/.git/'
|
|
55
|
+
|
|
56
|
+
``` python
|
|
57
|
+
(g.d/'.gitignore').mk_write('*.bak')
|
|
58
|
+
g.add('.gitignore')
|
|
59
|
+
g.commit(m='add .gitignore')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
'[main (root-commit) 5113ce1] add .gitignore\n 1 file changed, 1 insertion(+)\n create mode 100644 .gitignore'
|
|
63
|
+
|
|
64
|
+
Keyword arguments become options: single-letter names are short options (`n=1` → `-n 1`), longer names long options with underscores turned into dashes, and `True` passes the bare flag:
|
|
65
|
+
|
|
66
|
+
``` python
|
|
67
|
+
g.log(n=1, oneline=True)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
'5113ce1 add .gitignore'
|
|
71
|
+
|
|
72
|
+
You can also pass path arguments after `--` using the `__` parameter:
|
|
73
|
+
|
|
74
|
+
``` python
|
|
75
|
+
g.log('--oneline', __=['.gitignore'])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
'5113ce1 add .gitignore'
|
|
79
|
+
|
|
80
|
+
Frequent queries are properties:
|
|
81
|
+
|
|
82
|
+
``` python
|
|
83
|
+
g.current_branch, g.commits
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
('main', ['5113ce1 add .gitignore'])
|
|
87
|
+
|
|
88
|
+
A failed command prints git’s message and returns `None`; pass `raise_exc=True` (per call or at init) to raise instead. Where git uses exit 1 to mean “no” rather than “error” – like `grep` finding nothing – the output is returned as usual, with the code on `.returncode`:
|
|
89
|
+
|
|
90
|
+
``` python
|
|
91
|
+
res = g.grep('missing')
|
|
92
|
+
res.returncode
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
1
|
|
96
|
+
|
|
97
|
+
Pass `sync=False` for an async client: the same commands and properties, each returning an awaitable, so a server never blocks its event loop on git:
|
|
98
|
+
|
|
99
|
+
``` python
|
|
100
|
+
ag = Git(td, sync=False)
|
|
101
|
+
await ag.last_commit
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
'add .gitignore'
|
fastgit-0.1.0/PKG-INFO
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastgit
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Use git from python, fast
|
|
5
|
-
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Repository, https://github.com/AnswerDotAI/fastgit
|
|
8
|
-
Project-URL: Documentation, https://AnswerDotAI.github.io/fastgit
|
|
9
|
-
Keywords: nbdev,jupyter,notebook,python
|
|
10
|
-
Classifier: Natural Language :: English
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Development Status :: 3 - Alpha
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Requires-Python: >=3.9
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.12.27
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# fastgit
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
### Installation
|
|
29
|
-
|
|
30
|
-
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
31
|
-
|
|
32
|
-
``` sh
|
|
33
|
-
$ pip install fastgit
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### How to use
|
|
37
|
-
|
|
38
|
-
In this example we run `git init` on a directory, add a *.gitignore*, and commit it.
|
|
39
|
-
|
|
40
|
-
``` python
|
|
41
|
-
import shutil, tempfile
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
``` python
|
|
45
|
-
def _git_init(g):
|
|
46
|
-
if g.exists: return # Return early if git already initialised
|
|
47
|
-
g.init(b='main')
|
|
48
|
-
g.config('user.name', 'fastgit')
|
|
49
|
-
g.config('user.email', 'fastgit@example.com')
|
|
50
|
-
(g.d/".gitignore").mk_write("*.bak")
|
|
51
|
-
g.add(".gitignore")
|
|
52
|
-
g.commit(m="add .gitignore")
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
``` python
|
|
56
|
-
td = tempfile.mkdtemp()
|
|
57
|
-
g = Git(td)
|
|
58
|
-
_git_init(g)
|
|
59
|
-
assert 'add .gitignore' in g.last_commit
|
|
60
|
-
print(g.branch('--show-current'))
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
main
|
|
64
|
-
|
|
65
|
-
You can also pass path arguments after `--` using the `__` parameter:
|
|
66
|
-
|
|
67
|
-
``` python
|
|
68
|
-
g.log('--oneline', __=['.gitignore'])
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
'22a9a5d add .gitignore'
|
|
72
|
-
|
|
73
|
-
``` python
|
|
74
|
-
shutil.rmtree(td)
|
|
75
|
-
```
|
fastgit-0.1.0/README.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# fastgit
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
|
-
|
|
6
|
-
## Usage
|
|
7
|
-
|
|
8
|
-
### Installation
|
|
9
|
-
|
|
10
|
-
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
11
|
-
|
|
12
|
-
``` sh
|
|
13
|
-
$ pip install fastgit
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
### How to use
|
|
17
|
-
|
|
18
|
-
In this example we run `git init` on a directory, add a *.gitignore*, and commit it.
|
|
19
|
-
|
|
20
|
-
``` python
|
|
21
|
-
import shutil, tempfile
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
``` python
|
|
25
|
-
def _git_init(g):
|
|
26
|
-
if g.exists: return # Return early if git already initialised
|
|
27
|
-
g.init(b='main')
|
|
28
|
-
g.config('user.name', 'fastgit')
|
|
29
|
-
g.config('user.email', 'fastgit@example.com')
|
|
30
|
-
(g.d/".gitignore").mk_write("*.bak")
|
|
31
|
-
g.add(".gitignore")
|
|
32
|
-
g.commit(m="add .gitignore")
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
``` python
|
|
36
|
-
td = tempfile.mkdtemp()
|
|
37
|
-
g = Git(td)
|
|
38
|
-
_git_init(g)
|
|
39
|
-
assert 'add .gitignore' in g.last_commit
|
|
40
|
-
print(g.branch('--show-current'))
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
main
|
|
44
|
-
|
|
45
|
-
You can also pass path arguments after `--` using the `__` parameter:
|
|
46
|
-
|
|
47
|
-
``` python
|
|
48
|
-
g.log('--oneline', __=['.gitignore'])
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
'22a9a5d add .gitignore'
|
|
52
|
-
|
|
53
|
-
``` python
|
|
54
|
-
shutil.rmtree(td)
|
|
55
|
-
```
|
fastgit-0.1.0/fastgit/core.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"""Implementation of fastgit
|
|
2
|
-
|
|
3
|
-
Docs: https://AnswerDotAI.github.io/fastgit/core.html.md"""
|
|
4
|
-
|
|
5
|
-
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
6
|
-
|
|
7
|
-
# %% auto #0
|
|
8
|
-
__all__ = ['callgit', 'get_top', 'Git']
|
|
9
|
-
|
|
10
|
-
# %% ../nbs/00_core.ipynb #c63a3ea7
|
|
11
|
-
from fastcore.utils import *
|
|
12
|
-
import subprocess
|
|
13
|
-
from subprocess import CalledProcessError
|
|
14
|
-
|
|
15
|
-
# %% ../nbs/00_core.ipynb #e4c0a290
|
|
16
|
-
def callgit(path, *args, uname=None, pre=None):
|
|
17
|
-
"Run git in `path`, returning stripped stdout+stderr as a single `str`"
|
|
18
|
-
assert not (uname and pre), "Pass `uname` or `pre`, not both"
|
|
19
|
-
if uname:
|
|
20
|
-
warn("`uname` is deprecated; pass e.g `pre=['/usr/bin/sudo','-u',uname]` instead", DeprecationWarning, stacklevel=2)
|
|
21
|
-
pre = ['/usr/bin/sudo', '-u', uname]
|
|
22
|
-
fp = Path(path).expanduser().resolve()
|
|
23
|
-
args = ['git', '-C', str(fp)] + list(args)
|
|
24
|
-
if pre: args = [*pre, *args]
|
|
25
|
-
r = subprocess.run(args, capture_output=True, text=True, check=True)
|
|
26
|
-
return (r.stdout + r.stderr).strip()
|
|
27
|
-
|
|
28
|
-
# %% ../nbs/00_core.ipynb #4c0df6c6
|
|
29
|
-
def get_top(folder):
|
|
30
|
-
try: return callgit(folder, 'rev-parse', '--show-toplevel')
|
|
31
|
-
except CalledProcessError: return None
|
|
32
|
-
|
|
33
|
-
# %% ../nbs/00_core.ipynb #97f78839
|
|
34
|
-
class Git:
|
|
35
|
-
"Run git commands in dir `d`"
|
|
36
|
-
def __init__(self, d, pre=None, raise_exc=False): self.d,self.pre,self.raise_exc = Path(d).expanduser(),pre,raise_exc
|
|
37
|
-
|
|
38
|
-
def __call__(self, cmd, *args, mute_errors=False, raise_exc=None, **kwargs):
|
|
39
|
-
paths = [str(p) for p in listify(kwargs.pop('__', None) or [])]
|
|
40
|
-
args = listify(args)
|
|
41
|
-
kwargs = {k:v for k,v in kwargs.items() if v is not False}
|
|
42
|
-
args += concat((f'-{k}',str(v)) for k,v in kwargs.items() if len(k)==1 and v is not True)
|
|
43
|
-
args += [f'-{k}' for k,v in kwargs.items() if len(k)==1 and v is True]
|
|
44
|
-
args += [f'--{k.replace("_","-")}={v}' for k,v in kwargs.items() if len(k)>1 and v is not True]
|
|
45
|
-
args += [f'--{k.replace("_","-")}' for k,v in kwargs.items() if len(k)>1 and v is True]
|
|
46
|
-
if paths: args += ['--'] + paths
|
|
47
|
-
try: return callgit(self.d, cmd, *args, pre=self.pre)
|
|
48
|
-
except CalledProcessError as e:
|
|
49
|
-
if ifnone(raise_exc, self.raise_exc): raise
|
|
50
|
-
if not mute_errors: print(f'ERROR: Git.__call__ caught exception {e} \n with stderr={e.stderr}')
|
|
51
|
-
|
|
52
|
-
def __getattr__(self, nm):
|
|
53
|
-
if nm.startswith('_'): raise AttributeError(nm)
|
|
54
|
-
return partial(self, nm.replace('_','-'))
|
|
55
|
-
|
|
56
|
-
def top(self): return self.rev_parse('--show-toplevel', mute_errors=True, raise_exc=False)
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def exists(self): return self.top() is not None
|
|
60
|
-
@property
|
|
61
|
-
def head_sha(self): return self.rev_parse('HEAD', mute_errors=True, raise_exc=False)
|
|
62
|
-
|
|
63
|
-
# %% ../nbs/00_core.ipynb #b688e74b
|
|
64
|
-
@patch(as_prop=True)
|
|
65
|
-
def last_commit(self:Git): return self.log('-1', pretty='format:%s')
|
|
66
|
-
|
|
67
|
-
# %% ../nbs/00_core.ipynb #fdf315b8
|
|
68
|
-
@patch(as_prop=True)
|
|
69
|
-
def commits(self:Git): return (self.log('--oneline', mute_errors=True, raise_exc=False) or '').splitlines()
|
|
70
|
-
|
|
71
|
-
# %% ../nbs/00_core.ipynb #712ca55b
|
|
72
|
-
@patch(as_prop=True)
|
|
73
|
-
def current_branch(self:Git): return self.branch('--show-current')
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fastgit
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Use git from python, fast
|
|
5
|
-
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Repository, https://github.com/AnswerDotAI/fastgit
|
|
8
|
-
Project-URL: Documentation, https://AnswerDotAI.github.io/fastgit
|
|
9
|
-
Keywords: nbdev,jupyter,notebook,python
|
|
10
|
-
Classifier: Natural Language :: English
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Development Status :: 3 - Alpha
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Requires-Python: >=3.9
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.12.27
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# fastgit
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
### Installation
|
|
29
|
-
|
|
30
|
-
Install latest from [pypi](https://pypi.org/project/fastgit/)
|
|
31
|
-
|
|
32
|
-
``` sh
|
|
33
|
-
$ pip install fastgit
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### How to use
|
|
37
|
-
|
|
38
|
-
In this example we run `git init` on a directory, add a *.gitignore*, and commit it.
|
|
39
|
-
|
|
40
|
-
``` python
|
|
41
|
-
import shutil, tempfile
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
``` python
|
|
45
|
-
def _git_init(g):
|
|
46
|
-
if g.exists: return # Return early if git already initialised
|
|
47
|
-
g.init(b='main')
|
|
48
|
-
g.config('user.name', 'fastgit')
|
|
49
|
-
g.config('user.email', 'fastgit@example.com')
|
|
50
|
-
(g.d/".gitignore").mk_write("*.bak")
|
|
51
|
-
g.add(".gitignore")
|
|
52
|
-
g.commit(m="add .gitignore")
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
``` python
|
|
56
|
-
td = tempfile.mkdtemp()
|
|
57
|
-
g = Git(td)
|
|
58
|
-
_git_init(g)
|
|
59
|
-
assert 'add .gitignore' in g.last_commit
|
|
60
|
-
print(g.branch('--show-current'))
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
main
|
|
64
|
-
|
|
65
|
-
You can also pass path arguments after `--` using the `__` parameter:
|
|
66
|
-
|
|
67
|
-
``` python
|
|
68
|
-
g.log('--oneline', __=['.gitignore'])
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
'22a9a5d add .gitignore'
|
|
72
|
-
|
|
73
|
-
``` python
|
|
74
|
-
shutil.rmtree(td)
|
|
75
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|