201 lines
7.5 KiB
Markdown
201 lines
7.5 KiB
Markdown
# 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.
|
|
|
|
`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<number>` 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 currently registers commands 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`.
|
|
|
|
Most 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 vscode)
|
|
(git* config editor auto)
|
|
|
|
(git 'config 'editor "C:\\Program Files\\MyEditor\\editor.exe --wait")
|
|
```
|
|
|
|
`--list` returns `(name description command current?)` items. 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.
|
|
|
|
## 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, 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-mergetool-path)
|
|
(set-mergetool! "winmerge")
|
|
(git* mergetool)
|
|
(git* mergetool --tool=meld)
|
|
```
|