jj – Jujutsu용 CLI
(steveklabnik.github.io)- Jujutsu의 명령줄 인터페이스인
jj는 분산 버전 관리 시스템(DVCS) 기반 도구 git보다 단순하고 직관적이면서도 더 강력한 기능을 제공git과 Mercurial의 장점을 결합해 핵심 도구 수를 줄이고 유기적 연동을 강화git호환 백엔드를 사용해 기존 협업 환경을 유지하면서도 독립적 실험 가능- 고급 사용자는
git으로는 어려운 추가 버전 관리 기능을 활용할 수 있는 점이 중요
jj 소개와 특징
-
jj는 Jujutsu의 CLI(명령줄 인터페이스)로, Jujutsu는분산 버전 관리 시스템(DVCS)- 사용자는
git과 같은 다른 DVCS에 익숙할 수 있으며, 튜토리얼은git사용자 관점을 전제로 함 jj는git보다 단순하고 사용하기 쉬우면서도 강력한 도구로 설계됨- 일반적으로 “강력함”과 “복잡함”은 상충하지만,
jj는 이 균형을 새롭게 제시함 jj는git과 Mercurial(hg)의 장점을 결합해 새로운 형태의 DVCS를 구성- 핵심 도구 수를 줄이고, 각 도구 간의 유기적 연동을 통해 효율적인 작업 환경 제공
- 고급 사용자는
git으로는 어려운 추가 버전 관리 기능을 활용 가능 jj는git호환 백엔드를 사용해 협업 환경을 변경하지 않고도 독립적인 실험 가능- 기존
git저장소와의 호환성을 유지하며, 필요 시 손쉽게git으로 복귀 가능 - 튜토리얼은 이러한 특성을 통해
jj가 왜 주목할 만한 도구인지 직접 보여주는 과정을 예고함
- 사용자는
Hacker News 의견들
-
많은 논의가 git과 jj의 차이점에 집중되어 있지만, 나는 그냥 git을 잊고 jj의 기본 워크플로우에 집중하는 게 낫다고 생각함
깨끗한 저장소에서jj를 실행하면 상태를 확인할 수 있고, 변경 후jj commit -m "made changes"로 커밋하면 됨
실수했을 땐 수정 후jj squash로 마지막 커밋에 합치면 됨
새로운 브랜치처럼 특정 리비전에서 작업하고 싶을 때만jj new -r lmnop을 사용하면 됨
git 히스토리는git log로 확인 가능하며, jj 내부 동작은 보이지 않음- 나도 비슷한 걸 원해서
alias.save="!git add -A; git commit -m"같은 git alias를 만들어서$ git save "made changes"로 쓰곤 함
- 나도 비슷한 걸 원해서
-
JJ가 나에게 거꾸로 생각하라고 요구하는 것처럼 느껴짐
git에서는 변경 후 커밋 메시지를 작성하지만, jj는 먼저 새 커밋을 만들고 설명을 붙이는 식임
여러 기능이 섞인 더러운 상태에서 필요한 변경만 골라 커밋하는 게 익숙한데, jj 튜토리얼만 봐서는 그게 가능한지 확신이 안 섬jj new는 비어 있는 git 스테이징 영역 같은 개념임
jj에서는 항상 커밋이 존재하고, 그 커밋은 폴더 내용에 따라 계산된 값으로 안정적인 changeid를 가짐
여러 변경을 나눠 커밋하고 싶다면jj split을 쓰면 됨- 나는 자주
jj new로 임시 커밋을 만들고 메시지는 비워둠
나중에 준비가 되면 여러 커밋을 squash해서 하나로 합치고 메시지를 추가함
이 방식은 일종의 undo 히스토리처럼 작동해서 실험하기 훨씬 편함 - 사실
jj new는 단순히 “새 커밋을 위에 만들기”일 뿐이라, 바로 설명을 쓸 필요는 없음
나도 처음엔 습관을 들이려 했지만, 오히려 비효율적이었음 - JJ에서는 이런 방식이 표준적임
Git에서도 비슷한 워크플로우를 추천해왔고, Squash Workflow를 참고하면 Git의 인덱스와 유사한 흐름을 만들 수 있음 - 나도 여러 변경을 하다 보면 서로 다른 기능이 섞이곤 함
그래서 여러 워크스페이스를 두고 shelve 기능(IntelliJ)을 자주 씀
때로는 git 패치로 diff를 임시 저장해두기도 함
이런 혼란스러운 과정을 동료에게 숨기며 조금 더 프로페셔널해 보이려 함
-
jj를 써보니 마음에 안 드는 점은 파일 수정이 자동으로 커밋된다는 것임
과거 커밋을 탐색하려고 checkout 후 파일을 수정하면, 그 커밋이 바뀌고 이후 히스토리가 전부 리베이스됨
그래서 방어적으로 빈 커밋을 새로 만들어야 함
git은 내가 명시적으로 커밋하기 전까지는 저장소가 변하지 않아서 더 편함- 나도 예전에 그렇게 생각했지만,
jj evolog를 알고 나서 바뀌었음
jj는 스테이징보다 더 나은 해결책을 이미 갖고 있었음
git CLI에 익숙한 게 오히려 jj 학습의 장애물이었음
흥미롭게도 jj를 쓰면 git의 저장 엔진 구조를 더 잘 이해하게 됨 edit대신jj new를 쓰면 깔끔하게 변경을 추적할 수 있음
git의 stash를 juggling하는 것보다 훨씬 낫다고 느낌jj edit은 jj의 가장 큰 함정임
대신jj new를 쓰고, 실수했을 땐jj undo로 복구 가능함
jj는 커밋을 값싼 스냅샷으로 다루므로, 커밋보다 “변경”에 집중하는 게 맞음
자동 리베이스는 push 이후엔 불변으로 고정되어 안전함- 파일 수정이 자동 커밋되는 건 jj의 핵심 기능임
jj new와jj squash를 조합해 git 브랜치 헤드처럼 관리하면 됨
detached head 상태에서 작업하는 걸 jj가 쉽게 만들어줌 - 아마
jj edit으로 과거 커밋을 checkout한 것 같음
jj new로 바꾸면 문제가 해결됨
- 나도 예전에 그렇게 생각했지만,
-
jj의 마지막 문단이 핵심임
git과 완전히 호환되는 백엔드를 사용하므로, 팀 전체가 바꿀 필요 없이 혼자 써볼 수 있음
마음에 안 들면 언제든 git으로 돌아갈 수 있음- 단, LFS나 submodule, hook을 쓰는 조직에서는 예외임
- 완전한 호환은 아님. 상호운용은 가능하지만 완벽히 매끄럽진 않음
git 작업은 jj 로그에 기록되지 않아 수동으로 import해야 함
프로젝트에서는 한 가지 인터페이스만 쓰는 걸 권장함 - 예전엔 나도 git을 CVS, Subversion과 함께 그렇게 썼음
- 하지만 git과 jj를 같은 디렉토리에서 동시에 쓰면 망가질 수 있음
-
내가 가장 좋아하는 기능은
jj absorb임
현재 리비전의 변경을 이전 관련 커밋으로 자동 이동시켜줌
설정 파일이나.gitignore수정 깜빡했을 때 유용함
jj new후 변경하고jj absorb하면 됨
게다가 merge conflict를 다루지 않아도 되는 게 최고임- 만약
jj absorb가 잘못 적용되면jj undo로 되돌릴 수 있음
이 기능 덕분에 복잡한 rebase도 두렵지 않음 - 참고로 git에도
git absorb가 존재함
- 만약
-
튜토리얼을 오랫동안 업데이트 못 했지만, 여전히 매일 jj를 쓰고 있음
스타트업 ersc.io에서 바빠서 upstream 작업을 못 했음
질문이 있으면 언제든 환영함- git과 jj의 DAG 불변성 차이가 핵심임
jj는 stable change ID를, git은 immutable commit ID를 사용함
그래서 jj에서는 undo나 rebase가 훨씬 유연하게 느껴짐 - jj는 “흥미롭지 않은” 변경을 자동으로 숨김
가끔 더 많은 변경을 보고 싶을 때가 있는데, 이를 한 번에 표시하는 옵션이 있는지 궁금함
- git과 jj의 DAG 불변성 차이가 핵심임
-
jj가 git과 달라서 시도해볼 가치가 있음
다른 접근 방식을 경험하는 것만으로도 엔지니어링 시야가 넓어짐
모든 걸 다 시도할 필요는 없지만, 다양한 워크플로우의 트레이드오프를 이해하는 건 중요함- 물론, 99%의 경우 이득이 미미한 시도는 시간 낭비일 수도 있음
-
git과 jj의 관계는 C와 Python의 관계처럼 느껴짐
git은 포렌식 추적이고, jj는 이야기의 장(chapter) 같음
때로는 초반 챕터를 다시 써야 후반이 더 자연스러워짐- jj가 하는 일은 git에서도 가능하지만, git의 습관적 사고방식이 방해가 됨
jj는 “working tree 자체가 커밋”이고 “conflict도 커밋 가능”하다는 철학으로 설계됨
- jj가 하는 일은 git에서도 가능하지만, git의 습관적 사고방식이 방해가 됨
-
“더 강력하고 쉽다”는 주장엔 구체적 예시가 필요하다고 느낌
- jj의 장점 몇 가지를 꼽자면:
- rebase/merge conflict를 즉시 해결하지 않아도 됨
- 커밋 조작이 매우 쉬움 (특히 jjui 사용 시)
- jj는 git의 상태 변화를 추적하는 operation log를 가짐
- 이름 없는 브랜치 덕분에 실험적 작업이 자유로움
- git과 완전히 호환되어 팀 내 혼용이 가능함
- 우리는 SVN에서 git으로 옮겼을 때 큰 개선을 느꼈지만, 지금 git 워크플로우엔 큰 불편이 없음
- 여러 PR을 한 저장소에서 동시에 작업하고 각자에 맞게 푸시할 수 있음
이런 필요가 없다면 jj의 가치를 못 느낄 수도 있음 - jj의 매력은 명령어가 아니라 직관적인 워크플로우에 있음
직접 써봐야 이해됨 jj undo하나만으로도 충분히 값어치가 있음
git에서는 복구 불가능한 상태가 되기 쉬운데, jj에서는 몇 번의 undo로 해결 가능함
- jj의 장점 몇 가지를 꼽자면:
-
jj 덕분에 비선형 DAG를 활용하는 데 자신감이 생김
여러 부모나 자식이 있는 변경을 자유롭게 다룸
예전엔 불필요하게 순서를 강제했지만, 이제는 의존 관계를 명확히 표현할 수 있음
리뷰와 제출 과정도 훨씬 효율적임- 이런 분기된 변경 위에서 mega-merge + absorb 워크플로우를 쓰는지 궁금함