{"id":51586013,"url":"https://github.com/waddie/juju","last_synced_at":"2026-07-11T10:32:17.140Z","repository":{"id":366458950,"uuid":"1276012580","full_name":"waddie/juju","owner":"waddie","description":"Juju is a git/jj interface for the Helix editor","archived":false,"fork":false,"pushed_at":"2026-07-02T16:48:42.000Z","size":119,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-02T18:29:56.055Z","etag":null,"topics":["git","helix-editor","jujutsu","plugin","steel-scheme"],"latest_commit_sha":null,"homepage":"","language":"Scheme","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/waddie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-21T12:35:28.000Z","updated_at":"2026-07-02T16:48:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/waddie/juju","commit_stats":null,"previous_names":["waddie/juju"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/waddie/juju","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waddie%2Fjuju","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waddie%2Fjuju/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waddie%2Fjuju/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waddie%2Fjuju/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/waddie","download_url":"https://codeload.github.com/waddie/juju/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waddie%2Fjuju/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35360371,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","helix-editor","jujutsu","plugin","steel-scheme"],"created_at":"2026-07-11T10:32:17.066Z","updated_at":"2026-07-11T10:32:17.131Z","avatar_url":"https://github.com/waddie.png","language":"Scheme","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Juju\n\nA `git`/`jj` interface for the [Helix](https://helix-editor.com) editor.\n\nOne interface, two backends: Git and [Jujutsu](https://github.com/jj-vcs/jj).\n\n`juju` shells out to the `git` and `jj` binaries; there is no FFI.\n\nThis is very early alpha software. Use at own risk.\n\n## Demo\n\n![Interactive rebase of a git repo with Juju](https://github.com/waddie/juju/blob/main/images/juju.gif?raw=true)\n\n## Install\n\n### Forge (Steel package manager)\n\n```\nforge pkg install --git https://github.com/waddie/juju\n```\n\nThen add to `~/.config/helix/init.scm`:\n\n```scheme\n(require \"juju/juju.scm\")\n```\n\n### Manual\n\nFrom a checkout:\n\n```\n./install.sh\n```\n\nThis copies the plugin into `~/.steel/cogs/juju/` (the same place forge uses).\nAdd the `require` line above to your `init.scm` and restart Helix.\n\n## Commands\n\n| Command                           | Action                                                                                                               |\n| --------------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| `:juju` / `:juju-status`          | Open the status view                                                                                                 |\n| `:juju-log`                       | Recent commits / changes, with actions on the change under the cursor                                                |\n| `:juju-diff`                      | Working-copy diff                                                                                                    |\n| `:juju-blame`                     | Blame the current file interactively (show, chase revisions)                                                         |\n| `:juju-backend git\\|jj`           | Set or report the backend for this workspace                                                                         |\n| `:juju-stage`                     | Stage the current file (`git`)                                                                                       |\n| `:juju-unstage`                   | Unstage the current file (`git`)                                                                                     |\n| `:juju-discard`                   | Discard the current file (confirms first)                                                                            |\n| `:juju-stage-all`                 | Stage every change (`git`)                                                                                           |\n| `:juju-unstage-all`               | Unstage every change (`git`)                                                                                         |\n| `:juju-commit [msg]`              | Commit (prompts when no message given)                                                                               |\n| `:juju-amend [msg]`               | Amend HEAD / re-describe `@` (`jj`)                                                                                  |\n| `:juju-extend`                    | Fold changes into the latest commit                                                                                  |\n| `:juju-commit-fixup rev`          | Record a fixup! (`git`) / squash into (`jj`)                                                                         |\n| `:juju-fetch [remote]`            | Fetch                                                                                                                |\n| `:juju-pull [remote]`             | Pull / integrate                                                                                                     |\n| `:juju-push [remote]`             | Push                                                                                                                 |\n| `:juju-undo`                      | Undo the last operation (jj; reflog on git)                                                                          |\n| `:juju-redo`                      | Redo the last undone operation (`jj`)                                                                                |\n| `:juju-rebase [-as] ref`          | Rebase onto a ref (`--autosquash`) (git/jj)                                                                          |\n| `:juju-rebase-interactive [base]` | Open the interactive rebase editor (git/jj)                                                                          |\n| `:juju-rebase-continue`           | Continue a paused rebase (`git`)                                                                                     |\n| `:juju-rebase-abort`              | Abort a paused rebase, restore the tip (`git`)                                                                       |\n| `:juju-rebase-skip`               | Skip the current commit in a paused rebase (`git`)                                                                   |\n| `:juju-cherry-pick rev`           | Cherry-pick a commit (`git`)                                                                                         |\n| `:juju-revert rev`                | Revert a commit (git/jj)                                                                                             |\n| `:juju-reset [mode] rev`          | Reset HEAD: soft / mixed / hard (`git`)                                                                              |\n| `:juju-squash [rev]`              | Fold `@` into its parent or a rev (`jj`)                                                                             |\n| `:juju-split path...`             | Split files out of `@` into a new change (`jj`)                                                                      |\n| `:juju-abandon [rev]`             | Abandon a change, `@` when omitted (`jj`)                                                                            |\n| `:juju-describe [msg]`            | Set `@`’s description (`jj`)                                                                                         |\n| `:juju-switch [target]`           | Switch to a branch/bookmark/commit                                                                                   |\n| `:juju-edit [rev]`                | Edit a change: make it the working copy (`jj`)                                                                       |\n| `:juju-branch-create n`           | Create a branch/bookmark (optional rev)                                                                              |\n| `:juju-branch-rename`             | Rename a branch/bookmark: `\u003cold\u003e \u003cnew\u003e`                                                                              |\n| `:juju-branch-delete n`           | Delete a branch/bookmark                                                                                             |\n| `:juju-set-upstream`              | Set a branch’s upstream: `\u003cbranch\u003e \u003cup\u003e` (`git`)                                                                     |\n| `:juju-stash [msg]`               | Stash the working changes (`git`)                                                                                    |\n| `:juju-stash-pop [ref]`           | Pop a stash, latest when omitted (`git`)                                                                             |\n| `:juju-stash-apply [ref]`         | Apply a stash without dropping it (`git`)                                                                            |\n| `:juju-stash-drop [ref]`          | Drop a stash (`git`)                                                                                                 |\n| `:juju-refs`                      | List branches/tags/remotes, or bookmarks                                                                             |\n| `:juju-remote`                    | List configured remotes                                                                                              |\n| `:juju-oplog`                     | Show the operation log (`jj`)                                                                                        |\n| `:juju-reflog`                    | Show the reflog (`git`)                                                                                              |\n| `:juju-worktree`                  | List worktrees (`git`) / workspaces (`jj`)                                                                           |\n| `:juju-submodule`                 | List submodule status (`git`)                                                                                        |\n| `:juju-run args...`               | Run a raw backend line in root, show output                                                                          |\n| `:juju-dispatch`                  | Transient: top-level menu of the sub-menus                                                                           |\n| `:juju-rebase-menu`               | Transient: rebase (`--autosquash` on `git`, `--skip-emptied` on `jj`; interactive)                                   |\n| `:juju-remote-menu`               | Transient: fetch / pull / push (`--all-remotes`; `--prune`, `--rebase`, `--set-upstream`, force-with-lease on `git`) |\n| `:juju-branch-menu`               | Transient: create / switch / rename / delete (force delete on `git`)                                                 |\n| `:juju-commit-menu`               | Transient: commit / amend (`--no-verify`, `--signoff` on `git`)                                                      |\n| `:juju-log-menu`                  | Transient: log with a `-n count` infix                                                                               |\n| `:juju-annotate`                  | Alias of `:juju-blame`                                                                                               |\n| `:juju-reword [msg]`              | Alias of `:juju-describe`                                                                                            |\n| `:juju-drop [rev]`                | Alias of `:juju-abandon`                                                                                             |\n| `:juju-bookmark-create`           | Alias of `:juju-branch-create`                                                                                       |\n| `:juju-bookmark-rename`           | Alias of `:juju-branch-rename`                                                                                       |\n| `:juju-bookmark-delete`           | Alias of `:juju-branch-delete`                                                                                       |\n\nThe `:juju-stage` / `:juju-unstage` / `:juju-discard` typed commands act on the\nwhole current file. For hunk- or line-level granularity, use the status view,\nwhere the action applies to whatever is selected.\n\n## Status-view keys\n\n| Key                  | Action                                                    |\n| -------------------- | --------------------------------------------------------- |\n| `j` / `k`, `↑` / `↓` | Move                                                      |\n| `Ctrl-d` / `Ctrl-u`  | Page down / up                                            |\n| `Home` / `End`       | First / last                                              |\n| `}` / `{`            | Next / previous section                                   |\n| `^`                  | Jump to the enclosing section header                      |\n| `/`                  | Search; `n` / `N` jump to the next / previous match       |\n| `Tab`                | Fold / unfold the section or file under the cursor        |\n| `Enter`              | Visit a file / show a commit’s diff / fold a section      |\n| `v`                  | Mark / unmark the current row for a multi-row action      |\n| `s` / `u`            | Stage / unstage the selection                             |\n| `x`                  | Discard files / drop stash / abandon commit (confirms)    |\n| `S` / `U`            | Stage all / unstage all                                   |\n| `c` / `a` / `e`      | Commit / amend / extend (`e` on a commit row: edit, `jj`) |\n| `f` / `F` / `P`      | Fetch / pull / push                                       |\n| `V` / `y` / `r`      | Revert / cherry-pick / rebase-onto the selected commit    |\n| `i`                  | Interactive rebase from the selected commit to the tip    |\n| `b`                  | Switch to the selected branch/bookmark/commit             |\n| `p`                  | Pop the selected stash                                    |\n| `z` / `Z`            | Undo / redo (`jj op log`; `git reflog`, best-effort)      |\n| `?`                  | Key reference                                             |\n| `g`                  | Refresh                                                   |\n| `q` / `Esc`          | Close                                                     |\n\nActions are selection-first: mark rows with `v` and the next action applies to\nall of them; with nothing marked, it applies to the row under the cursor. The\ngranularity is whatever the selection covers, files, hunks, or individual diff\nlines, so there is no separate file-vs-hunk-vs-region distinction. The same key\nadapts to its operand: `x` discards file rows, drops a stash row, or abandons a\ncommit row, and `e` extends everywhere except on a commit row under `jj`, where\nit edits that change. History keys (`V` / `y` / `r` / `b`) act on commit rows in\nthe recent, bookmark, and unpushed/unpulled sections. Keys for features a\nbackend lacks (staging under `jj`, stash under `jj`) are inert.\n\n## Log view\n\n`:juju-log` (or `:juju-log-menu` for a `-n count` infix) opens a floating log of\nrecent commits/changes. Each row is a commit, and the commit actions from the\nstatus view work on the row under the cursor.\n\n| Key                           | Action                                               |\n| ----------------------------- | ---------------------------------------------------- |\n| `j` / `k`, `↑` / `↓`          | Move the cursor                                      |\n| `Ctrl-u` / `Ctrl-d`           | Move ten lines                                       |\n| `j` / `Ctrl-d` / `End` at end | Load an older page (paginate the full log)           |\n| `Enter`                       | Show the commit's diff                               |\n| `e`                           | Edit: make the change the working copy (`jj`)        |\n| `n` / `b`                     | New change on the commit (`jj new` / `git checkout`) |\n| `V` / `y` / `r`               | Revert / cherry-pick / rebase-onto the commit        |\n| `g`                           | Refresh                                              |\n| `?`                           | Key reference                                        |\n| `q` / `Esc`                   | Quit                                                 |\n\nScrolling to the bottom loads another page of older history in place: each step\nreveals `set-juju-log-count!` more entries, so you can page back to the complete\nlog without leaving the view. Under `jj` the first load expands from the curated\ndefault revset to the full `::@` ancestry of the working copy. When there is no\nmore history the status line shows \"end of history\".\n\nMutations refresh the log in place (the cursor stays on the same change) and\nany open status view. `jj` refuses to edit an immutable commit; the error shows\non the log's status line.\n\n## Interactive rebase editor\n\n`:juju-rebase-interactive [base]` (or `i` on a commit in the status view) opens a\nfloating editor listing the commits to be rebased, oldest at the top. Reorder\nthem and assign each an action, then `Enter` to apply or `q` to cancel. Without a\n`base` it edits the commits above the upstream (`git`) or the mutable ancestors of\n`@` (`jj`); launched from a commit it edits from that commit to the tip.\n\n| Key                  | Action                                                    |\n| -------------------- | --------------------------------------------------------- |\n| `j` / `k`, `↑` / `↓` | Move the cursor                                           |\n| `J` / `K`            | Move the commit under the cursor down / up                |\n| `p`                  | pick (keep as-is)                                         |\n| `r`                  | reword (prompts for a new message on apply)               |\n| `e`                  | edit (pause to amend; `git` only)                         |\n| `s`                  | squash (fold into the commit above, keep both messages)   |\n| `f`                  | fixup (fold into the commit above, drop its message)      |\n| `d`                  | drop (remove the commit)                                  |\n| `v`                  | Mark / unmark a row (an action then applies to all marks) |\n| `Enter`              | Apply the plan                                            |\n| `q` / `Esc`          | Cancel                                                    |\n\nThe same plan drives both backends. On `git` it becomes a `rebase -i` todo; an\n`edit` step or a conflict pauses the rebase, surfaced in the status header, and\n`:juju-rebase-continue` / `-abort` / `-skip` drive it from there. On `jj` it\nbecomes a sequence of `abandon` / `squash` / `describe` / `rebase` commands keyed\non change-ids; `jj` never pauses, and the whole batch is reversible with\n`:juju-undo`. A reword whose prompt is left blank keeps the commit unchanged.\n\n## Interactive blame\n\n`:juju-blame` (alias `:juju-annotate`) opens a floating blame of the current\nfile. Every line carries the commit (`git`) or change (`jj`) that introduced\nit; the first line of each run is coloured to mark the boundary.\n\n| Key                  | Action                                            |\n| -------------------- | ------------------------------------------------- |\n| `j` / `k`, `↑` / `↓` | Move the cursor                                   |\n| `Ctrl-u` / `Ctrl-d`  | Move ten lines                                    |\n| `Enter`              | Show the cursor line's commit                     |\n| `l`                  | Chase: reblame at the parent of the line's commit |\n| `h` / `Backspace`    | Go back to the previous blame                     |\n| `q` / `Esc`          | Quit                                              |\n\nChasing from a root commit's line (or a line whose file did not exist at the\nparent) reports and stays put. Under `git`, uncommitted lines blame to the\nall-zero id and have no commit to show.\n\n## Keybindings\n\n`juju` ships an example keymap rather than forcing one. See\n`keybindings-example.scm` for a `space.J` menu to copy into your `init.scm`.\n\n## Configuration\n\nSettings are plain functions you call from `init.scm` after requiring juju. All\nhave (hopefully) sensible defaults; set only what you want to change.\n\n| Setter                              | Default   | Effect                                      |\n| ----------------------------------- | --------- | ------------------------------------------- |\n| `(set-juju-recent-count! n)`        | `10`      | Entries in the Recent section               |\n| `(set-juju-log-count! n)`           | `50`      | Entries `:juju-log` lists / load-more page  |\n| `(set-juju-colocated-default! sym)` | `'jj`     | Backend for a colocated repo (`'git`/`'jj`) |\n| `(set-juju-auto-refresh! bool)`     | `#t`      | Typed commands refresh an open view         |\n| `(set-juju-warn-colocated! bool)`   | `#t`      | Show the colocated `git`→`jj` desync note   |\n| `(set-juju-overlay-scale! pct)`     | `90`      | Overlay size, percent of the terminal       |\n| `(set-juju-section-color! sym)`     | `'yellow` | Section-header colour                       |\n\nThe colour symbol is one of `'yellow 'green 'cyan 'blue 'magenta 'red 'white`\n(an unknown name falls back to the theme default).\n\n## Requirements\n\n- Helix with the Steel plugin system.\n- `git` and/or `jj` on `PATH`. `jj` parsing assumes a recent `jj` (0.42+),\n  pinned via explicit templates and `--git` diffs.\n\n## Development\n\nPure parsers (diff, porcelain status, `jj` summary, log records) are\nunit tested:\n\n```\n./tests/run.sh\n```\n\n## License\n\nAGPL-3.0-or-later.\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option) any\nlater version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaddie%2Fjuju","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwaddie%2Fjuju","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaddie%2Fjuju/lists"}