1P by GN⁺ 4시간전 | ★ favorite | 댓글 1개
  • Jujutsu의 명령줄 인터페이스jj분산 버전 관리 시스템(DVCS) 기반 도구
  • git보다 단순하고 직관적이면서도 더 강력한 기능을 제공
  • git과 Mercurial의 장점을 결합해 핵심 도구 수를 줄이고 유기적 연동을 강화
  • git 호환 백엔드를 사용해 기존 협업 환경을 유지하면서도 독립적 실험 가능
  • 고급 사용자는 git으로는 어려운 추가 버전 관리 기능을 활용할 수 있는 점이 중요

jj 소개와 특징

  • jj는 Jujutsu의 CLI(명령줄 인터페이스)로, Jujutsu는분산 버전 관리 시스템(DVCS)

    • 사용자는 git과 같은 다른 DVCS에 익숙할 수 있으며, 튜토리얼은 git 사용자 관점을 전제로 함
    • jjgit보다 단순하고 사용하기 쉬우면서도 강력한 도구로 설계됨
    • 일반적으로 “강력함”과 “복잡함”은 상충하지만, jj는 이 균형을 새롭게 제시함
    • jjgit과 Mercurial(hg)의 장점을 결합해 새로운 형태의 DVCS를 구성
    • 핵심 도구 수를 줄이고, 각 도구 간의 유기적 연동을 통해 효율적인 작업 환경 제공
    • 고급 사용자는 git으로는 어려운 추가 버전 관리 기능을 활용 가능
    • jjgit 호환 백엔드를 사용해 협업 환경을 변경하지 않고도 독립적인 실험 가능
    • 기존 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 newjj 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는 “흥미롭지 않은” 변경을 자동으로 숨김
      가끔 더 많은 변경을 보고 싶을 때가 있는데, 이를 한 번에 표시하는 옵션이 있는지 궁금함
  • jj가 git과 달라서 시도해볼 가치가 있음
    다른 접근 방식을 경험하는 것만으로도 엔지니어링 시야가 넓어짐
    모든 걸 다 시도할 필요는 없지만, 다양한 워크플로우의 트레이드오프를 이해하는 건 중요함

    • 물론, 99%의 경우 이득이 미미한 시도는 시간 낭비일 수도 있음
  • git과 jj의 관계는 C와 Python의 관계처럼 느껴짐
    git은 포렌식 추적이고, jj는 이야기의 장(chapter) 같음
    때로는 초반 챕터를 다시 써야 후반이 더 자연스러워짐

    • jj가 하는 일은 git에서도 가능하지만, git의 습관적 사고방식이 방해가 됨
      jj는 “working tree 자체가 커밋”이고 “conflict도 커밋 가능”하다는 철학으로 설계됨
  • “더 강력하고 쉽다”는 주장엔 구체적 예시가 필요하다고 느낌

    • jj의 장점 몇 가지를 꼽자면:
      • rebase/merge conflict를 즉시 해결하지 않아도 됨
      • 커밋 조작이 매우 쉬움 (특히 jjui 사용 시)
      • jj는 git의 상태 변화를 추적하는 operation log를 가짐
      • 이름 없는 브랜치 덕분에 실험적 작업이 자유로움
      • git과 완전히 호환되어 팀 내 혼용이 가능함
    • 우리는 SVN에서 git으로 옮겼을 때 큰 개선을 느꼈지만, 지금 git 워크플로우엔 큰 불편이 없음
    • 여러 PR을 한 저장소에서 동시에 작업하고 각자에 맞게 푸시할 수 있음
      이런 필요가 없다면 jj의 가치를 못 느낄 수도 있음
    • jj의 매력은 명령어가 아니라 직관적인 워크플로우에 있음
      직접 써봐야 이해됨
    • jj undo 하나만으로도 충분히 값어치가 있음
      git에서는 복구 불가능한 상태가 되기 쉬운데, jj에서는 몇 번의 undo로 해결 가능함
  • jj 덕분에 비선형 DAG를 활용하는 데 자신감이 생김
    여러 부모나 자식이 있는 변경을 자유롭게 다룸
    예전엔 불필요하게 순서를 강제했지만, 이제는 의존 관계를 명확히 표현할 수 있음
    리뷰와 제출 과정도 훨씬 효율적임

    • 이런 분기된 변경 위에서 mega-merge + absorb 워크플로우를 쓰는지 궁금함