Infra/Git

[220215][Git] 다른 저장소로 PR시, 충돌이 발생한다면? Git rebase와 cherry-pick을 이용해보자

kth990303 2022. 2. 16. 01:26
반응형

우아한테크코스 미션 2단계 제출을 하려던 도중, 아래 현상이 발생했다.

git에서 가장 싫어하는 현상이다...

크흐흑....

PR 날릴 때 충돌이 발생하면, 정말 원인도 찾기 힘들고, 해결하기에도 막막하다.

그동안 git add, push, commit 정도만 사용해왔던 나에게는 더더욱 막막했다.

 

이런 현상은 왜 발생했을까?


원인 발생한 이유

(** 주의: 정확한 원인이 아닐 수 있습니다. **)

 

결론부터 말하자면, PR 커밋과 upstream 커밋의 커밋 해시가 달라서이다.

 

사실 나는 1단계 과정을 거쳐 리팩토링 과정 후 merge됐던 코드와 로컬 코드(2단계를 시작할 때의 내 코드)가 동일했기 때문에, 2단계 시작 전, 따로 github 저장소의 merged된 코드를 fetch + merge하는 과정을 거치지 않았다.

코드가 동일했기 때문에 거기에 이어서 작업하고 PR을 날려도 충돌이 일어나지 않을 것이라 생각했기 때문이다.

 

따라서 별도 작업 없이, 원래 환경에서 step2 브랜치만 생성 후, 리팩토링을 시작했고,

커밋이랑 푸시까지 마친 후에 merge를 하려다가 위 현상이 발생한 것이다.

 

코드가 동일한데 왜 PR 충돌이 일어났을까?

1단계 merge된 코드를 살펴보자.

merge 과정도 해시가 존재한다.

리뷰어 미르가 내 PR을 merge한 과정도 commit 해시가 생긴 것을 볼 수 있다.

 

원래 merged가 이루어지면, step1(PR 날리기까지 요구된 코드) 브랜치를 삭제하고, 새로운 브랜치를 생성하는 것이 원칙인데,

그렇게 하지 않고 그대로 진행하려다가 woowacourse저장소의 kth990303 브랜치와 kth990303저장소의 step2 브랜치의 커밋 해시가 다른 게 존재하여 충돌이 발생한 것...

 

나와 비슷한 사례를 여기서 볼 수 있다.

https://stackoverflow.com/questions/13783023/cant-push-to-git-repository-after-merged-branch

 

Can't push to Git repository after merged branch

I'm working on a Git repository, I had my branch (called jviotti) and my changes were merged to Master. Now, after commiting some changes, I found myself unabled to push to my branch. $ git push o...

stackoverflow.com

확실히 merge가 이루어진 후엔, 추가적인 작업이 필요해보인다.


나의 해결방안

그렇다면 어떻게 해결할 수 있을까?

이 문제로 3시간은 고통받았던 것 같다.

아예 그동안 커밋했던 내역들을 버리고, 다시 처음부터 커밋할까까지 고민할 정도였다...

 

해결방안은 생각보다 간단하다.

 

1. merge된 1단계 미션 clone

일단 커밋 해시가 다르기 때문에 PR 충돌이 일어난 듯하여,

로컬 내 작업폴더를 아예 삭제해버리고,

머지된 1단계 미션을 clone해주었다.

git clone -b kth990303 --single-branch https://github.com/woowacourse/java-racingcar.git

이렇게 하여 woowacourse의 kth990303 브랜치만 따로 clone해주어 가져왔다.

 

1단계 merge 미션 코드를 담은 이 브랜치를 이하 K라고 하겠다.

2단계 작업을 완성한 코드를 담을 브랜치를 step2라고 하겠다.

 

2. 다른 브랜치에 2단계 미션 작업한 코드 fetch 후 rebase

원래는 바로 cherry-pick을 진행하려 했으나,

2단계 미션 코드를 remote add해주지 않아 아래 에러가 발생했다.

 

fatal: bad object 7e4e28d5d8c64e5cd33f4678d12fe69655ad3cc6

 

이것도 원인 찾는 데에 사실 한참 걸리긴 했다...

올바르지 않은 object라는 에러가 뜬다면 remote에 add해주지 않아 git이 인식을 못한다고 생각하면 될 듯하다.

 

따라서 git remote add 작업을 거쳐주자. 그리고 fetch를 진행해주자.

git remote add upstream https://github.com/kth990303/java-racingcar.git

git fetch upstream {가져오고 싶은 브랜치명}
ex) git fetch upstream kth990303

이제 rebase로 2단계 코드들도 로컬의 step2 브랜치에 가져와주자.

rebase란, 한 브랜치의 코드들을 다른 브랜치에 합칠 때 사용하는 명령어인데,

여기서는 upstream의 kth990303 브랜치 코드들을 로컬의 step2에 옮기고 싶을 때 사용하는 것이다.

옮긴 후에 cherry-pick으로 2단계 작업한 커밋 이력들을 덮어쓰기 하려는 것이다.

 

(근데 사실 rebase 과정에서도 이전에는 에러가 엄청 많이 났는데, 이 부분은 명확한 원인과 해결방안을 아직 찾지 못했다. 말 그대로 어쩌다보니 돼버렸다. git rebase --abort (리베이스 취소) 명령어도 정말 많이 사용했고, 

덕분에 이것저것 조작하느라 브랜치가 정말 많이 꼬였고... 정말 당황을 많이 했다.

이후에 같은 상황에서 rebase가 성공했기 때문에, 일단은 rebase가 성공했다는 가정 하에 작성하겠다.) 

 

3. 커밋 이력을 cherry-pick 해주자.

이제 cherry-pick만 해주면 된다.

1단계 merge된 코드 커밋 이력에,

이미 작업한 2단계 코드 커밋 이력을 cherry-pick으로 가져와주자.

즉, K 브랜치에 step2 브랜치 커밋 이력 일부를 가져오려는 것이다.

 

이런 경우는 cherry-pick을 이용하면 된다.

cherry-pick으로 다른 브랜치의 커밋 이력과 커밋된 코드들을 가져올 수 있는데,

다행히 이 과정에서는 충돌이 일어나지 않았다!

git cherry-pick c8804eb418664c~~~~~117513b78691200d7fad // (커밋 해시)

과 같이 하면, 그 커밋 내용과 커밋 이력을 K 브랜치로 가져와준다.

 

모든 커밋을 하나하나 가져오긴 굉장히 많은 노동력이 요구되므로

git cherry-pick a00e12^..b20a23과 같이 ^.. 키워드를 이용해 범위를 지정해주자.

 

a00e12는 old commit, 

b20a23은 latest commit이라 가정한다.

 

^를 빠뜨리면 a00e12를 제외한 그 사이 커밋들만 가져와지니 조심하도록 하자.

나의 경우 ^를 빠뜨렸는지, oldest commit을 놓치고 가져와져서, 이후에 한번 더 cherry-pick을 했기 때문에 커밋 이력 순서가 조금 이상하게 바껴버렸다...


PR 완료!

이렇게 험난한 과정을 거친 후,

드디어 PR을 날릴 수 있었다..!

 

다시는 겪고 싶지 않은 에러였다...

 

git rebase 충돌은 아직 어떻게 해결해야할지, 왜 발생했는지 감이 잘 안잡히는데,

git 공부를 통해 git에 대해 많이 익힌 후에 원인을 알게되면 추가 포스팅 예정이다 :)

반응형