- Published on
Git rebase 충돌 자동해결 - rerere 실전 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서로 다른 브랜치에서 같은 파일을 계속 건드리는 팀이라면 git rebase 는 생산성을 올려주지만, 반복되는 충돌 해결이 발목을 잡습니다. 특히 장수 브랜치(long-lived branch)나 리팩터링 브랜치에서 같은 구간이 여러 번 충돌하면, 해결 자체보다 “또 같은 충돌을 또 푸는” 시간이 더 커집니다.
이때 Git의 숨은 기능인 rerere 를 켜면, 한 번 해결한 충돌을 Git이 학습해두었다가 다음에 같은 형태의 충돌이 발생했을 때 자동으로 재적용해줍니다. 이름 그대로 reuse recorded resolution 입니다.
아래에서는 rerere 를 rebase 흐름에 실제로 녹여서 쓰는 방법, 어떤 경우에 잘 먹히고 어떤 경우에 오히려 위험한지, 팀 적용 시 체크리스트까지 다룹니다.
참고: rebase 후 강제푸시로 PR이 꼬였을 때의 복구는 별도 글에서 다뤘습니다. 충돌을 줄여도 강제푸시는 여전히 주의가 필요합니다: Git rebase 후 강제푸시로 PR 깨졌을 때 복구법
rerere가 해결하는 문제: “반복 충돌”
rebase 중 충돌이 나는 상황은 흔하지만, rerere 가 특히 효과적인 케이스는 다음처럼 같은 충돌 패턴이 여러 번 반복될 때입니다.
- 메인 브랜치에서 파일 포맷터/린터 적용으로 대규모 수정이 있었고, 작업 브랜치도 같은 파일을 수정 중
- 리팩터링 브랜치에서 함수 시그니처를 바꾸는 동안, 메인에서도 비슷한 위치를 계속 수정
- 체리픽(cherry-pick)이나
rebase --onto를 여러 번 반복하며 같은 커밋들을 다른 기반 위로 옮김 - 한 번의 rebase에 충돌이 여러 커밋에 걸쳐 연쇄적으로 발생
핵심은 rerere 가 “파일 전체가 동일”해야만 적용되는 것이 아니라, 충돌 난 덩어리(hunk)의 before/after 패턴을 기억한다는 점입니다. 그래서 커밋 해시가 바뀌는 rebase에서도 재사용이 가능합니다.
rerere 동작 원리 (알아두면 안전해짐)
rerere 는 충돌이 발생했을 때 다음을 수행합니다.
- 충돌 난 파일의 충돌 구간을 감지하고, 그 충돌 상태를 내부 DB에 기록
- 사용자가 충돌을 해결하고
git add하면 “해결 결과”를 함께 기록 - 이후 동일하거나 유사한 충돌이 다시 나타나면, 기록된 해결 결과를 자동 적용
기록은 보통 .git/rr-cache/ 아래에 쌓입니다. 이 캐시는 기본적으로 로컬 저장소 전용이며, 원하면 공유할 수도 있지만(뒤에서 설명) 팀에 무작정 공유하면 위험할 수 있습니다.
rerere 활성화: 전역 설정 vs 저장소 설정
전역으로 켜기 (추천)
자주 rebase를 한다면 전역 설정이 편합니다.
git config --global rerere.enabled true
자동으로 스테이징까지 해주진 않습니다. rerere 는 “해결안을 적용”할 뿐이고, 최종 판단은 여전히 개발자가 합니다.
특정 저장소에서만 켜기
레거시 저장소나 민감한 리포지토리에서만 실험하고 싶다면:
git config rerere.enabled true
적용 여부 확인
git config --get rerere.enabled
실전 시나리오: rebase 중 같은 충돌을 3번 푸는 상황
상황 만들기(예시)
main에서config.js의 기본값을 바꿈feature에서 같은 키를 다른 방식으로 바꿈feature를main위로rebase하면서 충돌 발생
충돌이 나는 순간 rerere 는 충돌 상태를 기록합니다.
1) 첫 번째 충돌: 사람이 한 번만 제대로 푼다
git checkout feature
git rebase main
충돌이 나면 파일에는 충돌 마커가 생깁니다. MDX 환경에서는 본문에 마커를 그대로 쓰면 위험하니, 예시는 인라인 코드로만 표현합니다.
- 충돌 마커 예시:
<<<<<<<=======>>>>>>>
이때 사람이 올바르게 수정합니다. 그리고 스테이징:
git add config.js
git rebase --continue
이 순간 rerere 는 “이 충돌 패턴은 이렇게 해결했다”를 저장합니다.
2) 두 번째 충돌: rerere가 자동 적용
rebase가 다음 커밋으로 넘어가며 비슷한 충돌이 다시 터지면, Git 출력에 보통 다음과 같은 힌트가 섞여 나옵니다.
Resolved 'config.js' using previous resolution.같은 문구
그리고 파일을 열어보면 이미 해결안이 반영되어 있을 가능성이 큽니다. 이때도 습관적으로 검토 후 스테이징합니다.
git status
# 충돌 마커가 사라졌는지 확인
git add config.js
git rebase --continue
3) 세 번째 충돌도 동일
같은 패턴이면 계속 재사용됩니다. 결과적으로 “한 번만 제대로 풀고, 나머지는 검토 후 진행”으로 바뀝니다.
rerere를 쓰면 rebase가 빨라지는 지점
rerere 가 진짜로 시간을 줄여주는 포인트는 다음입니다.
- 충돌 해결 자체보다, 해결 후 테스트/검증에 시간을 쓰게 해줌
- rebase 도중 중단과 재시도가 잦은 팀에서, 같은 충돌을 다시 만나도 부담이 줄어듦
- 대규모 포맷 변경(예: 프리티어 적용) 이후 장기간에 걸친 브랜치 정리 작업에서 효과가 큼
자주 쓰는 rerere 관련 명령
rerere 상태 확인
git rerere status
현재 충돌 파일 중 rerere 가 어떤 파일을 추적하고 있는지 확인할 때 유용합니다.
기록된 해결안을 적용(수동 트리거)
자동 적용이 애매하게 동작했거나, 중간에 설정을 켰다면:
git rerere
기록된 해결안 정리
캐시가 너무 쌓이거나, 잘못 학습된 해결안을 버리고 싶을 수 있습니다.
git rerere gc
또는 특정 충돌 기록을 지우려면 .git/rr-cache/ 를 직접 만지게 되는데, 이건 숙련자 영역이라 팀 규칙 없이 권장하진 않습니다.
팀 적용 팁: “개인 생산성”으로 시작하되, 공유는 신중하게
1) 개인 로컬에서만 써도 가치가 큼
대부분의 경우 rerere 는 개인 개발환경에서만 켜도 충분합니다. 같은 사람이 같은 브랜치에서 rebase를 반복할 때 효과가 바로 납니다.
2) rr-cache 공유는 장단이 뚜렷함
이론적으로 .git/rr-cache/ 를 공유하면 팀 전체가 같은 충돌 해결안을 재사용할 수 있습니다. 하지만 다음 리스크가 있습니다.
- 누군가의 “임시 해결”이 팀 전체에 전파될 수 있음
- 충돌 해결은 종종 문맥 의존적이라, 자동 적용이 오히려 버그를 숨길 수 있음
- 보안/정책상 특정 코드 조합이 로컬에만 있어야 하는 경우도 있음
그래서 rr-cache 공유는 다음 조건에서만 검토하는 편이 안전합니다.
- 충돌이 거의 항상 기계적으로 동일한 규칙(예: 포맷팅, 정렬, import 순서)으로 해결됨
- 코드리뷰에서 충돌 해결 커밋을 엄격하게 검증하고 있음
- 적용 범위를 제한(특정 디렉터리, 특정 파일 유형)할 수 있는 워크플로우가 있음
주의사항: rerere가 “정답”을 보장하진 않는다
rerere 는 충돌 해결을 자동화하지만, 의미적 정합성까지 보장하지 않습니다. 아래 케이스는 특히 조심해야 합니다.
1) 같은 충돌처럼 보여도 의도가 달라진 경우
예를 들어 같은 함수 블록이 충돌했지만, 이번엔 요구사항이 바뀌어 다른 로직이 들어가야 하는데 예전 해결안이 자동 적용되면 위험합니다.
대응:
- 자동 적용되더라도 항상
git diff로 확인 - rebase 중에는 최소한 충돌 파일만이라도 수동 리뷰
git diff
2) 테스트가 없으면 “조용히” 깨진다
충돌 해결은 컴파일 에러만 안 나면 지나가기 쉽습니다. rerere 를 쓰면 더 빨리 지나가므로, 테스트가 더 중요해집니다.
- rebase 완료 후 최소 스모크 테스트
- 가능하면 CI가 PR 기준으로 강제되도록
CI가 느려서 자주 우회하게 된다면, 빌드 캐시/레이어 최적화도 같이 보세요: Jenkins 빌드가 갑자기 느릴 때 Docker 레이어 캐시 복구
3) 바이너리/자동생성 파일 충돌
락파일, 생성 산출물, 바이너리 파일은 충돌 해결이 “정해진 규칙”이 아니면 rerere 가 오히려 혼란을 줄 수 있습니다.
- 예:
Podfile.lock같은 파일은 충돌 자체를 줄이는 전략이 더 중요할 때가 많습니다
관련해서 iOS 의존성 락파일 충돌은 별도 가이드가 있습니다: Flutter iOS 빌드 Podfile.lock 충돌 해결 가이드
rebase와 함께 쓰면 좋은 옵션/습관
1) rebase.autostash 로 작업 트리 정리
rebase 전에 로컬 변경사항이 있으면 흐름이 끊깁니다.
git config --global rebase.autostash true
2) 충돌을 줄이는 방향의 rebase 전략
- 기능 브랜치를 너무 오래 끌지 않기
- 대규모 포맷 변경은 한 번에, 그리고 빠르게 메인에 반영
- 리팩터링 브랜치는 작은 단위로 쪼개기
rerere 는 “충돌을 없애는 도구”가 아니라 “반복 충돌 비용을 낮추는 도구”입니다. 충돌 자체를 줄이는 전략과 같이 가야 효과가 극대화됩니다.
문제 해결 체크리스트
rerere가 적용되지 않는 것 같다
- 설정 확인:
git config --get rerere.enabled - 충돌을 해결한 뒤 반드시
git add를 했는지 확인 - 동일 파일이라도 충돌 패턴이 달라지면 재사용이 안 될 수 있음
자동 적용됐는데도 여전히 충돌 마커가 남아있다
rerere는 일부 hunk만 적용하고 나머지는 남길 수 있음- 남은 부분을 수동 해결 후 다시
git add
잘못된 해결을 학습한 것 같다
- 해당 충돌을 다시 올바르게 해결하고
git add하면 새로운 해결안으로 덮어쓰는 경우가 많음 - 그래도 계속 잘못 적용되면
git rerere gc로 정리 후 재시도
마무리
git rerere 는 “rebase 충돌은 어쩔 수 없다”는 체념을 “한 번만 제대로 풀자”로 바꿔주는 기능입니다. 특히 반복 충돌이 잦은 저장소라면 체감 효과가 큽니다.
정리하면:
git config --global rerere.enabled true로 켜고- 첫 충돌을 신중히 해결한 뒤
git add로 학습시키고 - 이후 자동 적용된 결과는
git diff와 테스트로 검증
이 3가지만 지켜도 rebase 스트레스가 확 줄어듭니다.