[Git] 4.3. 묵은 커밋을 새 커밋으로 이력 조작하기 : Rebase
카테고리: Git
태그: Git, GitHub, GUI, Rebase, SourceTree
Rebase 기능의 필요성
[Orchemi/iTshirt] Repo의 Contributor가 된 KKKachi가 티셔츠 찜하기 기능을 코딩한 뒤, Pull request를 보냈는데 코드 충돌이 난 경우, 내 브랜치로 먼저 병합한 후 충돌을 해결한 뒤 다시 Pull request를 보내야 할 것이다. 하지만 이 경우 내가 추가한 코드 이외에도 충돌 해결을 위한 병합 커밋이 생기는데 이를 피하기 위해서 사용한다.
Conflict 상황 만들기
가. KKKachi의 수정
iTshirt의 원본저장소와 KKKachi의 원격저장소에서 같은 코드를 고쳐 병합 시 충돌을 만들어본다.
먼저 Fork해온 KKKachi의 iTshirt 폴더의 like.md 파일에 ‘2. 찜하기 기능’을 추가해 수정한다.
Sourcetree에서 KKKachi의 계정으로 commit하고 GitHub에 push해 원격저장소에도 반영한다.
KKKachi GitHub의 [KKKachi/iTshirt]의 like.md 파일을 보면 잘 반영된 것을 알 수 있다.
나. 원본저장소의 수정
Sourcetree의 [iTshirt-cat]가 있는 [Git] 탭을 살펴본다. KKKachi의 수정, 그리고 Approve하여 merge된 내역이 반영되지 않은 local history이기 때문에 [origin/master]을 pull해서 최신 history로 업데이트해야 한다.
‘Merge pull request #2’ 커밋이 추가된 것을 확인할 수 있다. 원본저장소 주인인 cat의 로컬 파일을 수정하여 충돌을 만들어야한다. 일단 Sourcetree의 계정을 cat으로 바꿔준다.
[도구] -> [옵션] -> [인증] -> Orchemi@github.com -> [설정 초기화] 과정을 통해 default account로 바꿔주었다.
Git 폴더의 like.md에 ‘싫어요 기능’을 추가했다고 간주하여 위처럼 수정해준다.
commit message를 ‘싫어요 기능 추가’로 적고 commit & push 해준다.
[iTshirt-cat/REAEME.md] 파일에 위와 같이 개발자 목록에 ‘3. 까치’ 를 추가해주었다.
commit message를 ‘개발자 목록에 까치 추가’로 하고 sourcetree를 통해 commit & push 해주었다.
다. 까치의 pull request 시도
까치는 원본저장소에 어떤 커밋이 발생했는지 모르는 상황이다. 티셔츠 찜하기 기능을 원본저장소에 pull request를 보내려고 한다.
[KKKachi/iTshirt] 의 [Pull requests] 탭에서 [Create pull request]로 pull request를 시도하려 하였으나, 위처럼 ‘Can’t automatically merge’라는 빨간 메시지가 뜨는 것을 볼 수 있다. 이는 코드가 충돌했음을 의미하는 메시지이다.
원격저장소의 추가
가. 여러 원격저장소 히스토리 보기 : Add remote
방법은 다음과 같다. KKKachi의 remote branch로 [KKKachi/origin/master]가 있었다면, [Orchemi/origin/master] branch를 remote branch로 추가해 동시에 추적하도록 만든다.
다시 까치가 되었다고 생각하고, Sourcetree의 계정을 까치의 GitHub 계정으로 바꿔준다. 이제는 익숙해진 과정이다.
[iTshirt-KKKachi] 탭으로 온 뒤, 상단 탭의 [저장소] -> [원격 저장소 추가]를 통해 원격저장소를 추가한다.
현재 원격저장소는 [origin]만 있는 것을 알 수 있다. 처음에 git bash에서 ‘$ git remote add origin 원격저장소주소’ 를 입력하면서 등록한 원격저장소이다. 모달창의 왼쪽 하단 [추가] 버튼을 누르면 아래와 같이 추가할 원격저장소 정보를 입력할 수 있다. 정보를 다음과 같이 입력해보자.
위의 정보를 입력한 뒤 [확인] 버튼을 누르면
[upstream] 이라는 이름의 원격저장소가 추가된 것을 알 수 있다.
나. [upstream] branch Fetch하기
신설된 remote branch에는 아무 정보가 없다. 좌측 사이드바의 [원격] 탭의 [upstream] branch를 우클릭한 뒤, [upstream 에서 가져오기] 메뉴를 선택하면 [upstream] 원본저장소에 있는 커밋 히스토리를 받아온다. 이를 Fetch(패치)라고 한다.
※ fetch는 해당하는 원본저장소의 최신 이력을 업데이트하는 것이고, 이력만 가져오는 것이기 때문에 내 코드와는 무관하다. 반면 pull은 최신 코드를 내 코드에 반영하는 것이다. history를 확인하려다가 pull을 할 경우, 작성중이던 코드를 잃을 수 있으니 주의해야 한다.
Fetch를 마치면 이렇게 [origin] branch처럼 [upstream] branch도 [master]와 [feature] branch가 생기고 각각의 history를 확인할 수 있다. 쉽게 말해 ‘새로고침’과 같은 기능이다.
그래프를 보면 ‘Merge pull request #2’ 기준으로 까치는 찜하기 기능을 추가했고, cat은 싫어요 기능과 개발자 목록에 너구리를 추가한 커밋 이력을 볼 수 있다. 즉, 까치는 너무 예전 코드를 기점으로 새 코드를 추가한 것이다.
충돌을 해결하는 방법
가. Merge해서 재도전
1. 현재 커밋과 병합하고 싶은 커밋을 미리 내 branch에서 병합한다.
2. 이를 pull request로 보낸다.
이 방법은 pull request를 가능하게는 하겠지만 병합 커밋을 만들기 때문에 나의 pull request에 불필요한 이력이 남는다는 문제점이 있다.
나. 묵은 커밋을 방금 한 커밋처럼 만드는 : Rebase
1. 예전 커밋을 기준으로 이어나가던 커밋들을 분기점을 기준으로 똑 떼어낸다.
2. 최신 커밋을 베이스로 커밋들을 이어준다.
[iTshirt-KKKachi]의 Sourcetree를 살펴보자. 현재 커밋 ‘찜하기 기능 추가’의 베이스 커밋이 ‘좋아요 기능’인 것을 확인할 수 있다. 이 베이스 커밋을 [upstream/master] branch의 최신 커밋인 ‘개발자 목록에 너구리 추가’인 것처럼 이력을 조작하겠다.
새롭게 베이스 커밋으로 지정하고 싶은 커밋을 우클릭 한 뒤, [재배치]를 클릭한다. 여기서 새 베이스 커밋은 ‘개발자 목록에 까치 추가’ 커밋이다.
위와 같이 rebase에 대한 경고창이 뜬다. 이는 매우 의미 있는 경고이다. rebase는 히스토리를 강제로 조작하는 것이기 때문에 다른 사람이 만약 이 history를 보고 있다면 완전히 꼬이게 된다. 때문에 rebase는 반드시 혼자 쓰는 branch에서 수행해야 한다. 보통 기능 구현을 하는 branch는 혼자 쓰기 때문에 편하게 rebase해도 된다.
까치와 고양이가 같은 코드를 고쳤기 때문에 경고 팝업 창이 뜬다. 중복되는 수정 내용을 수정해주면 된다.
이렇게 스테이지 위와 아래에 동시에 like.md 파일이 있는 것을 확인할 수 있다. 충돌을 일으킨 문제의 파일이다.
IntelliJ에서 [iTshirt-KKKachi] 폴더의 like.md 파일을 열어보면 어떤 부분에서 충돌했는지 표시가 되었다.
다음과 같이 모든 수정 내용을 반영할 수 있도록 수정한 뒤 파일을 저장하고 Sourcetree에서 확인하면 된다.
스테이지에 올리지 않은 파일 목록에서 like.md 파일의 우측에 [+] 버튼을 클릭하면 충돌 없이 정상적으로 스테이지에 올라가는 것을 확인할 수 있다.
충돌을 해결했으니 다시 Rebase 과정을 밟으면 된다. Sourcetree 상단 탭의 [액션] -> [재배치 계속] 버튼을 누른다.
성공적으로 rebase를 마쳤다.
그래프를 보면 ‘찜하기 기능 추가’ 커밋의 베이스 커밋이 [upstream/master]의 ‘개발자 목록에 까치 추가’로 바뀐 것을 볼 수 있다.
그런데 local branch인 [master] branch는 정상적으로 rebase가 되었지만, remote branch인 [origin/master] branch는 아직도 옛날 커밋인 ‘좋아요 기능 추가’를 베이스로 하고 있다. 이를 remote branch에도 반영하려면 push를 해야 하는데 rebase는 이력을 조작하는 행위이기 때문에, 일반 push로는 수행할 수 없다.
그렇다면 어떻게 해야할까?
이력을 조작하는 경우 강제 푸시라는 특수한 옵션을 붙여 푸시해야 한다.
강제 푸시
Sourcetree의 상단 탭의 [도구 - 옵션 - Git]에서 [강제 푸시 가능]을 체크한다. 이것을 체크하면 푸시할 때 강제 푸시 체크박스를 볼 수 있다.
푸시 탭에서 강제 푸시를 체크하고 push한다.
그래프가 분기 없이 깔끔해진 것을 볼 수 있다.
Pull request 재도전
KKKachi의 GitHub [iTshirt] repo에서 Pull request에 재도전한다.
충돌되는 파일이 없어 Able to merge 라는 반가운 메시지가 뜨는 것을 볼 수 있다.
적당한 Pull request 제목과 메시지를 적고 [Create pull request]를 클릭해서 보낸다.
정상적으로 pull request가 올라간 것을 확인할 수 있다.
Pull request Approve/Merge
cat(Orchemi)의 GitHub로 재접속해 Pull request를 처리해보자.
잘 올라와있는 KKKachi의 pull request이다.
Pull request를 검토, 병합한 뒤 적절한 comment를 입력해 남긴다.
Reference
- 팀 개발을 위한 Git GitHub 시작하기, 한빛미디어, 정호영,진유림
댓글남기기