바쁜 개발자를 위한 Jujutsu
(maddie.wtf)- Jujutsu(jj)는 Git보다 단순한 개념과 명령어, 그러나 강력한 기능을 제공하는 버전 관리 시스템임
- Git을 백엔드로 사용하므로 동시에 사용하거나 Git으로 쉽게 복귀할 수 있는 장점이 있음
- 스택형 diff, 쉬운 rebase, 임시 리비전과 같은 기능이 자연스럽게 제공됨
- 분기(Branch) 대신 북마크(Bookmarks) 개념을 활용하며, 현업 작업 흐름에 더 직관적임
- 충돌(Conflict) 처리 방식이 유연하고, 일상적인 버전 관리 작업을 훨씬 간단하게 해줌
Elevator Pitch
Jujutsu(jj)는 Git과 비교해 훨씬 단순한 정신 모델과 명령줄 인터페이스를 제공하는 버전 관리 시스템임.
기능을 희생하지 않으면서도, 실은 더 강력하다고 볼 수도 있음
스택형 diff, 손쉬운 rebase, 임시 리비전과 같은 기능이 자연스럽게 동작함
백엔드로 Git을 사용하므로, 단 한 줄로 Git 저장소와 나란히 사용을 시작할 수 있으며 언제든지 Git으로 돌아갈 수 있음
다른 사용자가 저장소를 다루는 방식에도 영향을 주지 않음
Getting Started
-
jj
명령줄 도구를 설치한 뒤, 사용자 정보와 쉘 자동완성 설정을 권장함 - 기존 저장소에 적용 시 새로 클론하거나, 변경 없는 상태에서 사용 시작이 유리함
- 기존 저장소에서
jj git init --colocate .
명령으로 Git과 Jujutsu 저장소를 병행 생성할 수 있음 -
jj
저장소 데이터는 Git의.git
과 별도의.jj/
폴더에 보관됨 - Git과 Jujutsu 간의 데이터 동기화는 기본적으로 문제 없이 운영됨
- 단,
git clean -fdx
명령은.jj/
폴더를 삭제하므로 주의해야 함 - remote branch 트래킹도 해당 명령으로 한 번에 설정 가능함
How To Use It
Jujutsu의 명령줄 인터페이스는 Git보다 좁고 간결함
기본 개념이 단순하지만 작업 흐름에는 약간의 적응이 필요함
간단한 절차와 예시로 주요 사용 방법을 설명함
Starting A New Revision
- Git에서의 새 작업은 보통 브랜치 생성으로 시작하지만, Jujutsu는 리비전을 새로 만드는 방식임
- 최신 리모트 저장소 상태 반영:
jj git fetch
- 저장소 히스토리 확인:
jj log
- 히스토리 내 리비전은 고유한 Jujutsu별 리비전 ID와 Git 커밋 해시로 구분됨
- 신작업 시작은
jj new main
으로 현재 main 위에 새 리비전 생성 - 현재 편집 중인 리비전은
@
기호로 구분됨 - 동일한 접두사가 사라지면 리비전 ID의 짧은 접두사도 따라 변동됨
Making Changes
- 파일을 수정하면 즉시 해당 리비전에 포함됨(별도의 staging area 없음)
- 수정이 완료되면
jj describe -m "메시지"
로 리비전에 메시지 추가 - 새 리비전을 만들 때는
jj new
(또는 이전 리비전 지정) -
jj commit
은jj describe
+jj new
의 조합 연산임
Navigating
-
jj new
로 생성한 빈 리비전은 이동 시 자동 폐기됨 - 명시적으로 리비전 삭제:
jj abandon <rev ID>
- 삭제 취소:
jj op undo
- 기존 리비전으로 돌아가기는
jj edit <rev ID>
- 리비전 참조시 revset expressions도 사용 가능:
-
@
: 현재 리비전 -
x-
: 부모 리비전 -
y+
: 자식 리비전 -
z::
: z의 모든 자손
-
Branches (Bookmarks)
- Jujutsu는 Git처럼 "브랜치에 머무르는 상태"가 없음
- 대신, 북마크(Bookmarks)는 단지 특정 리비전을 가리키는 포인터임
- 새 북마크 생성:
jj bookmark create <이름> -r <리비전>
- 커밋해도 자동으로 북마크가 이동하지 않으므로, 필요시
jj bookmark move <이름> --to <리비전>
으로 별도 이동 필요 - push 시에는
--allow-new
플래그로 원격 새 브랜치 생성 인정 - 북마크가 원격과 다르면
*
로 표시되며jj git push
로 동기화 가능
Conflicts
- Jujutsu의 충돌 처리 방식은 Git보다 유연함
- 충돌 시 ‘conflicted’ 표식이 리비전 및 자손 리비전에 표시됨
- 충돌 파일은 충돌 마커가 삽입되고, 해당 마커를 제거하는 것으로 해결함
- 직접 수정 또는 새 리비전에서 변경 이후
jj squash
로 합칠 수 있음
결론
이상으로 Git에서 가장 많이 쓰이는 80%의 작업을 Jujutsu로 더욱 간단하게 처리하는 방법을 안내함
추후 일반적/고급 활용은 참조 핸드북(Quick Reference) 형태로 제공 예정임
Git처럼 jj status
명령 지원함
문의나 피드백은 본문 이메일로 가능함
Hacker News 의견
-
jj를 배울 가치가 있는지 고민하는 분들께 강조하고 싶은 점이 있음
Hacker News에서 jj에 대해 얘기하면, 두 부류로 나뉨: 아직 시도해보지 않은 사람들과 강력 추천하는 사람들임
일주일간 jj를 써본 뒤 다시 git으로 돌아가는 경우는 거의 본 적이 없음
정말 진지하게 써본 사람들은 거의 다 jj에 정착했음
오늘이 바로 jj를 써보는 계기가 되길 바람
생각보다 전환이 훨씬 간단하고, 나는 당일에도 바로 생산성을 유지했고, 일주일 안에 git 커맨드로 돌아갈 일이 전혀 없었음
짧은 시간 안에 더 생산적으로 변했고, 이전에 어떻게 git을 썼는지 신기할 따름임-
나는 git이 생산성 측면에서 전혀 불편하지 않음
git 커맨드를 매일 오래 쓰는 것도 아니고, 대부분의 경우 작업에 무리가 없음
레포를 git으로 많이 조작해야 한다면 jj가 더 나을 수 있지만, 내 상황엔 해당되지 않음
예를 들어, vim과 매우 비슷하면서 다른 기능이 추가된 bim을 무조건 써보라는 제안과도 유사함
하지만 "i"를 몇 번 더 치는 게 궁금하지 않고, julia 자동완성도 필요하지 않음
jj가 더 좋은 분들은 즐기시길 바람 -
jj는 VCS UI에서 확실히 발전이긴 하지만, git 고급 사용자라면 몇 가지 한계점이 있음
gitattributes 미지원으로 git-crypt, git-lfs 등 필터가 필요하면 불편함
줄 끝 처리 등 Windows에서의 호환성도 떨어질 수 있음
또, git-annex, git-bug 같은 외부 툴은 oplog 통합 안 되고 히스토리 어지럽힐 수도 있음 -
나는 실제로 일주일 넘게 써보고 다시 git으로 돌아간 경험이 있음
개인적으로 staging area의 워크플로우가 더 좋아서, 대부분은 git staging을 싫어하지만 나는 오히려 그게 jj에서 버릴 만큼 이득이 크지 않았음
습관은 바꿀 수 있겠지만, 아직 jj에 굳이 안 얽매임
언젠가 다시 시도할 마음은 열려있음 -
몇 달간 jj를 썼다가 다시 git으로 돌아갔던 이유는 성능 문제였음
jj 조작은 수 초가 걸렸고, git은 프로젝트 크기와 관계없이 즉각적이었음
또 jj를 쓰면 약간 더 정신적으로 복잡해지는 느낌을 받았고, 다시 git으로 오니 한결 가벼워짐
이건 나만의 장기간 git 사용 경험 때문일 수도 있음 -
“어차피 두 부류뿐”이라 말하는 자체가 jj 전도사들의 전형적 멘트 같음
-
-
jj에서 미치는 점은 변경사항이 무조건 자동 staging되었다는 점임
SVN 체계가 예전에 그랬고, git은 명시적으로 staging해서 훨씬 개선되었다고 느낌
레포에는 늘 더 많은 변경이 남아 있고, 다음 커밋에 넣고 싶은 것만 선택하는 게 git에선 당연한데
jj(SVN 포함)는 커밋 전 변경을 임시로 밖으로 옮기는 등 번거로운 수작업이 필요함-
이 이슈는 나도 딜레마임
staging이 항상 귀찮아서 Mercurial 쓸 때도 힘들었음
자동 추가가 90% 이상 필요한 경우엔 오히려 편한데,.gitignore
에 못 넣는 파일들이 문제임
auto add를 꺼도 불편해지고, 켜도 애매함
어느 쪽도 완전히 깔끔하지 않음 -
JJ의 워크플로우는 약간 다르니, 예를 들어 베이스 커밋을 먼저 만들고, 그 위에 익명 커밋을 쌓아서 작업
준비가 되면jj squash
나jj split
으로 정리하게 됨 -
나는
jj commit -i
나 여러 명령의-i
옵션, 그리고 config의snapshot.auto-track="none()"
을 씀
Mercurial 쓸 때도 비슷했음
실제론 absorb 기능을 많이 쓰면 불필요한 파일이나 청크는 자동으로 무시됨 -
“jj new” 명령어가 원하는 워크플로우에 적합한 것 아님?
-
jj는 트리 상에서 head가 아닌 브랜치도 제대로 추적하기 때문에, 리베이스와 머지가 stash 필요 없이 매끄럽게 작동함
-
-
자동 변경 추가에 적응하기가 쉽지 않음
로컬에서 특정 파일은 커밋할 생각 없이 개발 과정에서 잠깐 변경할 때가 있는데
git에선 staging하지 않으면 절대 커밋/푸시 안 되니 안심되지만
jj에선 뭔가 unstage하거나 조심해야 할 것 같음
습관 문제일 수 있겠으나, 나는 내가 커밋할 것만 명확히 지정하는 게 더 편함
혹시 내가 jj를 잘못 이해한 것인지 궁금함-
jj에선
jj split
으로 변경점을 나눌 수 있음
선택하면 첫 번째 리비전, 나머지는 두 번째 리비전으로 정리됨
여러 작업을 한 번에 하고 bite-sized 리비전으로 나눴을 때 git보다 훨씬 수월함
git처럼 새 리비전(jj new
) 만들고 바꾼 후,jj squash -i
로 딱 원하는 부분만 옮길 수 있음
개념적으로 “@”가 현재 리비전, “@-”가 한 단계 전이니 git의 워킹 카피/스테이지처럼 생각하면 됨 -
jj의 자동 스테이징이 항상 좋은 게 아님
jj 공식문서와 입문자들이 기본값을 쉽게 끌 수 있다는 점을 더 명확히 알렸으면 함
~/.jjconfig에
[snapshot]
auto-track = "none()"
이렇게 적으면 됨 -
나는 주로 작업 후
jj split
으로 커밋할 부분을 리뷰해서 정리함
이 워크플로우는 git의add -p
랑 비슷하게 움직임
맨 위 커밋은 보통 push하지 않는 워킹 카피처럼 쓸 수 있음
스태시 없이 브랜치 전환해도 진행중인 내용은 잘 보존됨 -
네, 그 습관이 나도 쉽지 않게 바뀜
바꿔야 한다는 주장의 합리적 근거는, untracked 상태로 코드를 돌리면 안 된다는 것임
의미 있는 변경은 커밋에 남기고, 나머지는 기록하지 않는 게 더 안전함
문제는 레포 전체의 설정과 로컬 설정이 분리되지 않을 때인데, VSCode가 대표적임
이럴 땐 환경별로 커밋해선 안 되는 변경이 필요해져서 더 복잡해짐 -
이런 워크플로우를 원하면, jj의 “@”를 git index처럼, 커밋 타임에 “@-”로 squash 하면 git add --patch와 비슷한 효과를 얻을 수 있음
-
-
팀을 jj로 바꾸려다 실패 중임
git에서 복잡한 작업을 jj로 얼마나 쉽게 할 수 있는지 한눈에 보여주는 페이지가 있으면 좋겠음
엘리베이터 피치 마냥 간단히
내가 직접 데모해서 보여주는 것도 필요하겠다는 생각임-
많은 사람들이 흔히 쓰는 git 작업이 jujutsu에서 더 쉬운 건 아닌 듯함
오히려 git에선 불가능하거나 번거로운 새로운 워크플로우가 jujutsu에 있음
이건 기존 git에 익숙한 개발자들에겐 잘 와닿지 않을 수 있음 -
나 역시 git을 특별히 좋아하는 건 아님
과거엔 mercurial만 썼었는데, 지금은 git이 이미 머릿속에 박혀있음
Jujutsu가 뭔가 확실한 장점이 있어도, 초기 셋업(복잡한 컬러 세팅, 익숙한 스크립트 세팅 등)까지 신경 쓸만큼의 매력은 아직 못 느낌 -
나는 jj와 git의 한 가지 대표 작업 비교가 큰 이해를 도왔음
https://lottia.net/notes/0013-git-jujutsu-miniature.html -
도움이 될만한 좋은 링크들도 추천함
https://v5.chriskrycho.com/essays/jj-init/
https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj-absorb/
https://ofcr.se/jujutsu-merge-workflow -
내가 이 포스트에 곧 추가할 다음 내용이 아마 마음에 들 것임
조만간 페이지 하단과 별도 포스트로 올릴 예정임
-
-
몇 주 jj를 쓰다가 자동 커밋 메시지 스크립트도 제작함
오늘 오랜 git 레포에서 같은 스크립트를 포팅하려고 했는데, git에선 커밋/어멘드 시점을 자동 판별할 코드가 필요하다는 걸 깨달았음
jj는 immutable 커밋 구조 덕분에 스크립트가 매우 간단했음
그냥 jj로 레포를 새로 클론함
커밋과 어멘드 헷갈림에서 순식간에 해방되었음
https://codeberg.org/jcdickinson/nix/…- 꼭 re-clone 할 필요 없이, 기존 git 레포에 jj를 추가로 쓰면 됨
-
git이 충분히 잘 작동함
학습 곡선이 조금 있을 뿐, 일단 익히면 모든게 이해됨
솔직히 95%의 상황에서 pull, add, reset, branch, commit 정도만 알면 충분함-
내 워크플로우에선 stacked diff가 필수임
리뷰 기다리느라 막히지 않고 미래 작업까지 쌓아두기 좋음
git에서 세팅은 안 어렵지만, 스택 아래쪽에 변경 추가하고 전체 restack 하는 건 정말 헬임 -
협업하는 경우엔 rebase/squash 숙지가 필수임
팀 전체가 안 쓰도록 설득하기 전엔 피할 수 없음
특히 여러 명이 한 브랜치에서 급하게 개발할 땐 git 습관이 불편함으로 다가옴
기본적으론 편하지만, 복잡한 상황에선 문제가 많음 -
특이한 상황만 경험해봐도 git의 단점이 보임
-
-
jj를 쓴 지 2주 정도 됐고, 처음으로 커맨드라인만으로 버전 관리를 편하게 쓰고 있음
git을 쓸 땐 항상 GUI(Git Graph in VSCode)에서 우클릭 위주였는데
jj는 튜토리얼 만 읽고도 바로 일관된 커맨드라인으로 진입이 가능했음
다만 jj 로그에서 id를 계속 복붙하는 걸 방지하려면 revset 언어를 배워야 할 듯함-
나도 비슷한 상황임
git을 오래 썼지만 복잡한 건 항상 UI로 처리했었음
한 달 가까이 jj CLI만 사용 중이고 꽤 만족스러움
다만 jj 공식 문서가 기본 지식이 있다는 전제로 쓰여 있어서 초보에겐 헷갈릴 때가 있음
블로그, 튜토리얼이 더 많아지길 바람
나도 revset 언어랑 rebase 좀 더 배워보고 싶음
간결한 cli, 충돌 해결, oplog 다 맘에 듦 -
나도 id 복사 붙여넣기 불편했었는데, jjui(https://github.com/idursun/jjui) 쓰고나선 훨씬 부드러워졌음
마치 로그를 훑듯이 빠르게 작업하게 됨
-
-
jj 최고의 기능은 undo라고 생각함
git에서 undo는 실수 종류에 따라 커맨드가 따로 있어서 어려움
jj는jj undo
한 번이면 끝남
백엔드 시스템에도 얽매이지 않아 로컬에서 혼자 jj 써도 동료에게 영향 없음 -
jj가 뭔지 혼란스러움
git 헷갈리는 사람을 위한 프론트엔드인지 궁금함
백엔드 추상화한다고 하지만 git에 너무 영향을 받아 Perforce, Piper처럼 순차 넘버 커밋이 강제되는 시스템에선 잘 상상이 안 됨
작업카피=커밋 설계는 퀄리티 컨트롤이 불가하다고 생각함
커밋의 본래 의미는 꼭 의도된 것만 올리는 것인데, 이런 구조면 "쓰레기 커밋" 구분이 더 어려워짐
git이나 jj 모두 대형 프로젝트에는 취약하고, 커밋 난립을 방지하지 못하는 점은 고쳐지지 않은 듯함
jj가 실제로 어떤 문제를 푸는지 궁금함, 단순히 저자 취향의 프론트엔드인지 모르겠음-
Piper 백엔드는 오히려 Git 백엔드보다 더 자연스럽게 돌아감
jj 커밋은 git 커밋과 완벽하게 대응하지 않으니, 오해할 필요 없음
실제로 “커밋”이라 하면 “이름 붙은 diff” 개념이고, 푸시 전 변경은 언제든 쉽게 갈아엎어 정리할 수 있음
git에서 리베이스, 히스토리 편집보다 훨씬 편함
나는 실험, 문서 등 여러 커밋을 작업 중간에 만들고, git이었다면 stash나 임시 브랜치에 억지로 넣었어야 했음 -
jj는 git 프론트엔드 이상임
버전 컨트롤 시스템이고, backend-agnostic함
대표 backend가 git이라 바로 기존 repo에서 전환 가능함
나는 git을 github 나오기 전부터 썼던 유저지만, jj 쓰다 다시 git으로 못 돌아가겠음
jj는 더 단순하면서도 강력함
working copy=commit은 실제로 “index도 하나의 커밋이다” 식으로 이해해야 함
예를 들어 feature x 작업 시작시jj new -m "working on feature x" trunk
로 새 커밋 만들고, 그 위에 비어있는 커밋 하나 더 얹음
작업물은 워킹 카피(@)에 들어가고, 이전 커밋(@-)으로 “옮기기(squash)”해서 git의 add-p, reset 등 복잡한 옵션 대신 커밋 간 diff로 모든 걸 처리함
이 구조 덕에 git index보다 유연하고 강력하게 커밋을 분할·정리할 수 있음
커밋 난립 문제는 pull request 문화에 더 가깝고, jj도 일정 부분만 해결해줄 수 있음 -
working copy가 깃허브에 무작정 푸시되는 건 아니고, 커밋 설명 붙일 때 리뷰/정리하면 됨
-
-
jj를 며칠 써봤지만, 기존 lazygit 및 내 워크플로우, 스크립트 셋업에 충분히 만족하고 있음
jj가 참신하고 괜찮긴 한데, 이제 막 버전 관리 시작하는 입장 아니면 굳이 바꿀 동기가 부족함- GitButler처럼 다른 도구 쓰면 프론트엔드가 덜 중요한데, 며칠 전 Jujutsu 도입해서 Claude에게 주요 동작(커밋, 브랜치 이동, 깃허브 푸시/풀) 묻고 10분 만에 숙달됨
여전히 Lazygit의 diff는 쓰지만 detached HEAD 상태로 있어도 전혀 문제 없음
JJ는 git과 잘 호환되고, 복잡한 커맨드를 외울 필요 없이 훨씬 쉽게 모든 걸 처리해줌
브랜치 간 이동 때 미커밋 파일이 자동으로 같이 움직이는 건 최고의 기능임
개발, 버그 수정, 카피 변경 등 오가며 작업하기 매우 수월함
AI 에이전트가 변경 작업하면 worktree 분리해 쓰면 됨
작업 완료 전에 변경 설명(=커밋 메시지) 붙여서 트리에서 미리 볼 수 있는 것도 정말 좋음
JJ는 전반적으로 상당히 훌륭함
- GitButler처럼 다른 도구 쓰면 프론트엔드가 덜 중요한데, 며칠 전 Jujutsu 도입해서 Claude에게 주요 동작(커밋, 브랜치 이동, 깃허브 푸시/풀) 묻고 10분 만에 숙달됨