Git (Magit)
Macros includes a Magit-style git porcelain: an interactive status buffer where you stage hunks, write commits, branch, push, pull, stash, and read diffs. The porcelain — the sectioned view, the transient popups, the keymaps — is written entirely in Scheme on the editor's special-buffer and transient primitives. Underneath, git data comes from two sources: pure-Rust gitoxide (gix) primitives for metadata reads (the HEAD commit, recent log, branches, tags, remotes, repo state, and blame) with no subprocess at all, falling back to shelling out (call-process to the git binary) for the working-tree scan, diffs, and mutating actions like stage / commit / push. The status buffer is a read-only faced buffer that's rebuilt on every refresh.
Opening the status buffer
Run magit-status with C-x g (or M-x magit-status). You get the familiar sectioned view: untracked, unstaged, staged, and recent commits, with colored diffs and per-file status labels.
Status buffer keys
Navigation & folding
| Key | Action |
|---|---|
| n / p | move line by line |
| M-n / M-p | next / previous section |
| Tab | fold / unfold the section at point |
| S-Tab | cycle all sections |
| Enter | visit the file at point |
| g | refresh |
| q | close |
Staging
| Key | Action |
|---|---|
| s / S | stage the item / stage all |
| u / U | unstage the item / unstage all |
| k | discard the change at point |
The "item" follows point: a hunk, a file, a selected region of files/lines — or, on a section header (s on Untracked files / Unstaged changes, u on Staged changes), the whole section.
Transient popups
Each of these opens a transient menu (a popup of further keys), mirroring Magit:
| Key | Popup |
|---|---|
| c | commit |
| b | branch |
| P / F | push / pull |
| f | fetch |
| l | log |
| z | stash |
| m | merge |
| r | rebase |
| t | tag |
| X | reset |
| M | remote |
| B | blame |
| ? | dispatch (all commands) |
Other handy keys: i add to .gitignore, y copy a ref, $ show the git process output, H toggle history.
Committing
Press c to open the commit popup, then start a commit. The message buffer is in git-commit-mode:
| Key | Action |
|---|---|
| C-c C-c | confirm and create the commit |
| C-c C-k | cancel |
Log
The log popup (l) opens a magit-log-mode buffer. From there: Enter shows a commit, A cherry-picks, v reverts, q closes.
Inline blame
Annotate the commit that last touched each line as faded virtual text at the end of the line (GitLens-style). This works in any file buffer — you don't need the status buffer open:
| Key | Action |
|---|---|
| C-x v b | toggle blame on every line |
| C-x v B | toggle blame on the current line only (follows the cursor) |
| C-x v Enter | show the commit that last touched this line |
| C-x v y | copy that commit's hash |
Each annotation shows the short hash, author, a humanized date ("3 weeks ago"), and the commit summary. Full-file mode (C-x v b) collapses runs of lines from the same commit to a single annotation and tints by age — recent edits pop, ancient code recedes. Current-line mode (C-x v B) shows one quiet grey annotation that re-places itself as point moves. All four commands are also available via M-x.
The status buffer's B is a separate view: it opens a full git blame of the file at point in a read-only buffer.
Git gutter (diff-hl)
In any file buffer, Macros marks the lines you've changed since HEAD in the line-number gutter, the way Emacs's diff-hl / git-gutter do — a small colored marker at the gutter's left edge on each changed line:
| Marker | Meaning | Face |
|---|---|---|
| green bar | line added since HEAD |
magit-gutter-added |
| amber bar | line modified since HEAD |
magit-gutter-modified |
| red mark | line(s) deleted just above this point | magit-gutter-removed |
The marks come from diffing the buffer's live text (not the on-disk file) against the file's blob as committed at HEAD, computed in pure Rust via gitoxide — no subprocess. A brand-new, untracked file reads as entirely added.
Refreshing
The gutter recomputes when a buffer is saved (after-save-hook) or first opened (find-file-hook). To make it update edit-by-edit, add the refresh to a more frequent hook in your init.scm:
(add-hook "post-command-hook" "magit-gutter-refresh")
You can also force a recompute at any time with M-x magit-gutter-refresh, and toggle the whole feature on/off for the session with M-x magit-gutter-mode.
Customizing
The gutter is on by default. Tune it with set-option:
(set-option "magit-gutter" #f) ; disable the gutter entirely
(set-option "magit-gutter-width" 3) ; bar width in px (1–8, default 2)
(set-option "magit-gutter-deleted" #f) ; hide the deletion marks
Recolor the markers by redefining their faces (in init.scm, or in a theme so they survive a runtime load-theme):
(set-face "magit-gutter-added" "#6cae3e")
(set-face "magit-gutter-modified" "#c7a23a")
(set-face "magit-gutter-removed" "#c1574b")
Like the diagnostic gutter dots, the markers are only drawn when the line-number gutter itself is shown for that buffer.
Customizing
magit-status is on C-x g by default. Rebind it, or any key inside the status buffer (the "magit-status" map), from your init.scm:
;; Rebind the global entry point:
(define-key "fundamental" "ctrl-x g" 'magit-status)
;; Rebind keys inside the status buffer — e.g. make `C` open the commit popup:
(define-key "magit-status" "shift-c" 'magit-commit-popup)
How it's built
Because Magit here is plain Scheme over a few primitives — gix-* (read metadata, log, and blame straight from gitoxide, no subprocess), call-process (run the git binary for the working-tree scan, diffs, and mutating actions), render-buffer (draw a faced read-only buffer), buffer-line / goto-line (find and restore the section at point), and transient (the popups) — you can read it, extend it, add your own popups, or change any binding. Folding works by regeneration: a refresh simply rebuilds the buffer text. See Scripting with Steel.