* 프로그래머스, 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 5기 강의 수강 내용을 정리하는 포스팅.
* 원활한 내용 이해를 위해 수업에서 제시된 자료 이외에, 개인적으로 조사한 자료 등을 덧붙이고 있음.
간혹 Github 관련 자료를 찾다보면, Git을 최초로 만들었을 때 자동으로 생성되는 브랜치 이름이 'Main'과 'Master'의 2가지로 혼용되고 있는 것을 볼 수 있다.
따라서 Github 사용을 위해서 구글링 한 자료 등에서 제시된 명령어를 그대로 복붙했다가 브랜치 이름이 달라서 문제를 겪고는 하는데..
도대체 왜 2가지 이름이 혼용되고 있는 것인가? 그 이유는 Git, Github 사이에 기본으로 사용되는 브랜치 이름이 일치하지 않기 때문이다. (물론 지금은 해결된 문제. 인터넷 상의 자료는 과거의 것이 많기 때문에 구글링해서 찾은 자료에 의존하면 여전히 문제가 될 수 있다.)
목적에 따라서 브랜치를 복수로 사용하는 것은 Github 사용의 상식과 같은 일이다.
그렇다면 효율적인 협업을 위해 브랜치의 이름을 짓는 규칙이라는 게 존재하는 것도 당연한 일. 대략 아래와 같은 규칙이 존재한다.
(실제 실무에서는 특정 회사, 부서, 팀, 파트 등의 단위에 따라 각자 다른 규칙을 사용하는 경우도 있으니 대략 참조만 할 것.)
메인 브랜치 (main/master):
기능 브랜치 (feature branches):
새로운 기능을 개발할 때 사용.
feature/ 접두사를 붙이고 기능 이름을 명시.
예: feature/user-login, feature/add-shopping-cart
버그 수정 브랜치 (bugfix/fix branches):
기존 기능에 대한 버그를 수정할 때 사용.
bugfix/ 또는 fix/ 접두사를 붙임.
예: bugfix/fix-login-issue, fix/cart-update-error
단계 브랜치 (staging branch):
배포 전 최종 테스트를 위한 브랜치.
보통 staging 또는 pre-release와 같은 이름을 사용.
예: pre-release-1.1
핫픽스 브랜치 (hotfix branches):
긴급하게 배포해야 하는 수정이 있을 때 사용.
hotfix/ 접두사를 붙임.
예: hotfix/fix-crash-on-launch, hotfix/security-patch
실험 브랜치 (experiment branches):
새로운 아이디어나 실험적인 기능을 테스트할 때 사용.
experiment/ 접두사를 붙임.
예: experiment/new-ui-test, experiment/ai-recommendations
git branch
git branch 'branch-name'
git checkout -b 'branch-name'
git checkout 'branch-name'
git merge 'branch-name'
git branch -d 'branch-name'
git push origin --delete 'branch-name'
git fetch origin 'branch-name'
git checkout -t origin/'branch-name'
git branch -m 'new-branch-name'
git branch -m 'old-branch-name' 'new-branch-name'
git branch --show-current
git branch -r
git branch -a
git diff 'branch-A' 'branch-B'
특정 브랜치에서 작업하여 수정된 파일을, 다른 브랜치로 이동해보면 수정된 파일이 그대로 다른 브랜치까지 이동된다.
이렇게되면 브랜치를 복수로 사용하는 이유가 없는데, 어째서 이런 일이 생기는가?
수정된 작업을 커밋해두지 않으면, 수정된 내용은 브랜치를 이동해도 그대로 유지되기 때문이다.
git push origin 'branch-name'
git push -u origin 'branch-name'
로컬에서 생성한 브랜치에서 작업을 수행하고, 커밋까지 수행한 뒤. 위 명령어로 변경사항을 푸시해주면된다.
로컬 브랜치를 원격 저장소에 업로드하는 명령어. -u 옵션은 원격 브랜치와 로컬 브랜치 간에 추적 관계를 설정해 주는 것으로, 이후부터는 git push 명령어만으로도 쉽게 푸시할 수 있게 된다.
Fast-Forward 병합은 병합하려는 브랜치가 단순히 앞쪽으로 이동하는 방식.
Git은 기본적으로 히스토리가 분기되지 않고 직선으로 이어진 경우, 기존 커밋 히스토리를 유지하면서 HEAD를 앞으로 이동.
이 과정에서 새로운 병합 커밋이 생성되지 않으며, 브랜치의 포인터가 빠르게 이동하는 것처럼 보인다고 해서 Fast-Forward라는 이름이 붙었다.
C 기능까지 수행하고, 새 브랜치를 만들어서 D/E를 수행한 뒤, 두 브랜치를 병합시키는 것.
별도의 병합 커밋을 생성하지 않기 때문에 커밋 히스토리가 깔끔하게 유지되고, 히스토리가 깔끔하게 이어지므로, 로그를 봤을 때 병합 작업이 일어난 부분을 명확하게 구분하지 않아도 된다는 장점이 있다.
경우에 따라 히스토리의 명확성을 위해 병합 커밋을 남기고 싶은 경우, --no-ff 옵션을 사용하여 No Fast-Forward 병합을 강제로 수행할 수 있다.
git merge --no-ff 'branch-name'
Git에서 브랜치를 병합할 때, 두 개 이상의 브랜치가 다른 경로로 발전하여 Fast-Forward 병합이 불가능할 때 사용하는 병합 방식.
두 브랜치와 공통 조상(공통 부모 커밋)을 참고하여 세 가지 커밋을 기반으로 병합하는 방식이라 3-Way 병합이라 명명됨.
두 브랜치가 분기된 시점의 공통 조상 커밋, 현재 병합을 수행하려는 브랜치의 최신 커밋, 병합하려는 대상 브랜치의 최신 커밋의 3개 커밋을 참고하여 병합을 수행한다.
부모 커밋의 정보가 포함하는 새로운 병합 커밋을 생성, 각 브랜치가 다른 변경 사항을 가지고 발전했을 때도 병합을 처리할 수 있음.
두 브랜치가 분기된 이후 각각 독립적으로 여러 커밋을 거쳐 다른 변경 사항을 포함하게 된 경우, 기능 개발이 완료된 기능 브랜치를 메인 브랜치로 병합하거나, 여러 사람이 독립적으로 작업한 브랜치를 병합할 때 자주 사용함.
다만, 만약 같은 파일의 동일한 부분이 두 브랜치에서 각각 다르게 수정되었다면, Git이 자동으로 병합할 수 없어 병합 충돌(merge conflict)이 발생할 수 있고 이는 사용자가 스스로 해결해야한다.
얼핏 이해하기가 어려운 내용인데.. 사실 이게 가장 흔하게 사용되는 브랜치 전략 중 하나이다. 서로 다른 기능 구현을 위해 브랜치가 나뉘어지고, 작업이 길어질 수록 두 브랜치에서 다루는 파일의 내용들이 점차 상이해진다.
그런데 테스트든 배포든 결국에는 완전히 다른 상태인 브랜치를 하나로 합쳐주어야 한다. 3-way라고 말을 어렵게 표현해서 그렇지, 결국에는 브랜치를 나누어서 작업하다가 나중에 하나로 합친다는 소리.
주요 브랜치
main/master: 최종 배포 코드를 유지하는 안정적인 브랜치.
develop: 개발 중인 코드가 모이는 브랜치.
하위 브랜치
feature: 기능 개발을 위한 브랜치.
release: 배포 준비를 위해 테스트하는 브랜치.
hotfix: 긴급한 수정이 필요할 때 사용하는 브랜치.
주요 브랜치
main: 배포 가능한 최신 코드가 유지되는 브랜치.
하위 브랜치
feature: 기능 개발을 위한 브랜치로, 작업이 완료되면 main에 병합합니다.
주요 브랜치
main: 개발이 진행되는 기본 브랜치.
staging: 배포 전 테스트를 진행하는 브랜치.
production: 실제 서비스가 운영되는 배포 브랜치.
주요 브랜치
main: 모든 개발 작업이 직접 병합되는 기본 브랜치.
하위 브랜치
기능이나 버그 수정 시 임시 브랜치를 만들지만, 작업이 완료되면 빠르게 main에 병합하고 삭제합니다.
브랜치 병합은 각자 독립적으로 작업한 브랜치를 하나로 합치는 작업, 여러 개발자가 각자의 브랜치에서 기능을 개발하거나 버그를 수정한 뒤, 이 작업을 메인 브랜치로 병합하여 하나의 통합된 코드로 만들게 된다.
브랜치 전략이라는 것은 목적을 위해 복수의 브랜치를 나누어 사용하는 것이다. 그런데 나누어진 브랜치는 언젠가는 반드시 하나의 브랜치로 다시 합쳐져야한다.
브랜치 생성, 작업 수행 및 완료/커밋, PR 생성, 코드 리뷰 및 피드백, 피드백 반영, 병합 승인 및 완료의 순서로 진행된다.
Pull Request. GitHub와 같은 원격 저장소에서 브랜치를 병합하기 전에, 코드 리뷰와 논의를 위한 요청을 보내는 것. 팀 협업에서 중요한 역할을 하며, 코드 품질을 높이고 버그를 사전에 예방하는 데 도움이 된다.
PR을 통해 다른 사람의 코드를 검토하고 피드백을 주고 받아 코드의 품질을 높이고 오류를 방지할 수 있다.
특정 기능이나 버그 수정에 대해 팀원들과 논의하여 팀이 같은 방향으로 나아가도록 조율할 수 있다.
PR에 자동화된 테스트나 빌드 파이프라인이 연결된 경우, 코드가 병합되기 전에 오류가 있는지 확인할 수 있다.
GitHub에서 특정 브랜치의 안정성과 품질을 유지하기 위해 설정할 수 있는 규칙.
주요 브랜치에서 실수로 잘못된 코드가 병합되거나 삭제되는 것을 방지하고, 협업 시 코드 품질을 관리하기 위해 사용한다.
코드 리뷰 필수 설정, CI 검사 실행 여부, 변경사항 승인 후 병합, 강제 푸시 방지, 병합 전 모든 커밋 검토, 최신 상태로 유지, 서명된 커밋만 허용 등의 규칙을 설정할 수 있다.
어느 브랜치에서 Main 브랜치로 합쳐지는지, 충돌의 발생 여부는 어떠한지, 해당 PR 작업의 제목/설명, 각 파트의 내용 (작업자, 리뷰어, 작업의 성격, 프로젝트 및 마일스톤 내용) 등의 내용을 기재할 수 있다.
description 파트는 마크다운 문법을 지원한다. 이왕이면 가독성 있게 내용을 작성하는게 협업에도 도움이 된다.
이후 코드 변경 내용을 확인하고, 리뷰어로부터 승인을 받아 최종적으로 브랜치를 병합할 수 있다.
PR의 제목 및 설명, 상태, 팀원 등이 리뷰를 남기는 댓글, 병합 조건 및 가능 상태, 병합 버튼 등이 존재한다.
병합 작업은 대단히 중요하고, 되돌리기 힘든 것이기에 확인 작업을 몇 단계 거치게 된다. 다만 Github에서 Revert를 제공해주고 있기 때문에, 한번 병합하면 절대 못 되돌린다 이런 것까지는 아니지만, 주의하고 또 주의해서 병합을 진행해야한다.
병합되고 남은 브랜치는 삭제를 권장한다.
git fetch -p
git branch -d 'branch-name'
git pull origin main
git fetch -p를 통해 원격에서 삭제된 브랜치를 로컬에서도 인식하도록 수행.
이후, 로컬에 남아 있는 브랜치를 git branch -d 'branch-name' 명령어로 수동 삭제.
최종적으로, git pull origin main을 통해 원격의 최신 상태를 로컬에 반영, 모든 기록이 동기화되도록 수행.
두 브랜치에서 동일한 파일의 같은 부분을 다르게 수정했을 때 발생하는 문제. Git은 기본적으로 각 커밋을 병합할 때 변경 사항을 자동으로 병합하지만, 같은 위치의 내용이 다르게 변경된 경우에는 자동 병합이 불가능하여 사용자가 수동으로 충돌을 해결해야한다.
동일 파일, 동일한 줄을 서로 다르게 수정 / 한 브랜치에서는 파일을 수정하고 다른 브랜치에서는 삭제 / 병합 시 여러 브랜치에서 동일한 부분이 반복적으로 수정된 경우 등에서 충돌이 발생한다.
충돌이 발생하면 Git은 자동으로 해결할 수 없는 부분을 알려주고, 사용자가 직접 충돌을 해결하도록 요청한다.
Github 사용 중에 경험하는 대단히 빡치는 일 중 하나. 쉽게 해결되는 상황이라고 해도 귀찮은 일이 하나 더해지는 것이고, 해결이 어려울 수록 빡침의 농도가 깊어진다.
Git 충돌 및 해결 방법에 대한 내용은 매우 중요한 것으로, 향후 독립된 포스팅에서 상세하게 다룰 예정이다.
<<<<<<< HEAD
// 현재 브랜치의 변경 사항
=======
// 병합하려는 브랜치의 변경 사항
>>>>>>> branch-name
필요에 따라 "현재 브랜치의 변경 내용" 또는 "병합하려는 브랜치의 변경 내용" 중 하나를 선택하거나, 두 내용을 합친 새로운 내용을 작성하여 충돌을 해결할 수 있다.
충돌 구분자(<<<<<<<, =======, >>>>>>>)는 반드시 삭제해야한다.
충돌을 해결한 파일을 저장하고, 변경 사항을 저장소에 반영. 충돌이 해결된 파일을 Git에 다시 스테이징하여 처리하면 완료.
충돌은 일단 발생하는 순간부터 작업에 지장을 주는 요소이다. 따라서 처음부터 충돌을 방지하는 것이 가장 좋은 충돌 해결 방법이다.
작업 전 git pull로 최신 상태 유지, 협업 시에는 PR을 사용하여 충돌을 사전에 해결하고 코드 리뷰를 통해 오류를 최소화, 작업을 자주 커밋하고 푸시 (다른 팀원들이 최신 변경 사항을 빠르게 반영할 수 있도록) 해주는 방법 등을 적용해봄직 하다.