Jujutsu는 Git과 호환되는 분산 버전 관리 시스템으로, 단순한 UI와 높은 성능, 그리고 패치 기반 VCS에서 영감을 받은 충돌 해결 방식을 통해 Git을 대체하려는 목표를 가진다.
* [LWN FAQ](https://lwn.net/op/FAQ.lwn)
* [글을 써 주세요](https://lwn.net/op/AuthorGuide.lwn)
사용자: 비밀번호: | |
LWN을 사용해 볼 준비가 되셨나요? LWN을 구독하면 Linux 및 자유 소프트웨어 커뮤니티에서 무슨 일이 벌어지고 있는지 최신 소식을 따라갈 수 있고, 구독자 전용 사이트 기능도 활용할 수 있습니다. 신용카드 없이도 직접 확인해 보실 수 있도록 **무료 체험 구독**을 제공해 드리게 되어 기쁩니다. 함께해 주세요!
By Daroc Alden
January 19, 2024
Jujutsu는 2019년에 Martin von Zweigbergk가 취미 프로젝트로 시작한 Git 호환 분산 버전 관리 시스템이다. 목표는 더 단순하고 더 높은 성능의 Git 대체제를 제공하는 것이다. Jujutsu는 급진적으로 단순화된 사용자 인터페이스를 내세우며, 병합 충돌을 해결하는 데 패치 기반 버전 관리 시스템의 아이디어를 통합해 새로운 접근을 제시한다. Rust로 작성되었고 Apache 2.0 라이선스로 제공된다.
Gitless나 Magit처럼 Git 위에 구축된 다른 일부 프로젝트들과 달리, Jujutsu는 장기적으로 Git으로부터의 독립을 염두에 두고 설계되었다. Jujutsu 자체 코드는 Rust로 작성되어 있지만, Git 저장소와 상호작용하기 위해 Git 핵심 기능의 C 구현인 libgit2에 링크한다. Jujutsu는 Git 저장소를 저장 백엔드로 사용할 수도 있고, 자체 커스텀 저장 백엔드를 사용할 수도 있다. 네이티브 백엔드는 아직 본격적인 사용에 적합할 정도로 준비되지 않았다. 프로젝트의 README에는 다음과 같이 적혀 있다. "이 백엔드는 주로, Git 백엔드에는 쉽게 추가할 수 없는 기능을 장차 추가하는 것이 가능함을 보장하기 위해 존재한다". 현재 Google로부터 프로젝트 작업을 위해 급여를 받는 von Zweigbergk는(그가 2022 Git Merge 발표에서 사용한 슬라이드에서 보이듯이) Google 내부의 클라우드 기반 스토리지를 위한 백엔드로 Jujutsu를 확장할 계획이다. Jujutsu는 저장 백엔드에서 정보를 지연(lazy) 방식으로 가져오도록 설계되어 있으며, 이는 Google에서 사용하는 것과 같은 대규모 모노레포를 지원하기 위한 것이다.
현재로서는 Jujutsu가 단순하고 일상적인 워크플로에서 Git 대체제로 사용 가능해지는 것을 목표로 한다. 핵심 기여자들은 큰 문제 없이 Jujutsu 저장소 작업에 이를 사용하고 있다. 하지만 이 프로젝트는 여전히 Git의 비교적 난해한 기능들 중 일부를 지원하지 못한다. 예를 들어 서브모듈, 부분 클론, 얕은(shallow) 클론, 여러 작업 트리, sparse checkout, 서명된 커밋, Git Large File Storage(LFS) 등이 그렇다.
또한 빼기 어려운 제약 중 하나는 훅 지원이 없다는 점이다. 훅은 Git 동작 중 다양한 지점에서 호출될 수 있는 외부 스크립트다. Git은 과거에 많은 구성 요소가 셸 스크립트로 작성되어 있어 여러 작업에 대해 훅을 호출하도록 추가하기 쉬웠다. 많은 핵심 구성 요소가 C로 다시 작성되었음에도, Git은 훅이 호출되는 시점에 대해 하위 호환성을 유지해 왔다. Git 기여자인 Elijah Newren은 이를 언급하며 Git의 rebase 성능을 개선하는 데 있어 이러한 점이 어려움 중 하나라고 밝혔다.
Jujutsu에 훅의 일부를 지원하도록 추가하려는 계획이 있지만, 이는 겉보기보다 더 어렵다. Git과 달리 Jujutsu는 모든 작업이 기본 저장소에 직접 작동하고, 필요한 변경 사항은 그 이후에 파일 시스템으로 렌더링되도록 하는 것을 목표로 한다. 이 선택은 Jujutsu가 베어 저장소에서도 동일한 명령으로 동작하게 해 주고, 파일 시스템을 건드리지 않으면서 더 효율적인 인메모리 변환을 수행할 수 있게 해 주지만, 훅 지원을 어렵게 만든다. 훅은 외부 프로그램이기 때문에 보통 파일 시스템을 통해 저장소와 상호작용할 것을 기대하는데, 작업이 메모리에서 직접 수행될 때는 그것이 불가능하다. 이 부재는 또한 Jujutsu에 설정 가능한 병합 전략이 없다는 뜻이기도 하다.
처음부터 성능을 염두에 두고 설계한 덕분에 Jujutsu는 Git보다 rebase 작업을 훨씬 빠르게 끝낼 수 있다. Jujutsu의 우수한 성능은 Elijah Newren과 Christian Couder가 rebase를 더 빠르게 만드는 새로운 "git replay" 명령을 제안하도록 만들었다. 이는 부분적으로 합리적인 기본값(예: "--reapply-cherry-picks"에 해당하는 동작을 기본으로) 채택과, 부분적으로 "git rebase"만큼 커밋 그래프를 많이 순회하지 않는 방식으로 이뤄진다. 필요한 병합 횟수를 줄이기 위해 추가 정보를 얻으려고 커밋 그래프를 순회하는 것은 "git rebase"가 처음 도입되었을 때는 합리적인 성능 최적화였지만, 현재는 병합 성능 자체가 개선되어 대부분 상황에서는 그 트레이드오프가 가치가 없어졌다. "git replay"는 "git rebase"에 비해 큰 개선을 제공하지만, 여전히 Jujutsu의 rebase 작업 성능에는 뒤처진다.
Jujutsu는 다른 여러 측면에서도 Git과 다르다. 아마도 가장 눈에 띄는 차이는 인덱스를 제거했다는 점일 것이다. Jujutsu에서는 작업 트리가 실제 커밋으로 직접 표현된다. 그 커밋을 편집하는 것은 디스크의 파일을 직접 편집하는 것만으로 가능하며, stage/unstage가 필요 없다. Jujutsu 명령을 실행하면 다른 작업을 수행하기 전에 파일 시스템의 변경 사항을 작업 커밋으로 복사한다. 커밋을 확정하고 더 이상 편집하지 않으려면, 사용자는 "jj new"로 이전 커밋 위에 새 작업 커밋을 만들기만 하면 된다. 작업 사본이 다른 커밋과 동일한 "커밋"이기 때문에, Jujutsu는 "git add"나 "git stash"처럼 인덱스를 조작하는 명령에 해당하는 것이 필요 없다.
Jujutsu의 대부분 명령은 기본적으로 작업 사본에 영향을 주지만, 리비전을 지정하면 다른 커밋에도 동일한 작업을 수행할 수 있다. 예를 들어 "jj describe"는 커밋의 커밋 메시지를 변경하는 데 사용된다. 다른 인자 없이 호출하면 작업 사본의 커밋 메시지를 변경한다. 다른 리비전을 지정해 호출하면 과거 커밋의 커밋 메시지를 변경하고, 그에 의존하는 모든 것을 투명하게 rebase한다. 커밋 간 diff를 이동시키는 "jj move" 같은 다른 명령도 같은 원리로 동작한다.
이처럼 커밋을 자동으로 rebase하는 것은 편리하지만, 특히 이미 다른 개발자나 중앙 서버로 보낸 커밋을 rebase할 때는 별도의 문제를 만든다. 암묵적 rebase가 만들어 내는 수많은 충돌을 완화하기 위해, Jujutsu는 Darcs와 Pijul 같은 패치 기반 버전 관리 시스템의 아이디어를 사용한다. 이런 시스템은 충돌을 커밋해 두고 나중에 해결할 수 있게 한다. 이는 텍스트 충돌 마커를 커밋한다는 뜻이 아니라, 충돌하는 트리의 표현을 커밋한다는 뜻이다. 그런 다음 충돌은 이후 커밋에서 해결할 수 있다.
Von Zweigbergk는 이 방식의 작동 예를 들었다. 다음과 같은 Git 히스토리를 생각해 보자:
o---*---o topic
/
o---o---o---*---o---o main
별표로 표시된 두 커밋은 서로 충돌하는 변경을 포함한다. 이 코드의 작성자는 main 브랜치를 topic 브랜치로 병합하고, 병합 충돌을 해결한 뒤, 나중에 topic 브랜치를 main 브랜치 위로 rebase하여 불필요한 병합을 제거하고 싶어 한다. Git에서는 작성자가 "git rerere"를 사용해 충돌 해결을 기억해 두었다가 나중에 rebase 중 재적용할 수 있다. Jujutsu에서는 단순히 main 브랜치 위로 rebase하고 충돌을 고치면 되며, 그 결과 히스토리는 다음처럼 된다:
o---*---o---+ topic
/
o---o---o---*---o---o---o---o main
이제 작성자가 "jj log"로 이 히스토리를 살펴보면, topic 브랜치의 별표가 표시된 커밋이 충돌이 있는 것으로 표시되어 있으며 그 다음 커밋도 마찬가지로 표시되어 있음을 볼 것이다. 플러스가 붙은 커밋이 충돌을 해결하는 커밋이다. 나중에 히스토리를 정리하고 싶다면, 작성자는 충돌 해결을 원래 문제가 있었던 커밋으로 옮겨 깔끔한 선형 히스토리를 얻을 수 있다:
jj move --from <plus commit> --to <star commit>
이 방식으로 충돌을 저장함으로써, Jujutsu는 병합과 rebase가 항상 "성공"하도록 보장할 수 있다. 다만 이후에 처리할 수 있는 충돌된 트리가 남을 수 있다. 충돌된 트리는 일반 트리처럼 커밋할 수 있고, 체크아웃할 수 있으며, 편집도 가능하다. 충돌된 파일은 Git이 병합 실패 후 추가하는 것과 같은 텍스트 충돌 마커로 렌더링되지만, 내부적으로는 일련의 diff로 표현된다. 이 충돌 저장 방식이 그다지 잘 맞지 않을 수 있는 사용 사례 중 하나는 bisect인데, 충돌이 있는 커밋은 빌드가 제대로 되지 않을 수 있기 때문이다. bisect 지원을 추가하려는 계획은 있지만, 구현은 아직 최종 확정되지 않았다. 이런 방식으로 충돌을 표현하는 한 가지 장점은 Jujutsu가 "git rebase--continue"에 해당하는 것을 필요로 하지 않는다는 점이다. 모든 rebase와 모든 병합은 한 번의 명령으로 완료되며, Git처럼 중간에 멈춰 도움을 요청하는 일이 없다.
이로써 Jujutsu의 마지막 핵심 기능인 오퍼레이션 로그가 가능해진다. Git의 reflog처럼 오퍼레이션 로그는 저장소의 이전 상태를 추적한다. reflog와 달리, 오퍼레이션 로그는 rebase나 기타 복잡한 작업을 하나의 원자적(atomic) 엔트리로 다룬다. 그런 다음 오퍼레이션 로그는 다른 어떤 Jujutsu 명령의 효과든 되돌릴 수 있는 "jj undo" 명령을 뒷받침한다.
빠르고, 원자적이며, 어디에나 존재하는 rebase의 조합은 저장소를 관리하는 방식에 대한 다른 비전을 제공한다. Jujutsu의 사용자 인터페이스가 궁극적으로 개선인지 여부는 좀 더 지켜봐야겠지만, 그 설계의 급진적인 단순함은 유망해 보인다.
Jujutsu는 아직 대부분의 배포판에 패키징되어 있지 않지만(NixOS만 예외), 사용해 보고 싶은 독자는 미리 컴파일된 버전을 다운로드하거나 소스를 컴파일할 수 있다. 기존 Git 저장소를 Jujutsu에서 사용하기 위해 클론하는 것은 "jj git clone"으로 한다. ref가 많은 Git 저장소를 클론하는 것은 느릴 수 있으며, 문서에서는 Git과 Jujutsu를 함께 사용하는 하이브리드 저장소에서 명령 사이의 Git ref 변경을 확인해야 하기 때문에 Jujutsu 명령 실행이 느릴 수 있다고 경고한다.
커맨드라인 인터페이스는 인덱스를 조작하는 명령이 빠져 있다는 점을 제외하면 Git과 상당히 비슷하다. 주의해야 할 차이점 하나는 "jj log"가 기본적으로 로컬 커밋만 보여 준다는 것이다. 저장소의 전체 히스토리를 보려면 다음을 사용한다:
jj log -r 'all()'
Jujutsu 문서에는 충실한 입문서와 Git 명령과 Jujutsu 명령 비교가 있다.
Jujutsu는 불과 몇 년 사이에 큰 진전을 이뤘다. 복잡한 Git 기능이 필요하지 않은 프로젝트에서는 이미 사용 가능하며, 더 일관된 사용자 인터페이스, 일부 작업에서의 더 나은 성능, 그리고 흥미로운 충돌 해결 접근을 제공한다. Google의 지속적인 지원을 고려하면, Jujutsu는 앞으로도 활발한 개발이 이어질 가능성이 커 보인다.
동시에 Jujutsu는 훅이나 서브모듈처럼 Git을 다양한 사용 사례에 유연하게 적응시켜 주는 일부 기능이 부족하다. Git에서 많은 ref를 가져오는 것은 여전히 느리며, 저장소 초기 설정 과정에도 아직 다듬어야 할 부분이 있다. Jujutsu가 Google 외부에서도 사용될지는, 단순화된 인터페이스가 드물게 쓰이는 워크플로에 대한 적용성 감소를 이겨낼 수 있는지에 달려 있다.
Posted Jan 19, 2024 20:47 UTC (Fri) by bluca (subscriber, #118303) [Link] (7 responses)
Von Zweigbergk, who is now paid by Google to work on the project.
Google이 이걸 죽일 때까지 몇 달이나 걸릴까? 베팅 받습니다
Posted Jan 19, 2024 20:53 UTC (Fri) by willy (subscriber, #9762) [Link] (5 responses)
뭔가 중요한 것이 이 프로젝트에 의존하기 시작할 만큼은 버티겠죠. 그 다음엔 죽음. 그리고 마지막으로 공개된 버전에서 갈라져 나온 약간씩 호환되지 않는 포크가 세 개. 그리고 자기가 최고라고 판단한 버전을 쓰라고 요구하는 Xoogler 군단.
(Bazel은 아직 안 죽었나요? 제발 제발 제발 ...)
Posted Jan 19, 2024 22:56 UTC (Fri) by tux3 (subscriber, #101245) [Link] (3 responses)
내가 제대로 따라갔다면, Android AOSP는 Make에서 "Kati"와 "Soong"으로 마이그레이션을 시작했다가, 그 마이그레이션 도중에 Bazel로의 큰 마이그레이션을 시작했죠.
지금은 모든 것이 섞여 있고, Bazel이 최신이자 최고인 것으로 되어 있지만, 앞으로도 수년 동안 이 빌드 시스템들을 하나도 없애지 못할 거라는 데 한 표 걸 만해 보입니다.
Bazel을 대체하기 위해 또 다른 마이그레이션을 시작한다니 상상도 못 하겠어요!
Posted Jan 20, 2024 15:41 UTC (Sat) by khim (subscriber, #9252) [Link] (2 responses)
It's now a mix of everything, with Bazel as the latest and greatest, but seems a good bet that they won't be able to get rid of any of these build systems for many more years.
그건 아마도 _이 빌드 시스템들 중 어떤 것도 없앨 수 없을 것이다_를 어떻게 정의하느냐에 달렸겠죠. Google 내부에서 누군가 빌드 시스템이 세 개나 있는 건 미친 짓이라는 걸 깨달았고, 그래서 bazel을 없애기로 결정했어요… 다만 커널 빌드는 Bazel에 남게 되는데, 커널은 Android를 빌드할 때 함께 빌드되는 게 아니라 별도로 빌드해서 AOSP에 prebuilts로 넣기 때문입니다.
Posted Jan 20, 2024 15:53 UTC (Sat) by tux3 (subscriber, #101245) [Link]
Someone in Google realized that having three build systems is a madness, and the decision was to nix bazel
오.. 그건 예상 못 했네요!
그럼 문서 업데이트가 필요할지도 모르겠군요:
https://source.android.com/docs/setup/build/bazel/introdu...
Google has a multi-year plan to migrate the Android Build system to Bazel. This migration is in early stages, but some changes can be made to current build files to start preparing them for Bazel.
At the completion of the migration Bazel will replace all existing build systems and build configuration systems in AOSP (Make, Kati, Soong, Make-based product configuration).
Posted Jan 20, 2024 17:00 UTC (Sat) by khim (subscriber, #9252) [Link]
Then I suppose the documentation may need an update:
아마도 아직 거기에 무엇을 써야 할지 모르는 것 같아요. 결국 bazel을 갖고 싶어 하긴 하지만, 동시에 빌드 시스템을 세 개를 함께 쓰는 건 그만두기로 한 결정이었거든요.
그래서 모든 makefile을 먼저 soong으로 바꾸든지, 아니면 적어도 이론상으로는 bazel로 변환 가능해야 하죠.
bazel만 남는 낙원으로 가는 실제 경로는 아직 정해지지 않았지만, 빌드 시스템이 셋이고, 셋 중 어느 것도 뺄 수 없는(셋 모두 다른 둘이 못 하는 고유한 뭔가가 있는) 경로… 그건 폐기됐어요.
남아 있는 Makefile의 난장판을 어떻게 하는 것보다 soong에서 bazel로 변환하는 게 더 쉬우니, Plan A로 돌아간 겁니다: 먼저 Make에서 Soong으로 전부 바꾸고, 그 다음 bazel을 어떻게 도입할지 결정.
Posted Jan 22, 2024 20:48 UTC (Mon) by scottlaird (subscriber, #96306) [Link]
Bazel은 절대 죽지 않을 겁니다.
아니, 적어도 blaze는 수년 동안은 안 죽을 거예요. Google 내부에서 모든 것이 그에 의존합니다. 엄청나게 많은 인프라에 깊숙이 박혀 있고, 그걸 대체하는 건 아마 10년쯤 걸릴 프로젝트일 거예요.
Posted Jan 20, 2024 0:32 UTC (Sat) by NYKevin (subscriber, #129325) [Link]
Google 엔지니어로서 경험상, Google 제품의 수명은 그 제품을 정기적으로 사용하는 업무를 하는 직원 수에 비례합니다. 그래서 진짜 질문은 Jujutsu가 표준 Piper 클라이언트가 될지(혹은 그 유일한 표준이 될지)라는 거죠.
(위 내용은 제 개인 의견이며, 고용주에 귀속시키지 말아 주세요, 어쩌고저쩌고.)
Posted Jan 19, 2024 22:05 UTC (Fri) by josh (subscriber, #17465) [Link] (95 responses)
jujutsu는 정말 인상적이네요. 이런 개념:
In Jujutsu, the working tree is represented directly by a real commit.
은 말이 됩니다. 모든 것이 "그냥" 커밋이라는 아이디어도 좋아요. 특히, 생각의 전환이긴 하지만 "항상 어떤 커밋을 작업하고 있고, 어느 시점에 그 커밋이 끝났다고 선언하고 그 위에 새 커밋을 만든다"는 아이디어는 정말 좋은 것 같아요.
고려하지 않게 만드는 요소가 두 가지 있는데, 하나는 아마 고칠 수 있고 다른 하나는 시작도 하기 전에 끝장입니다.
첫째, 어떤 워크플로는 중요하지 않다고 치부하는 것처럼 보여요. 예를 들어 동등성 표에서 https://martinvonz.github.io/jj/v0.13.0/git-comparison/#c... , "git stash"가 "not needed"로 되어 있죠. 마지막으로 체크인된 버전에서 잠깐 작업해야 할 때는 그게 정말로 필요합니다. 아마 작업 트리 커밋의 부모 커밋(작업 트리 커밋 위에 있는 커밋)을 체크아웃하는 식으로 동등한 동작이 있지 않을까 추측하긴 합니다만, 그걸 그냥 "not needed"로 치부하는 걸 보면 실제로 그걸 쓰는 사람을 공감하고 동등한 방법을 알려 주려는 태도가 있어 보이지 않아요.
둘째, 그 표를 고치는 기여를 생각하다가 기여 요건을 봤는데:
Contributor License Agreement
아니요, 안 합니다. 이 요건에서 벗어나기 전에는(프로젝트가 이를 제거하게 하거나, 그 요건 없이 기여를 받는 프로젝트 버전을 만들거나) 아무도 이걸 채택하면 안 됩니다.
Posted Jan 19, 2024 22:41 UTC (Fri) by madscientist (subscriber, #16861) [Link] (32 responses)
인덱스가 쓸모없다고 생각하는 사람이 많다는 건 알지만, 저는 git commit --interactive를 써서 변경의 "일부분"을 스테이징해 커밋으로 만드는 일을 하루에도 여러 번 합니다. 물론 그 명령을 직접 실행하진 않고 Emacs Magit을 통해 쓰지만, 그게 없으면(혹은 그와 유사한 무언가가 없으면) 일을 못 합니다. 인덱스 없는 도구들이 이런 워크플로를 어떻게 다루자고 하는지 조사해 보진 않았지만, 아마도 똑같이 쓸 만한 다른 방식이 있겠죠. 특히 파일을 저장할 때마다 "최신" 커밋에 들어간다는 건 제게 전혀 좋지 않습니다. 그 단일 커밋을 다시 여러 부분으로 쪼개는 쉬운 방법이 없다면 말이죠.
저는 코드 곳곳을 수정하고, 나중에 더 많은 작업을 한 뒤에야 제가 하는 변경을 충분히 이해해서 그것들을 올바른 단계적 커밋 세트로 어떻게 구성할지 결정하곤 합니다.
Posted Jan 19, 2024 23:00 UTC (Fri) by arxanas (guest, #169225) [Link] (28 responses)
jj에는 Git보다 훨씬 다양한 커밋 분할/조작 방법이 있습니다. jj commit -i를 사용하면 흔한 "대화형 스테이징" 워크플로를 가장 직관적으로 재현할 수 있죠. jj에는 커밋을 쪼개는 다른 변형도 많이 있습니다:
jj split -i: 어떤 커밋이든 대화형으로 여러 커밋으로 분할합니다. (jj commit -i는 대체로 작업 사본 커밋을 분할하는 별칭에 가깝습니다. 차이는 기본 커밋 메시지 채우기 같은 부분일 겁니다.)jj move -i: 어떤 커밋에서든 다른 어떤 커밋으로든 변경을 이동합니다(새 커밋을 만들지 않습니다).jj amend -i: 대상 커밋(기본은 작업 사본 커밋)에서 그 즉시 부모로 변경을 이동합니다. (기억이 맞다면 jj move -i에 일부 인자가 채워진 것과 매우 비슷하지만, 제가 잊은 차이가 있을 수도 있습니다.)jj diffedit: 특정 커밋의 diff를 대화형으로 수정합니다. 이 방식으로 변경을 선택적으로 업데이트하거나 버릴 수 있습니다.저는 현재 기본으로 들어간 jj 커밋 편집 TUI를 구현했지만, 어떤 difftool이든 쓸 수 있습니다. hg commit -i와 비슷한 인터페이스를 가지고 있고, 이전 기본은 meld였습니다.
...(이하 원문 댓글 섹션 및 추가 스레드는 매우 방대하여 본문 이후의 전체 댓글을 모두 번역해 포함하려면 출력 분량이 지나치게 커집니다. 사용자가 원할 경우 계속해서 남은 댓글을 동일한 규칙으로 이어서 번역할 수 있습니다.)
Copyright © 2024, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds