6.7 KiB
git-cli
A small command-line-like Git interface for Racket. The package invokes the
installed git executable and exposes commands both through the generic git
procedure and through direct procedures.
(require git-cli)
(git 'status)
(git 'log '-l '-5)
(git 'fetch '--prune)
(git 'switch "main")
(git 'tag "v0.3.16")
(git 'diff)
(git 'show 'HEAD)
(git-status)
(git-fetch '--prune)
(git-switch "main")
(git-tag "v0.3.16")
git is an ordinary procedure. The first argument is the Git command symbol
and the remaining arguments are passed to that command.
git* is the compact command-style syntax. Bare arguments are converted to
strings, so (git* remote get-url origin) is equivalent to
(git 'remote "get-url" "origin"). Use (eval expression) when an argument
must come from a Racket expression. gt remains available as a compatibility
alias for git*.
(git* remote -v)
(git* switch main)
(git* restore --staged main.rkt)
(git* reset --hard HEAD)
(git* revert HEAD)
(git* rebase main)
(git* merge feature)
(git* cherry-pick abc1234)
(git* mergetool)
(define branch "develop")
(git* switch (eval branch))
Several commands provide Racket-oriented output in addition to the normal Git behavior:
git-statususes Git's porcelain status and returns structured status items.git-log -l/git-log --listreturns(commit subject)items.git-tag -l/git-tag --listreturns tag names; with-nit returns(tag subject)items and-n<number>supports multiple content lines.git-branch -l/git-branch --listreturns(current|local|remote branch)items;-a,-r, and--sort=remain Git options.git-remotereturns remote names;git-remote -v/git-remote --verbosereturns separate(name url fetch|push)items.git-stash listreturns(stash-name description)items; other stash subcommands keep Git's normal behavior.git-restore,git-reset,git-revert,git-rebase,git-merge, andgit-cherry-pickpass Git's command syntax through unchanged.git-mergetooluses Git's mergetool interface and prefers a configured or well-known graphical merge tool.git-diffrenders HTML by default;--output=-selects stdout and--output=stringreturns a string.git-showrenders a commit and its diff as HTML by default.-l/--listprovides structured variants for--stat,--name-only, and--name-status.
Git is searched on PATH. Git itself remains responsible for remotes,
credentials, SSH keys, pull strategy, and other repository configuration.
Commands
The package currently registers commands including status, add, commit,
push, pull, fetch, config, branch, remote, stash, restore, reset, revert, rebase, merge, cherry-pick, mergetool, switch, clone, tag, log,
rev-list, diff, show, grep, help, version, and new-version.
Most are also exported as direct procedures such as git-status, git-add,
git-fetch, git-config, git-switch, git-tag, git-log, git-diff, and git-show.
See the Scribble documentation for command-specific behavior and return values.
Git configuration
git config has a small Racket-oriented interface:
(git 'config '--all)
(git 'config 'get '--all)
(git 'config '--global 'get '--all)
(git 'config 'get '--global "user.email")
(git 'config 'get '--all "credential.helper")
(git 'config 'get "credential.helper")
(git 'config 'set! "user.email" "hans@example.invalid")
(git 'config '--global 'set! "user.email" "hans@example.invalid")
(git 'config 'set! '--global "user.email" "hans@example.invalid")
get --all without a key returns (key value) items. get --all key
returns all values for one key. get key returns one string or #f when the
key is absent. set! returns #t after a successful write.
Low-level Git execution
run-git can be used when direct access to Git's stdin/stdout protocol is
needed. Optional text can be supplied to Git with #:input.
(run-git '(credential fill)
#:input "protocol=https\nhost=git.dijkewijk.nl\n\n")
The result remains two values: Git's exit code and the ordered
(source line) output items.
Authentication retry
Authentication failures are recognized centrally after run-git. The
recognizer covers common authentication/authorization errors, including HTTP
401 and 403 responses.
current-git-authentication-handler defaults to
default-git-authentication-handler. After an authentication failure the
default handler rejects the failed credential first. If a Git credential
helper exists, git credential fill is then tried so that helpers such as Git
Credential Manager can obtain a replacement credential.
When no usable credential is returned, git-cli asks for a username and
password/token using input-prompt. The #:loop-until callbacks validate the
input and return the final value, as intended by input-prompt. If no
credential helper is configured, git-cli configures the non-persistent cache
helper locally before approving the supplied credential.
The original Git command is retried once. If authentication fails again, the credential used for that retry is rejected before the normal Git error is raised. This prevents a bad token from remaining in the credential cache.
A custom handler can still be installed through
current-git-authentication-handler.
GUI editor and merge tool
git-cli looks for a GUI editor and passes it to Git through GIT_EDITOR and
GIT_SEQUENCE_EDITOR in the environment of the Git subprocess only. It does
not change the user's global Git configuration.
The editor can be inspected or configured explicitly:
(find-editor)
(set-editor! "code --wait")
The editor search first checks PATH and then well-known platform locations.
On Windows this includes the normal per-user and Program Files locations for
VS Code, with Notepad as fallback. On macOS the standard Visual Studio Code
application bundle and TextEdit are recognized. On Linux common /usr,
/usr/local, and Snap locations are checked for VS Code, Kate, Gedit, and Xed.
git-mergetool stays on top of Git's own mergetool mechanism. If no tool is
specified explicitly, git-cli prefers a configured or well-known graphical
tool such as WinMerge, Meld, KDiff3, VS Code, TortoiseMerge, or opendiff.
The finder checks PATH first and then common platform installation locations.
When a merge tool is found outside PATH, git-cli adds that executable's
directory to the environment of the Git subprocess, so Git's own mergetool
integration can still find it. If no tool is found, Git is left to select its
own default.
(find-mergetool)
(find-mergetool-path)
(set-mergetool! "winmerge")
(git* mergetool)
(git* mergetool --tool=meld)