rebase 충돌에서 --ours를 골랐는데 남의 코드가 남은 이유
문제 발생
리베이스 중 충돌이 나서 "내 코드를 남기자"는 생각으로 --ours를 골랐는데, 정작 남은 것은 상대 브랜치의 코드였습니다.
$ git rebase main
CONFLICT (content): Merge conflict in src/search.ts
$ git checkout --ours src/search.ts # 내 코드가 남을 줄 알았음머지에서는 같은 옵션이 의도대로 동작했기 때문에 더 헷갈렸습니다.
원인 분석
ours/theirs는 "브랜치 이름"이 아니라 지금 체크아웃되어 있는 쪽 / 지금 적용하려는 쪽을 가리키는 상대적인 이름입니다. 그래서 리베이스에서는 머지와 반대가 됩니다.
공식 문서가 직접 설명합니다 — 리베이스는 작업 브랜치의 커밋을 <upstream> 위에 하나씩 다시 재생(replay) 하는 방식이라, 충돌이 났을 때 ours로 보고되는 쪽은 <upstream>에서 시작해 지금까지 재생된 결과이고, theirs는 작업 브랜치입니다. 즉 양쪽이 뒤바뀝니다.
정리하면 이렇습니다.
| 상황 | ours | theirs |
|---|---|---|
git merge feature (main에서) | main (지금 체크아웃한 쪽) | feature |
git rebase main (feature에서) | main + 지금까지 재생된 커밋 | feature (지금 적용 중인 커밋) |
리베이스에서 체크아웃되어 있는 것은 내 브랜치가 아니라 재생 대상이 되는 토대입니다. 내 커밋은 그 위에 하나씩 "가져와 붙이는" 쪽이라 theirs가 됩니다.
cherry-pick과 revert도 같은 구조입니다 — 지금 HEAD가 ours, 가져오는 커밋이 theirs입니다.
해결 방안
- 브랜치 이름으로 생각하지 말고 "지금 무엇을 어디에 붙이는 중인가"로 생각합니다. 리베이스 중 내 커밋을 남기고 싶으면
--theirs입니다.
git checkout --theirs src/search.ts # 리베이스 중 = 내 브랜치 쪽
git add src/search.ts
git rebase --continue- 헷갈리면 실제 커밋을 확인합니다. 리베이스 중에는 지금 적용 중인 커밋이
REBASE_HEAD로 잡힙니다.
git log -1 --oneline REBASE_HEAD # 지금 재생 중인 내 커밋
git log -1 --oneline HEAD # 지금까지 쌓인 토대- 한쪽을 통째로 고르지 말고 3-way 마커를 봅니다. 대부분의 충돌은 양쪽 변경을 모두 살려야 합니다.
diff3스타일을 켜면 공통 조상까지 보여서 판단이 쉬워집니다.
git config --global merge.conflictStyle zdiff3- 잘못 골랐으면 그 파일만 되돌립니다. 리베이스 전체를 취소할 필요는 없습니다.
git checkout --merge src/search.ts # 충돌 상태로 되돌리기
git rebase --abort # 정말 처음부터 다시- 같은 충돌이 반복되면
rerere를 켭니다. 한 번 해결한 충돌의 해법을 기록해 두었다가 같은 충돌에 자동으로 적용합니다. 긴 브랜치를 여러 번 리베이스할 때 효과가 큽니다.
git config --global rerere.enabled true
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.