Git 기초와 협업 워크플로우
저장소·커밋·브랜치·병합의 개념과 기본 명령을 익히고 기능 개발부터 리뷰까지 협업 흐름을 구성합니다.
13장까지는 웹 애플리케이션을 만들고 안전하고 빠르게 다듬는 방법을 익혔습니다. 14장 개발 도구와 워크플로우에서는 그 코드를 관리하고 빌드하는 도구를 다룹니다. 소프트웨어 개발은 결국 코드 변경의 연속입니다. 기능이 추가되고 버그가 고쳐지고 구조가 계속 바뀌는데, 문제는 변경 자체가 아니라 그 변경을 추적하지 못할 때 생깁니다. 누가 언제 무엇을 바꿨는지 알 수 없으면 협업이 급격히 느려집니다.
버전 관리 시스템(version control system, VCS)은 이런 변경 이력을 체계적으로 관리합니다. 모든 변경을 기록해 언제든 특정 시점의 코드로 돌아갈 수 있고, 여러 개발자가 같은 코드를 동시에 작업한 뒤 변경을 합칠 수 있습니다. 새 기능이나 실험적인 변경은 안정적인 코드에 영향을 주지 않는 별도의 공간에서 작업할 수 있고, 버그가 생기면 어느 변경 때문인지 찾아 되돌리기 쉽습니다. Git은 내 컴퓨터에서도 커밋 이력을 모두 기록하는 분산 버전 관리 시스템(distributed version control system)이라서, 네트워크 연결 없이도 커밋할 수 있고 원격 저장소와의 공유는 따로 합니다.
Git의 기본 개념
저장소(repository)는 프로젝트의 모든 파일과 변경 이력이 저장되는 곳으로, 내 컴퓨터의 로컬 저장소와 GitHub, GitLab, Bitbucket 같은 서비스에 있는 원격 저장소(remote repository)가 있습니다. 커밋(commit)은 코드의 변경을 저장소에 기록하는 단위로, git commit으로 만들며 각 커밋은 고유한 해시 값을 가집니다. 커밋 메시지에는 어떤 변경인지 분명하게 적습니다.
브랜치(branch)는 커밋을 가리키는 움직일 수 있는 참조입니다. 새 브랜치를 만든다고 폴더 전체가 복사되는 것이 아니며, 어느 브랜치로 전환했는지에 따라 작업 폴더의 내용이 바뀝니다. main은 보통 주된 안정적인 코드의 줄기이고, 개발자들은 각자 다른 브랜치에서 작업해 main에 영향을 주지 않습니다. 병합(merge)은 두 브랜치의 변경을 하나로 합치는 작업입니다. HEAD는 보통 지금 작업 중인 브랜치를 가리키며, 특정 커밋으로 직접 이동한 detached HEAD 상태에서는 그 커밋을 가리킵니다.
푸시(push)는 로컬 저장소의 커밋을 원격 저장소로 올리는 작업입니다. 풀(pull)은 원격의 변경을 가져와 지금 브랜치에 합치는 작업으로, git fetch로 가져온 뒤 설정이나 옵션에 따라 git merge나 git rebase로 합칩니다. 이 글의 실습은 병합 방식을 씁니다.
Git 변경 기록과 원격 교환 흐름 — 상태 추적
add 당시 index 값 A가 이후 작업 파일 B 편집에도 보존되고 commit이 A를 기록하는 실제 snapshot 변화가 핵심이다. working/index/local/remote를 고정해 값 변화를 추적한다.
GIT SNAPSHOT
commit은 작업 폴더 전체가 아니라 스테이징한 내용을 기록합니다
git add는 그 시점의 파일 내용을 Index에 담습니다. 로컬 기록과 원격 교환을 구분하면 commit·push·fetch가 어디에 영향을 주는지 분명해집니다.
1. 편집 → add → commit
add 이후 파일을 다시 편집해도, Index는 자동으로 갱신되지 않습니다. 다음 예시의 commit은 A를 기록합니다.
예시: add 후의 추가 편집
파일 내용 A
git add index.html
파일을 B로 다시 편집
commit 대상: A · 작업 파일: B현재 브랜치와 HEAD
일반적인 브랜치 작업에서는 새 커밋으로 현재 브랜치 참조가 이동합니다. HEAD는 보통 그 브랜치를 가리키며, detached HEAD에서는 커밋을 직접 가리킵니다.
push·fetch·pull의 차이
push는 로컬의 커밋을 원격에 공유합니다. 일반적인 git fetch origin은 커밋과 origin/main 같은 원격 추적 참조를 갱신합니다. 현재 브랜치·작업 파일에 반영하는 통합은 별도이며, pull은 fetch 뒤 merge 또는 rebase를 수행합니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 전체 관계·조건 설명 | 위 노드 3개·관계 2개는 작업 파일→Index→커밋, 아래 노드 2개·방향 2개는 로컬→원격 push/원격→로컬 fetch다. 아래 로컬은 커밋 객체 및 원격 추적 참조를 가진 저장소이고 fetch가 현재 작업 파일을 병합하는 화살표는 없다. add 후 B 편집에도 A 스냅샷은 유지되며 mobile도 같다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 작업 파일 A | working tree: A / index: 이전 내용 | working tree의 현재 내용을 준비한다 | 작업 수정은 staging에 자동 반영되지 않는다 |
| A를 add 뒤 B로 수정 | working tree: B / index: A | index에는 A가, 작업 파일에는 B가 남는다 | add는 그 순간 내용을 snapshot으로 저장한다 |
| commit은 A | commit: A / working tree: B 수정 남음 | commit은 index를 기록한다 | 커밋 대상은 현재 작업 파일 전체가 아니라 index다 |
Git 변경 기록과 원격 교환 흐름 — 상태 추적
add 당시 index 값 A가 이후 작업 파일 B 편집에도 보존되고 commit이 A를 기록하는 실제 snapshot 변화가 핵심이다. working/index/local/remote를 고정해 값 변화를 추적한다.
GIT SNAPSHOT
commit은 작업 폴더 전체가 아니라 스테이징한 내용을 기록합니다
git add는 그 시점의 파일 내용을 Index에 담습니다. 로컬 기록과 원격 교환을 구분하면 commit·push·fetch가 어디에 영향을 주는지 분명해집니다.
1. 편집 → add → commit
add 이후 파일을 다시 편집해도, Index는 자동으로 갱신되지 않습니다. 다음 예시의 commit은 A를 기록합니다.
예시: add 후의 추가 편집
파일 내용 A
git add index.html
파일을 B로 다시 편집
commit 대상: A · 작업 파일: B현재 브랜치와 HEAD
일반적인 브랜치 작업에서는 새 커밋으로 현재 브랜치 참조가 이동합니다. HEAD는 보통 그 브랜치를 가리키며, detached HEAD에서는 커밋을 직접 가리킵니다.
push·fetch·pull의 차이
push는 로컬의 커밋을 원격에 공유합니다. 일반적인 git fetch origin은 커밋과 origin/main 같은 원격 추적 참조를 갱신합니다. 현재 브랜치·작업 파일에 반영하는 통합은 별도이며, pull은 fetch 뒤 merge 또는 rebase를 수행합니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 전체 관계·조건 설명 | 위 노드 3개·관계 2개는 작업 파일→Index→커밋, 아래 노드 2개·방향 2개는 로컬→원격 push/원격→로컬 fetch다. 아래 로컬은 커밋 객체 및 원격 추적 참조를 가진 저장소이고 fetch가 현재 작업 파일을 병합하는 화살표는 없다. add 후 B 편집에도 A 스냅샷은 유지되며 mobile도 같다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 작업 파일 A | working tree: A / index: 이전 내용 | working tree의 현재 내용을 준비한다 | 작업 수정은 staging에 자동 반영되지 않는다 |
| A를 add 뒤 B로 수정 | working tree: B / index: A | index에는 A가, 작업 파일에는 B가 남는다 | add는 그 순간 내용을 snapshot으로 저장한다 |
| commit은 A | commit: A / working tree: B 수정 남음 | commit은 index를 기록한다 | 커밋 대상은 현재 작업 파일 전체가 아니라 index다 |
위 그림의 위쪽은 변경이 기록되는 세 단계입니다. 작업 파일(working tree)은 지금 편집하고 있는 파일이고, git add를 하면 그 순간의 파일 내용이 다음 커밋에 담을 스테이징 영역(staging area), 즉 인덱스(index)에 담깁니다. git commit은 작업 폴더 전체가 아니라 인덱스에 담긴 스냅샷을 로컬 커밋으로 기록합니다. 그래서 add한 뒤에 파일을 다시 고쳐도 인덱스는 저절로 바뀌지 않습니다. 그림의 예처럼 파일을 A로 고쳐 add한 뒤 B로 다시 고치고 바로 커밋하면, 커밋에는 A가 기록되고 작업 파일에는 B가 남습니다.
그림의 아래쪽은 로컬 저장소와 원격 저장소 사이의 교환입니다. push는 로컬의 커밋을 원격에 공유하고, 일반적인 git fetch origin은 원격의 커밋을 가져와 origin/main 같은 원격 추적 참조(remote-tracking reference)를 갱신합니다. 가져온 내용을 지금 브랜치와 작업 파일에 반영하는 일은 별도이며, pull이 fetch 뒤에 병합이나 리베이스까지 해 줍니다. 일반적인 브랜치 작업에서 새 커밋을 만들면 지금 브랜치의 참조가 그 커밋으로 움직이고, HEAD는 그 브랜치를 가리킵니다.
같은 파일에 스테이징한 변경과 스테이징하지 않은 변경이 함께 있을 수 있으므로(Pro Git의 변경 기록하기), 커밋하기 전에 git status와 git diff --staged로 무엇이 담겼는지 확인하는 습관이 좋습니다. 또 브랜치를 전환한다고 저장하지 않은 변경이 무조건 사라지거나 따로 보관되는 것은 아니어서, 전환이 거절되거나 변경이 그대로 따라올 수 있으므로 먼저 상태를 확인하고 어느 커밋에 담을지 정합니다.
더 깊이: .git 폴더 안의 커밋과 브랜치
Git의 핵심은 내용을 넣으면 그 내용으로 계산한 키를 돌려주는 키-값 저장소입니다. Pro Git은 이를 내용 주소 파일 시스템(content-addressable filesystem)이라고 부릅니다. 파일의 내용은 블롭(blob), 폴더의 목록은 트리(tree), 커밋은 커밋 객체로 저장되며, 각 객체의 이름은 내용에 머리말을 붙여 계산한 40자리 SHA-1 해시입니다. 그래서 내용이 같은 파일은 몇 번을 커밋해도 같은 블롭 하나로 저장되고, 내용이 한 글자라도 바뀌면 이름이 달라집니다.
커밋 객체는 변경 내용의 차이가 아니라, 그 시점 프로젝트 전체를 가리키는 최상위 트리와 부모 커밋, 작성자 정보, 메시지를 담습니다. 바뀌지 않은 파일은 이전 커밋과 같은 블롭을 가리키므로, 커밋마다 전체를 담는다고 저장 공간이 그만큼 늘지는 않습니다. 부모 커밋의 해시가 커밋의 내용에 들어 있으므로, 과거의 커밋 하나를 바꾸면 그 뒤 커밋들의 해시도 모두 바뀝니다.
브랜치는 이 위에 놓인 아주 작은 파일입니다. .git/refs/heads/main 같은 파일에 커밋 해시 하나가 적혀 있을 뿐이고, .git/HEAD에는 보통 ref: refs/heads/main처럼 지금 브랜치의 이름이 적혀 있습니다. 새 브랜치를 만드는 것은 이 파일을 하나 더 쓰는 일이라 파일이나 이력을 복사하지 않으며, 커밋하면 브랜치 파일의 해시가 새 커밋으로 바뀝니다. 객체의 구조는 Pro Git의 Git 객체 설명에 있습니다.
Git 기본 명령어
Git은 Git 공식 웹사이트에서 내려받아 설치합니다. 다음 명령은 새 실습 폴더에서 따라 하는 순서이며, 지금 쓰고 있는 실제 저장소나 다른 사람의 브랜치에서 실행하는 과제가 아닙니다. 사용자 이름, 이메일, 원격 주소는 자신의 실습 값으로 바꾸고, GitHub에 만들 대상 저장소는 이 예제와 충돌하는 이력이 없는 빈 저장소로 준비합니다.
처음 한 번은 커밋에 기록될 이름과 이메일을 설정합니다.
git config --global user.name "Your Name"
git config --global user.email "[email protected]"새 실습 폴더를 만들고 저장소를 초기화합니다. -b main은 처음 브랜치의 이름을 main으로 정하며, 아래 예제는 이 이름을 씁니다.
mkdir my-web-project # 새 실습 폴더 만들기
cd my-web-project # 프로젝트 폴더로 이동
git init -b main # 새 실습 저장소의 초기 브랜치를 main으로 지정명령을 실행하면 폴더 안에 숨김 폴더 .git이 생기고, Git은 이 폴더에 커밋과 브랜치 정보를 기록합니다.
간단한 index.html 파일을 만들어 스테이징 영역에 추가하고 커밋합니다. 첫 줄의 echo와 >는 따옴표 안의 글자를 index.html 파일에 써 넣는 셸 문법일 뿐이고, Git 명령은 그 뒤의 git add, git status, git commit입니다. 큰따옴표 안의 !는 macOS의 기본 셸인 zsh에서 이전 명령을 불러오는 기호로 해석되어 오류가 날 수 있으므로, 이 예는 작은따옴표를 씁니다.
# index.html 파일 생성 (예시)
echo '<h1>Hello Git!</h1>' > index.html
git add index.html # 변경된 파일을 스테이징 영역에 추가
git status # 현재 Git 저장소 상태 확인
git commit -m "feat: Add initial index.html page" # 스테이징 영역의 변경 내용을 커밋git add index.html은 그 파일을 스테이징 영역에 넣고, git status는 어떤 파일이 스테이징되었고 어떤 파일이 아직인지 보여 줍니다. 바뀐 파일을 한꺼번에 담고 싶다면 git add .로 지금 폴더의 변경을 모두 담을 수 있지만, 의도하지 않은 파일까지 담기지 않도록 git status로 확인합니다. 커밋한 뒤에는 git log로 커밋 이력을 볼 수 있습니다.
새 기능을 개발하기 위해 feature/login 브랜치를 만들고 전환합니다.
git branch feature/login # feature/login 브랜치 생성
git checkout feature/login # feature/login 브랜치로 전환
# 또는 한 번에: git checkout -b feature/login최근 Git에서는 브랜치 전환에 특화된 git switch feature/login과, 만들면서 전환하는 git switch -c feature/login도 쓸 수 있습니다. checkout은 파일을 되돌리는 기능까지 함께 가지고 있어서, 브랜치 작업에는 switch가 의도를 더 분명하게 드러냅니다.
그 브랜치에서 login.js를 만들고 커밋합니다.
# login.js 파일 생성 (예시)
echo "console.log('Login logic here');" > login.js
git add login.js
git commit -m "feat: Implement basic login logic"이 커밋은 feature/login 브랜치에만 기록되므로, 이 시점의 main에는 아직 login.js가 없습니다.
작업이 끝나면 main 브랜치로 돌아와 feature/login의 변경을 병합하고, 다 쓴 브랜치를 지웁니다. 이 예에서는 브랜치를 만든 뒤 main에 새 커밋이 없었으므로, 병합은 main을 feature/login의 마지막 커밋으로 옮기는 빨리 감기(fast-forward)로 끝납니다. git branch -d는 병합된 브랜치만 지우므로, 병합하지 않은 브랜치를 지우려 하면 Git이 거부합니다.
git checkout main # main 브랜치로 전환
git merge feature/login # feature/login 브랜치를 main에 병합
git branch -d feature/login # 병합된 feature/login 브랜치 삭제GitHub에 새 빈 저장소를 만들고, 로컬 저장소를 연결해 푸시합니다. -u는 처음 한 번 지금 브랜치가 원격의 main을 따라가도록 연결하며, 그 뒤로는 git push만 써도 됩니다.
git remote add origin https://github.com/your-username/your-repo-name.git # 원격 저장소 연결
git push -u origin main # main 브랜치를 원격 저장소로 푸시 (최초 1회 -u 사용)
# 이후부터는 git push 만 사용다른 개발자가 원격에 올린 변경을 가져와 지금 브랜치에 병합합니다. git pull은 원격의 변경을 내려받는 git fetch와, 그것을 지금 브랜치에 합치는 병합을 한 번에 실행하는 명령입니다.
git pull --no-rebase origin main # main 브랜치의 최신 내용을 가져와 병합--no-rebase는 가져온 변경을 리베이스가 아니라 병합으로 합치라는 뜻입니다. git pull의 기본 동작은 Git 설정에 따라 달라질 수 있어서, 이 예에서는 직접 밝혀 두었습니다. 내 커밋과 원격의 커밋이 같은 줄을 서로 다르게 고쳤다면 병합 충돌이 날 수 있습니다.
협업 워크플로우
Git으로 협업하는 방식에는 여러 가지 워크플로우가 있습니다. 가장 널리 알려진 Git Flow는 브랜치마다 역할을 나눠 복잡한 프로젝트도 체계적으로 관리하게 해 줍니다.
Git Flow에서 main(또는 master) 브랜치는 운영에 배포할 수 있는 안정적인 버전을 담으며, 직접 커밋하지 않고 다른 브랜치를 병합하기만 합니다. develop 브랜치는 다음 출시를 준비하는 개발 브랜치로, 모든 기능 브랜치는 여기서 시작해 여기로 병합됩니다. feature/기능_이름 브랜치는 develop에서 갈라져 새 기능을 만들고, 완성되면 develop으로 병합되며, 여러 개발자가 각자의 기능 브랜치에서 동시에 작업할 수 있습니다.
release/버전_번호 브랜치는 출시를 준비하려고 develop에서 갈라져 마지막 버그 수정과 테스트를 하고, 준비가 끝나면 main과 develop 모두에 병합됩니다. hotfix/버그_이름 브랜치는 운영 중인 main에서 발견된 긴급한 버그를 고치려고 main에서 갈라지며, 고친 뒤에는 main과 develop 모두에 병합합니다. 출시할 때는 main에 버전을 표시하는 태그(tag)를 붙입니다.
REVIEW FLOW
검토·검증을 통과한 변경을 기준 브랜치에 병합합니다
기능 브랜치의 변경을 PR로 공유하고 리뷰·테스트를 확인합니다. 수정이 생기면 다시 검토하며, 병합 후에는 변경을 찾을 커밋·태그를 남깁니다.
통과하지 못했다면
리뷰 지적 또는 검사 실패의 원인을 고칩니다. 수정된 변경에 대한 리뷰와 검사를 다시 통과한 뒤 병합합니다.
커밋·태그는 복구의 기준
어떤 변경을 되돌릴지 찾는 식별자입니다. 실제 복구는 revert나 검증된 이전 버전 재배포 등 팀 절차로 수행합니다.
위 그림은 어떤 브랜치 모델을 쓰든 공통으로 거치는 검토와 병합의 흐름입니다. 기능 브랜치에서 독립적인 변경을 커밋하고, 풀 리퀘스트(pull request, PR)로 변경 내용과 그것을 검증한 결과를 공유합니다. 동료의 리뷰와 자동 테스트를 거쳐, 지적을 받거나 검사에 실패하면 기능 브랜치에서 원인을 고치고 다시 공유해 검토받습니다. 승인을 받고 검사를 통과한 변경만 기준 브랜치에 병합하며, 병합한 뒤에는 커밋과 태그로 그 변경을 찾을 수 있게 남깁니다.
기준 브랜치는 팀이 고른 모델에 따라 develop이나 main이며, Git Flow에서 기능을 병합하는 곳은 develop입니다. 그림 아래의 설명처럼 커밋과 태그는 어떤 변경을 되돌릴지 찾는 표지일 뿐, 실제로 되돌리는 일은 git revert나 검증된 이전 버전을 다시 배포하는 것처럼 팀이 정한 절차로 합니다.
Git Flow의 일반적인 흐름은 이렇습니다. 새 기능을 만들 때는 develop으로 전환해 git pull origin develop으로 최신 내용을 받은 뒤 feature/새_기능 브랜치를 만들고, 기능을 개발하며 여러 번 커밋한 뒤 원격에 푸시합니다. 기능이 완성되면 GitHub이나 GitLab에서 develop으로 병합하는 풀 리퀘스트를 만들고, 리뷰를 통과하면 병합합니다. 출시를 준비할 때는 release 브랜치에서 마지막 수정과 테스트를 마친 뒤 main과 develop에 병합하고 main에 태그를 붙이며, 긴급한 버그는 hotfix 브랜치로 고쳐 같은 방식으로 병합합니다.
release와 hotfix를 따로 두는 Git Flow는 여러 출시 버전을 함께 관리할 때 알맞습니다. 팀의 크기만으로 고르지 말고 출시 주기와 유지할 버전의 수, 검증 비용을 함께 비교합니다. 작은 팀이나 자주 배포하는 팀은 main과 기능 브랜치만 쓰는 GitHub Flow 같은 단순한 방식을 쓰기도 합니다. 중요한 것은 팀원 모두가 같은 워크플로우를 이해하고 따르는 것입니다.
이 글의 첫 실습은 main에서 기능 브랜치를 만들어 main으로 바로 병합했고, Git Flow는 develop에서 만들어 develop으로 병합합니다. 같은 작업에서 두 출발점과 병합 대상을 섞지 말고, 고른 모델의 기준 브랜치를 먼저 확인하세요.
정리
- Git은 내 컴퓨터에 모든 이력을 기록하는 분산 버전 관리 시스템이며, 커밋은 로컬에서 하고 원격과의 공유는
push와fetch로 따로 합니다. git add는 그 순간의 파일 내용을 인덱스에 담고,git commit은 작업 폴더가 아니라 인덱스의 스냅샷을 기록합니다.- 브랜치는 커밋을 가리키는 참조이고, 기능은 브랜치에서 개발해 병합하며, 브랜치 전환에는
git switch도 쓸 수 있습니다. fetch는 원격의 커밋과 원격 추적 참조를 갱신할 뿐이고,pull이 그 뒤에 병합이나 리베이스까지 합니다.- Git Flow는
main,develop,feature,release,hotfix로 역할을 나누며, 팀에 맞게 GitHub Flow 같은 단순한 방식을 고를 수도 있습니다. - 변경은 풀 리퀘스트로 공유해 리뷰와 검사를 통과한 뒤에만 병합하고, 커밋과 태그는 되돌릴 변경을 찾는 표지로 씁니다.
연습 문제
1. 커밋에 담기는 것
index.html은 B로 고쳐 add한 뒤 다시 C로 고쳤고, 새 파일 login.js는 아직 add하지 않았습니다. 이 상태에서 git commit을 하면 무엇이 기록되고 무엇이 작업 폴더에 남을까요? 커밋한 직후, 아직 push하지 않았다면 원격 저장소가 바뀌었다고 할 수 있을까요?
해설 보기
커밋에는 index.html의 B가 들어갑니다. 작업 파일의 C와, 아직 추적하지도 스테이징하지도 않은 login.js는 이번 커밋에 들어가지 않고 로컬에 남습니다.
push하기 전에는 이 커밋이 내 로컬 저장소에만 있으므로 원격 저장소는 바뀌지 않았습니다. 커밋에 성공한 것과 원격에 반영된 것은 서로 다른 단계입니다.
2. Git Flow의 병합 대상
Git Flow에서 develop에서 시작한 기능 브랜치가 리뷰를 통과했습니다. 이 기능을 처음 병합할 대상은 어디일까요? 또 main에서 갈라진 긴급 hotfix는 고친 뒤 어디에 병합해야 할까요? 검사에 실패한 PR을 병합한 뒤 태그만 붙여 두면 검증을 대신할 수 있을까요?
해설 보기
기능은 develop으로 병합합니다. hotfix는 main과 develop 양쪽에 반영해, 다음 출시 때 develop에서 같은 버그가 되살아나지 않게 합니다.
태그는 특정 커밋을 가리키는 표지일 뿐, 실패한 검사를 해결하거나 배포의 안전을 보장하지 않습니다. 실패한 원인을 고친 변경을 다시 리뷰하고 검사를 통과시킨 뒤에 병합해야 합니다.
3. 같은 줄을 고친 두 브랜치
main과 feature/title 브랜치가 모두 index.html의 같은 <h1> 줄을 서로 다르게 고쳐 커밋했습니다. main에서 git merge feature/title을 실행하면 어떻게 될까요? 어떻게 마무리해야 할까요?
해설 보기
Git이 어느 쪽을 택할지 스스로 정할 수 없어 병합 충돌(merge conflict)이 납니다. index.html의 그 부분에 <<<<<<<, =======, >>>>>>> 표시와 함께 두 브랜치의 내용이 모두 들어가고, 병합은 멈춘 상태가 됩니다.
파일을 열어 남길 내용으로 고치고 표시를 지운 뒤, git add index.html로 해결했다고 알리고 git commit으로 병합을 마칩니다. 병합을 그만두고 싶다면 git merge --abort로 병합 전으로 돌아갈 수 있습니다. 해결한 뒤에도 페이지가 의도대로 보이는지 확인해야 합니다.
다음 글 npm과 패키지 관리에서는 Node.js 생태계의 패키지를 설치하고 버전을 관리하는 npm과 package.json, 잠금 파일을 살펴봅니다.