gitutils.gitrepo: Interact with git repos using system interface

This module provides the GitRepo class, which provides a basic interface to a git repository (whether working or bare). Users may perform basic git operations such as git-push and git-pull using commands like GitRepo.push(), GitRepo.pull().

class lfc._vendor.gitutils.gitrepo.GitRepo(where=None)

Git repository interface class

Call:
>>> repo = GitRepo(where=None)
Inputs:
where: {None} | str

Path from which to look for git repo (default is CWD)

Outputs:
repo: GitRepo

Interface to git repository

Attributes:
add(*fnames)

Add a file, either new or modified

Has no effect if name is unchanged since last commit

Call:
>>> repo.add(*fnames)
Inputs:
repo: GitRepo

Inteface to git repository

fnames: tuple[str]

One or more file name or file name patterns

Versions:
  • 2022-12-02 @ddalle: v1.0

assert_bare(cmd=None)

Assert that current repo is working (non-bare)

Call:
>>> repo.assert_working(cmd=None)
Inputs:
repo: GitRepo

Inteface to git repository

cmd: {None} | str

Command name for error message

Versions:
  • 2023-09-16 @ddalle: v1.0

assert_working(cmd=None)

Assert that current repo is working (non-bare)

Call:
>>> repo.assert_working(cmd=None)
Inputs:
repo: GitRepo

Inteface to git repository

cmd: {None} | str

Command name for error message

Versions:
  • 2022-12-20 @ddalle: v1.0

bare

True | False – Whether this instance is in a bare repository

call(cmd, cwd=None, **kw) int

Run a command with generic options

Call:
>>> ierr = repo.call(cmd, cwd=None, **kw)
Inputs:
repo: GitRepo

Interface to git repository

cmd: list[str]

Command to run in list form

cwd: {None} | True | str

Location in which to run subprocess; None is current working directory, and True is repo.gitdir

stdout: {None} | subprocess.PIPE | file

Option to supress or pipe STDOUT

stderr: {None} | subprocess.PIPE | file

Option to supress or pipe STDERR

Outputs:
ierr: int

Return code from subprocess

Versions:
  • 2023-10-25 @ddalle: v1.0

  • 2024-04-23 @ddalle: v2.0; use _call()

call_oe(cmd, codes=None, cwd=None)

Run a command, capturing STDOUT and checking return code

Call:
>>> stdout, stderr, ierr = repo.call_oe(cmd, cwd=None)
Inputs:
repo: GitRepo

Interface to git repository

cmd: list[str]

Command to run in list form

cwd: {None} | True | str

Location in which to run subprocess; None is current working directory, and True is repo.gitdir

Outputs:
stdout: str

Captured STDOUT from command, if any

stderr: str

Captured STDERR from command, if any

ierr: int

Return code

Versions:
  • 2023-01-08 @ddalle: v1.0

check_call(cmd, codes=None, cwd=None, **kw) int

Run a command and check return code

Call:
>>> ierr = repo.check_call(cmd, codes=None, cwd=None)
Inputs:
repo: GitRepo

Interface to git repository

cmd: list[str]

Command to run in list form

codes: {None} | list[int]

Collection of allowed return codes (default only 0)

cwd: {None} | True | str

Location in which to run subprocess; None is current working directory, and True is repo.gitdir

Outputs:
ierr: int

Return code from subprocess

Versions:
  • 2023-01-08 @ddalle: v1.0

check_ignore(fname: str) bool

Check if fname is (or would be) ignored by git

Call:
>>> q = repo.check_ignore(fname)
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of file

Outputs:
q: True | False

Whether file is ignored (even if file doesn’t exist)

Versions:
  • 2022-12-20 @ddalle: v1.0

check_o(cmd, codes=None, cwd=None) str

Run a command, capturing STDOUT and checking return code

Call:
>>> stdout = repo.check_o(cmd, codes=None, cwd=None)
Inputs:
repo: GitRepo

Interface to git repository

cmd: list[str]

Command to run in list form

codes: {None} | list[int]

Collection of allowed return codes (default only 0)

cwd: {None} | True | str

Location in which to run subprocess; None is current working directory, and True is repo.gitdir

Outputs:
stdout: str

Captured STDOUT from command, if any

Versions:
  • 2023-01-08 @ddalle: v1.0

check_track(fname: str) bool

Check if a file is tracked by git

Call:
>>> q = repo.check_track(fname)
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of file

Outputs:
q: True | False

Whether file is tracked

Versions:
  • 2022-12-20 @ddalle: v1.0

checkout_branch(branch=None)

Check out a branch on a working repo

Call:
>>> repo.check_branch(branch=None)
Inputs:
repo: GitRepo

Interface to git repository

branch: {None} | str

Name of branch to checkout if non-empty

commit(m=None, **kw)

Commit current changes

Call:
>>> repo.commit(m=None, **kw)
Inputs:
repo: GitRepo

Inteface to git repository

m, message: {None} | str

Commit message

a: True | False

Option to commit all modifications to tracked files

Versions:
  • 2023-10-24 @ddalle: v1.0

connect()

Connect a persistent subprocess at base of repo

Call:
>>> shell = repo.connect()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
shell: shellutils.SSH

Subprocess running on either

Versions:
  • 2023-10-25 @ddalle: v1.0

create_patch(fname: str, contents, m=None, ref='HEAD')

Create a patch file that changes contents of a file

Call:
>>> repo.patch_file(
    fname, contents, m=None, branch=None, clean=True)
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of file (relative to root of git repo) to edit

contents: str | bytes

Contents to save for fname

m: {None} | str

Commit message for patch; default is “Patch ‘{fname}’”

ref: {"HEAD"} | str

Git reference from which to base patch

get_branch_list()

Get list of branches

Call:
>>> branches = repo.get_branch_list()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
branches: list[str]

List of branches, branches[0] is HEAD branch

get_configdir()

Get the path to the *.git/ folder, where config is

Call:
>>> fdir = repo.get_configdir()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
fdir: str

Path to {project}.git/ or {project}/.git/

get_gitconfig_opt(sec: str, opt: str) str

Get value of any git config setting

Call:
>>> v = repo.get_gitconfig_opt(sec, opt)
Inputs:
repo: GitRepo

Interface to git repository

sec: str

Name of config section to read

opt: str

Name of option to read in config section

Outputs:
v: object

Value from .git/config for sec > opt

get_gitdir(path: str)

Get absolute path to git repo root, even on bare repos

Call:
>>> repo.get_gitdir(where=None, bare=None)
Inputs:
repo: GitRepo

Interface to git repository

path: str

Working directory; can be local path or SSH pathlly)

Outputs:
gitdir: str

Full path to top-level of working repo or git-dir of bare

Versions:
  • 2022-12-22 @ddalle: v1.1; support older git vers

  • 2023-08-26 @ddalle: v2.0; move to instance method

get_ref(ref='HEAD')

Get the SHA-1 hash of commit pointed to by ref

Call:
>>> commit = repo.get_ref(ref="HEAD")
Inputs:
repo: GitRepo

Interface to git repository

ref: {"HEAD"} | str

Git reference, can be branch name, tag, or commit hash

Outputs:
commit: str

Full (SHA-1) hash of commit pointed to by ref

get_remotes() dict

Get names and URLs of remotes

Call:
>>> remotes = repo.get_remotes()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
remotes: dict[str]

Dictionary of remotes and URLs for each

get_tmpdir() str

Create random name for a working repo if a bare repo

Call:
>>> tmpdir = repo.get_tmpdir()
Inputs:
repo: GitRepo

Interface to git repository

branch: {None} | str

Name of branch to checkout if non-empty

Outputs:
tmpdir: str

Name of folder that’s a working repo

Attributes:
repo._tmpbase: str

Base name of suggested working repo top-level folder

repo._tmpdir: str

Absolute path to suggested working repo top-level folder

get_user_email() str

Get author’s email address according to git config

Call:
>>> addr = repo.get_user_email()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
addr: str

Email address

get_user_name() str

Get current user name according to git config

Call:
>>> uid = repo.get_user_name()
Inputs:
repo: GitRepo

Interface to git repository

Outputs:
uid: str

User name saved in .git/config

get_working_repo(branch=None)

Check out a branch on a working repo

Call:
>>> tmp_repo = repo.check_branch(branch=None)
Inputs:
repo: GitRepo

Interface to git repository

branch: {None} | str

Name of branch to checkout if non-empty

Outputs:
tmp_repo: GitRepo

Interface to working repo, repo if it’s a working repo or new temporary repo if repo is bare

gitdir

str – Absolute path to root directory

host

None | str – Name of remote host, if any; usually not used

ls_tree(*fnames, r=True, ref='HEAD')

List files tracked by git, even in a bare repo

Calling this function with no arguments will show all files tracked in repo.

Call:
>>> filelist = repo.ls_tree(*fnames, r=True, ref="HEAD")
Inputs:
repo: GitRepo

Interface to git repository

fnames: tuple[str]

Name of 0 or more files to search for

r: {True} | False

Whether or not to search recursively

ref: {"HEAD"} | str

Git reference, can be branch name, tag, or commit hash

Outputs:
filelist: list[str]

List of file names meeting above criteria

Versions:
  • 2022-12-21 @ddalle: v1.0

mv(fold: str, fnew: str)

Move a file or folder and inform git of change

Call:
>>> repo.mv(fold, fnew)
Inputs:
repo: GitRepo

Interface to git repository

fold: str

Name of existing file

fnew: str

Name of file after move

Versions:
  • 2022-12-28 @ddalle: v1.0

patch_file(fname: str, contents, m=None, branch=None, clean=True)

Commit contents to a file, whether bare or working repo

Call:
>>> repo.patch_file(
    fname, contents, m=None, branch=None, clean=True)
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of file (relative to root of git repo) to edit

contents: str | bytes

Contents to save for fname

m: {None} | str

Commit message for patch; default is “Patch ‘{fname}’”

branch: {None} | str

Name of branch to work in; default is HEAD

clean: {True} | False

Option to delete tmp working repo if repo is bare

pull(remote: str, ref='HEAD')

Pull from a remote repo

This function does not reproduce the full functionality of the original git-pull command

Call:
>>> repo.pull(remote, ref="HEAD")
Inputs:
repo: GitRepo

Interface to git repository

ref: {"HEAD"} | str

Git reference, usually branch name

push(remote: str, ref='HEAD')

Push to a remote repo

This function does not reproduce the full functionality of the original git-push command

Call:
>>> repo.push(remote, ref="HEAD")
Inputs:
repo: GitRepo

Interface to git repository

ref: {"HEAD"} | str

Git reference, usually branch name

rm(fname: str, *fnames, r=False)

Remove files or folders and stage deletions for git

Call:
>>> repo.rm(fname, *fnames, r=False)
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of first file/folder to remove

fnames: tuple[str]

Additional file/folder names or patterns to remove

r: True | {False}

Recursive option needed to delete folders

Versions:
  • 2022-12-29 @ddalle: v1.0

rm_working_repo() int

Remove the working repo, if it exists

Call:
>>> repo.rm_working_repo()
Inputs:
repo: GitRepo

Interface to git repository

shell

None | gitutils._vendor.shellutils.SSH – Optional persistent subprocess to run commands with; usually not used

show(fname, ref='HEAD')

Show contents of a file, even on a bare repo

Call:
>>> fbytes = repo.show(fname, ref="HEAD")
Inputs:
repo: GitRepo

Interface to git repository

fname: str

Name of file to read

ref: {"HEAD"} | str

Git reference, can be branch name, tag, or commit hash

Outputs:
fbytes: bytes

Contents of fname in repository, in raw bytes

Versions:
  • 2022-12-22 @ddalle: v1.0

status(*fnames) dict

Check status of file(s) or whole repo

Call:
>>> statusdict = repo.status(*fnames)
Inputs:
repo: GitRepo

Interface to git repository

fnames: tuple[str]

File name(s); if empty, show status of entire repo

Outputs:
statusdict: dict[str]

Two-letter status code for each modified file

Versions:
  • 2023-10-30 @ddalle: v1.0

validate_branch(branch=None)

Make sure branch exists before using it

Call:
>>> repo.validate_branch(branch=None)
Inputs:
repo: GitRepo

Interface to git repository

branch: {None} | str

Name of branch to checkout if non-empty

lfc._vendor.gitutils.gitrepo.get_gitdir(where=None, bare=None)

Get absolute path to git repo root, even on bare repos

Call:
>>> gitdir = get_gitdir(where=None, bare=None)
Inputs:
where: {None} | str

Working directory; can be local path or SSH path

bare: {None} | True | False

Whether repo is bare (can be detected automatically)

Outputs:
gitdir: str

Full path to top-level of working repo or git-dir of bare

Versions:
  • 2022-12-22 @ddalle: v1.1; support older git vers

lfc._vendor.gitutils.gitrepo.identify_host(where=None)

Identify possible remote host/local path breakdown

Call:
>>> host, path = identify_host(where)
Inputs:
where: {None} | str

Local path like "/home/user" or remote path like "pfe://home"

Outputs:
host: None | str

Remote host nae preceding : if any

path: str

Path on host; matches where if no host

Versions:
  • 2022-03-06 @ddalle: v1.0

lfc._vendor.gitutils.gitrepo.is_bare(where=None)

Check if a location is in a bare git repo

Call:
>>> q = is_bare(where=None)
Inputs:
where: {None} | ;class:str

Location to check

Outputs:
q: True | False

Whether or not where is in a bare git repo

Versions:
  • 2023-01-08 @ddalle: v1.0

lfc._vendor.gitutils.gitrepo.run_gitdir(func)

Decorator to run a function within the parent folder

Call:
>>> func = run_rootdir(func)
Wrapper Signature:
>>> v = repo.func(*a, **kw)
Inputs:
func: func

Name of function

cntl: Cntl

Control instance from which to use cntl.RootDir

a: tuple

Positional args to cntl.func()

kw: dict

Keyword args to cntl.func()

Versions:
  • 2018-11-20 @ddalle: v1.0

  • 2020-02-25 @ddalle: v1.1: better exceptions