Branch는 버전 관리 시스템에서 코드베이스의 여러 버전을 동시에 개발하고 관리할 수 있도록 하는 기능입니다. 쉽게 말하면, 나뭇가지처럼 코드베이스가 여러 방향으로 분기되어 발전하는 것을 가능하게 합니다.
작업 트리(Work Tree) = 작업 공간(working space)
- 개인 컴퓨터 환경에서 소스 코드를 편집하는 일반적인 프로젝트 폴더입니다.
- 브랜치 또는 커밋 단위로 헤드를 움직일 시에 작업 트리도 같이 변화합니다.
HEAD⁉️
HEAD는 현재 작업중인 브랜치를 가리키는 화살표라고 생각하면 됩니다. master는 기본 개발 라인의 이름입니다. 따라서 HEAD -> master는 "현재 작업 중인 브랜치는 기본 개발 라인(master)입니다" 라는 뜻 입니다.
# branch 생성
git branch [새로운 브랜치 이름] # 지정된 이름의 새로운 브랜치를 생성합니다.
# branch 관리
git branch # 로컬 브랜치 목록을 확인합니다.
git branch -r # 원격 저장소의 브랜치 목록을 확인합니다.
git branch -a # 로컬 및 원격 저장소의 모든 브랜치 목록을 확인합니다.
git branch -v # 로컬 브랜치와 연결된 원격 브랜치 정보를 확인합니다.
# branch 이름 변경
git branch -m [브랜치 이름] # 브랜치의 이름을 변경, 이름을 변경하려면 해당 브랜치에서 이름을 변경해야 합니다.
# branch 삭제
git branch -d [브랜치 이름] # 로컬에서 지정된 브랜치를 삭제합니다.
git branch -D [브랜치 이름] # 로컬에서 지정된 브랜치를 강제 삭제합니다.
⭐️ Branching Exercise ⭐️
1. git checkout
git checkout
은 작업 브랜치를 변경하는데 사용됩니다. 예를 들어, git checkout my-branch
명령어는 현재 작업 중인 브랜치에서 my-branch
브랜치로 변경합니다.git checkout
은 특정 파일 또는 디렉토리의 변경 사항을 최근 커밋 상태로 되돌리는데도 사용됩니다. 예를 들어, git checkout -- myfile.txt
명령어는 myfile.txt
파일의 모든 변경 사항을 취소하고 최근 커밋의 상태로 되돌립니다.git checkout
명령어와 함께 커밋 해시를 사용합니다. 이렇게 하면 해당 커밋의 상태로 작업 트리가 변경됩니다. 즉, git checkout a1b2c3d4
와 같이 명령어를 입력하면 a1b2c3d4
라는 해시를 가진 커밋으로 작업 트리가 이동하게 됩니다. 이를 통해 특정 커밋의 상태를 확인하거나 이동할 수 있습니다.# branch 변경
git checkout [브랜치 이름] # 지정된 브랜치로 이동합니다.
git checkout -b [새로운 브랜치 이름] # 새로운 브랜치를 생성하고 해당 브랜치로 이동합니다.
git checkout -t [원격 브랜치 이름] # 원격 브랜치를 추적하는 로컬 브랜치를 생성 해당 브랜치로 이동
# 특정 커밋으로 이동
git checkout [커밋 해시] # 지정된 커밋으로 이동합니다.
git checkout HEAD~1 # 현재 브랜치의 이전 커밋으로 이동합니다.
# 파일 복원
git checkout [파일 이름] # 지정된 파일을 최신 버전으로 복원합니다.
git switch
git checkout -b [새로운 브랜치 이름]
명령어와 동일 합니다.git checkout
의 명령어 중 브랜치 변경 기능이 git switch
로 분리되어 사용되기 시작했습니다.git switch
를 사용할 때 스테이징되지 않은 변경 사항이 있을 경우, 새로운 브랜치에 포함되지 않기 때문에 변경 사항을 만들 때마다 브랜치 이동 전에 항상 git add
로 등록하고 git commit
할 것을 권장합니다. git switch
명령어를 사용할 때 스테이징되지 않은 변경 사항은 새로운 브랜치에 포함되지 않고 사라집니다. 따라서, 변경 사항을 만들 때마다 브랜치 이동 전에 git add
로 변경 사항을 스테이징하고 git commit
으로 커밋하는 것이 좋습니다.# branch 변경
git switch [브랜치 이름]
# 지정된 브랜치로 이동합니다.
# 로컬에 해당 브랜치가 없는 경우 자동으로 해당 원격 브랜치를 추적하는 로컬 브랜치가 만들어집니다.
git switch -c [새로운 브랜치 이름] # 새로운 브랜치를 생성하고 해당 브랜치로 이동합니다.
# 특정 커밋 이동
git switch [커밋 해시] # 특정 커밋으로 이동할 수 있습니다.
⭐️ Git Merging Exercise ⭐️
git merge
는 브랜치와 브랜치를 병합할 수 있는 명령입니다. 브랜치를 병합한다는 것은 다른 브랜치의 변경 사항을 현재 브랜치에 적용하는 것을 의미합니다.
# 브랜치 병합
git merge [병합할 브랜치명]
# merge를 진행할 때 아무 옵션을 주지 않는 경우
# 현 브랜치와 병합할 브랜치가 Fast-forward 관계이면 Fast-forward 병합을 진행하며, 그렇지 않은 경우는 Merge 커밋을 생성하여 3 way-merge를 진행
git merge --no-ff [병합할 브랜치명]
# 현재 브랜치와 병합 대상의 관계가 Fast-forward관계 여부와 상관없이 merge 커밋을 생성하여 병합
git merge --ff-only [병합할 브랜치명]
# 현재 브랜치와 병합 대상의 관계가 Fast-forward인 경우에만 병합을 진행, merge 커밋 생성되지 않는다.
git merge --squash [병합할 브랜치명]
<remote>/<branch>
형식으로 되어 있다. 예를 들어 리모트 저장소 origin
의 master
브랜치를 보고 싶다면 origin/master
라는 이름으로 브랜치를 확인하면 된다. 다른 팀원과 함께 어떤 이슈를 구현할 때 그 팀원이 iss53
브랜치를 서버로 Push 했고 당신도 로컬에 iss53
브랜치가 있다고 가정하자. 이때 서버의 iss53
브랜치가 가리키는 커밋은 로컬에서 origin/iss53
이 가리키는 커밋이다.git checkout origin/main
# 원격 추적 브랜치의 커밋으로 이동합니다.
Gitflow Workflow 에서는 항상 유지되는 메인 브랜치(master, develop), 일정 기간 동안만 유지되는 보조 브랜치들(feature, release, hotfix) 을 포함하여 총 5가지의 브랜치를 사용합니다.
master
: 제품으로 출시될 수 있는 브랜치develop
: 다음 출시 버전을 개발하는 브랜치feature
: 기능을 개발하는 브랜치release
: 이번 출시 버전을 준비하는 브랜치hotfix
: 출시 버전에서 발생한 버그를 수정 하는 브랜치develop branch
를 master branch
에 병합(merge)합니다.develop branch
를 기반으로 개발을 진행합니다.feature branch
는 새로운 기능 개발 및 버그 수정이 필요할 때마다 develop branch
로 부터 분기합니다.feature branch
에서의 작업은 기본적으로 공유할 필요가 없기 때문에, 자신의 로컬 저장소에서 관리한다. 개발이 완료되면 develop branch
로 병합(merge)하여 다른 사람과 공유 합니다.develop branch
에서 배포 가능한 수준의 기능이 모이거나 정해진 배포 일정이 되면 release branch
를 생성합니다.master branch
에서 분기하는 브랜치입니다. develop branch
에서 문제를 수정하여 배포 가능한 버전을 만드는데는 시간이 많이 걸리며 안정성을 보장하기 어렵습니다. 따라서, 바로 배포 가능한 master branch
에서 직접 브랜치를 생성하고, 필요한 부분만을 수정한 후 다시 master branch
에 병합(merge)하여 배포합니다.Fast-Forward merge는 Git에서 사용하는 병합 방식 중 하나입니다. 이 방식은 현재 브랜치에 변경 사항이 없고, 병합하려는 브랜치가 현재 브랜치로부터 직접 분기된 경우에 사용할 수 있습니다. 이런 경우, Git은 병합 과정을 "Fast Forward" 하여 이전 브랜치의 모든 커밋을 그대로 가져오고, 브랜치 포인터를 최신 커밋으로 이동시킵니다. 즉, 별도의 병합 커밋을 생성하지 않고, 새 브랜치의 커밋을 현재 브랜치로 "빠르게 앞당기는" 방식입니다.
my-branch
브랜치는 master
브랜치에서 분기되었습니다.my-branch
브랜치에서 변경 사항을 추가하고 커밋했습니다.master
브랜치에서 git merge my-branch
명령어를 사용하여 my-branch
브랜치의 변경 사항을 병합했습니다.my-branch
브랜치의 커밋이 master
브랜치에 "빠르게 앞당겨졌습니다."3-way merge는 Git에서 사용하는 병합 방식 중 하나로, 두 브랜치가 최근에 분기된 지점(공통 조상 커밋)을 기준으로 두 브랜치의 변경 사항을 비교하여 병합결과를 생성합니다.
예를 들어, feature 브랜치와 master 브랜치가 있다고 가정해봅시다. 각 브랜치의 마지막 커밋은 f2와 m2이고, 공통 조상 커밋(base)을 b라고 합시다. 3-way merge 과정은 다음과 같습니다.
1. 3-way-merge에서는 공통 조상 커밋(base)을 기준으로 각 브랜치의 마지막 커밋(f2, m2)의 차이점을 비교합니다.
2. 각 파일의 변경 사항을 분석하여 충돌 여부를 확인합니다.
3. 충돌이 없는 경우 Git은 자동으로 새로운 커밋을 생성합니다.
4. 충돌이 발생하면 개발자가 직접 해결해야 합니다.
1. 내 브랜치 커밋
2. 남의 브랜치 커밋
3. Base는 공통 조상 커밋을 의미하며, 두 브랜치가 공유하는 가장 최근의 커밋
Base
가 공통 조상 커밋으로서 기준이 되며, 이를 통해 동일한 내용을 공유하게 됩니다.
My | Base | Other |
---|---|---|
A | A | A |
B | B | B |
C | C | C |
D | D | D |
다음 표는 My
브랜치와 Other
브랜치의 변경 사항을 보여줍니다. Base
는 두 브랜치의 공통 조상 커밋입니다.
My | Base | Other |
---|---|---|
A | A | A |
H | B | B |
C | C | T |
H | D | T |
Base
커밋을 기준으로 My
브랜치와 Other
브랜치의 변경 사항을 비교합니다.
My | Base | Other | 3 way merge |
---|---|---|---|
A | A | A | A |
H | B | B | H |
C | C | T | T |
H | D | T | Conflict |
Base
와 My
및 Other
가 모두 동일한 경우 Base
의 내용이 병합 결과에 반영됩니다.Base
와 Other
가 동일하고 My
가 다른경우 My
의 내용이 병합 결과에 반영됩니다.Base
와 My
가 동일하고 Other
가 다른경우 Other
의 내용이 병합 결과에 반영됩니다. Base
, My
, Other
모두 다른 경우 충돌(Conflict)이 발생합니다. 두 브랜치 모두 Base와 다르게 변경되었기 때문입니다. 어떤 변경이 최신 업데이트인지 시스템이 판단할 수 없으므로, 사용자는 이 충돌을 수동으로 해결해야 합니다.병합 결과가 반영되어 작성된 내용과 아래의 충돌이 발생한 내용이 있습니다. 사용자는 이를 수동으로 해결해야 하는 상황입니다.
HEAD
의 내용 (현재 master
브랜치의 내용)을 사용합니다.feature
브랜치의 내용을 사용합니다.HEAD
및 feature
의 내용을 모두 사용합니다.<<<
, >>>
, ===
)를 제거하고 완전히 다른 내용을 입력합니다.commit
하면 merge
가 성공적으로 이루어집니다.Squash는 여러개의 커밋을 하나의 커밋으로 합치는 것을 의미합니다. Squash merge는 병합할 브랜치의 모든 커밋을 하나의 커밋으로 Squash한 새로운 커밋을 Base 브랜치에 추가하는 방식으로 병합하는 것을 의미합니다.
F
, G
, H
를 결합하여 새로운 commit, I
를 생성하고 main에 추가합니다. I
의 커밋의 부모는 E
커밋 입니다. 이는 Feature 브랜치의 commit history를 정리하기 위해 사용됩니다.Squash를 하게 되면 모든 커밋 이력이 하나의 커밋으로 합쳐지고 사라진다는 점을 주의해야 한다.
git switch main
git merge --squash my-branch
git commit -m "Squash and Merge"
rebase
를 사용하면 원하는 브랜치에 직접적으로 변경 사항을 적용할 수 있어서 커밋 히스토리가 선형적으로 유지됩니다.rebase
는 현재 브랜치를 선택한 브랜치의 최신 커밋 위로 옮기므로, 현재 브랜치와 선택한 브랜치를 병합할 때 추가적인 merge 커밋이 생성되지 않습니다.rebase
는 과거의 커밋을 변경하므로, 이미 원격 저장소에 Push된 커밋은 Rebase를 하지 않는 것이 일반적입니다.rebase
는 각 변경 사항을 순서대로 적용하기 때문에, 같은 충돌을 여러 번 해결해야 할 수도 있습니다.feature
브랜치와 master
브랜치가 있습니다.f2
와 m2
입니다.b
입니다.# feature 브랜치로 변경 HEAD 포인터는 feature를 가리킨다.
git switch feature
# feature 브랜치의 이력을 master 브랜치의 최신 커밋 위에 재배치
git rebase master
# master 브랜치로 변경 HEAD 포인터는 master를 가리킨다.
git switch master
# master 브랜치에 feature 브랜치의 변경 사항을 병합
git merge feature
git checkout feature # feature 브랜치로 변경 HEAD 포인터는 feature를 가리킨다.
git rebase master # feature 브랜치의 이력을 master 브랜치의 최신 커밋 위에 재배치
git checkout master # master 브랜치로 변경 HEAD 포인터는 master를 가리킨다.
git merge feature # master 브랜치에 feature 브랜치의 변경 사항을 병합
rebase
를 실행하면 기존 브랜치의 커밋(f1
과 f2
)들이 새로운 베이스 커밋 위에 다시 적용됩니다. 이 과정에서 기존의 커밋 객체들은 그래프 구조에서 더 이상 어떠한 브랜치나 태그에도 참조되지 않는 dangling 상태가 됩니다. 이러한 dangling 커밋들은 Git 내부에서는 여전히 존재하지만, 접근하기 위해서는 직접적인 참조가 필요하며 더 이상 필요하지 않은 경우에는 정리해주는 것이 좋습니다.