# 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. ```racket (require git-cli) (git 'init) (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. Registered commands use their git-cli wrapper and can provide structured Racket results or additional behavior. Any other command is passed directly to the installed Git executable and handled with git-cli's standard command result processing. For example, commands that do not have a dedicated wrapper can still be used: ```racket (git 'blame "main.rkt") (git* clean -n) (git* worktree list) (git* archive --format=zip HEAD) ``` A successful fallback command returns `#t` after displaying normal Git output. A failing fallback command raises the same standard git-cli error as an ordinary pass-through wrapper. `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*`. ```racket (git* init) (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-status` uses Git's porcelain status and returns structured status items. - `git-log -l` / `git-log --list` returns `(commit subject)` items. - `git-tag -l` / `git-tag --list` returns tag names; with `-n` it returns `(tag subject)` items and `-n` supports multiple content lines. - `git-branch -l` / `git-branch --list` returns `(current|local|remote branch)` items; `-a`, `-r`, and `--sort=` remain Git options. - `git-remote` returns remote names; `git-remote -v` / `git-remote --verbose` returns separate `(name url fetch|push)` items. - `git-stash list` returns `(stash-name description)` items; other stash subcommands keep Git's normal behavior. - `git-restore`, `git-reset`, `git-revert`, `git-rebase`, `git-merge`, and `git-cherry-pick` pass Git's command syntax through unchanged. - `git-mergetool` uses Git's mergetool interface and prefers a configured or well-known graphical merge tool. - `git-diff` renders HTML by default; `--output=-` selects stdout and `--output=string` returns a string. - `git-show` renders a commit and its diff as HTML by default. `-l` / `--list` provides 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 registers wrappers for commands where git-cli adds useful behavior, including `init`, `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`. Other Git commands do not need a wrapper and are passed directly to Git. Most registered commands are also exported as direct procedures such as `git-init`, `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: ```racket (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. `config editor` is a git-cli configuration command rather than a Git configuration key. With no additional arguments it presents an interactive list of discovered GUI editors: ```racket (git* config editor) ``` The non-interactive forms are: ```racket (git* config editor --list) (git* config editor --downloads) (git* config editor vscode) (git* config editor notepad++) (git* config editor auto) (git 'config 'editor "C:\\Program Files\\MyEditor\\editor.exe --wait") ``` `--list` returns `(name description command current?)` items and `--downloads` returns official download pointers for optional editors. A known editor name selects the matching detected editor. `auto` clears the explicit git-cli editor choice and returns to automatic detection. Any other single value is stored as the editor command. Changing the editor immediately updates `GIT_EDITOR` and `GIT_SEQUENCE_EDITOR` for subsequent Git commands. On Windows, Notepad++ is detected both on `PATH` and in the normal Program Files locations. It is started with `-multiInst -nosession`, so Git waits for the separate editor instance to close. `config mergetool` uses the same git-cli configuration pattern: ```racket (git* config mergetool) (git* config mergetool --list) (git* config mergetool --downloads) (git* config mergetool winmerge) (git* config mergetool auto) ``` `--list` returns `(name description path current?)` items. A known tool name selects the detected tool, while another single value is stored as the Git mergetool name. `auto` clears the explicit git-cli choice and returns to automatic detection. The interactive form also offers download/install suggestions. ## 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`. ```racket (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 When git-cli is loaded, it configures the current Racket process once for the Git commands it starts. `GIT_TERMINAL_PROMPT` is set to `0`. When a GUI editor is found, `GIT_EDITOR` and `GIT_SEQUENCE_EDITOR` are set to that editor command. `run-git` itself no longer copies or rewrites the process environment. The editor can be inspected or configured explicitly: ```racket (find-editor) (find-editors) (set-editor! "code --wait") (set-editor-auto!) ``` 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 and Notepad++, 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. `find-mergetool-path` can be used to inspect the executable that was found. If no tool is found, Git is left to select its own default. ```racket (find-mergetool) (find-mergetools) (find-mergetool-path) (mergetool-downloads) (set-mergetool! "winmerge") (set-mergetool-auto!) (git* mergetool) (git* mergetool --tool=meld) ```