- Zig 89.7%
- Nix 7.5%
- Fluent 1.5%
- Python 1.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01BivPw4GP4cNYFxvVdC256f |
||
| .forgejo/workflows | ||
| dist | ||
| docs/adr | ||
| LICENSES | ||
| locales | ||
| src | ||
| tests | ||
| tools | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| build.zig.zon.nix | ||
| CONTRIBUTING.md | ||
| flake.lock | ||
| flake.nix | ||
| GLOSSARY.md | ||
| msi.nix | ||
| package.nix | ||
| README.md | ||
| REUSE.toml | ||
grebe
A history browser for git, meant to replace gitk, in Zig 0.17. It draws
its own interface on the GPU with zig-aerie, the window,
renderer and text stack of the terminal emulator
harrier.
$ grebe # the current branch
$ grebe --all # every branch and tag
$ grebe main..topic -- src/ # what topic adds to main, under src/
It is new. What it does today is the start of what gitk does; see
What it does. It runs on Linux under Wayland, and on
Windows; see Windows.
What it does
- Takes
gitk's arguments. Whatever followsgrebeis handed togit logas it is: ranges,--all,--author=,--date-order,-- paths. git chooses and orders the commits, and with paths names each commit's nearest ancestor that is shown as its parent, so they mean exactly what they mean to git,.mailmapand replace refs included. The order is topological, as ingitk, unless you ask for another. - Streams the history. Commits are shown as git writes them, so the first screenful of a large repository is up while the rest is still coming. The window stays live while it loads; a repository of 88,000 commits is read in under a second.
- A graph beside the commits: each line of descent in a lane of its
own, merges opening lanes and forks closing them, as
gitkdraws it. A line keeps its color from where it starts, so more history arriving never repaints what is already there. - A commit list with the subject, the author and the date, in your own
time zone (
TZ, or/etc/localtime). Only the rows in view are ever laid out or drawn. - Branches and tags are labels ahead of the subject of the commit they
point at, as in
gitk: green for a local branch, with a heavier border for the one checked out; orange for a remote's; yellow for a tag. They are the refsgit logdecorates with, solog.excludeDecorationhides any you would rather not see. - Selecting a commit shows, below the list, its details — author,
committer, parents, the local branches it is on, the nearest tags before
it and after it (
gitk's Follows and Precedes), the whole message — and its diff, with the files it changes listed beside them. Clicking a file scrolls the diff to it, and the file the diff is showing is marked. A merge's diff is the combined onegitkshows: only what the merge did that neither side did. The bar above the diff sets how many lines of context it has, and whether changes only in whitespace count. - Changes not committed yet are rows of their own at the top, leading
down to
HEAD, as ingitk: one for the working tree, with a red node, and one for the index, with a green one. Selecting one shows its diff. They are there when the history starts fromHEAD, and with paths, only for changes to those paths. - Find, in the bar under the commit list, as in
gitk: commits containing some text — in the headline, the comments, the author, the committer, or all of them, exactly, ignoring case, or as a regular expression — or touching paths, adding or removing a string (git log -S), or changing lines that match (git log -G). Matches are lit as they are found; Enter or Ctrl-G goes to the next below the selected commit, and Shift-Enter or Ctrl-Shift-G to the one above./or Ctrl-F goes to the find bar, and Escape back to the list. The headline and the author are searched by grebe as you type; everything else by git, with the same revision arguments. - The panes are
gitk's, and the dividers between them can be dragged. Each has a scrollbar, and the diff one along the bottom as well, for lines wider than the pane: drag the thumb, or click either side of it for a page. Shift and the wheel, or a touchpad, scroll the diff sideways. - Selecting text in the details and the diff: drag across it, or double-click a word, or triple-click a line. What is selected goes in the primary selection, for a middle click, and Ctrl-C copies it to the clipboard. Dragging past the top or the bottom scrolls.
- Selecting a commit shows its whole ID, and which row it is, at the
right of the status bar, and puts the ID in the primary selection, ready
for a middle click in a terminal, as
gitkdoes. - A menu on each commit, with a right click: copy its ID, or a
summary of it as git writes one (
--pretty=reference); diff it against the selected commit, either way round; mark it, and diff the marked commit against another; create a new branch or a tag there; or reset the branch checked out to it, asgitkdoes. A tag with a message is annotated, and signed if git is configured to sign tags; one without is lightweight. A reset can be soft, mixed or hard, and a hard one discards changes that have not been committed. - A menu on each local branch, with a right click on its label: copy its name, rename it, or delete it. git deletes a branch only if it is merged. If it is not, grebe shows why and offers to delete it anyway.
- A menu on each tag, with a right click on its label: copy its name, or delete it from this repository (not from any remote).
- Keys: ↑ and ↓ or
kandjmove the selection, Page Up and Page Down a page, Home and End to either end; Space andbpage through the diff;/or Ctrl-F finds, and Ctrl-G finds again; F5 reads the history again, keeping the selected commit selected where it was in the list, and the diff where it was (or, if that commit is gone, the row it was on); Ctrl-C copies; Ctrl-Q quits. Any of them can be bound to other keys; see Configuration. - Light or dark, as the desktop prefers, in the desktop's own fonts.
grebe asks the XDG desktop portal, and follows it when the preference
changes while grebe is open. With no portal, or no preference, it is
light, in fontconfig's
sans-serifandmonospace. - A title bar of its own on a desktop whose compositor draws none, such as GNOME.
grebe changes the repository only from the menus above: making, renaming, deleting and resetting branches, and making and deleting tags. Each change is confirmed in a dialog first, and is made by running the git command a user would type, so git checks it as it would on the command line. The window waits while git runs, including while gpg asks for a passphrase to sign a tag.
Building and running
With Nix:
$ nix run git+https://git.jcollie.dev/jeff/grebe.git -- --all
or, to work on it, everything is in the development shell:
$ nix develop
$ zig build test
$ zig build run -- --all
Without Nix you need Zig 0.17.0 to build it, and at run time git, a Vulkan
loader and driver, and a Wayland compositor. git is the one on your PATH,
so your configuration applies; the Nix package brings its own only for when
there is none.
grebe shows the repository the current directory is in, or the one
--repository=DIR names. Installed — the Nix package does it, or
zig build -Dbindir=/usr/local/bin --prefix /usr/local — it comes with
its icon and a desktop entry. The entry is not in the application menus,
since grebe started from one would have no repository to show; it is
offered instead for opening a folder from a file manager, and it is what
gives grebe's windows their icon.
On Windows
grebe.exe is cross-compiled, from Linux or anywhere else Zig runs, for the GNU ABI on Zig's own MinGW headers, so no Visual Studio is needed:
$ zig build -Dtarget=x86_64-windows-gnu
The installer is an MSI, built by zixl
from dist/windows/grebe.wxs as one more step of the build:
$ zig build msi -Dtarget=x86_64-windows-gnu -Doptimize=ReleaseSafe
$ nix build .#msi
Either puts grebe-VERSION-x64.msi in zig-out or result, with its
ProductCode beside it in grebe-VERSION-x64.msi.ProductCode. Built with
Nix, one commit makes one MSI, bit for bit. It installs grebe for all
users, in Program Files\grebe, with the directory on the PATH and
Open in grebe on the menu Explorer shows for a folder. It adds nothing
to the Start menu, for the same reason the desktop entry is not in the
application menus. An MSI replaces any grebe of its version or an older
one; it will not install over a newer one.
Installing with winget
Every release is also published to the JCOllie winget source, as
JCOllie.Grebe. Add the source once, from an elevated prompt:
> winget source add -n JCOllie -a https://winget.jcollie.dev/ -t Microsoft.PreIndexed.Package --trust-level trusted
then install grebe, and later upgrade it, as any other package:
> winget install JCOllie.Grebe -s JCOllie
> winget upgrade JCOllie.Grebe -s JCOllie
The MSI is not signed. winget marks an installer from an untrusted source
as downloaded from the internet, and Microsoft Defender SmartScreen then
stops an unsigned one with "Windows protected your PC". A source added with
--trust-level trusted skips that; one added without it can be removed
with winget source remove JCOllie and added again. An MSI downloaded from
the release page gets the
same warning, and Run anyway installs it.
Windows
grebe on Windows is the same program in a Win32 window, drawing with Vulkan through the GPU driver's own loader. What differs:
- git is the one on the
PATH, which Git for Windows puts there. grebe brings none. - Light or dark follows Settings > Personalization > Colors, and changes when it does; the fonts are Windows' own.
- Times are in the time zone Windows is set to, or the one
TZnames, from a copy of the IANA database built into grebe. - There is no primary selection. Selecting a commit keeps its ID for a middle click inside grebe only; Ctrl-C, or Copy ID on the commit's menu, puts it on the clipboard.
- What goes wrong at the start — no repository there, no git, a mistake in the configuration — is said in a message box, since grebe started from Explorer has no terminal to say it on.
- The configuration is
%APPDATA%\grebe\config.struthio.
Configuration
grebe needs none. If there is a file at ~/.config/grebe/config.struthio
($XDG_CONFIG_HOME/grebe/config.struthio; on Windows,
%APPDATA%\grebe\config.struthio), it is read, in
struthio: Zig's ZON with let, if, @include of other files
and a context to decide by. GREBE_CONFIG names another file instead, and
set but empty, none.
.{
// The interface's font and the diff's; unset is the desktop's.
.font = .{ .mono = .{ .family = "Iosevka", .size = 11 } },
// Any of the palette's colors, by name, over the light or dark one.
.colors = if (ctx.appearance.color_scheme == .dark)
.{ .list_background = "#101418", .graph = .{ "#58a6ff", "#ff7b72" } }
else
.{ .selection_background = ctx.appearance.accent_color },
// How a diff is shown at first.
.diff = .{ .context = if (ctx.repository == "/src/linux") 5 else 3 },
// Over gitk's keys; `.unbind` takes one away.
.keybind = .{
.{ .keys = "ctrl+r", .action = .reload },
.{ .keys = "ctrl+q", .action = .unbind },
},
}
What the file can decide by is ctx: ctx.appearance — the desktop's
color_scheme, accent_color, contrast and reduced_motion —
ctx.repository, the working tree being shown, ctx.hostname and
ctx.os. @env("NAME") is an environment variable, and
@setting("namespace", "key") any other setting the desktop publishes
through the portal. When the desktop's settings change, the file is
evaluated again, and grebe draws as it then says. .language chooses the
language grebe speaks, in place of the environment's. The fields, the
palette's color names and the actions are in src/config/Config.zig.
A mistake in the file is said on the terminal, with where it is, and grebe does not start.
Translations
Everything grebe says is in Project Fluent files in locales/,
one per language, built into the program. It speaks the language the
environment asks for — LANGUAGE, then LC_ALL, LC_MESSAGES and LANG —
or the nearest it has, and English for anything a translation leaves out.
English is the only one there is so far; a new one is a copy of
locales/en-US.ftl, translated, and a line in build.zig.
What git says, when it says something went wrong, is shown as git said it, in whatever language git speaks.
Design
The words grebe uses for what it shows are in GLOSSARY.md, and the decisions behind it in docs/adr:
- ADR 0001: grebe runs
gitrather than linking libgit2, behind a seam that carries commits and diffs as data, so that another backend can be put behind it. - ADR 0002: harrier's window, renderer and text stack became a library, zig-aerie, rather than being copied.
- ADR 0003: grebe makes, renames, deletes and resets branches, and makes and deletes tags, from its menus, through git, and writes nothing else.
Where this lives
The code is at https://git.jcollie.dev/jeff/grebe, and mirrored on Tangled at https://tangled.org/jcollie.dev/grebe:
git clone https://git.jcollie.dev/jeff/grebe.git
Report issues and send pull requests on the Tangled mirror. See CONTRIBUTING.md for what to know before changing it.
License
MIT; see LICENSES/MIT.txt. The project follows
REUSE: every file says what it is licensed
under. The icon, in dist/icons/, is a photograph of a western grebe by
Jeffrey C. Ollie, under CC BY 4.0; see
LICENSES/CC-BY-4.0.txt.
References cited
- Git project. Git [Computer software]. https://git-scm.com/
- Git project. git-log — Show commit logs. Git documentation. https://git-scm.com/docs/git-log
- Git project. gitk — The Git repository browser. Git documentation. https://git-scm.com/docs/gitk
- freedesktop.org. Desktop Entry Specification. https://specifications.freedesktop.org/desktop-entry/latest/
- freedesktop.org. Icon Theme Specification. https://specifications.freedesktop.org/icon-theme/latest/
- freedesktop.org. XDG Base Directory Specification. https://specifications.freedesktop.org/basedir-spec/latest/
- Flatpak and XDG Desktop Portal contributors. XDG Desktop Portal documentation: Settings. https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
- ziglibs. known-folders [Computer software]. https://github.com/ziglibs/known-folders
- Ollie, J. C. struthio [Computer software]. https://git.jcollie.dev/jeff/struthio
- Ollie, J. C. zig-aerie [Computer software]. https://git.jcollie.dev/jeff/zig-aerie
- Ollie, J. C. zig-datetime [Computer software]. https://git.jcollie.dev/jeff/zig-datetime
- Ollie, J. C. zregex [Computer software]. https://git.jcollie.dev/jeff/zregex
- Ollie, J. C. zig-fluent [Computer software]. https://git.jcollie.dev/jeff/zig-fluent
- Mozilla. Fluent Syntax Guide. Project Fluent. https://projectfluent.org/fluent/guide/
- IANA. Time Zone Database. https://www.iana.org/time-zones
- Olson, A., Eggert, P., & Murchison, K. (2019). The Time Zone Information Format (TZif) (RFC 8536). RFC Editor. https://www.rfc-editor.org/info/rfc8536
- Zig contributors. The Zig programming language [Computer software]. https://ziglang.org/
- Git for Windows contributors. Git for Windows [Computer software]. https://gitforwindows.org/
- Ollie, J. C. zixl [Computer software]. https://git.jcollie.dev/jeff/zixl
- Marler, J. zigwin32: Zig bindings for the Win32 API [Computer software]. https://github.com/marlersoft/zigwin32
- Microsoft. RegNotifyChangeKeyValue function (winreg.h). Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regnotifychangekeyvalue
- Microsoft. Synchronous and Asynchronous I/O. Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/fileio/synchronous-and-asynchronous-i-o
- Microsoft. GetOverlappedResult function (ioapiset.h). Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult
- Microsoft. Extending Shortcut Menus. Microsoft Learn. https://learn.microsoft.com/en-us/windows/win32/shell/context
- Microsoft. Create your package manifest. Microsoft Learn. https://learn.microsoft.com/en-us/windows/package-manager/package/manifest
- Microsoft. source command (winget). Microsoft Learn. https://learn.microsoft.com/en-us/windows/package-manager/winget/source
- FireGiant. WiX Toolset v3 Manual. FireGiant Docs. https://wixtoolset.org/docs/v3/
- Free Software Foundation Europe. REUSE Specification, Version 3.3. https://reuse.software/spec-3.3/