큰 PR을 올리기 전, 지저분한 브랜치를 정리하던 하루

두 주 동안 틈틈이 작업한 기능이 드디어 끝났습니다. 커밋만 서른 개가 넘게 쌓여 있어서, PR을 올리기 전에 정리부터 하기로 했습니다.
에이전트에게 지난 대화 내용 중 초반에 정했던 네이밍 규칙을 다시 물었더니, 정확히 기억은 하는데 답이 나오기까지 묘하게 뜸을 들이는 느낌이 들었습니다.

오래된 대화일수록 답이 느려지는 이유, 웜·콜드 메모리

공식 문서를 찾아보니 이유가 나와 있었습니다. 방금 나눈 대화는 바로 꺼내 쓸 수 있는 자리에 놓아두지만, 한참 전에 나눈 대화는 접근이 조금 더 느린 자리로 옮겨뒀다가 필요할 때 다시 불러오는 방식이었습니다.
자주 쓰는 물건은 책상 위에 두고, 가끔 쓰는 물건은 창고에 넣어뒀다가 필요할 때 꺼내옵니다. 창고에서 꺼내오는 그 잠깐의 시간이 방금 겪은 뜸이었습니다.
최근 대화와 오래된 대화가 서로 다른 속도로 저장되고 불려오는 구조
최근 대화와 오래된 대화가 서로 다른 속도로 저장되고 불려오는 구조
네이밍 규칙을 다시 확인하고 정리를 시작하려는데, 동료가 지난주에 남긴 코멘트에서 낯선 단어 하나가 눈에 들어왔습니다.

대화가 길어지면 자동으로 정리되는 이유, 컴팩션

동료가 PR 코멘트에 "세션 컴팩션 때문에 그거 다시 말해줘야 할 수도 있어"라고 남겨둔 걸 보고 무슨 뜻인지 찾아봤습니다.
text
1Compacting conversation... (summarizing older turns)
대화가 계속 쌓여 한도에 가까워지면, 오래된 부분을 통째로 요약해서 압축하고 최근 내용 위주로 공간을 다시 확보하는 과정이었습니다. 여행 가방에 옷을 눌러 담듯, 다 들고 갈 수는 없으니 부피를 줄여서 최대한 챙겨가는 셈이었습니다.
오래된 대화를 통째로 압축해 요약본으로 바꾸는 과정
오래된 대화를 통째로 압축해 요약본으로 바꾸는 과정
브랜치 정리로 돌아와서, 메인 브랜치가 그새 많이 앞서나가 있는 걸 발견했습니다. 명령어를 실행했더니 도구가 스스로 상황을 설명해줬습니다.

브랜치를 최신 상태 위에 다시 쌓고 싶을 때, 리베이스

bash
1$ git rebase main
2Rebasing (3/12): replaying your commits on top of main
화면에 뜬 설명을 그대로 읽어보니 이해가 됐습니다. 제 커밋들을 그대로 둔 채 메인 브랜치를 합치는 대신, 제 커밋 하나하나를 메인 브랜치의 가장 최신 지점 위에 다시 쌓아 올리는 방식이었습니다.
이미 쌓아둔 블록탑을 바닥부터 다시 쌓는 대신, 더 높아진 바닥 위에 같은 블록들을 순서대로 다시 얹는 것과 비슷합니다. 히스토리가 훨씬 깔끔해졌습니다.
내 커밋을 그대로 둔 채 메인 브랜치의 최신 지점 위에 다시 쌓아 올리는 구조
내 커밋을 그대로 둔 채 메인 브랜치의 최신 지점 위에 다시 쌓아 올리는 구조
리베이스를 마치고 나니, 다른 브랜치에서 만든 버그 수정 커밋 하나가 여기에도 필요하다는 게 생각났습니다. 예전에 비슷한 상황에서 남겨둔 메모를 뒤져봤습니다.

커밋 하나만 쏙 뽑아서 옮기고 싶을 때, 체리픽

bash
1$ git cherry-pick a1b2c3d
메모에 적어둔 대로 커밋 번호 하나만 지정했더니, 그 브랜치의 다른 변경사항은 전혀 안 딸려오고 딱 그 커밋 하나만 지금 브랜치에 그대로 옮겨졌습니다.
나무에 열린 열매 중 딱 하나만 골라 따듯이, 특정 커밋 하나만 지정해서 다른 브랜치로 옮겨오는 방식이었습니다. 전체를 병합할 필요 없이 필요한 수정 하나만 정확히 가져올 수 있었습니다.
다른 브랜치의 다른 변경사항은 두고 커밋 하나만 골라 옮기는 구조
다른 브랜치의 다른 변경사항은 두고 커밋 하나만 골라 옮기는 구조
나뭇가지에서 열매 하나만 골라 따는 모습을 손으로 그려보며 커밋 로그를 살펴보는 장면
커밋을 옮기고 나니, 서른 개 넘는 자잘한 커밋을 하나하나 리뷰어에게 보여주는 게 민망하다는 생각이 들었습니다. 비슷한 고민을 몇 번이나 반복하고서야 늘 이 문제로 정리 시간을 다 쓴다는 걸 깨달았습니다.

자잘한 커밋 서른 개를 하나로 합치고 싶을 때, 스쿼시

bash
1$ git rebase -i HEAD~30
2pick → squash (29개 커밋에 적용)
"오타 수정", "다시 오타 수정", "진짜 마지막 오타 수정"처럼 자잘하게 쌓인 커밋들을 의미 있는 단위 하나로 눌러 합치는 작업이었습니다. 종이 서른 장에 나눠 쓴 메모를 깔끔한 보고서 한 장으로 정리하는 것과 비슷합니다. 리뷰어는 서른 개의 시행착오 대신, 완성된 변경 하나만 보면 됐습니다.
자잘하게 쌓인 커밋 여러 개를 의미 있는 단위 하나로 눌러 합치는 구조
자잘하게 쌓인 커밋 여러 개를 의미 있는 단위 하나로 눌러 합치는 구조
카페 창가, 정리된 커밋 목록과 함께 PR 버튼을 누르기 직전의 노트북 화면
정리를 마치고 올린 PR은 커밋 서른 개가 아니라 다섯 개짜리가 됐습니다. 리뷰어가 보게 될 화면을 기준으로 커밋을 다시 짜는 데 반나절이 걸렸는데, 그 반나절은 리뷰가 짧아지는 것으로 그대로 돌아왔습니다.

자주 묻는 질문

컴팩션되면 이전 세부 내용은 완전히 사라지나요?

완전히 사라지기보다는 요약된 형태로 남습니다. 다만 요약 과정에서 자잘한 조건이 빠질 수 있어서, 중요한 규칙은 대화가 아니라 파일에 남겨두는 게 안전합니다.

리베이스와 병합은 뭐가 다른가요?

병합은 두 히스토리를 합친 지점을 새로 만들어 기록을 그대로 남기고, 리베이스는 히스토리 자체를 재배치해서 마치 처음부터 그렇게 작업한 것처럼 깔끔하게 정리합니다. 팀 컨벤션에 따라 선호가 갈립니다.

스쿼시하면 예전 커밋 기록은 완전히 없어지나요?

합쳐진 브랜치 기준으로는 하나로 보이지만, 원래 브랜치나 로컬 기록에는 한동안 남아있을 수 있습니다. 다만 원격에 이미 푸시된 커밋을 스쿼시할 땐 팀원과 미리 상의하는 게 안전합니다.

<a href="/glossary/code-generation" class="glossary-link" title="AI가 자연어 설명이나 기존 코드 문맥을 바탕으로 프로그래밍 코드를 자동으로 작성하는 기술입니다.">AI 코딩</a> 도구 용어 사전 연작의 10편입니다. 다음 편 예고 · 11편 긴 휴가를 앞두고 인수인계 문서를 쓰던 마지막 하루 핸드오프 · 스크래치패드 · <a href="/glossary/token" class="glossary-link" title="LLM이 텍스트를 인식하고 생성하는 기본 단위로, 문장을 단어·글자보다 작은 의미 조각으로 나눈 것입니다. AI 모델의 연산 비용, 응답 속도, 한 번에 기억할 수 있는 정보량(컨텍스트 윈도우)을 결정하는 핵심 지표입니다.">토큰</a> 버닝 · 디태치드 헤드 · 업스트림 · .gitignore

관련 글