브랜치(Branch)란?
소프트웨어를 개발할 때 개발자들은 동일한 소스코드를 공유하고 다루게 된다. 동일한 소스코드 기반으로 여러 사람이 서로 다른 작업을 하게 된다면 서로 다른 버전의 코드가 만들어지게 되는데 이럴 때 여러 개발자들이 동시에 다양한 작업을 할 수 있게 만들어 주는 기능인 브랜치를 사용한다.
- 기존 소스 코드에 새로운 기능을 만들어 새 버전을 만들더라도 기능이 오류 없이 완벽하게 동작한다는 보장이 없고 오히려 기존에 잘 작동되던 기능도 동작하지 않을 가능성도 있다.
- 깃에서 브랜치는 여러 작업을 각각 독립된 공간에서 진행할 수 있도록 하는 기능이다.
- 하나의 브랜치는 각자의 독립적인 워크스페이스, 인덱스, 로컬 레포지토리, 리모트 레포지토리 공간을 가져 다른 브랜치의 영향을 받지 않고 소스 코드와는 상관없이 독립적으로 개발을 진행할 수 있다.
모든 버전 관리 시스템은 브랜치를 지원하지만 깃의 브랜치는 매우 가볍기 때문에 다른 버전 관리 시스템과는 달리 Git은 브랜치를 만들어 작업하고 나중에 병합(Merge) 하는 방법을 권장한다.
- 브랜치는 원래 나뭇가지라는 뜻으로 버전 관리 시스템에서는 나무가 가지에서 새 줄기를 뻗듯이 여러 갈래로 퍼지는 데이터 흐름을 가리키는 의미로 사용 된다.
깃(Git)의 branch
파일이 하나 존재하는 디렉토리를 git init 명령어를 실행하면 Git 저장소가 생성되고 ref:refs/heads/master 라는 내용을 가진 HEAD 파일이 생성이 된다.
- HEAD는 현재 작업중인 브랜치를 가리키는 포인터이다.
- 아직 존재하지 않는 브랜치를 가리키는 상태이다.
커밋을 하게 되면 refs/heads경로에 master파일(브랜치)이 생성된다. 브랜치는 브랜치에 담긴 커밋 중 가장 마지막 커밋을 가리킨다. 깃은 HEAD파일을 통해 현재 브랜치가 가리키는 마지막 커밋을 보고 가장 최신 커밋이 무엇인지 알 수 있다.
- 그리고 그 이전 커밋은 커밋 오브젝트의 parent를 통해서 탐색할 수 있다.
- 지금의 HEAD가 가리키는 커밋은 바로 다음 커밋의 부모가 된다.
- 단순하게 생각하면 HEAD는 현재 브랜치 마지막 커밋의 스냅샷이다.
브랜치 생성(git branch)
git branch 명령어를 통해 새로운 브랜치를 생성할 수 있다. 새로운 브랜치를 생성하지 않으면 master 브랜치에서 작업을 수행하게 된다.
- 기존 브랜치에서 새 브랜치를 만드는 것을 '분기한다' 라고 한다.
$ git branch <브랜치 이름>
// $ git branch exp
새로운 브랜치를 생성하게 된다면 refs/heads경로에 새로운 파일이 생성되어 가장 최근에 커밋한 커밋 오브젝트의 아이디 값을 가지게 된다.
- 현재 작업중인 브랜치(지금은 master)의 작업 내용을 그대로 복사한 브랜치가 생성된다.
- 새로운 커밋을 하지 않는다면 기존 브랜치와 같은 커밋을 가리키고 있다.
이를 통해 깃에서 브랜치라는 것은 중요하고 강력한 일을 하지만 단지 refs 디렉토리의 하위에 있는 파일이며 가장 최근에 커밋한 커밋 오브젝트의 아이디를 가리킨다는 것을 알 수 있다.
브랜치 변경(git checkout / git switch)
git checkout 명령어를 실행하면 작업할 브랜치가 변경된다.
$ git checkout <브랜치 이름>
$ git switch <브랜치 이름>
// $ git checkout exp
// $ git switch exp
- 명령어를 실행하면 현재 작업중인 브랜치를 의미하는 HEAD파일의 내용이 변경된다.( ref:refs/heads/exp)
- 현재 작업중인 브랜치를 기리키는 HEAD파일이 다른 브랜치를 가리키도록 바뀌고, tree를 통해 얻은 정보를 Index에 놓는다 그리고 그리고 Index의 내용을 워킹 디렉토리로 복사하여 각각의 브랜치가 독립적으로 개발할 수 있게된다.
$ git cat-file -p bf7cb91f4e7fa875de334e02547257beda169431
> tree b55739284721c1ee2787670c3c6a65e102e0f993
> parent 8804281115e2aa3f8ded2ae89420f44c946dda5e
> author
> committer
3
$ git cat-file -p b55739284721c1ee2787670c3c6a65e102e0f993
> 100644 blob 422c2b7ab3b3c668038da977e4e93a5fc623169c f1.txt
> 100644 blob b68025345d5301abad4d9ec9166f455243a0d746 f2.txt
- 브랜치를 변경한 뒤 다시 커밋을 하게 된다면 새로운 브랜치의 파일 내용만이 변경된다.
- 커밋을 하면 HEAD가 가리키는 브랜치(exp)의 내용이 새로운 커밋 아이디로 변경된다.
git stash
다른 요청이 들어와 작업이 아직 끝나지 않았는데 브랜치를 변경이 필요한 상황이 발생 했다고 작업이 완료되지 않은 채 commit하는 것은 껄끄럽다. 이런 상황에서 stash를 이용해 작업했던 내용을 숨겨 놓고 브랜치의 가장 최신커밋 헤드의 버전으로 이동해서 현재 브랜치의 상황을 깔끔하게 만들고 다른 브랜치로 checkout할 수 있다.
- 작업중인 파일 두개 중 하나만 add하고 git status 명령어를 사용한다.
OK@DESKTOP-SJ9C79T MINGW64 ~/study/stash (exp)
$ git status
On branch exp
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: f3.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: f1.txt
- git stash 명령어를 사용하고 다시 확인해 보면 결과가 깨끗해 진것을 확인할 수 있다.
$ git stash
$ status
bash: status: command not found
- 저장된 stash를 확인하고 불러와 다시 적용할 수 있다.(git stash list / git stash apply)
// 저장된 stash
$ git stash list
stash@{0}: WIP on exp: 0015d93 3
// git stash apply <스태시 이름> 를 사용하여 Stash를 다시 적용할 수 있다.
// 이름을 입력하지 않으면 가장 최신 Stash를 적용한다.
$ git stash apply stash@{0}
On branch exp
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: f3.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: f1.txt
브랜치의 생성과 병경을 동시에?
두가지 방법을 통해 브랜치의 생성과 변경을 동시에 할 수 있다.
git checkout -b <브랜치 이름> // git checkout --branch <브랜치 이름>
git switch -c <브랜치 이름> // git switch --create <브랜치 이름>
- 새로운 브랜치가 생성될 브랜치를 지정하지 않으면 HEAD가 가리키는 브랜치가 지정된다.
$ git checkout -b <브랜치 이름> <커밋 아이디>
$ git switch -c <브랜치 이름> <커밋 아이디>
브랜치 삭제(git branch -d)
$ git branch -d <브랜치 이름>
브랜치 병합
기존 브랜치(master)에서 분기한 새 브랜치(exp)에서 작업할 내용을 끝나치고 분기 했던 브랜치(exp)를 기존브랜치( master)에 합치는 것을 병합(merge)한다고 한다.
- 깃에서 한 브랜치에서 다른 브랜치로 합치는 방법으로는 두 가지가 있으며 하나는 Merge 명령어 이고 다른 하나는 Rebase명령어 이다.
git merge
$ git merge <브랜치 이름>
- 현재 작업중인 브랜치를 기준으로 <브랜치 이름>을 병합한다.
fast-forward
OK@DESKTOP-SJ9C79T MINGW64 ~/study/fast-forward (master)
$ git merge hotfix
Updating d13a880..dcfdf3f
Fast-forward
c4.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 c4.txt
C4 커밋은 C2 커밋에 기반한 브랜치이기 때문에 별도의 커밋을 생성하지 않으며 Merge 과정 없이 브랜치 포인터( ref:refs/heads/master)가 그저 최신 커밋으로 이동한다.
- A 브랜치(master)에서 다른 B 브랜치(hotfix)를 Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 별도의 커밋을 생성하지 않으며 그저 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동시킬 뿐이다.
- 작업을 완료하고 더 이상 필요 없어진 브랜치(hotfix)는 삭제한다.
3-way merge
OK@DESKTOP-SJ9C79T MINGW64 ~/study/3-way merge (master)
$ git merge iss53
Merge made by the 'ort' strategy.
c3.txt | 1 +
c5.txt | 1 +
2 files changed, 2 insertions(+)
create mode 100644 c3.txt
create mode 100644 c5.txt
현재 브랜치가 가리키는 커밋이 Merge 할 브랜치의 조상이 아니므로 Fast-forward를 사용하지 않고 각 브랜치가 가리키는 커밋 두 개와 공통 조상을 사용하여 3-way Merge를 한다.
- Fast-forward와 달리 단순히 브랜치 포인터를 최신 커밋으로 옮기는 게 아니라 3-way Merge의 결과를 별도의 커밋으로 만들고 나서 해당 브랜치가 그 커밋을 가리키도록 이동시킨다.

- 3-way Merge방식으로 병합된 커밋은 부모가 여러 개이며 Merge 커밋이라고 부른다.
OK@DESKTOP-SJ9C79T MINGW64 ~/study/3-way merge (master)
$ git cat-file -p d4c0b0d6a5f2aab747ebfee2f24413c7c5861b9b
tree 84ab701ad75794cf142bf9f086161c531797ac5f
parent dcfdf3fd84420d16f998f792c925bb949870af78
parent e1c137426d0cc187654e9837a9c9fb21f94d924d
author PiggPotato <ki9678@naver.com> 1712916151 +0900
committer PiggPotato <ki9678@naver.com> 1712916151 +0900
Merge branch 'iss53'
- 작업을 완료하고 더 이상 필요 없어진 브랜치(hotfix)는 삭제한다.
git rebase
두 브랜치를 합치는 가장 쉬운 방법은 merge 명령을 사용하는 것이지만 다른 방법으로 Rebase 방법이 있다.
$ git rebase <브랜치 이름>
- 현재 작업중인 브랜치의 commit들을 <브랜치 이름>(대상 branch)에 재배치한다.
$ git checkout experiment
$ git rebase master
- 일단 두 브랜치가 나뉘기 전인 공통 커밋(C2)으로 이동하고 나서 그 커밋부터 지금 Checkout 한 브랜치(experiment)가 가리키는 커밋(C4)까지 diff를 차례로 만들어 어딘가에 임시로 저장해 놓는다.
- Rebase 할 브랜치(experiment)가 합칠 브랜치(master)가 가리키는 커밋(C3)을 가리키게 하고 아까 저장해 놓았던 변경사항을 차례대로 적용한다.
-
그리고 나서 master 브랜치를 Fast-forward 시킨다.
$ git checkout master $ git merge experiment
Merge 이든 Rebase 든 둘 다 브랜치를 합치는 관점에서는 서로 다를 게 없지만 Rebase가 좀 더 깨끗한 히스토리를 만든다.
- 여러 브랜치가 병렬로 동시에 진행해도 Rebase 하고 나면 모든 작업이 차례대로 수행된 것처럼 보인다.
- Rebase 한 브랜치는 히스토리가 선형(일자)으로 Rebase를 하든지, Merge를 하든지 최종 결과물은 같고 커밋 히스토리만 다르다.
Rebase 의 경우는 브랜치의 변경사항을 순서대로 다른 브랜치에 적용하면서 합치고 Merge 의 경우는 두 브랜치의 최종결과만을 가지고 합친다.
'Git' 카테고리의 다른 글
[Git] Git-flow (0) | 2024.06.17 |
---|---|
[Git] 깃(Git)의 원리(4, 충돌과 충돌 해결)와 Git flow (1) | 2024.06.11 |
[Git] 커밋 취소/되돌리기/덮어쓰기(reset / revert / amend) (0) | 2024.06.04 |
[Git] 태그(tag)의 기초와 사용법 (0) | 2024.06.02 |
[Git] 깃(Git)의 원리(2, commit)와 깃 객체(Git Object) (0) | 2024.06.02 |