[Tip] github 사용법 - Branch&PR 1편
in Tip on Github, Git
지난 포스팅
4. git 사용법 - 코드 관리(SCM) & 버전 관리(VCS)
지난 포스팅에서는 github의 협업 시나리오 중 push & pull에 대해 알아보았습니다. 이번 포스팅에서는 현재 가장 표준적인 협업 모델인 Branch & PR에 대해서 알아보도록 하겠습니다.
끝말 잇기와 같이 간단한 프로젝트의 경우 push & pull만으로도 충분하겠지만 복잡한 프로젝트를 수행할 때는 비효율적입니다. 메이플 아일랜드 밖에 없는 메이플 스토리에 빅토리아 아일랜드를 추가한다고 해봅시다. push & pull 방식으로 만든다면 개발자A가 먼저 리스항구를 만들고 push를 하면 개발자 B가 그것을 pull해서 헤네시스를 만들고 push하고 개발자 C는 그것을 받아 커닝시티를 만들고… 아마 빅토리아 아일랜드를 만드는데만 10년은 걸릴 것 같습니다.
그렇다고 각자 만든 것을 본 게임에 push 해버리면 에러가 날 것이 자명해보입니다. 심지어는 잘 돌아가고 있던 메이플 아일랜드 마저 망쳐버릴지도 모릅니다. 그렇기 때문에 게임 회사는 본 게임에 반영하기 전에 알파/베타 서버라는 것을 만듭니다. 알파/베타 서버에서 에러가 발생하지 않는지 충분히 실험해보고 수정을 거친 후 본 서버에 반영하게 됩니다. (그럼에도 불구하고 버그가 생긴다는 것이 협업의 어려움을 방증하는 것이기도 합니다 ㅠ)
다시 git으로 돌아와봅시다. 예시로 들었던 본 서버를 git에서는 master, 알파/베타 서버를 branch라고 합니다. 그리고 알파/베타 서버의 내용을 본 서버에 반영하는 것을 merge라고 합니다. 여기까지는 이해가 잘 됐을 것이라고 생각합니다. 앞으로 설명할 내용은 다소 추상적이므로 시각화를 통해 이해를 돕는 사이트를 하나 소개해드리겠습니다. 해당 사이트를 이용해 설명드리도록 하겠습니다.

첫 번째 commit이 이루어진 상태를 가정하고 시작합니다.
(1) git branch
git branch는 브랜치를 생성하는 명령어입니다. git branch 브랜치이름으로 생성하시면 됩니다. 저는 test라고 이름을 짓도록 하겠습니다.
$ git branch test

HEAD 밑에 test라는 브랜치가 생성됐습니다. 아직 브랜치에 아무 것도 commit하지 않았기 때문에 가지가 뻗어나가지는 않았습니다. 브랜치에 commit을 하기 위해서는 먼저 해당 브랜치를 선택해야 합니다. 이 때 지난 포스트에서 배웠던 git checkout을 사용하시면 됩니다. 그리고 git branch를 입력하면 현재 존재하는 브랜치와 선택 현황을 알 수 있습니다.
$ git checkout test
Switched to branch 'test'
$ git branch
master
* test

Head가 test 밑으로 이동한 것으로 보아 test 브랜치가 선택된 것을 알 수 있습니다. test 브랜치에 commit해보도록 하겠습니다.
$ git commit -m "second"

test 브랜치의 가지가 하나 뻗어나간 것을 볼 수 있습니다. 그럼 이제 test를 master 브랜치에 반영하도록 하겠습니다.
(2) git merge
git merge는 두 개의 브랜치를 합치는 명령어 입니다. git merge 합칠 브랜치 이름으로 사용하시면 됩니다. 주의하실 점은 반드시 메인이 되는 브랜치로 이동해서 사용해야 한다는 것입니다. 만약 master 브랜치에서 test를 합친다면
$ git checkout master
$ git merge test
위의 순서로 하셔야 합니다. 이걸 안 지켜서 피눈물을 흘릴 일이 많을지도…
git merge를 사용하는 상황은 3가지로 나눌 수 있습니다.
- Fast - forward Merge
- Auto - Merge
- Conflict
(2.1) Fast - forward Merge
한 브랜치에만 commit이 있다면 적용되는 merge입니다.

앞에서 봤던 이 상황에서 Merge를 하면 적용됩니다. 위에서 말했듯이 반드시 master로 checkout하고 merge를 하셔야 합니다.
$ git checkout master
$ git merge test

위와 같이 master 브랜치가 test 브랜치로 이동하여 merge가 이루어집니다.
(2.2) Auto - Merge
test 브랜치 뿐 아니라 master에도 commit이 생긴 경우를 생각해봅시다. 아래와 같은 상황입니다.

다행히 master 브랜치에 새로 commit이 일어났음에도 test와 파일 간 충돌이 발생하지 않는다면 Auto Merge가 발생하게 됩니다. merge를 수행해보도록 하겠습니다.

위와 같이 자동으로 merge가 수행됩니다.
(2.3) Conflict
가장 골치 아픈 상황입니다. master 브랜치에 새로 commit이 일어나는 과정에서 파일의 수정이 발생해 master 브랜치와 test 브랜치 간 충돌이 발생하는 경우입니다.

(2.2)의 상황과 비슷해보이지만 파일 간 충돌이 생겨 자동으로 merge가 일어나지 않습니다. merge가 가능하도록 직접 파일을 수정해주어야 합니다. 해당 상황을 직접 구현해보겠습니다.
먼저 a.txt 파일에 hi를 추가하고 add, commit을 해줍니다.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ touch a.txt # a.txt 생성
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ echo hi > a.txt # a.txt에 hi 작성
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git add a.txt # add
warning: LF will be replaced by CRLF in a.txt.
The file will have its original line endings in your working directory
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git commit -m "add a.txt" # commit
[master (root-commit) f7a1643] add a.txt
1 file changed, 1 insertion(+)
create mode 100644 a.txt
test 브랜치를 생성한 뒤 이동해서 a.txt 파일의 내용을 hi에서 hello로 수정하고 add, commit 해줍니다.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git branch test # test 브랜치 생성
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git checkout test # test 브랜치로 이동
Switched to branch 'test'
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (test)
$ echo hello > a.txt # a.txt 파일을 hi에서 hello로 수정
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (test)
$ git add a.txt # add
warning: LF will be replaced by CRLF in a.txt.
The file will have its original line endings in your working directory
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (test)
$ git commit -m "modify a.txt" # commit
[test 2267a10] modify a.txt
1 file changed, 1 insertion(+), 1 deletion(-)
다시 master 브랜치로 이동해 a.txt 파일을 hi my name is hakmin 으로 수정 후 add, commit 해줍니다.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (test)
$ git checkout master # master 브랜치로 이동
Switched to branch 'master'
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ echo hi my name is hakmin > a.txt # a.txt 파일을 hi에서 hi my name is hakmin으로 수정
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git add a.txt # add
warning: LF will be replaced by CRLF in a.txt.
The file will have its original line endings in your working directory
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git commit -m "a.txt modify2" # commit
[master 370117b] a.txt modify2
1 file changed, 1 insertion(+), 1 deletion(-)
git merge를 실행한 후 결과를 확인해봅시다.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git merge test #merge 실행
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master|MERGING)
$
CONFLICT (content): Merge conflict in a.txt 라는 경고가 뜨면서 경로 옆의 표시가 (master|MERGING)으로 바뀐 것을 확인할 수 있습니다.
git status를 찍으면 어떤 부분에서 문제가 발생했는지 바로 확인 가능합니다.
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master|MERGING)
$ git status #상태 확인
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: a.txt
conflict 상황을 해결하는 방법은 직접 충돌이 난 파일을 수정한 후 add, commit하는 것입니다. a.txt 를 열어보겠습니다.

신기하게도 충돌이 난 부분이 구분되어 있는 것을 볼 수 있습니다. 아래와 같이 특수 문자를 모두 지운 후 저장하고 add, commit 해보도록 하겠습니다.

lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master|MERGING)
$ git add a.txt # add
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master|MERGING)
$ git commit -m "merge finish" # commit
[master 9fd2ff1] merge finish
lhmlh@DESKTOP-99JLML6 MINGW64 ~/conflict (master)
$ git status # 상태 확인
On branch master
nothing to commit, working tree clean
경로 옆에 표시가 (master|MERGING)에서 (master)로 변경되었고 git status를 찍으면 commit이 잘 된 것을 확인할 수 있습니다.
쓰다보니 글이 너무 길어져서 Pull Request를 보내는 방법에 대해서는 다음 포스팅으로 넘기도록 하겠습니다. 읽어주셔서 감사합니다~
