Version Control에 대한 추가
Source: Hacker News
놀랍게도, 그리고 기쁘게도 제 버전 관리에 관한 마지막 포스트가 Hacker News에 소개되어 많은 조회수를 얻었습니다. 참여해 주신 모든 분들께 감사드리며, 새로운 구독자 여러분을 환영합니다. 저는 이런 것들에 터무니없이 많은 작업을 투자했는데, 그에 대한 결과가 보일 때가 좋습니다.
Safe Rebase & Safe Squash
마지막 글을 쓸 때 완전히 깨닫지 못했던 점은, **“safe rebase”**를 지원하기 위해 부모 중 하나를 primary 로 선택하는 것 외에도, 더 오래된 조상을 primary 로 선택함으로써 **“safe squash”**를 지원할 수 있다는 것이다.
이 접근 방식의 장점은 Git 방식보다 훨씬 더 많은 정보를 제공한다는 점이다. blame/history의 safe 버전이 항상 primary 경로를 따르도록 하면, 두 구현 모두에서 동일한 명령을 같은 순서로 실행했을 때 출력이 거의 동일하게 보이도록 만들 수 있다—단, safe 버전이 더 합리적으로 동작하는 미묘한 경계 상황을 제외하고는 말이다.
safe 버전은 여전히 전체 히스토리를 기억하는데, 이는
- 실제로 무슨 일이 일어났는지 살펴볼 수 있게 해 주며
- 이미 squash되었거나 rebase된 것을 다시 끌어오는 경우와 같은 실수를 크게 줄여준다.
unsafe 버전은 히스토리를 완전히 버리고, 최종 작업을 수행한 사람이 모든 것을 작성했거나 원래 작성자가 실제로 작성한 내용과 크게 달라질 수 있다는 허구로 대체한다.
왜 Git이 아닌가?
Git은 매우 간단하고 신뢰할 수 있으며 다재다능하지만, 기능적이지는 않다. 그것은 마치 펜과 종이로 글을 쓰는 것이 인라인 편집을 “지원”하는 것처럼 스쿼시와 리베이스를 “지원”한다—즉, 인간이 버전‑컨트롤 시스템의 많은 작업을 암묵적으로 수행하도록 만든다.
보다 정교한 버전‑컨트롤 시스템들이 다재다능함과 신뢰성의 암묵적 감소를 보상할 만큼 새로운 기능을 충분히 제공하지 못했기 때문에 여전히 사용되고 있다.
내 목표는 전환할 가치가 있을 만큼 설득력 있는 이야기를 전달할 수 있는 기반을 제공하는 것이다. 나는 그 사례가 충분히 좋다고 생각한다:
- 커밋 시점에 차이를 커밋하는 작은 비용만으로도 그대로 작동하는 더 안전한 스쿼시와 리베이스를 가질 수 있다.
- 또한 가끔 발생하는 악몽 같은 상황에 대비한 더 나은 로컬 언두 기능을 얻는다.
- 보너스로, 꽤 좋은 체리‑피킹 기능도 제공한다.
“커밋 시점에 차이점 커밋하기”
행동 관점에서 이는 명확한 장점이지만 구현 위험을 초래합니다. 이러한 구현은 매우 잘 테스트되고 감사된 핵심 기능을 필요로 하며, 매우 보수적으로만 업데이트되어야 합니다.
그래서 저는 최종 일관성의 제약 내에서 데모 구현을 가능한 한 단순하게 만들었습니다. 이를 수행한다고 주장하는 다른 시스템들은 제가 이해할 수 없는 기술 문서를 가지고 있었고, 그들의 동작에 대한 직관을 형성하는 것은 말할 것도 없었습니다. “충돌은 너무 가까이 발생하는 업데이트”라는 접근 방식을 사용하면 동작을 직관적으로 추론할 수 있습니다.
좌/우 별명
일부 사람들은 **“left”**와 “right” 별명이 혼란스럽다고 느꼈습니다. 이는 전적으로 권고 사항이며 원하는 어떤 정보로도 교체할 수 있습니다—예를 들어, 해당 브랜치 이름, 로컬인지 원격인지 여부, 혹은 blame 정보 등.
CRDT를 위한 앵커링 알고리즘
CRDT를 위한 앵커링 알고리즘은 발명되었습니다 여러 번 있었습니다 독립적인 그룹에 의해 지난 몇 년간 이루어졌습니다. 나는 이것이 최근 몇 년 안에야 등장한 것이 다행이라고 생각합니다. 왜냐하면 나는 20년 전쯤에 이 아이디어를 생각해냈어야 한다고 느꼈기 때문입니다.
이상하게도, 그들은 생성‑카운팅 트릭을 아직 파악하지 못한 것 같습니다. 이 트릭은 내가 20년 전쯤에 고안한 것입니다. 두 아이디어를 결합하면 히스토리에서 커밋 ID에 대한 참조가 전혀 없게 되고 전체 알고리즘을 구조적으로 만들 수 있습니다.
충돌‑섹션 처리
한 구현 세부 사항에 대해 언급하지 않았는데, 제 데모 구현에서는 양쪽 모두 삽입이 없는 삭제만으로 구성된 conflict 섹션을 플래그하지 않습니다.
저는 이와 관련된 의미론을 사람들과 논의했으며, 대부분의 경우 사용자들은 이를 과도하게 보수적이라고 여기고 “모두 사라짐”으로 깔끔하게 병합되는 것을 괜찮게 생각하는 것 같습니다.
이는 의심할 여지 없이 사람들이 종교 전쟁처럼 논쟁할 수 있는 사안이며, 누군가 실제 데이터를 수집한다면 매우 흥미로울 것입니다. 실제로는 결국 사용자가 원하는 대로 설정할 수 있는 사용자별 플래그가 될 것이며, 논쟁의 핵심은 기본값에 관한 것입니다.
두 개의 동일한 브랜치 병합
버전 관리에 관한 흥미로운 질문 중 하나는 두 개의 브랜치가 동일해 보일 때 그것들을 부모 어느 쪽과도 닮지 않은 버전으로 병합할 수 있는가 하는 것이다.
제가 제안한 접근 방식은 실무에서 얼굴에 폭발적으로 나타날 수 있는 상황들을 많이 방지하지만, 절대 일어나지 않는 것은 아니다. 예를 들어:
Start: XaXbX
Branch 1: XaXbX → aXbX → XX (첫 번째 X를 삭제)
Branch 2: XaXbX → XaXb → XX (마지막 X를 삭제)이를 병합하면, 첫 번째 브랜치는 세 개 중 첫 번째 X를 삭제했고, 두 번째 브랜치는 세 개 중 마지막 X를 삭제했으므로 두 브랜치가 깔끔하게 하나의 X로 병합된다.
이는 실제로 발생하기 어려운 다소 인위적인 예시이며—X가 빈 줄이라 할지라도—반복되는 라인을 신뢰성 있게 일관되게 유지하려고 노력하는 diff 알고리즘을 사용할 경우 특히 그렇다.
만약 이런 일이 발생한다면, 히스토리는 이 특이한 동작을 “얻어냈고”, 충돌 없이 하나의 X로 축소되는 것이 직관에 반할지라도 올바른 처리처럼 보인다. 예제를 직접 살펴보면 그 이유를 이해할 수 있다.
Artisanal Code vs. AI‑Generated Code
마지막 글 하단에 달린 제 댓글에 대해 약간의 논의가 있었습니다. 그 댓글에서는 코드가 artisanal(장인)이라고 했지만 글 자체는 그렇지 않다고 했죠.
AI의 위험은 코드가 spaghetti mess(스파게티 같은 엉망)으로 변해버려서, 아무도 읽어본 적이 없기 때문에 아무도 그 문제를 인식하지 못한다는 점입니다. 장인 코딩—또는 최소한 인간이 감审하는 것—에는 명확한 이점이 있습니다.
AI‑보조 작문의 위험은 훨씬 적습니다. 인간은 글이 무엇을 말하는지 이해할 수 있으니, AI가 쓰는 문장이 … 좀 더 가볍고 흥미는 덜하더라도 괜찮습니다. 참고 자료라면 바로 그 정도가 바람직할 겁니다. 저는 지난 글 전체를 여러 차례 검토하면서 AI에게 잘못된 부분을 알려주고 수정하도록 했습니다. 그 경험은 수동으로 편집하는 것보다 훨씬 좋았습니다. 왜냐하면 AI가 제가 말한 내용을 더 빠르게, 더 적절한 위치에, 그리고 손으로 직접 할 때보다 더 나은 문장으로 삽입해 주었기 때문입니다. 저는 AI가 AI처럼 들리지 않게 톤을 바꾸려 하지 않았습니다. 그게 더 많은 작업을 요구하고, 저는 AI가 그런 글을 쓸 수 있다는 모습을 보여 주면서 AI 종말론을 조롱하는 것이 재미있다고 생각했기 때문이죠. 이 글은 전적으로 artisanally(장인 방식)으로 작성되었습니다. **“artisanal code”**라는 용어가 하나의 흐름이 되길 바랍니다.
No posts