Git 사용법
브랜치란?
개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다.
코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데,
이렇게 독립적으로 개발하는 것이 브랜치다.
커밋하면 Git는 한 Saging Area에 있는 데이터의 스냅샷에 대한 포인터,
저자나 커밋 메시지 같은 메타 데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 개체를 저장한다.
이전 커밋 포인터가 있어서 현재 커밋이 무엇을 기준으로 바뀌었는지를 알 수 있다.
최초 커밋을 제외한 나머지 커밋은 이전 커밋 포인터가 적어도 하나씩 있고 브랜치를 합친 Merge 커밋 같은 경우에는
이전 커밋 포인터가 여러개 있다.
파일이 3개 있는 디렉토리가 하나 있고 이 파일을 Staging Area 에 저장하고 커밋하는 예제를 보자.
파일을 Stage 하면 Git 저장소에 파일을 저장하고(Git는 이것을 Blob 이라고 부른다) Staging Area에 해당
파일의 체크섬을 저장한다.
git commit 으로 커밋하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 개체를 체크섬과 함께
저장소에 저장한다. 그 다음에 커밋 개체를 만들고 메타데이터와 루트 디렉토리 트리 개체를 가리키는 포인터 정보를
커밋 개체에 넣어 저장한다. 그래서 필요하면 언제든지 스냡샷을 다시 만들 수 있다.
이 작업을 마치고 나면 Git 저장소에는 다섯 개의 데이터 개체가 생긴다.
각 파일에 대한 Blob 세개 파일과 디렉토리 구조가 들어 있는 트리 개체 하나, 메타데이터와 루트 트리를 가리키는 포인터가
담긴 커밋 개체 하나이다.
커밋과 트리 데이터
다시 파일을 수정하고 커밋하면 이전 커밋이 무엇인지도 저장한다.
커밋과 이전 커밋
Git의 브랜치는 커밋 사이를 이동할 수 있는 포인터 같은 것이다. 기본적으로 Git는 master 브랜치를 만든다.
처음 커밋하면 이 master 브랜치가 생성된 커밋을 가리킨다.
이휴 커밋을 만들면 브랜치는 자동으로 가장 마지막 커밋을 가리킨다.
브랜치와 커밋 히스토리
새 브랜치 생성하기
새로 만든 브랜치도 지금 작업하고 있던 마지막 커밋을 가리킨다.
한 커밋 히스토리를 가리키는 두 브랜치
Git는 'HEAD'라는 특수한 포인터가 있다. 이 포인터는 지금 작업하는 로컬 브랜치를 가리킨다.
브랜치를 새로 만들었지만 Git는 아직 master 브랜치를 가리키고 있다.
git branch 명령은 브랜치를 만들기만 하고 브랜치를 옮기지 않는다.
현재 작업 중인 브랜치를 가리키는 HEAD
git log 명령에 --decorate 옵션을 사용하면 쉽게 브랜치가 어떤 커밋을 가리키는지도 확인할 수 있다.
"master" 와 "testing" 이라는 브랜치가 ddb3afb 커밋 옆에 위치하여 브랜치가 가리키는 커밋을 확인할 수 있다.
브랜치 이동하기
git chekout 명령으로 다른 브랜치로 이동할 수 있다. testing 브랜치로 바꿔보자.
HEAD는 testing 브랜치를 가리킴
새로 커밋해서 testing 브랜치는 앞으로 이동했다. 하지만, master 브랜치는 여전히 이전 커밋을 가리킨다.
$ git checkout master |
HEAD가 Checkout 한 브랜치로 이동함
방금 실행한 명령이 한 일은 두 가지다.
master 브랜치가 가리키는 커밋을 HEAD 가 가리키게 하고 워킹 디렉토리의 파일도 그시점으로 되돌려 놓았다.
앞으로 커밋을 하면 다른 브랜치의 작업들과 별개로 진행 되기 때문에 testing 브랜치에서 임시로 작업하고
원래 master 브랜치로 돌아와서 하던 일을 계속할 수 있다.
Note. 브랜치를 이동하면 워킹 디렉토리의 파일이 변경된다는 점을 기억해두어야한다.
파일 변경시 문제가 있어 브랜치를 이동시키는게 불가능한 경우 Git는 브랜치 이동 명령을 수행하지 않는다.
파일을 수정하고 다시 커밋을 해보자.
브랜치를 하나 만들어 그 브랜치에서 작업을 하고, 다시 원래 브랜치로 되돌아와서 다른 작업을 했다.
두 작업 내용은 독립적으로 각 브랜치에 존재한다.
커밋 사이를 자유롭게 이동하다가 때가 되면 두 브랜치를 Merge 한다.
branch , checkout , commit 명령을 사용해서 말이다.
갈라지는 브랜치
git log 명령으로 쉽게 확인할 수 있다. 현재 브랜치가 가리키도 있는 히스토리가 무엇이고,
어떻게 갈라져 나왔는지 보여준다.
git log --oneline --decorate --graph --all 이라고 실행하면 히스토리를 출력한다.
브랜치가 필요할 때 프로젝트를 통째로 복사해야 하는 다른 버전 관리 도구와 Git의 차이는 극명하다.
통째로 복사하는 작업은 프로젝트 크기에 따라 수십 초에서 수십 분까지 걸린다.
그에 비해 Git는 순식간이다. 커밋을 할 때마다 이전 커밋의 정보를 저장하기 때문에 Merge 할 때 어디서부터
합쳐야 하는지 알 수 있다.
브랜치와 Merge의 기초
1. 작업 중인 웹사이트가 있다.
2. 새로운 이슈를 처리할 새 Branch을 하나 생성한다.
3. 새로 만든 Branch에서 작업을 진행한다.
이때 중요한 문제 발생으로 그것을 해결하는 Hotfix를 먼저 만들어야 한다.
1. 새로운 이슈를 처리하기 이전의 운영(Production) 브랜치로 이동한다.
2. Hotfix 브랜치를 새로 하나 생성한다.
3. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge 한다.
4. 다시 작업하던 브랜치로 옮겨가서 하던 일을 진행한다.
현재 커밋 히스토리
이슈 관리 시스템에 등록된 53번 이슈를 처리한다고 하면 이 이슈에 집중할 수 있는 브랜치를 새로 하나 생성한다.
브랜치를 만들면서 Checkout 까지 한번에 하려면 git checkout 명령에 -b 옵션을 추가한다.
위 명령은 아래 명령을 줄여놓은 것이다.
$ git branch iss53 $ git checkout iss53 |
브랜치 포인터 새로 생성
iss53 브랜치를 Checkout 했기 때문에 작업을 하고 커밋하면 iss53 브랜치가 앞으로 나아간다.
진행 중인 iss53 브랜치
다른 상황을 가정해보자. 만드는 사이트에 문제가 생겨서 즉시 고쳐야한다.
버그를 해결한 Hotfix에 iss53이 섞이는 것을 방지하기 위해 iss53 과 관련된 코드를 어딘가에 저장해두고
원래 운영 환경의 소스로 복구해야한다.
Git를 사용하면 이런 노력을 들일 필요 없이 그냥 maste 브랜치로 돌아가면 된다.
그렇지만 브랜치를 이동하려면 해야 할 일이 있다.
아직 커밋하지 않은 파일이 Checkout 할 브랜치와 충돌 나면 브랜치를 변경할 수 없다.
브랜치를 변경할 때는 워킹 디렉토리를 정리하는 것이 좋다. Stashing 과 Cleaning 을 사용해서 정리한다.
지금은 작업하던 것을 모두 커밋하고 master 브랜치로 이동한다.
이때 워킹 디렉토리는 53번 이슈를 시작하기 이전 모습으로 되돌려지기 때문에
새로운 문제에 집중할 수 있는 환경이 만들어진다.
해결해야 할 핫픽스가 생겼을 때 hotfix라는 브랜치를 만들고 새로운 이슈를 해결할 때 까지 사용한다.
hotfix 라는 브랜치를 생성하고 index.html 파일을 생성한다.
새로 생성한 index.html 파일을 커밋 한다.
브랜치에서 갈라져 나온 hotfix 브랜치
운영 환경에 적용하려면 문제를 제대로 고쳤는지 테스트하고 master 브랜치에 합쳐야 한다.
git merge 명령으로 아래와 같이 한다.
Merge 메시지에서 'fast-forward' 라는 메지시가 있다.
hotfix 브랜치가 가리키는 C4 커밋이 C2 커밋에 기반한 브랜치이기 때문에
포인터는 Merge 과정 없이 최신 커밋으로 이동한다.
이런 Merge 방식을 'Fast forward' 라고 부른다.
이제 hotfix는 master 브랜치에 포함됐고 운영환경에 적용할 수 있는 상태가 되었다고 가정해보자.
Merge 후 hotfix와 같은 것을 가리키는 master 브랜치
급한 문제를 해결하고 master 브랜치에 적용하고 나면 다시 일하던 브랜치로 돌아가야 한다.
이제 더이상 필요없는 hotfix 브랜치는 삭제한다. git branch 명령에 -d 옵션을 주고 브랜치를 삭제한다.
이제 이슈53번을 처리하던 환경으로 되돌아가서 하던일을 계속 진행하자.
master와 별개로 진행하는 iss53 브랜치
merge master 명령으로 master 브랜치를 iss53 브랜치에 Merge 하면 iss53 브랜치에 hotfix 가 적용된다.
Merge 의 기초
53번 이슈를 다 구현하고 master 브랜치에 Merge 하는 과정을 살펴보자.
iss53 브랜치를 mster 브랜치에 Merge 하는 것은 앞서 살펴본 hotfix 브랜치를 Merge 하는 것과 비슷하다.
git merge 명령으로 합칠 브랜치에서 합쳐질 브랜치를 Merge 하면 된다.
hotfix 를 Merge 했을 때와 메시지가 다르다.
현재 브랜치가 가리키는 커밋이 Merge 할 브랜치의 조상이 아니므로 Git는 'Fast-forward' 로 Merge 하지 않는다.
이 경우에는 GIt는 각 브랜치가 가리키는 커밋 두개와 공통 조상 하나를 사용하여 3-way Merge 한다.
커밋 3개를 Merge
브랜치 포인터를 최신 커밋으로 옮기는게 아니라 3-way Merge의 결과를 별도의 커밋으로 만들고 나서
해당 브랜치가 그 커밋을 가리키도록 이동시킨다.
그래서 이런 커밋은 부모가 여러 개고 Merge 커밋이라고 부른다.
Merge 커밋
Git는 Merge 하는데 필요한 최적의 공통 조상을 자동으로 찾는다.
충돌의 기초
3-way Merge가 실패할 때도 있다. Merge 하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고
Merge 하면 Git는 해당 부분을 Merge 하지 못한다.
예를 들어 53번 이슈와 hotfix가 같은 부분을 수정했다면 Git는 Merge 하지 못하고 아래와 같은 충돌 메시지를 출력한다.
Git는 자동으로 Merge 하지 못해서 새 커밋이 생기지 않는다.
변경사항의 충돌을 개발자가 해결하지 않는 한 Merge 과정을 진행 할 수 없다.
Merge 충돌이 일어났을 때 Git는 어떤 파일을 Merge 할수 없었는지 살펴보려면 git status 명령을 이용한다.
충돌이 일어난 파일은 unmerged 상태로 표시된다.
Git는 충돌이 난 부분을 표준 형식에 따라 표시해준다.
그러면 개발자는 해당 부분을 수동으로 해결한다.
충돌난 부분은 아래와 같이 표시된다.
====== 위쪽의 내용은 HEAD 버전(merge 명령을 실행할 때 작업하던 master 브랜치)의 내용이고
아래쪽은 iss53 브랜치의 내용이다.
충돌을 해결하려면 위쪽이나 아래쪽 내용 중에서 고르거나 새로 작성하여 Merge 한다.
다른 Merge 도구로 충돌을 해결할 수 있다. git mergetool 명령으로 실행한다.
Merge 도구를 종료하면 Git는 Merge가 잘 되었는지 물어본다.
잘 마쳤다고 입력하면 자동으로 git add 가 수행되고 해당 파일이 Staging Area에 저장된다.
git status 명령으로 충돌이 해결된 상태인지 다시 한번 확인해볼 수 있다.
Git 사용법 VI (0) | 2018.06.25 |
---|---|
Git 사용법 V (0) | 2018.06.22 |
Git 사용법 III (되돌리기/수정 파일 되돌리기/Pull/Fetch/tag) (0) | 2018.06.19 |
Git 사용법 II (0) | 2018.06.19 |
Git 사용법 I (저장소 생성/Clone/Status/Stage/.gitignore (0) | 2018.06.18 |