Grit: 에이전트와 함께 Rust로 Git 재작성
출처: Hacker News
몇 달 전, Anthropic이 C 컴파일러를 만들기 위해 에이전트 무리를 풀어놓은 실험을 읽고(https://www.anthropic.com/engineering/building-c-compiler) 15년째 꿈꾸어 온 Git을 처음부터 라이브러리 기반으로 다시 쓰는 작업을 같은 방식으로 할 수 있을지 궁금해졌습니다(https://github.com/libgit2/libgit2/commit/4b7483a285024937df9623c3543390e717e2338a).
Git은 물론 매우 복잡한 소프트웨어입니다. 수많은 “plumbing”(https://git-scm.com/docs/git#_low_level_commands_plumbing) 명령과 고수준(https://git-scm.com/docs/git#_high_level_commands_porcelain) 명령이 존재하고, 지난 20년간 수천 명에 걸친 기여자들이 대규모·소규모 프로젝트에 걸쳐 점진적으로 만들어 왔습니다. Git은 연결 가능한 재진입 가능한 라이브러리를 기반으로 만든 것이 아니라, “Unix” 철학에 따라 더 간단한 명령들을 체인식으로 조합하도록 설계돼 있어, 포크·exec 오버헤드 없이 장기 실행 프로세스에서 사용하기가 어렵습니다.
그럼에도 흥미롭게도 Git 프로젝트에는 1,400개가 넘는 스크립트에 걸쳐 42,000개가 넘는 테스트가 포함된 매우 포괄적인 테스트 스위트가 있어, 모든 동작이 어떻게 되어야 하는지를 꽤 명확히 정의하고 있습니다.
Anthropic이 처음부터 만든 C 컴파일러에 사용한 기본 아이디어를 그대로 적용한다면 어떨까요? 새 구현을 시작하고, 이를 Rust 라이브러리로 설계한 뒤, 에이전트 무리를 투입해 테스트가 모두 통과할 때까지 계속 작업을 진행하는 겁니다.
그렇게 몇 달 동안(간헐적으로) 진행한 결과가 Grit 입니다. 처음부터 만든, 라이브러리 기반이며 메모리 안전성을 갖춘, 관용적인 Rust 구현으로, 전체 Git 테스트 스위트의 99% 이상을 통과합니다.
Achtung! Grit가 테스트를 통과했지만 테스트된 것은 아닙니다. 아직 실제로 사용된 사례가 없습니다. 직접 사용해 보려면 현재 잘못된 동작을 할 가능성이 높고, 심지어 데이터를 손상시킬 수도 있다는 점을 유념하시기 바랍니다. 위험을 감수하고 사용하시고, 문제가 발견되면 알려 주세요.
Anthropic 실험과 달리, 이 작업은 단순히 “가능한가?”를 확인하기 위한 것이 아니었습니다. 시작할 때, 만약 성공한다면 C 기반 Git이 가진 여러 문제점을 보완할 실제로 유용한 무언가를 얻을 수 있겠다고 생각했습니다. 그 부분은 곧 다루겠습니다.
내가 얻고자 했던 것은?
원하지 않았던 것은 C Git을 Rust로 순수 포팅하는 것이었습니다. 오히려, Git의 모든 설계 결정을 그대로 복제해야 할지조차 확신이 서지 않을 정도로, 처음부터 Rust‑only 코어 라이브러리를 만들고 싶었습니다. 이 라이브러리는 Git 저장소와 정통하게 상호작용할 수 있어야 하며, 재진입 가능하고, 링크 가능하며, 모듈화되고 포괄적이어야 합니다. 그리고 그 포괄성을 검증하기 위해, 해당 라이브러리를 CLI 형태로 감싸는 별도 크레이트를 만들어 가능한 한 많은 Git 테스트를 통과하도록 하는 것이 목표였습니다.
우리는 바로 그 목표를 달성했습니다.
아니, 정확히는 아직 완전하진 않지만, 이미 꽤 흥미롭고 실용적이라고 할 수 있습니다.
몇 가지 주의사항
-
모든 단일 테스트를 통과하는 것은 아니며, 이는 의도적인 선택입니다. 이메일 관련 기능, i18n, Perforce/SVN 임포터, 일부
midx/bitmap등 라이브러리 차원에서 재현할 가치가 낮다고 판단된 부분은 “skip” 처리했습니다. 하지만 여기서 다루는 대부분의 핵심 기능에 대해서는 Grit 라이브러리·CLI가 Git 테스트 스위트를 완전히 통과합니다. -
완벽하다고 보기는 어렵습니다. 경우에 따라 지수적으로 느려지기도 하고, 아직 테스트되지 않은 기능이 존재하며, API가 다소 정제되지 않았고, Windows 빌드가 제공되지 않는 등 첫 번째 마일스톤에 해당하는 초기 단계입니다.
그럼에도 불구하고, 몇 달과 수십억 토큰에 달하는 작업을 통해 매우 흥미로운 출발점을 마련했습니다.
이걸로 뭘 할 수 있을까?
프로젝트 자체는 흥미로웠지만, 단순히 가능성을 확인하기 위해 만든 것이 아니라 실제 활용 가능성을 염두에 두었습니다. Grit는 충분히 유용한 도구로 성장할 잠재력이 있습니다.
가장 기대되는 활용 사례 중 하나는 GitButler(https://gitbutler.com/)와 같은 툴에 복잡한 push·fetch 기능을 내장하는 것입니다. 현재 Gitoxide(https://github.com/gitoxidelabs/gitoxide)와 libgit2(https://github.com/libgit2/libgit2)의 네트워킹 기능은 부분적이거나 느리거나 아예 존재하지 않습니다. GitButler와 Jujutsu(https://github.com/jj-vcs/jj) 같은 도구는 데이터를 푸시·풀하기 위해 Git을 포크해 실행합니다. 이는 복잡한 인증 로직 때문인데, 이 로직은 이론적으로 Grit에 모두 포함되어 있습니다.
또 다른 가능성은 WASM 빌드입니다. 이를 활용하면 Vercel 엣지 함수와 같은 환경에서 거의 모든 Git 명령을 실행하거나, Cloudflare Artifacts(https://blog.cloudflare.com/artifacts-git-for-agents-beta/)와 같은 서비스를 부분 구현체인 isomorphic-git(https://isomorphic-git.org/) 대신 완전 호환되는 Grit WASM 빌드로 구현할 수 있습니다.
Git을 개별적인 임베디드 라이브러리 조각으로 제공하면, Rust로 커스텀 Git 서버나 클라이언트 기능을 만드는 것도 쉬워집니다.
예를 들어, 에이전트 데스크톱 빌드나 Zed(https://zed.dev/) 같은 에디터에 필요한 Git 기능만을 특정 버전으로 네이티브하게 포함시킬 수 있습니다.
전체 Rust 구현체의 크기는 현재 약 27 MB이며, 라이브러리 형태이기 때문에 기능별 서브크레이트로 쉽게 분리해 필요한 부분만 사용하면 됩니다.
오늘날의 Grit가 아직 모든 요구를 충족하지는 않지만, 이번 마일스톤은 약간의 추가 작업만으로도 충분히 목표에 도달할 수 있음을 증명합니다.
WASM이라니…
자세히 들어가기 전에, 거의 모든 코드가 메모리 안전하다는 점을 강조하고 싶습니다.
C와 FFI를 통해 연결되는 모듈은 날짜·시간 처리와 TTY 체크 하나뿐이며, localtime_r·strftime·mktime 같은 함수는 TZ 환경 변수를 올바르게 처리하는 순수 Rust 구현이 아직 없어 불가피하게 FFI를 사용합니다.
그 외 모든 코드는 안전한 Rust로 작성되었습니다.
에이전트와 함께한 작은 여정
처음엔 “에이전트 파일을 정의하고, 에이전트를 루프 돌려 며칠 안에 모든 작업을 끝내자”는 생각을 했습니다. 하지만 복잡한 프로젝트에서는 그렇게 되지 않았습니다. 혹은 제가 에이전트를 다루는 데 서툴렀을 수도 있지만, 꽤 비싼 교훈이었고, 매우 좌절스럽게도 비결정적이었습니다.
따라서 모든 과정을 일일이 나열하기보다는, 작업하면서 배운 몇 가지 교훈을 TL;DR 형태로 정리해 보겠습니다.
-
에이전트에게 “Git 테스트를 모두 통과하게 해라”라고 하면, 에이전트는 Git을 그대로 호출하는 간단한 함수를 만들고 싶어합니다. 테스트가 너무 빨리 통과되는 것을 몇 번 보고 나서야 이것이 속임수라는 것을 깨닫고,
AGENTS파일을 수정해 이런 행동을 차단했습니다. -
마치 소원을 들어주는 요정과 같습니다. 규칙을 극히 명확히 정의해야 합니다. “소원을 더 빌게 해줘” 같은 요구는 절대 허용되지 않도록 해야 합니다.
-
좋은 예시:
sha256지원을 테스트하는 부분이 여러 곳에 있는데, 에이전트가 테스트만 통과하도록 구현하면 실제sha256연산을 구현하지 않아도 된다고 판단했습니다. 제가 실제sha256초기화 저장소에서 Grit가 동작하지 않는 것을 발견하고 Claude에게 물었을 때… (이하 생략)
새 Grit 프로젝트 웹사이트