CATEGORY

카테고리 (672)
AI (70)
Language & Specs (260)
FrameWork (36)
Library (20)
App (41)
Git (10)
Build & Dependency (2)
AWS (15)
DataBase (45)
OS (33)
Tool (17)
IT (120)
SEEMINGLY ONLINE

Seemingly
Online

이모저모 방방곡곡 두루두루 개발지식 저장소

RECENT POSTS

Git/Git

git merge 취소 — 충돌 중·병합 후·푸시 후 3가지 상황별 되돌리기

반응형

git merge 취소는 지금 어느 단계인지에 따라 명령이 다릅니다. ① 충돌이 나서 병합이 멈춰 있으면 git merge --abort, ② 병합 커밋까지 만들었지만 아직 push 전이면 git reset --hard ORIG_HEAD, ③ 이미 push해서 남들이 받아 갔으면 git revert -m 1 <병합커밋>입니다. 아래 명령과 출력은 전부 git 2.43.0에서 직접 실행해 확인한 것입니다.

상황 명령 히스토리
충돌로 병합이 멈춰 있음 git merge --abort 아무 흔적 없음
병합 커밋 생성, push 전 git reset --hard ORIG_HEAD 병합 커밋이 사라짐
이미 push함(공유 브랜치) git revert -m 1 <해시> 되돌리는 커밋이 추가됨

1. 충돌 중이라 병합이 멈춰 있을 때 — git merge --abort

git merge를 실행했더니 충돌이 나서 진행이 멈춘 상태입니다.

$ git merge feature
자동 병합: a.txt
충돌 (내용): a.txt에 병합 충돌
자동 병합이 실패했습니다. 충돌을 바로잡고 결과물을 커밋하십시오.

$ git status -sb
## main
UU a.txt

UU는 양쪽에서 수정돼 충돌한 파일이라는 뜻입니다. 이때 파일을 열어 보면 충돌 표시가 들어가 있습니다. 여기서 그냥 그만두고 싶다면:

$ git merge --abort

$ git status -sb
## main

$ cat a.txt
hello from main

충돌 표시가 사라지고 병합 전 상태로 완전히 돌아갑니다. 히스토리에도 아무 흔적이 남지 않습니다. 충돌 파일을 잘못 손댔더라도 --abort 하나면 정리됩니다.

2. 병합 커밋까지 만들었는데 push 전일 때 — reset --hard ORIG_HEAD

병합이 성공해 커밋까지 생긴 상태입니다.

$ git merge feature -m "merge feature"
Merge made by the 'ort' strategy.
 b.txt | 1 +
 1 file changed, 1 insertion(+)

$ git log --oneline -3
c281339 merge feature
bb2b6df main: add c
e4bb4f8 feat: add b

여기서 ORIG_HEAD를 씁니다. git 공식 문서(git-reset)는 이렇게 설명합니다. "pull이나 merge는 언제나 현재 브랜치의 원래 끝점을 ORIG_HEAD에 남겨 두므로, 여기로 hard reset 하면 인덱스와 작업 트리가 그 상태로 돌아온다."

$ git reset --hard ORIG_HEAD
HEAD의 현재 위치는 bb2b6df입니다 main: add c

$ git log --oneline -3
bb2b6df main: add c
f13441c first

$ ls
a.txt  c.txt        ← 병합으로 들어왔던 b.txt가 사라졌다

해시를 직접 적어도 됩니다. git reset --hard HEAD~1도 같은 결과지만, 병합 커밋의 부모가 둘이라 헷갈리기 쉬우니 ORIG_HEAD를 쓰는 편이 안전합니다.

커밋 안 한 작업이 있다면 --hard 대신 --merge

--hard는 작업 중이던 수정까지 지웁니다. 실제로 확인한 차이입니다. 병합 후 a.txt에 "수정중" 한 줄을 추가해 두고(커밋 안 함) 각각 실행해 봤습니다.

# (A) --merge : 커밋 안 한 수정이 살아남는다
$ git reset --merge ORIG_HEAD
$ git status -s
 M a.txt
$ cat a.txt
line1
수정중          ← 유지됨

# (B) --hard : 그냥 날아간다
$ git reset --hard ORIG_HEAD
$ git status -s
                ← 깨끗함(= 수정분 소실)
$ cat a.txt
line1           ← "수정중"이 사라짐

공식 문서의 --merge 설명도 같습니다. "인덱스를 리셋하고 <commit>과 HEAD 사이에서 달라진 파일만 갱신하되, 인덱스와 작업 트리 사이에서 달라진 것(= 아직 add 안 한 변경)은 그대로 둔다."

⚠️ git reset --hard는 커밋하지 않은 변경을 복구할 방법 없이 지웁니다. 작업 중인 게 있으면 먼저 git stash로 치워 두거나 --merge를 쓰세요. (이미 만든 커밋은 git reflog로 되찾을 수 있지만, 커밋 안 한 변경은 못 되찾습니다)

3. 이미 push했을 때 — git revert -m 1

공유 브랜치(예: main)에 push한 병합은 reset으로 지우면 안 됩니다. 남의 히스토리와 어긋나 강제 push가 필요해지고, 그 사이 다른 사람이 받은 커밋이 꼬입니다. 이때는 "되돌리는 커밋을 새로 쌓는" revert를 씁니다.

병합 커밋은 부모가 둘이라 어느 쪽을 남길지 알려 줘야 합니다. 그게 -m(mainline) 옵션이고, 보통 -m 1이 병합을 받은 쪽 브랜치(예: main)입니다.

$ git revert -m 1 <병합커밋해시>
 1 file changed, 1 deletion(-)
 delete mode 100644 b.txt

$ git log --oneline -3
f97740d Revert "merge feature"
d78a998 merge feature
0e803af main: add c

병합 커밋은 히스토리에 남고, 그 변경을 지우는 커밋이 하나 더 쌓입니다. 이 상태를 push하면 됩니다.

함정 — revert한 브랜치는 다시 merge해도 안 들어온다

이게 가장 많이 당하는 부분입니다. 위 상태에서 feature를 다시 병합해 봤습니다.

$ git merge feature
이미 업데이트 상태입니다.

$ ls
a.txt  c.txt        ← b.txt가 여전히 없다

git 입장에서 feature의 커밋은 이미 병합된 조상이라 새로 가져올 게 없고, 내용은 revert로 지워진 상태입니다. 공식 문서(git-revert)도 경고합니다. "병합 커밋을 revert하면 그 병합이 가져온 트리 변경을 앞으로도 원하지 않겠다고 선언하는 것이다. 그래서 이후의 병합은 이미 revert된 병합의 조상이 아닌 커밋의 변경만 가져온다."

해결은 revert를 다시 revert하는 것입니다.

$ git revert <Revert 커밋 해시>
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt

$ ls
a.txt  b.txt  c.txt   ← 돌아왔다

$ git log --oneline -3
59d8b6a Reapply "merge feature"
f97740d Revert "merge feature"
d78a998 merge feature
💡 그래서 "언젠가 다시 병합할 브랜치"라면 revert 대신 병합을 되돌리지 말고 그 위에서 문제만 고치는 편이 깔끔할 때가 많습니다. revert는 "이 병합은 영영 안 쓴다"는 선언에 가깝습니다.

4. 잘못 지웠을 때 되찾기 — git reflog

reset으로 날린 커밋도 커밋된 것이라면 되찾을 수 있습니다. git reflog는 HEAD가 지나온 자리를 전부 기록합니다.

$ git reflog -6
59d8b6a HEAD@{0}: revert: Reapply "merge feature"
f97740d HEAD@{1}: revert: Revert "merge feature"
d78a998 HEAD@{2}: merge feature: Merge made by the 'ort' strategy.
0e803af HEAD@{3}: commit: main: add c
7c7e859 HEAD@{4}: checkout: moving from feature to main
9acae35 HEAD@{5}: commit: feat: add b

# 병합 직후로 되돌아가기
$ git reset --hard d78a998

다만 앞서 말했듯 커밋하지 않은 변경은 reflog에도 없습니다. 이것이 --hard 전에 git stash를 권하는 이유입니다.

자주 묻는 질문 (FAQ)

Q. -m 1과 -m 2 중 뭘 쓰나요?
-m 1은 병합 커밋의 첫 번째 부모, 즉 병합을 받은 쪽(main에서 git merge feature를 했다면 main)입니다. 대개 -m 1이 맞습니다. git log --oneline --graph로 부모 순서를 확인하고 정하면 확실합니다.

Q. git merge --abort가 "There is no merge to abort"라고 합니다.
병합이 이미 커밋까지 끝났다는 뜻입니다. 그때는 2번(reset) 또는 3번(revert)으로 갑니다.

Q. push 전인데 revert를 써도 되나요?
동작은 합니다. 다만 히스토리에 "병합 + 되돌림" 두 커밋이 남습니다. 혼자만의 브랜치라면 reset으로 깔끔하게 지우는 쪽이 낫습니다.

Q. git reset --merge와 --hard는 언제 갈리나요?
커밋 안 한 변경이 있느냐입니다. 있으면 --merge가 그것을 지켜 줍니다. 단 공식 문서 설명대로, 되돌릴 대상 커밋과 인덱스가 다른 파일에 스테이징 안 된 변경이 있으면 reset이 중단됩니다.

마무리

정리하면 충돌 중이면 git merge --abort, push 전이면 git reset --hard ORIG_HEAD(작업 중이면 --merge), push 후면 git revert -m 1입니다. 그리고 revert한 브랜치는 다시 merge해도 안 들어온다는 점, 이때는 revert를 한 번 더 revert한다는 점을 기억해 두면 나중에 헤매지 않습니다.


📚 참고 출처 (2026년 7월 21일 확인 · 명령과 출력은 git 2.43.0에서 직접 실행)
· git-reset 공식 문서
· git-revert 공식 문서
· git-merge 공식 문서

반응형

COMMENTS