Git branch
git의 사용 이유와 정수를 보여주는 '가지치기' (1장에서 언급)에 대해 알아보자
Git branch란?
- 독립적으로 어떤 작업을 진행하기 위한 개념이다.
- 각각의 branch는 다른 branch의 영향을 받지 않는다. (merge 등의 작업을 하지 않는 이상)
- 브랜치를 만들고, 여러 작업을 독립적으로 진행 한 뒤, 각 브랜치들을 하나로 모두 병합하는 것을 살펴보자.
1. branch 생성하기
git branch like_feature(이름)
으로 브랜치를 생성할 수 있다.
- 그러면 아래와 같이된다. master는 자동으로 생성되는, 기본이 되는 브랜치다.
2. branch 이동하기
git branch
git checkout like_feature
git checkout -b like_feature
- checkout을 하면 아래 그림과 같이 HEAD가 우리가 만들고, 지금 이동한 브랜치를 가르키고 있다. HEAD는 (2)장 '파일 상태 되돌리기' 에서 잠깐 언급을 했었다.
- checkout은 branch를 전환하는데 사용할 수 있고, 아래와 같이
git log
로 확인한 snapshot을 넘나들때도 사용이 가능하다.
- 아래 사진에서
git log
로 snap shot hash값을 보고, 옮기면 된다.
git checkout <snapshot hash>
- snapshot의 hash값 이용해 과거의 파일 내용을 확인 할 수 있다.
Git branch 병합, merge
1. fast-forward 방식의 병합
- 위 브랜치 like_feature 로 이동해서 작업을 했고, add와 commit을 했다고 하자.
- 그러면 위와 같이 HEAD는 like_feature를 가르키고 있는 상태에서, 새로운 커밋이 생기고 저장저이 생긴것이다. master 브랜치로 이동해서, like_feature를 합쳐보자.
git checkout master
git merge like_feature
- 위와 같이 merge를 진행하면, 아래 그림과 같이 c2(커밋2)를 가르키던 master브랜치는 c3으로, head는 master로, like_feature의 모든 내용들은 master로 합쳐진다.
- like_feature 의 이력은 master의 이력을 모두 포함하고 있고, '업데이트 된 내용(새로운 내용)'만 가지고 있는 브랜치를 merge해서 곧바로 되고, 이렇게 이뤄지는 merge가 fast-forward 방식이다.
2. merge commit (non fast-forward)
- 위와 다르게, 아래 그림처럼 브랜치가 갈라지고, 합쳐져야 할 두 브랜치의 이력(커밋)이 다르다면 어떻게 되는 것일까?
git checkout master
git merge like_feature
- fast-forward 와 같은 방식으로 merge를 진행하면 아래 그림과 같이 진행이 된다.
- fast-forward와 다르게 커밋 지점이 c5로, 이전 경우와 다르게 새로운 지점을 가르키고 있다.
- like_feature 브랜치는 목적을 다 달성 했기 때문에 삭제를 진행하면 된다. -> 여기서 알 수 있듯이, 흔히 'feature'를 붙이는 브랜치 명은 '해당 브랜치 당 하나의 목적 / 기능'을 명시하고 있다.
git branch -d like_feature
로 브랜치 삭제가 가능하다.
git log --graph --pretty=oneline --all
명령어를 통해서 해당 커밋지점과 같이 브랜치들이 어디에서 나오고, 어디에 합쳐지는지 확인이 가능하다.
- 사실 fast-forward 경우, merge 옵션을 아래와 같이 줘서 무조건 새로운 커밋 지점을 만들고 병합하는 non fast-forward 도 가능합니다.
git merge --no-ff like_feature # 즉 디폴트는 그냥 --ff 옵션이라는 것
3. 3way 방식의 병합
-
실제 협업을 하면서 가장 많이 이뤄지는 방식의 merge 형태다. 위 like_featrue로 다루는 것이 아니라, 전혀 다른 경우를 살펴보자. 출처 / 클릭
-
위 fast-foward 방식에서 다시 출발해 보자. 아래의 다른 사진이지만, 같은 경우이다. master는 그냥 c3 커밋을 가르키기만 하면 fast-forward merge의 끝이다.
- 하지만 위 상황에서 master 에서 hot fix를 해결하는 코드를 만들기 위해 'hotfix'브랜치를 하나 파서 코딩 후 add, commit을 했다고 가정을 하자. 그러면 아래 그림과 같이 된다.
- 그러면 위 같은 그림이 된다. master 입장에서는 두 개를 차근 차근 순차적으로 병합을 할 것 이다. hotfix 브랜치 부터 merge를 해보자.
- hotfix는 해결이 되었으나, iss53 브랜치는 기능 개발을 따로 계속 진행 중이다. / 실제로 우리가 협업으로 버전관리를 할때는 브랜치들이 다 따로 기능개발을 하고 있을 테니 말이다.
- 이제 master는 non fast-foward로 c4 기점으로 merge를 했고, iss53 브랜치 기능 개발이 끝나 이제 merge를 해야한다.
- 여기서 핵심은 C4가 C5와는 같은 조상이 아니라서 (위에서 분기점이 갈린 점을 생각하자) 절대 fast-forward merge를 하지 않는다.
- 3way merge의 최종 결과는 위 이미지와 같다. 3way merge의 결과를 별도의 커밋으로 만들고 해당 브랜치가 그 커밋을 가리키도록 이동시킨다. 그 커밋은 C6이고 해당 커밋의 부모는 여러개가 되어있는 것을 볼 수 있다.
- 이때, 3way merge에서 merge conflict가 발생할 수 있는 것이다. merge하는 두 브랜치에서 같은 파일의 같은 부분을 동시에 수정하게 되면 git은 해당 부분을 merge를 하지 못한다.
Git merge conflict
conflict 해결
- merge한 두 branch에서 '같은 파일' 을 변경했을 때 충돌이 발생한다. (충돌 발생의 예시는 사실 3way merge에서 그 이유가 상세하게 드러난다!!)
- 충돌이 발생했으면, git에서 automatic merge를 해주진 않고, 개발자가 해당 부분을 해결하고 다시 merge를 해줘야만 한다!
- 위 사진의 경우 conflict를 해결해 보자.
git status
로 어느 파일에서 충돌이 발생했는지 확인이 가능하다.
- 위 명령어를 치면 "both modified" 라고 어떤 파일에서 동시 수정이 일어났는지 가르쳐 준다. 해당 파일을 확인해보자.
- head가 무엇인지 충분히 이제 숙지를 했을 것이다. head가 가르키는 commit의 snapshot에서는 해당 내용이, like_feature 브랜치의 commit의 snapshot에서는 아래의 내용이 있다는 것을 명시한다.
- 이제 충돌이 난 두 코드 중 어떤 부분을 선택할지 또는 둘 다 혼합할지 직접 수정을 해줘야 한다.
- 특수 기호가 포함 된 부분을 모두 삭제해주고 다시
git add / git commit
을 다시 진행하면 conflict가 풀리고 merge 진행이 된다.
conflict 방지, 정리
- 우선 conflict는 무조건 날 수 있다. conflict를 무조건 적으로 막는 것이 아니라, 당연히 일어날 수 있는 부분은 수용을 하고 해당 commit을 잘 '정리' 해줘야 한다.
- 그리고 기본적으로 master가 되는 브랜치는 직접 수정을 하거나 접근하는 일은 없도록 해야 올바른 브랜치 사용이자 기본적은 충돌 예방이다. master는 무조건 배포가 바로 가능한 안정적인 버전만 유지되어야 한다.
- commit을 정리 하는 방법에 대해서는
rebase / commit --amend / merge --squash (스쿼시 머지)
정도로 볼 수 있다. 물론 때에 적절한 reset은 당연하다.
- 위 '정리' 에 대해 다음 장에서 rebase를 살펴보며 살펴보자.