git branch
~/my-git-project$ git branch
*master
*master 가 출력된다. 지금 내가 master 브랜치에 있다는 의미다. 우리는 지금까지 master에서 커밋을 했다.
master 에서 index.html을 만들고 제목도 넣고 메인 이미지도 추가(트와이스)해봤다.
index.html
<html>
<title>트와이스</title>
<h1>트와이스 홈페이지</h1>
<div>트와이스 메인사진</div>
</html>
이번 실습에서는 메인 이미지를 트와이스에서 방탄소년단으로 바꿔보도록 하자. 단, master가 아닌 새로운 브랜치에서 변경을하고 commit을 해보자.
방탄소년단 사진을 메인 이미지로 넣을 Commit 이기 때문에 브랜치 이름은 bts-image로 했다.
~/my-git-project$ git branch bts-image
~/my-git-project$ git branch
bts-image <====브랜치가 만들어졌다.
*master
~/my-git-project$ git checkout bts-image
~/my-git-project$ git branch
*bts-image <====브랜치 이동을 했다.
master
$git branch <name>은 현재 base브랜치(master)에서 분기하는 새로운 브랜치를 만드는 명령어다.
$git checkout <name> 은 <name>브랜치로 이동하는 명령어다.
bts-image 브랜치를 만들어서 이동까지 해봤다.
이제부터 staging된 내용을 bts-image 브랜치에서 커밋할 수 있다.
잠깐!
일단 시켜서 따라하긴 했는데 '왜 굳이 브랜치를 따로 만들어서 작업을 하는가? '
근본적인 (혹은 원초적인) 이유는 master에 변경 내용을 반영하지 않기 위해서다.
master가 대체 뭐길래? master는 완전무결한 완제품이어야 한다(보통 master 브랜치를 최종 버전으로 서비스를 배포한다).
완제품이 무엇일까?
제작 공정을 완전히 마친 제품, 즉 출시/배포 될 완성품이다. 배포나갈 완제품에 불완전한(혹은 검증되지 않은) 변경사항을 적용한다면 어떻게 될까? 어디선가 사고가 터지고 말것이다. 그렇기 때문에 master와 격리된 공간에서 수정하고 테스트하기 위해 브랜치를 만들어서 작업을 한다.
(고딕체) 약속하자. master 손대지 않기로=커밋 하지 않기로
bts-image 브랜치에 두 개의 커밋을 만들어보자.
1) 홈페이지 제목과 메인 이미지 방탄 소년단으로 바꾸기
2) 방탄소년단 소개글 본문에(<body></body>) 추가하기
(메인 이미지 변경)
~/my-git-project$ git commit -m "이미지변경(방소단)"
[bts-image f949149] 이미지변경(방소단)
1 file changed, 1 insertion(+), 1 deletion(-)
(방탄 소년단 소개글 추가)
~/my-git-project$ git commit -m "방탄소년단 소개글 추가"
[bts-image f949149] 이미지변경(방소단)
1 file changed, 1 insertion(+), 1 deletion(-)
bts-image 브랜치 커밋 후 index.html
<html>
<title>방탄소년단</title>
<h1>방탄소년단 홈페이지</h1>
<div>방탄소년단 메인사진</div>
<body>방탄소년단 어쩌구 저쩌구</body>
</html>
커밋을 했으니 커밋로그도 확인해보자.
~/my-git-project$ git log --online
* 4486ec2 (master) 방탄소년단 소개글 추가
* f949149 (HEAD -> bts-image) 이미지 변경(방소단)
.....
master로 이동해서 커밋로그를 확인해보자. bts-image 브랜치에서 추가한 로그가 보이지 않는다.
당연하지만 master브랜치에서 커밋을 안했기 때문에 반영이 안된 것이다. 우리가 앞서 의도한대로 변경 내역이 master에 영향을 끼치지 않고 있다.
다시 bts-image 브랜치로 이동해서 브라우저에서 페이지를 열어보자. 방탄소년단으로 이미지가 잘 변경된 걸 확인했는가?
master로 왜 병합해야하나요? 앞에서 말했듯이 master는 완제품이라고 했다. 즉, 최종결과물이기 때문이다. bts-image(자식 브랜치)에서 잘 작동하는걸 확인했다면 master에 반영해도 좋다.
$git merge 와 $git rebase 모두 두개 브랜치의 작업 내용을 병합하는 명령어다. 'Merge와 Rebase 중 무엇을 선택하는가'는 커밋로그를 관리하는 방법에 따라 다르다. 두 개의 차이점에 대해서는 별도 글에서 다루겠다. (글: Merge 와 Rebase의 차이점에 대한 개인적 정리)
이번 글에서는 $git merge로 실습을 해보자
커밋로그를 확인해보니 master의 Head가 bts-image의 Head와 같은 곳에 위치해있다.
대상 브랜치(bts-image)가 베이스 브랜치(master)의 모든 커밋로그를 갖고 있다면?
베이스 브랜치의 HEAD를 대상 브랜치의 Head로 이동하는 방식으로 병합을 한다. 이를 fast-forward merge라고 한다.
bts-image 브랜치와 충돌시키기 위해 master에 트와이스 소개글을 추가해보자.
새롭게 실습하기 전에 git reset --hard HEAD~2 로 병합하기 전으로 돌아가자.
(실습)
index.html 파일 안에 <body>태그에 트와이스 소개글을 간단하게 작성해서 추가해본다.)
<html>
..........
<body><div>트와이스 어쩌구 저쩌구<div></body> <-----새로 추가할 내용
</html>
~/my-git-project$ git checkout master
~/my-git-project$ git commit -am "메인페이지 본문글 추가"
[master c098] 메인페이지 본문글 추가
1 file changed, 1 insertion(+)
이제 bts-image 브랜치를 master로 병합해보자.
(현재 위치 master)
~/my-git-project$ git merge bts-image
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html(1)
Automatic merge failed; fix conflicts and then commit the result.(2)
~/my-git-project$ git merge bts-image
......
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: main.html
Merge Conflict 라니!
두 브랜치가 같은 파일/부분에 각자 변경/커밋을 하게되면 다음과 같이 충돌하게 된다. git 입장에서 같은 부분이 다른 내용으로 수정(both modified)이 됐는데 어떻게 병합할 지 모르기 때문에 Conflict(충돌)를 낸것이다. 그래서 "네가 하세요~(fix conflicts and then commit the result)" 떠넘긴격이다.
충돌이 생긴 index.html을 열어보자.
<html>
===HEAD (1)
<title>트와이스</title>
<h1>트와이스 홈페이지</h1>
<div>트와이스 메인사진</div>
<body>트와이스 어쩌구 저쩌구</body>
===(2)
<title>방탄소년단</title>
<h1>방탄소년단 홈페이지</h1>
<div>방탄소년단 메인사진</div>
<body>방탄소년단 어쩌구 저쩌구</body>
===bts-image (3)
</html>
(1)~(2)까지는 master의 내용이고 (2)~(3)까지는 bts-image의 내용인데 두 부분이 충돌이 났다.
그래서 충돌난 부분을 수정하면서 직접 병합해보자. 수동 병합은 작성자의 재량이다. 내가 어떻게 하냐에 따라 병합 내용도 달라진다.
실습에서는 트와이스 내용을 없애고 bts-branch에서 작업한 내용으로 병합하겠다.
수동 병합 후 index.html
<html>
<title>방탄소년단</title>
<h1>방탄소년단 홈페이지</h1>
<div>방탄소년단 메인사진</div>
<body>방탄소년단 어쩌구 저쩌구</body>
</html>
다시 Commit 하면 실패했던 Merge가 완료된다.
~/my-git-project$ git add index.html
~/my-git-project$ git commit //이번에는 -m 옵션을 사용 안하고 해보겠다.
// 에디터에 아래 내용이 이미 준비가 돼있다.
Merge branch 'bts-image'
# Conflicts:
# index.html
:wq // 저장하고 나가자. ( esc -> ':wq' 누른 후 엔터)
[master b123456] Merge branch 'bts-image'
이제 commit tree가 아래 그림처럼 만들어졌다.
실습 끝!
git을 사용할 때 흔히 말하는 '브랜치 따서 커밋하고 merge한다.' 의 실습 예제였다.
git checkout -b bts-image => (변경 & 커밋) -> git checkout master => git merge bts-image => (파일 병합) =>git commit
지금까지는 내 로컬 저장소에서 혼자 북치고 장구치면서 Git의 A to Z를 해봤다.
다음 글에서는 Github을 활용 해서 협업이란걸 해보자.
(끝)