카테고리 없음

Git Branch란?

GoJay 2024. 11. 10. 14:30

Git Branch란?

Git은 코드의 변경 이력을 추적 관리하고, 다른 사람과의 협업을 원활하게 하기 위한 도구이다. 그리고, 해당 목적을 위해 브랜치(Branch)라는 개념이 사용된다.

프로젝트를 진행하다 보면 작성하던 코드에 오류가 있어서 뒤로 돌아가야 하는 상황이 생길 수 있다. 예를 들어, A 시점까지 커밋을 했다 문제가 생겨 B 시점으로 코드를 되돌리고, 해당 시점에서 기존과는 전혀 다른 방식으로 개발을 이어가게 되는 경우가 생길 수 있다.

이 상황에서 A까지 개발이 진행됐던 프로젝트 흐름과 B 시점으로 돌아가서 새롭게 (다른 방식으로) 개발을 진행하는 프로젝트의 진행 흐름은 두 개로 쪼개진 셈이다. 이런 상황을 '브랜치가 나눠졌다'라고 부른다. 실제론 프로젝트가 잘못됐기 때문에 롤백하는 차원에서 브랜치를 쪼개는 것 말고도 브랜치라는 개념이 사용되는 다양한 상황들이 존재한다.

대표적으로 2명 이상의 사람이 협업하여 하나의 프로젝트에 들어가는 기능을 병렬적으로 함께 개발하는 상황이다. 두 명 이상의 사람이 하나의 프로젝트를 같이 동시에 개발한다면 한 명이 작업한 코드가 다른 사람이 작업한 코드에 영향이 갈 가능성이 매우 높다. 그런 상황마다 같이 개발하는 사람들이 둘러앉아 처리 방법을 매번 논의해야 한다면 상당히 피곤할 것이다.

대신, 하나의 프로젝트에 두 명 이상이 동시에 다른 기능을 개발하더라도, 개발하는 시점에선 서로가 개발하는 게 서로에게 영향이 가지 않도록 독립적인 환경에서 만들고, 나중에 한 번에 논의하면서 합치는 게 더 좋을 수 있다. 이러한 상황에서도 브랜치가 사용된다. 아래는 하나의 서비스를 2명 이상의 사람이 동시에 개발할 때 브랜치를 나눠서 작업하는 예시이다.

둘 이상이 함께 작업하는 상황에서 브랜치를 어떻게 쪼개고 관리할지를 결정하는 것은 협업에 있어 매우 중요하다. 심지어, 브랜치 관리의Best Practice 관련 여러 논의가 존재하고, '브랜치 전략'이라는 게 있을 정도로, 브랜치를 잘 쪼개고 관리하는 것은 현업에서 매우 중요한 아젠다이다.

Git Branch 관련 명령어

로컬 레포지토리 브랜치 명령어

branch 명령어는 브랜치 관리 관련 다양한 기능을 제공한다. 가장 먼저, git init에서 초기화한 폴더를 터미널에 띄워놓고 git branch라는 명령어를 입력해 보면 master라는 브랜치가 확인된다.

참고로, git을 가장 처음 시작할 때 기본 브랜치 하나가 자동으로 생성되는데, 해당 브랜치의 로컬 이름은 master, 원격 레포지토리(e.g. Github)에서의 이름은 main이다. 최초엔 원격 레포지토리도 초기 브랜치 이름을 master로 사용했었다. 그러다 Github은 2020년 10월에 기본 브랜치의 이름을 main으로 바꾸겠다고 발표했다. 하지만, 로컬에서는 아직 main으로 이름이 변경되지 않아 두 브랜치 이름에 차이가 생겼다.

해당 상태로 그냥 프로젝트를 진행해도 큰 문제는 없지만, 만약에 둘의 이름을 맞춰주고 싶다면 아래 명령어를 사용하면 된다.

git branch -m master main

branch -m은 로컬 레포지토리에 있는 브랜치 이름을 바꿔주는 명령어이다(굳이 mastermain으로 바꾸는 게 아니라, 어떠한 브랜치 이름을 바꿀 때에도 해당 명령어가 사용된다). 만약에 로컬 레포지토리의 기본 브랜치 이름을 아예 main으로 세팅해두고 싶으면 아래 명령어를 사용하면 된다.

git config --global init.defaultBranch main

만약에 새로운 브랜치를 생성하고 싶으면 git branch <브랜치 이름>으로 생성할 수 있다. 참고로, 브랜치를 생성할 때 현재의 브랜치 위치가 어디인지가 굉장히 중요하다. git branch <브랜치 이름>으로 새로운 브랜치를 만들면 해당 명령어를 한 브랜치의 커밋 해시 위치에서 브랜치가 쪼개지기 때문이다. 아래는 특정 커밋 해시 위치에서 브랜치를 생성하는 명령어이다(커밋 해시는 git log로 확인 가능하다).

git branch <브랜치명> <커밋 해시>

A 브랜치의 B 커밋 위치에서 쪼개진 브랜치는 A 브랜치의 B 커밋까지의 커밋 기록을 공유한다. 그렇기 때문에, 브랜치를 새로 생성하는 커밋 위치가 브랜치 위치와 커밋 위치 확인하는 걸 습관화해야 한다.

로컬 레포지토리에 현재 적용되는 브랜치를 변경하기 위해선(브랜치 위치를 바꾸기 위해선) checkout 또는 switch 명령어를 사용할 수 있다.

git checkout <이동할 브랜치 이름>
git switch <이동할 브랜치 이름>

git checkout은 브랜치 이동 말고 다른 목적으로도 사용된다. 예를 들어서 브랜치 이동이 아니라 다른 커밋 위치로 이동할 때라던가(git checkout <돌아갈 커밋 해시>), 워킹 디렉토리에서 특정 파일을 마지막 커밋 상태로 복원할 때(git checkout -- <파일명>), 새 브랜치 생성 및 체크아웃(이동, git checkout -b <브랜치 이름>) 등에도 사용된다.

git switch는 브랜치 이동에만 특화된 명령어다. 만약에 checkout으로 브랜치 이동을 하게 되면 의도치 않게 checkout의 다른 기능이 적용되어 의도치 않은 동작이 발생할 수도 있기 때문에, 목적이 브랜치를 이동하는 것에 한정된다면 switch 명령어를 사용하는 걸 권장한다.

브랜치를 삭제하고 싶으면 git branch -d <브랜치 이름>을 사용하면 된다. 단, 브랜치에 완료되지 않은 커밋(추적 중인 변경 사항, 스테이징 된 사항)이 있을 경우 브랜치 삭제가 되지 않는다. 그럴 경우 git branch -D <브랜치 이름>처럼 대문자를 사용해 명령하면 변경 내용 존재 여부와 상관없이 삭제가 가능하다.

원격 레포지토리 명령어

위의 명령어들은 로컬 레포지토리의 git 브랜치를 관리하기 위한 명령어들이다. 하지만, 위의 절차대로 로컬에 있는 git branch 정보를 수정했다고 하더라도, 이 정보들이 원격 레포지토리에 바로 반영되진 않는다. 그래서, 몇몇 수정사항들이 원격에도 반영되도록 하기 위한 절차가 필요하다.
git push origin --delete <기존_브랜치명>

먼저, `git branch -a`를 쓰면 원격 레포지토리 목록을 확인할 수 있다. 해당 명령어를 통해 로컬과 원격 레포지토리에 생성된 브랜치들의 차이를 비교할 수 있다.

로컬에서 새로운 브랜치를 생성했다면 `push`를 통해 원격에도 이를 반영해줘야 한다.

```bash
git push origin -u <새로운 브랜치 이름>

여기서 -u--set-upstream의 줄임말로, 해당 키워드를 붙여주면 추적 브랜치로 설정되어 이후에는 간단히 git push 또는 git pull만 입력해도 해당 원격 브랜치와 동기화할 수 있다.

branch 이름을 로컬에서 수정했다면 위의 절차로 원격 레포지토리에 브랜치 정보를 push 해주고, 원격 레포지토리에 남아있을 기존 이름의 브랜치를 삭제해 주면 된다. 삭제에는 아래 명령어가 사용된다.

git push origin --delete <기존_브랜치명>

아쉽게도 원격 레포지토리의 브랜치 이름을 바로 변경하는 명령어는 없기 때문에, 수정이 필요할 시 원격으로 푸시하고 기존 브랜치를 삭제해줘야 한다(이름을 바꿔도 로컬 레포지토리의 브랜치에 기존 커밋 히스토리는 그대로 남아있기 때문에 기존 기록이 휘발되지 않는다).

가끔씩 원격 레포지토리의 브랜치 정보가 더 최신이고, 로컬에서 원격의 브랜치 정보를 받아와야 하는 경우가 있다. 해당 경우엔 git fetch 명령어를 사용하면 된다. 해당 명령어를 사용하면 원격에만 있고 로컬에 없는 브랜치 정보를 로컬로 받아올 수 있다. 단, 원격에 없고 로컬에만 있는 브랜치가 있을 시 해당 브랜치를 삭제하진 않는다.

만약에 원격에 없고 로컬에만 있는 브랜치를 삭제까지 하고 싶다면 git fetch --prune 명령어를 사용하면 된다. 뒤에 --prune을 붙여지면 원격에 있는 브랜치 정보와 100% 일치되게 정보를 받아올 수 있다.

참고로 fetch 명령어는 원격에 있는 브랜치, 커밋 정보 등을 로컬로 받아오는 역할을 한다는 점에서 pull과 유사한 명령어다. 하지만, pull은 원격의 코드를 기준으로 로컬의 코드가 바로 변경되게 동작되기 때문에 그 과정에서 충돌이 나거나, 또는 사라지면 안 되는 로컬의 변경 사항이 없어질 수 있다. 반면, fetch는 정보만 일단 끌어온 뒤 로컬에서 변경 정보를 비교해 가면서 원하는 정보만 병합해갈 수 있다. fetch에 대해선 아직 사용 경험이 많지 않아서, 추가적으로 학습해 봐야겠다.

결론

지금까지 브랜치 생성, 수정, 삭제, 이동 등에 대한 명령어를 간단히 알아봤다. 기본 명령어만 해도 다양하다. 하지만, 브랜치 사용에 있어 가장 중요하고, 또 가장 실수를 많이 할 수 있는 건 '병합(merge)'에 대한 거다. 브랜치의 병합 메서드는 다시 공부해서 따로 정리를 해봐야겠다.