API

Implementation of fastgit

Imports

import io, tempfile
from contextlib import redirect_stdout
from fastcore.test import test_eq,expect_fail

source

callgit

def callgit(
    path, *args, uname:NoneType=None, pre:NoneType=None
):

Run git in path, returning stripped stdout+stderr as a single str

callgit returns git’s stdout and stderr combined as a single str, just as a terminal shows them. Call .splitlines() on the result when you need line-oriented output.

with tempfile.TemporaryDirectory() as td:
    msg = callgit(td, 'init', '-b', 'main')
    assert 'Initialized' in msg
    test_eq(callgit(td, 'rev-parse', '--git-dir'), '.git')
    assert '\n' in callgit(td, 'status')

source

get_top

def get_top(
    folder
):

Call self as a function.

with tempfile.TemporaryDirectory() as td:
    test_eq(get_top(td), None)
    msg = callgit(td, 'init', '-b', 'main')
    assert get_top(td) in msg

Git represents a python wrapper for the git command. It will execute every command as if git were being run with the working directory being the directory d passed in at init time.

As a result, callers should take care not to checkout a branch which does not have the directory used to initialize Git.

Pass pre to prefix every git command with additional arguments, e.g. a sudo invocation to run as another user.

Keyword arguments become git options: single-letter names are short options (n=1-n 1) and longer names are long options (pretty='format:%s'--pretty=format:%s), with underscores converted to dashes. True passes the flag alone, and False omits it. Use the __ parameter to pass path arguments after --.

Failed commands print the error and return None. Pass mute_errors=True to silence the message, or raise_exc=True (per call, or at init to make it the instance default) to raise the CalledProcessError instead.


source

Git

def Git(
    d, pre:NoneType=None, raise_exc:bool=False
):

Run git commands in dir d

with tempfile.TemporaryDirectory() as td:
    g = Git(td)
    assert not g.exists
    msg = g.init(b='main')
    assert g.top() in msg

Options compose naturally: values like n=1 are stringified, and False omits the option entirely:

with tempfile.TemporaryDirectory() as td:
    g = Git(td)
    g.init(b='main')
    (g.d/'a.txt').write_text('a')
    g.add('a.txt'); g.commit(m='first')
    test_eq(g.log(n=1, format='%s'), 'first')
    test_eq(g.log(n=1, format='%s', p=False), 'first')

Errors print as a terse one-line message (like git itself) and return None by default, which suits interactive and quick-tooling use. Library code that consumes a command’s output should pass raise_exc (at init, or per call) so failures raise instead of returning None:

with tempfile.TemporaryDirectory() as td:
    test_eq(Git(td).log(mute_errors=True), None)
    s = io.StringIO()
    with redirect_stdout(s): Git(td).log()
    test_eq(s.getvalue().count('\n'), 1)  # errors print as one terse git-style line
    assert s.getvalue().startswith('ERROR: git log: fatal:')
    with expect_fail(CalledProcessError): Git(td, raise_exc=True).log()
    with expect_fail(CalledProcessError): Git(td).log(raise_exc=True)

source

Git.last_commit

def last_commit():

Call self as a function.


source

Git.commits

def commits():

Call self as a function.

A repo with no commits yet has an empty commits list:

with tempfile.TemporaryDirectory() as td:
    g = Git(td)
    g.init(b='main')
    test_eq(g.commits, [])

source

Git.current_branch

def current_branch():

Call self as a function.

current_branch returns the currently checked-out branch. Note that git writes checkout’s status messages to stderr, which fastgit includes in the returned output just as a terminal would show them:

with tempfile.TemporaryDirectory() as td:
    g = Git(td)
    g.init(b='main')
    test_eq(g.current_branch, 'main')
    assert 'Switched' in g.checkout('-b', 'tmp')
    test_eq(g.current_branch, 'tmp')
with tempfile.TemporaryDirectory() as td:
    g = Git(td, pre=['env'])
    g.init()
    (Path(td)/'a.txt').write_text('a')
    g.add('a.txt')
    g.commit(m='first')
    test_eq(len(g.commits), 1)
    assert 'first' in g.commits[0]
    with expect_fail(Exception, 'not both'): callgit(td, 'status', uname='x', pre=['env'])