Lobsters

Beyond jj: config & tools ecosystem

jj를 넘어: 설정과 도구 생태계

Jujutsu(jj)를 설치한 뒤 활용할 수 있는 내장 명령, 설정·alias 확장, forge 연동, GUI·TUI·에디터 플러그인과 워크플로 도구를 폭넓게 정리합니다. jj가 외부 스크립트에 의존하던 기능을 core에 흡수하는 동시에 독립적인 도구 생태계를 확장하고 있다는 점을 보여줍니다.

AI 요약

이 글은 JJ Con 2026에서 발표한 “Beyond jj” 세션을 바탕으로, Jujutsu(jj)를 단순한 버전 관리 CLI가 아니라 명령, 설정, alias, forge, GUI·TUI, 에디터 플러그인과 워크플로 도구가 결합된 생태계로 살펴봅니다. 발표자는 전년도 세션에서 이름 설정, templates, revsets, commands, aliases를 중심으로 jj 설정 자체를 설명했다면, 이번에는 jj를 설치한 이후 실제로 무엇을 할 수 있는지에 초점을 맞춥니다. 공식 문서, wiki, awesome jj 저장소 등에 흩어진 내용을 한데 모으고, 아직 다른 목록에 충분히 소개되지 않은 도구도 함께 제시합니다.

■ jj 자체에 들어온 기능

jj는 별도 도구나 스크립트를 추가하지 않아도 custom templates, revsets, aliases를 이용해 상당한 수준의 자동화를 구성할 수 있습니다. 특히 과거에는 설정이나 외부 스크립트가 필요했던 기능이 jj core로 이동하고 있습니다. `jj bookmark advance` 또는 `jj b a`는 과거 `jj tug` alias가 하던 역할을 대체합니다. 기본 동작은 bookmark를 working copy로 이동하는 것이며, 예전 tug처럼 가장 가까운 pushable change까지만 이동하게 하려면 `revsets.bookmark-advance-to`를 설정할 수 있습니다.

`jj bisect run`은 bisect 대상 change를 자동으로 찾기 위해 별도 wrapper script를 작성해야 했던 공백을 메웁니다. 발표자는 이 기능이 jj와 Git 사이의 큰 기능 차이였다고 평가하며, 이제는 스크립트를 찾아 조합할 필요 없이 통합된 기능으로 사용할 수 있다는 점을 강조합니다. `jj run`은 bisect가 아닌 일반적인 일괄 수정에 사용합니다. revset과 script를 넘기면 각 change에 대해 스크립트를 실행하고, 파일이 변경된 경우 해당 change를 갱신합니다. 전체 change tree의 구조는 유지되며, 실행이 진행되는 동안 필요한 rebase도 자동으로 처리됩니다.

`jj fix`는 `jj run`과 비슷해 보이지만 목적과 실행 방식이 다릅니다. fix는 변경된 파일만 대상으로 하며, 각 change 전체를 checkout하지 않고 한 번에 하나의 파일을 스크립트에 전달해 수정된 결과를 받습니다. 따라서 디스크에 여러 파일의 checkout 상태가 필요한 작업에는 적합하지 않지만, 여러 change에 걸쳐 formatter나 linter를 변경된 파일에 소급 적용하는 작업에서는 `jj run`보다 훨씬 빠를 수 있습니다.

Git의 기능에서 jj 내부로 이동한 사례로는 `jj tag`가 있습니다. 이제 `git tag`를 직접 실행하지 않고 `jj tag set`을 사용할 수 있으며, `jj git push --all`로 모든 bookmark와 tag를 함께 push할 수 있습니다. 특정 tag만 보내려면 `jj git push --tag NAME`을 사용합니다. `jj arrange`는 텍스트 파일을 편집하는 방식의 `git rebase -i`보다 직접적인 interactive rebase 경험을 제공합니다. 로그 형태의 그래프에서 change를 선택하고 이동할 수 있어, 최근 change의 이름을 확인한 뒤 여러 명령을 실행하는 절차를 줄입니다.

가장 새롭고 중요한 기능으로는 `jj converge`가 소개됩니다. 동일한 change를 서로 다른 위치에서 갱신하면 divergent change가 발생할 수 있는데, 과거에는 이를 참조하고 합치는 과정이 불편했고 여러 터미널이나 remote branch를 통해 실수로 divergence가 생기기도 했습니다. 현재는 `/N` 문법으로 divergence의 각 측면을 쉽게 참조할 수 있으며, `converge`가 서로 다른 change 흐름을 하나의 비분기 흐름으로 합치려고 시도합니다. 이 과정에서 merge conflict가 생길 수 있지만, 저자는 수동으로 조정해야 하는 두 개의 독립 branch를 남기는 것보다 명시적인 conflict 하나가 낫다고 설명합니다.

■ 설정과 alias로 확장하기

jj 설정의 흥미로운 활용으로 subcommand alias를 만드는 기법이 소개됩니다. jj는 공백을 포함한 alias command를 허용하므로, alias가 다른 alias를 호출하고 `jj util exec`를 통해 Bash를 거쳐 다시 jj를 실행하는 구성을 만들 수 있습니다. 예를 들어 `aliases.subcommand`를 `bash -c` 실행기로 정의하고, `aliases.foo = ["subcommand", "foo"]`, `aliases."foo bar" = [...]`처럼 설정하면 `jj foo bar ARGS`를 실제 하위 명령처럼 사용할 수 있습니다. 호출은 alias `foo`에서 `subcommand foo bar`로, 다시 Bash를 거쳐 `jj "foo bar" ARGS`로 확장됩니다. 발표자는 이 방식을 “완전히 기괴하지만 마음에 듭니다”라고 표현하면서도, jj에 내장 subcommand 지원이 추가되기 전까지는 실용적으로 사용할 수 있는 해법이라고 설명합니다.

현재 jj 생태계에서 가장 큰 내장 기능 공백으로는 Git의 `push`에 해당하는 통합 명령을 꼽습니다. 커뮤니티 alias는 보통 pre-push hook 실행, push 가능한 change로 bookmark를 advance, 현재 bookmark를 origin에 push, 추적되지 않은 bookmark를 track하는 절차를 한데 묶습니다. 예시로는 `jj git-hooks-run-pre-push @`, `jj bookmark advance -t 'closest_pushable(@)'`, `jj git push -b 'closest_bookmark(@)'`, `jj track-if-untracked 'closest_bookmark(@)'`를 순서대로 실행하는 `publish` alias가 제시됩니다. `push -c`로 branch를 만들고 push할 수는 있지만, 토론과 code review에 사용할 사람이 읽을 수 있는 이름을 제공하지는 못한다는 것이 글의 설명입니다. 저자는 pre-push hook과 위 절차를 묶어 현재 repository backend로 최신 change를 보내는 내장 명령을 제안하며, 이름으로는 `jj publish`를 선호한다고 말합니다. 또한 사용자가 alias를 공유하고 투표할 수 있는 웹 기반 jj alias directory도 소개합니다.

■ forge 지원의 현재와 방향

forge 통합은 지난 발표 이후 눈에 띄게 진전됐지만 앞으로 성장할 여지가 가장 큰 영역으로 설명됩니다. GitHub는 “stacked PRs”라는 이름으로 여러 change가 서로 의존하는 PR 묶음을 push하는 server-side 및 `gh` CLI 지원을 제공합니다. 별도의 jj 도구를 이용해 jj stack을 일반 GitHub PR로 연결하는 방법도 있습니다. Erisera의 JJHub는 GitHub 위에 overlay를 제공하며, 일관된 change ID와 agent가 사용할 skills 및 MCP servers를 제공한다고 소개됩니다.

P2P forge인 Radicle은 jj와 patch request를 함께 사용하는 흐름을 문서화했습니다. 이 방식은 명시적으로 쌓인 PR보다는 Git email workflow에 가까우며, patch가 accept되고 merge될 때까지 jj로 생성·수정하는 절차를 보여줍니다. 저자가 가장 기대하는 forge는 ATProto identity를 기반으로 하는 Tangled입니다. 사용자가 자신의 identity와 활동 데이터를 통제하고, Bluesky의 게시물, Tangled의 repository, Leaflet의 blog 등 여러 ATProto 애플리케이션에서 하나의 identity를 유지하는 구조를 지향합니다.

Tangled는 jj change ID를 이용한 “stacking”을 명시적으로 지원하고, PR의 서로 다른 revision 사이의 diff 또는 interdiff를 검토할 수 있으며, 여러 change를 지정해 리뷰하고 change ID를 추적할 수 있는 새로운 review UX를 공개 preview로 제공합니다. 글은 Tangled를 현재 가장 “jj-native”에 가까운 forge로 평가하며 사용을 권합니다. 이 밖에 East River Source Control(ERSC)이 곧 실험 가능한 프로젝트로 언급되고, revset.dev, juju.bi, vex.sc도 시험 사용을 요청할 수 있는 후보로 소개됩니다.

■ jj 바깥의 GUI·TUI·에디터 도구

jj 전용 GUI로는 Rust와 Tauri로 제작되어 Linux, Windows, macOS와 web 환경을 지원하는 `gg`, macOS의 SwiftUI와 Linux의 Zed GPUI를 사용하는 keyboard-friendly client `JayJay`, local 또는 remote repository를 SSH port forwarding으로 연결할 수 있는 web GUI `lightjj`가 소개됩니다. lightjj는 3-pane diff, conflict resolution, divergence resolution, Markdown diff rendering을 제공합니다. macOS용 native UI와 drag-and-drop change graph 조작을 제공하는 `jjewel`, web GUI인 `weiff`도 새로운 시도로 언급됩니다.

TUI 영역은 jj 생태계에서 가장 빠르게 성장하는 분야로 설명됩니다. `lazyjj`는 change graph, file browsing, bookmark 관리가 가능한 terminal 인터페이스를 제공하고, `jj_tui`는 commit, rebase, push, pull, squash, split과 revset 필터링까지 TUI 안에서 수행하게 합니다. `jj-fzf`는 fuzzy finder인 `fzf`를 jj 명령 전반에 적용해 log, split, merge, rebase와 mega-merge를 지원합니다. `jjui`는 live preview와 autocomplete가 가능한 revset 표현식에 초점을 맞추며, 작성한 revset을 즉시 확인한 뒤 rebase, squash, browse, split, abandon을 실행할 수 있습니다. `majjit`는 Magit에서 영감을 받은 UI로 change와 bookmark의 fuzzy matching 및 keyboard shortcut 기반 조작을 제공합니다. 이 밖에도 blazingjj, gojo, jif, jj-tui, jk, jufu가 추가 후보로 제시됩니다.

에디터 통합도 폭넓습니다. VS Code에서는 Visual JJ, `jjk`, `jj-view`가 소개됩니다. 특히 `jjk`는 file status, 상세 diff, line-by-line blame, commit, split, squash, rebase와 op log용 두 번째 pane을 제공하며, `jj-view`는 change graph뿐 아니라 Gerrit, GitHub, GitLab review discussion과 inline comment도 VS Code 안에서 보여줍니다. JetBrains 계열에는 selvejj와 jj-idea, Vim에는 fujjitive.vim과 jj.nvim, Emacs에는 jj-mode.el과 Majutsu, Helix에는 jj.hx와 juju가 있습니다.

■ stacked workflow, merge, diff와 workspace 도구

GitHub stacked PR workflow를 위한 도구로 `jj-spr`, `jj-stack`, `jj-vine`이 소개됩니다. jj-spr는 jj change를 amend하고 stack하면서도 review 가능한 GitHub PR을 만들고, 개발 중에는 일반적인 jj workflow를 유지할 수 있도록 append-only branch를 자동 관리합니다. jj-stack은 TypeScript CLI이며 GitHub stacked PR 관리에 초점을 둡니다. jj-vine은 “unopinionated and flexible”한 도구로 GitHub, GitLab, Forgejo·Codeberg·Gitea, Azure DevOps를 지원하고, 하나의 stacked PR에 둘 이상의 change를 포함할 수 있습니다. 추가로 jj-gh, jj-ryu, jx, stakk도 언급됩니다.

Merge conflict 해결에는 언어 구조를 이해하는 merge driver인 Mergiraf와 tree-sitter를 사용해 code entity 단위로 충돌을 다루는 weave가 있습니다. weave는 agent가 작성한 변경에서 conflict를 95% 줄인다고 주장합니다. diff 표시 도구로는 syntax-aware diff를 제공하는 difftastic과 syntax highlighting 및 다양한 theme을 지원하는 delta가 소개됩니다. jj가 Git-style diff를 출력할 수 있어 delta를 함께 사용할 수 있으며, 발표자는 개인적으로 delta를 사용하고 좋아한다고 말합니다.

diff 편집에서는 jj에 내장되어 `jj split`, `jj restore`, `jj resolve`에서 호출되는 `scm-record`가 설명됩니다. 이는 `git add -p`의 interactive 대안이며 jj뿐 아니라 git-branchless와 Git 또는 Mercurial에서 scm-record를 설정한 사용자에게도 사용됩니다. `jj-hunk`는 hunkset 언어와 CLI flag 또는 JSON을 이용해 diff의 일부를 split, commit, squash하고, `hunk.nvim`, `jj-diff`, `jj-diff.el`, `oyui`는 각각 Neovim, TUI, Emacs와 독립적인 diff editing 경험을 제공합니다. workspace 관련 도구로는 jujutsu-gi, jj-navi, hrd, renri, smth가 나열됩니다.

글의 결론은 jj가 core 명령만으로 완결되는 도구라기보다 설정과 alias, forge, GUI·TUI, editor integration, workflow helper가 함께 발전하는 플랫폼에 가깝다는 것입니다. 발표자는 독자가 자신에게 맞는 명령이나 도구를 시험해 보거나, 직접 jj 설정과 도구를 만들어 생태계에 공유하기를 권합니다.

원문: andre.arko.net / 번역·요약: Trawling