부스트캠프 그룹 프로젝트를 하면서 깃과 관련된 이슈가 발생했다. 예상하지 못한 문제라 굉장히 당황스러웠고, 어찌저찌 수습은 했지만 깔끔하게 해결되지는 않았다.
그 동안 git을 잘은 몰라도, pull, merge 등의 개념은 알고 있다고 생각했다. 또 부스트캠프에서 fetch, rebase도 사용하게 돼서 어떤 방식으로 동작하는지 이해하고 있다고도 생각했다. 그런데 전혀 아니었다. 문제가 발생했을 때 왜 발생한 것인지도 잘 알지 못했고, 그래서 해결도 제대로 하지 못했다. 그래서 발생한 문제와 관련된 기본 개념들을 학습해보고자 한다.
git remote
를 통해 프로젝트에 등록된 리모트 저장소 확인 가능하며, 리모트 저장소의 단축 이름을 보여준다. 저장소를 clone하면 origin
이라는 리모트 저장소가 자동으로 등록된다.
→ origin
은 github에 생성된 레포지토리임을 알 수 있다.
git remote
로 확인하면 origin
을 볼 수 있다.-v
옵션을 통해 단축 이름 + URL 확인 가능리모트 저장소는 여러 개가 될 수 있다. 저장소를 추가하려면 git remote add [remote 별칭] [url]
명령어를 사용하면 된다.
로컬에는 없지만 리모트 저장소에는 있는 모든 데이터를 가져오는 명령어
git fetch [remote 별칭] [refspec ...]
데이터를 가져오기만 할 뿐 따로 merge하지는 않아서 이후 수동으로 merge해야 한다. → 리모트 저장소에 있는 파일이 merge되기 전까지는 로컬 저장소에 반영되지 않는다.
fetch 후 git diff
명령어를 통해 리모트 저장소와 로컬 저장소의 차이를 확인할 수 있다. 따라서 fetch하여 변경 사항을 확인한 후 pull 하는 것이 안전하다.
github에 test-branch를 만들고 이를 로컬에 clone 받았다. 이후 test-branch에 fetch_test.txt 파일을 만들고 커밋했다. 그 다음 fetch를 진행해보자.
git fetch origin
을 하면 이처럼 fetch_test.txt 파일이 생기지는 않지만, 변경 사항과 관련된 object와 fetch_test.txt를 작성한 커밋 내역이 생성된 것을 확인할 수 있다.
또 git diff HEAD [변경된 커밋]
, git diff [변경된 커밋]
을 통해 달라진 점을 확인할 수 있다.
이 상태에서 merge 또는 rebase를 하면 커밋이 반영된다.
리모트 저장소에서 데이터를 가져오고(git fetch
) 자동으로 로컬 브랜치와 merge하는 명령어
실제로 git pull은 git fetch와 git merge를 순서대로 진행한 것이다.
git pull [options] [repository] [refspec ...]
git pull origin master
는 origin이라는 리모트 저장소의 master 브랜치를 pull 하겠다는 의미git pull
은 git fetch
후, 갈라진 브랜치를 일치시키기 위해 git rebase
또는 git merge
를 호출한다. fetch 후 항상 rebase하도록 하고 싶으면 git config pull.rebase true
명령어를 통해 설정하면 된다.
한 브랜치에서 다른 브랜치로 합치는 방법 중 하나
git merge [병합할 브랜치]
fast-forward 동작 확인
fast-forward 동작을 보기 위해 test-branch에서 fast-forward 브랜치를 파고 파일을 생성해보자.
이후 add 및 commit 하고 그래프를 보면 다음과 같다.
이제 test-branch에서 git merge fast-forward
로 머지하면 메시지에 Fast-forward라고 나오며, 새로운 커밋이 생성되지 않고 test-branch의 head만 이동한 것을 볼 수 있다.
3-way-merge 동작 확인
test-branch에서 파일을 하나 만들고 커밋하고, one-branch를 만들어서 그 내부에서도 파일을 만들고 커밋했다. 이후 이 둘을 머지하니 다음과 같이 새로운 커밋이 만들어지면서 머지된 것을 확인할 수 있다.
메시지에도 Fast-Forward가 아니라 다른 메시지가 쓰였다.(ort는 Ostensibly Recursive Three-way를 의미한다.)
test-branch로부터 one-branch를 만들었지만 test-branch에서 새로운 커밋을 했으니 더 이상 test-branch가 가리키는 커밋은 one-branch 커밋의 조상이 아니다. 따라서 머지 커밋이 생성되고 합쳐지게 된다.
한 브랜치에서 다른 브랜치로 합치는 방법 중 하나
git rebase [병합할 브랜치]
병합할 브랜치에 있는 변경 사항을 patch로 만들고(묶음으로 관리하는 것) 이를 현재 브랜치에 적용시키는 방식
현재 브랜치와 병합할 브랜치가 나뉘기 전의 공통 커밋으로 이동한 후, 그 커밋부터 현재 브랜치가 가리키는 커밋까지 diff를 차례대로 만들어 임시 저장한다. 그 후 rebase할 브랜치(현재 브랜치)가 병합할 브랜치가 가리키는 커밋을 가리키게 하고 저장해 놓은 diff를 차례대로 적용한다.
rebase 브랜치를 만들고 파일을 생성해 커밋했으며, test-branch에도 다른 파일을 하나 생성해 커밋했다.
그래프를 보면 역시 더 이상 test-branch가 가리키는 커밋이 rebase 커밋의 조상이 아님을 알 수 있다.
git rebase [생성한 브랜치(여기에선 rebase)]
명령어를 통해 rebase 한 결과 머지가 되는 것이 아니라 아래처럼 공통 커밋으로부터 rebase 브랜치의 커밋과 test-branch의 커밋이 차례대로 적용된 것을 확인할 수 있다.
git pull, fetch, rebase, merge 등에 대해 알아보았다. 이렇게 정리하기 전까지 pull이 fetch와 merge를 실행한 것임을 알지 못했다. 이를 통해 내가 얼마나 git에 대해 모르고 있었는지 깨닫게 됐다. 그렇지만 이번 학습을 통해 앞으로 문제가 발생해도 당황하지 않고 잘 해결할 수 있을 거라 생각한다.