[git] revert시 Conflict가 발생할 수 있는 상황과 이유

Benjamin·2023년 5월 23일
1

git

목록 보기
2/4

git을 사용하다 예상치 못한 문제를 많이 마주쳤었고, 그럴때마다 당황하며 긴장한채로 구글링을 열심히 하기 시작했다.
언제나 해결책은 있었지만, 늘 그 문제를 해결하고나면 당시 문제 해결을 위해 급하게 습득했던 지식들은 휘발유처럼 날아갔다.
그렇게 똑같은 명령어도 다음번에 또 까먹어서 옵션이 주는 차이가 뭔지 또 찾아보고, 해당 명령어의 원리를 깊이 이해하지못한채로 사용하다가 또 다른 실수를 유발하고..
막무가내로 git을 사용했던것같다. 그래서 git을 제대로 공부하다가 revert를 사용하는 중 이해되지 않는 상황(충돌)을 맞닥뜨렸는데, 왜 충돌이 발생한건지 이해되지않아서 꽤 여러 글을 찾아봤다.

그 과정에서 배운것들을 정리한다.

revert

우선 revert가 하는 주 기능은 다음과 같다.

  • 커밋을 삭제하지 않고 되돌리기

즉 취소한 커밋을 남겨두는것이다.

상황

revert를 이용해 여러가지 상황을 실험을 하고있었다.

revert를 사용해보기위해 우선 rev4.txt파일을 아래처럼 작성하고 커밋했다.

<첫번째 커밋>

a 

git add rev4.txt
git commit -m "rev4-1"

<두번째 커밋>

a 
b

git add rev4.txt
git commit -m "rev4-2"

<세번째 커밋>

a 
b
c

git add rev4.txt
git commit -m "rev4-3"

<네번째 커밋>

a 
b
c
d

git add rev4.txt
git commit -m "rev4-4"

이렇게 완성된 rev4.txt파일에서 rev4-2를 revert하기위해 아래 명령어를 실행했다.

(rev4-2의 커밋 해시 = f4ee038119de431af16c68d370a5c288e727190d)
git revert f4ee038119de431af16c68d370a5c288e727190d

그랬더니 다음 이미지와 같이 충돌이 발생했다!

나는 개념적으로 정확하게 이 충돌이 왜 발생한건지 이해하지 못했다.

이제 나와 같이 비슷한 상황에서 일어나는 충돌의 이유와 해결방법에 대해 알아보겠다.

충돌이 발생한 이유

revert는 커밋 이력을 삭제하지 않고, 남겨둔채로 돌아오는 것이다.
그리고 이는 "커밋을 없던 일로 해줄테니 다시 커밋해."라는 명령어이다.

일부 블로그 글을 보면 '이전으로 돌아가기' 기능을 한다고 설명되어있는데, 이는 잘못된 설명이다!

또한 돌아온 시점~돌아오기 전 사이에 무시되는 커밋때문에 충돌이 발생한다고 설명되어있기도한데, 이 또한 잘못된 설명이다.
(돌아온 시점~돌아오기 전 사이에 커밋이 있는 경우에도 충돌이 일어나지 않을 수 있다)

정확히 말하면, revert를 하면 특정 커밋에 해당하는 파일 내용만 없던일로 해준다.
또한 이때 한 파일에서 가로든 세로든 어떤 문자사이의 값에 해당하는 커밋을 revert해줄 경우에 충돌이 발생한다.

아래 예시를 보며 이해해보자.

<상황 1>
t.txt 한 파일에 3번의 커밋을 진행한 후, 두번째 커밋으로 revert한다.

  • 첫번째 커밋
    커밋 메시지 = one
a
b
c
  • 두번째 커밋
    커밋 메시지 = two
a
b
cde
  • 세번째 커밋
    커밋 메시지 = three
abc
b
cde

위처럼 작성한 후, revert two를 실행하면?

충돌이 발생하지않는다!
t.txt파일은 어떻게 수정됐을까?

우선 돌아온 시점~돌아오기 전 사이에 three라는 커밋이 있는데도 충돌이 발생하지않음을 알 수 있다.
또한 three에서 넣은 첫번째 줄의 'bc'는 그대로있고, two때 넣은 세번째 줄의 'de'만 사라졌다.

기존 내용에서 특정 내용을 지웠을 때, 가로든 세로든 빈 여백이 생기지 않으면 충돌이 발생하지 않는것같다.

<상황 2>
t2.txt에 3번의 커밋을 진행한다.

  • 첫번째 커밋
    커밋 메시지 = one
a
  • 두번째 커밋
    커밋 메시지 = two
ab
  • 세번째 커밋
    커밋 메시지 = three
abc

위처럼 작성한 후, revert two를 실행하면?

충돌이 발생한다!
충돌이 발생한 후 파일의 내용은 아래와같다.

b가 지워지면, a와c사이에 공백이 생긴다.
이때 'a c'로 해야할지, 'ac'로 해야할지 컴퓨터가 결정하지 못해서 충돌을 일으키는 것이다.

<상황 3>

3개의 파일을 만들고, 총 5번의 커밋을 진행한다.

  • 첫번째 커밋
    커밋 메시지 = one

t-1.txt

a
  • 두번째 커밋
    커밋 메시지 =

t-1.txt

ab
  • 세번째 커밋
    커밋 메시지 = three

t-1.txt

abc

t-2.txt

d
  • 네번째 커밋
    커밋 메시지 = four

t-1.txt

abc

t-2.txt

d

t-3.txt

e
  • 다섯번째 커밋
    커밋 메시지 = five

t-1.txt

abc

t-2.txt

d

t-3.txt

ef

이렇게 완성한 상황에서 revert two를 실행하면?

Auto merging t-1.txt
CONFLICT (content): Merge conflict in t-1.txt

라는 에러메시지가 발생한다.

t-1.txt을 처리하다가 충돌이 발생한것이다.

t-1 파일 내용을 확인해보면 상황 2와 결과가 동일하다.
우선 이를 해결하기위해 t-1을 원하는 내용으로 수정한 후, git add t-1.txt를 진행한다. (commit은 하지않는다!)

나 같은 경우는 파일 내용을 ac로 하겠다.

git revert --continue를 하면, 나머지 파일에 대한 revert처리는 충돌없이 진행됨을 볼 수 있다.

따라서 결과는 아래와 같다.

t-1.txt

ac

t-2.txt

d

t-3.txt

ef

revert two로 돌아가지만, t-1.txt에서만 충돌이 발생하며, 결과적으로는 t-2.txt, t-3.txt의 파일과 파일내용이 그대로 유지됨을 볼 수 있다.

즉 revert하려는 커밋이 그 이후 커밋의 내용에 영향을 줬다면, (특정 커밋을 revert했을 때, 그 이후 커밋의 작성하는 경우의 수가 여러가지라면) 충돌이 발생한다.

충돌 해결하기

앞에서 <상황>에서 살펴본 네번의 커밋을 진행한 파일의 충돌난 상황을 이어서 살펴보자.

<< HEAD == >> parent of f4ee038 이런 식으로 표시가 되어있다.

간단히 말하면, << HEAD부터 == 까지현재 저장소에 작성되어 있는 소스코드를 의미하고 ==부터 >> parent of f4ee038까지merge대상인 브랜치에 작성되어 있는 소스코드를 의미한다.

어떻게 파일을 저장하고싶은지에 따라 충돌을 직접 수정해주자.

나같은 경우는 처음에 의도했던게 revert rev4-2이므로, rev4-2의 파일내용만 지우는것을 선택했다.

a

c
d

이렇게 변경한 후 git status로 확인해보면 아래와같은 결과를 보여준다.

git status명령어는 add를 하기 전 변경이 일어난 파일을 확인하는 명령어로도 사용되지만 위처럼 merge되지 않은 파일을 확인하는 명령어로도 사용된다.

Unmerged paths:라는 곳을 보면 rev4.txt 파일이 merge되지 않았다고 친절하게 표현해주고 있다.

rev4.txt를 add,commit해준후 다시 git status로 확인해보면

이렇게 잘 반영된것을 볼 수 있다.

git addgit revert --continue가 더 정확한 방법일 것 같다.

충돌 일어나지 않게 revert하기

방법 1. git revert {commit ID} .. {commit ID}

rev4-2까지 reset을 사용하지 않고, 충돌을 피하면서 되돌아가기 위해서는 위 방법을 이용해 한 단계씩 뒤로가는 방법이 있다.
revert rev4-4, revert rev4-3을 순서대로 진행하면된다.

그런데 여러 커밋을 이렇게 직접 돌려야한다면 작업이 번거로워질것이다.

이럴 때는 커밋 순서대로 봤을 때 되돌아갈 커밋..되돌리기 시작할 최근 커밋를 써주면 된다.

즉 위같은 상황에서는, revert rev4-2 .. revert rev4-4명령어를 수행하면된다.
그럼 충돌이 일어나지 않는다.

주의해야 할 점은 범위의 앞에 쓰는 커밋은 취소되지않는것이다.
revert명령어를 쓰지만 예를들어 a커밋..c커밋으로 범위를 준다면 a다음 커밋부터 c커밋까지 없애준다.

참고
https://www.yagom-academy.kr/fac3e1ea-b792-420a-a1e3-d95afca093c1
https://jforj.tistory.com/125

1개의 댓글

comment-user-thumbnail
4일 전

항상 헷갈렸던건데.. 잘보고갑니다

답글 달기