Dev Environment/Git

Git branch(브랜치)

jaiyah 2016. 2. 2. 09:51

Git 브랜치

모든 버전 관리 시스템은 브랜치를 지원합니다. 개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생길 수 있습니다. 

코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치입니다. 

버전 관리 시스템에서 브랜치를 만드는 과정은 고생스럽습니다. 

개발자가 수동으로 소스코드 디렉토리를 복사해서 브랜치를 만들어야 하고 소스코드의 양이 많으면 브랜치를 만드는 시간도 오래 걸립니다. 

사람들은 브랜치 모델이 Git의 최고의 장점이라고, Git이 다른 것들과 구분되는 특징이라고 말합니다. 당최 어떤 점이 그렇게 특별한 것일까? 

Git의 브랜치는 매우 가볍습니다. 순식간에 브랜치를 새로 만들고 브랜치 사이를 이동할 수 있습니다. 

다른 버전 관리 시스템과는 달리 Git은 브랜치를 만들어 작업하고 나중에 Merge하는 방법을 권장합니다. 심지어 하루에 수십 번씩해도 괜찮습니다. 

Git 브랜치에 능숙해지면 개발 방식이 완전히 바뀌고 다른 도구를 사용할 수 없게 될 것입니다.





브랜치와 실제 개발과정

실제 개발과정에서 겪을 만한 예제를 하나 살펴보겠습니다. 

브랜치와 Merge는 보통 이런 식으로 진행합니다: 

  1. 작업 중인 웹사이트가 있다. 
  2. 새로운 이슈를 처리할 새 Branch를 하나 생성. 
  3. 새로 만든 Branch에서 작업 중. 

이때 중요한 문제가 생겨서 그것을 해결하는 Hotfix를 먼저 만들어야 합니다. 


그러면 다음과 같이 할 수 있습니다: 

  1. 새로운 이슈를 처리하기 이전의 운영(Production) 브랜치로 이동. 
  2. Hotfix 브랜치를 새로 하나 생성. 
  3. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge. 
  4. 다시 작업하던 브랜치로 옮겨가서 하던 일 진행.




Git Branch - 깃 가지치기

깃의 브랜치는 놀랍도록 가볍습니다. 브랜치는 특정 커밋에 대한 참조(reference)에 지나지 않습니다. 

이런 사실 때문에 브랜치를 많이 만들어도 메모리나 디스크 공간에 부담이 되지 않기 때문에, 여러분의 작업을 커다른 브랜치로 만들기 보다, 작은 단위로 잘게 나누는 것이 좋습니다. 

브랜치와 커밋을 같이 쓸 때, 어떻게 두 기능이 조화를 이루는지 알아보겠습니다. 

하지만 우선은, 단순히 브랜치를 "하나의 커밋과 그 부모 커밋들을 포함하는 작업 내역"이라고 기억하시면 됩니다.

가지는 안전하게 격리된 상태에서 무언가를 만들 때 사용합니다. 여러분이 저장소를 새로 만들면 기본으로 master 가지가 만들어집니다. 

이제 다른 가지를 이용해서 개발을 진행하고, 나중에 개발이 완료되면 master 가지로 돌아와 병합하면 될 것입니다.


Git
git branch [브랜치명]
git checkout [브랜치명]
git commit

위 명령어는 아래와 같이 한줄로 실행할 수도 있습니다.

위 명령어의 의미는 "브랜치명"라는 이름의 가지를 만들고 갈아탄다는 것을 의미합니다.. 즉, 새 브랜치로 이동하는 것입니다.

Git
git checkout -b newData

이렇게 하면 변경분을 커밋하기 전에 새 브랜치로 이동하게 됩니다.

그리고 이제 여러분의 변경이 새 브랜치에 기록될 것입니다.




Git 브랜치 합치기(Merge)

지금까지 커밋하고 브랜치를 만드는 방법을 알아봤습니다. 이제 두 별도의 브랜치를 합치는 몇가지 방법을 알아볼 차례입니다. 

이제부터 배우는 방법으로 브랜치를 따고, 새 기능을 개발 한 다음 합칠 수 있게 될 것입니다. 

처음으로 살펴볼 방법은 git merge입니다. Git의 합치기(merge)는 두 개의 부모(parent)를 가리키는 특별한 커밋을 만들어 냅니다. 

두개의 부모가 있는 커밋이라는 것은 "한 부모의 모든 작업내역과 나머지 부모의 모든 작업, 그리고 그 두 부모의 모든 부모들의 작업내역을 포함한다"라는 의미가 있습니다.

브랜치를 땄다면 각 브랜치에는 독립된 커밋이 하나씩 있을 것이고 그 말은 이 저장소에 지금까지 작업한 내역이 나뉘어 담겨 있다는 얘기입니다. 

두 브랜치를 합쳐서(merge) 이 문제를 해결해 보겠습니다.  

아래는 bugFix라는 브랜치를 땄다는 가정하에 실행하도록 하겠습니다.

Git
git merge bugFix master
git merge master bugFix

첫 번째 명령어를 실행하면 master가 두 부모가 있는 커밋을 가리키고 있을 것입니다. 이 말은 master 가지 상태와 bugFix 가지 상태가 하나로 합쳐집니다. 

하지만 이 상태는 가지를 bugFix의 가지를 master로 이동시킨 것 뿐입니다. 다시말해, 첫번째 명령어는 간단히 bugFix를 master가 붙어 있는 커밋으로 이동시켰을 뿐입니다. 

그 다음 두 번째 명령어를 실행하여 이 가지를 master로 합칠 수 있습니다.


브랜치를 합치지 않은 상태에서 브랜치 간에 이동할 수 있습니다.

Git
git checkout master


다음의 명령으로는 가지를 삭제할 수 있습니다.

Git
git branch -d bugFix


여러분이 새로 만든 가지를 원격 저장소로 전송하기 전까지는 다른 사람들이 접근할 수 없습니다.

Git
git push origin <가지 이름>




Git 갱신(update)과 병합(merge)

pull 은 git 저장소에서 내려받는 것을 의미합니다. clone 과 다른 점은, pull은 변경사항을 내려받는 것이고, clone은 모든 프로젝트의 사항을 통째로 내려받는다는 것입니다..

여러분의 로컬 저장소를 원격 저장소에 맞춰 업데이트하려면 아래 명령을 실행합니다.

Git
git pull

이렇게 하면 원격 저장소의 변경 내용이 로컬 작업 디렉토리에 받아지고(fetch), 병합(merge)될 것입니다.


다른 가지에 있는 변경 내용을 현재 가지(예를 들면, master 가지)에 병합하려면 아래 명령을 실행합니다.

Git
git merge <가지 이름>

첫번째 명령이든 두번째 명령이든, git은 자동으로 변경 내용을 병합하려고 시도합니다. 

문제는, 항상 성공하는 게 아니라 가끔 충돌(conflicts)이 일어날지도 모릅니다. 이렇게 충돌이 발생하면, git이 알려주는 파일의 충돌 부분을 여러분이 직접 수정해서 병합이 가능하도록 해야 합니다. 

충돌을 해결했다면, 아래 명령으로 git에게 아까의 파일을 병합하라고 알려주도록 합니다.

Git
git add <파일 이름>


변경 내용을 병합하기 전에, 어떻게 바뀌었는지 비교해볼 수도 있습니다.

Git
git diff <원래 가지> <비교 대상 가지>




Git 리베이스(rebase)

Git에서 한 브랜치에서 다른 브랜치로 합치는 방법은 두 가지가 있습니다. 

하나는 Merge이고 다른 하나는 Rebase입니다. 

여기서는 Rebase가 무엇인지, 어떻게 사용하는지, 좋은 점은 뭐고, 어떤 상황에서 사용하고 어떤 상황에서 사용하지 말아야 하는지 알아 봅니다.

리베이스는 기본적으로 커밋들을 모아서 복사한 뒤, 다른 곳에 떨궈 놓는 것입니다. 

조금 어려게 느껴질 수 있지만, 리베이스를 하면 커밋들의 흐름을 보기 좋게 한 줄로 만들 수 있다는 장점이 있습니다. 리베이스를 쓰면 저장소의 커밋 로그와 이력이 한결 깨끗해집니다.

bugFix와 master 가 있을 경우 : 

Git
git rebase master

이제 bugFix 브랜치의 작업 내용이 master의 바로 위에 깔끔한 한 줄의 커밋으로 보이게 됩니다.

다시 말해, master 로 이동된 것은 bugFix의 복사본입니다. 그리고 bugFix가 master보다 순서적으로 앞서기 때문에 master를 bugFix로 이동시켜줘야 합니다.

Git
git rebase bugFix

이는 master가 bugFix의 부모쪽에 있었기 때문에, 단순히 그 브랜치를 더 앞쪽의 커밋을 가리키게 이동하는 것이 전부입니다.



rebase는 단어 그대로 재구축입니다. 근데 뭘 어떻게 재구축하는지 기억하는 게 중요할 것입니다. 

이는 현재 branch를 재구축하는 것입니다. 명령을 내릴 때는 뭘 기반으로 재구축하는 것은지를 명시해 주는 것입니다. 

그래서 git rebase master는 'master branch를 바탕으로 현재 branch를 재구축하라'는 명령이 된다. 

다른 branch의 내용을 끌어오는 것으로는 git merge master도 있습니다. 

개념은 똑같은데 '현재 branch에 master branch를 가져와서 합쳐라'라는 의미가 됩니다. 

rebase가 과거 커밋들을 바탕으로 commit history를 재작성하는 반면에 merge는 합친 내용으로 현재 branch에 새로운 커밋을 하게 될 것입니다. 

rebase는 가져온 branch에 commit이 많았다면, 그만큼 과거의 commit이 많이 생기게 되지만, merge는 변경 내역을 가져와서 다 합친 다음 새로운 커밋을 한 번만 하게 됩니다.





브랜치 관리

브랜치를 관리하는 데 필요한 다른 명령도 살펴보도록 합니다.

git branch 명령은 단순히 브랜치를 만들고 삭제해 주기만 하는 것이 아닙니다. 

아무런 옵션 없이 실행하면 다음과 같이 브랜치의 목록을 보여줍니다:

GIT
git branch
  iss53
* master
  testing

* 기호가 붙어 있는 master브랜치는 현재 Checkout해서 작업하는 브랜치를 나타냅니다. 

즉, 지금 수정한 내용을 커밋하면 master 브랜치에 커밋되고 포인터가 앞으로 한 단계 나아갑니다.

git branch -v 명령을 실행하면 브랜치마다 마지막 커밋 메시지도 함께 보여줍니다 :

GIT
git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes


각 브랜치가 지금 어떤 상태인지 확인하기에 좋은 옵션도 있습니다. 

현재 Checkout한 브랜치를 기준으로 Merge된 브랜치인지 그렇지 않은지 필터링해 볼 수 있다.

--merged--no-merged 옵션을 사용하여 해당 목록을 볼 수 있습니다.

git branch --merged 명령으로 이미 Merge한 브랜치 목록을 확인합니다 :

GIT
git branch --merged
  iss53
* master

iss53 브랜치는 앞에서 이미 Merge했기 때문에 목록에 나타나는 것입니다. 

* 기호가 붙어 있지 않은 브랜치는 git branch -d 명령으로 삭제해도 되는 브랜치입니다. 

이미 다른 브랜치와 Merge 했기 때문에 삭제해도 정보를 잃지 않습니다.


반대로 현재 Checkout한 브랜치에 Merge하지 않은 브랜치를 살펴보려면 git branch --no-merged 명령을 사용합니다 :

GIT
git branch --no-merged
  testing

위에는 없었던 다른 브랜치가 보일 것입니다. 

아직 Merge하지 않은 커밋을 담고 있기 때문에 git branch -d 명령으로 삭제되지 않습니다 :

GIT
git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.

위와 같이 Merge하지 않은 브랜치를 강제로 삭제하려면 -D 옵션으로 삭제합니다.

GIT
git branch -D testing




Jaehee's WebClub