동작하는 코드
예제 1: Fast-forward 머지
브랜치를 만들고 작업 후 병합합니다:
git init
echo "# 프로젝트" > README.md
git add README.md
git commit -m "feat: 초기 커밋"
git checkout -b feature-about
echo "<h1>소개</h1>" > about.html
git add about.html
git commit -m "feat: about 페이지 추가"
병합 전 상태 확인:
git log --oneline --graph --all
출력:
* a1b2c3d (HEAD -> feature-about) feat: about 페이지 추가
* e4f5g6h (main) feat: 초기 커밋
main으로 이동해서 병합:
git checkout main
git merge feature-about
출력:
Updating e4f5g6h..a1b2c3d
Fast-forward
about.html | 1 +
1 file changed, 1 insertion(+)
create mode 100644 about.html
병합 후 히스토리:
git log --oneline --graph --all
출력:
* a1b2c3d (HEAD -> main, feature-about) feat: about 페이지 추가
* e4f5g6h feat: 초기 커밋
main이 feature-about과 같은 커밋을 가리킵니다. 히스토리가 선형입니다.
예제 2: --no-ff 머지 (히스토리 보존)
git checkout -b feature-contact
echo "<h1>연락처</h1>" > contact.html
git add contact.html
git commit -m "feat: contact 페이지 추가"
git checkout main
git merge --no-ff feature-contact -m "merge: contact 페이지 병합"
출력:
Merge made by the 'ort' strategy.
contact.html | 1 +
1 file changed, 1 insertion(+)
create mode 100644 contact.html
병합 후 히스토리:
git log --oneline --graph --all
출력:
* b2c3d4e (HEAD -> main) merge: contact 페이지 병합
|\
| * f7g8h9i (feature-contact) feat: contact 페이지 추가
|/
* a1b2c3d feat: about 페이지 추가
* e4f5g6h feat: 초기 커밋
--no-ff는 분기했다가 합친 구조를 히스토리에 보존합니다.
직접 수정하기
두 브랜치가 동시에 진행된 경우
main과 feature 모두에 새 커밋이 있을 때:
# main에 새 커밋 추가
git checkout main
echo "// 메인 기능" > main-feature.js
git add main-feature.js
git commit -m "feat: 메인 기능 추가"
# feature 브랜치도 앞서 있음
git checkout -b feature-sidebar
echo "<nav>사이드바</nav>" > sidebar.html
git add sidebar.html
git commit -m "feat: 사이드바 추가"
히스토리 확인:
git log --oneline --graph --all
출력:
* j0k1l2m (HEAD -> feature-sidebar) feat: 사이드바 추가
* b2c3d4e (main) merge: contact 페이지 병합
...
main이 앞서 있으므로 fast-forward 불가. 병합하면 merge commit이 자동 생성됩니다:
git checkout main
git merge feature-sidebar
출력:
Merge made by the 'ort' strategy.
sidebar.html | 1 +
1 file changed, 1 insertion(+)
create mode 100644 sidebar.html
git log --oneline --graph --all
출력:
* m3n4o5p (HEAD -> main) Merge branch 'feature-sidebar'
|\
| * j0k1l2m (feature-sidebar) feat: 사이드바 추가
* | b2c3d4e merge: contact 페이지 병합
...
병합 후 정리
# 병합된 브랜치 삭제
git branch -d feature-contact
git branch -d feature-sidebar
# 모든 브랜치 확인
git branch
"왜?" — Fast-forward vs Merge Commit
Fast-forward 머지
main에 새 커밋이 없고, feature 브랜치만 앞서 있으면 단순히 포인터를 앞으로 이동합니다:
Before:
main → A → B
↑ main
C → D
↑ feature
After:
main → A → B → C → D
↑ main (포인터 이동)
히스토리가 선형이 됩니다. 브랜치가 있었다는 흔적이 없습니다.
Merge Commit
두 브랜치 모두에 새 커밋이 있거나, --no-ff를 사용하면 새 merge commit을 만듭니다:
Before:
main → A → B → E
↘
C → D ← feature
After:
main → A → B → E → M (merge commit)
↘ ↗
C → D
히스토리에 브랜치 구조가 보존됩니다.
| | Fast-forward | Merge Commit |
| -------- | ------------- | ------------------- |
| 히스토리 | 선형 (깔끔) | 분기 구조 보존 |
| 언제 | 브랜치 최신화 | PR 병합, 협업 |
| 옵션 | 자동 (기본) | --no-ff 또는 자동 |
충돌 맛보기 (다음 레슨 예고)
같은 파일의 같은 줄을 두 브랜치에서 다르게 수정하면 충돌이 발생합니다:
# main에서 수정
echo "main의 내용" > shared.txt
git add shared.txt && git commit -m "main 수정"
# feature에서 같은 파일 다르게 수정
git checkout -b feature-conflict
echo "feature의 내용" > shared.txt
git add shared.txt && git commit -m "feature 수정"
git checkout main
git merge feature-conflict
출력:
CONFLICT (content): Merge conflict in shared.txt
Automatic merge failed; fix conflicts and then commit the result.
이 충돌을 해결하는 방법은 레슨 7에서 자세히 다룹니다.
흔한 실수
실수 1: 잘못된 브랜치에서 merge
# 실수: feature 브랜치에서 main을 merge
git checkout feature-login
git merge main # feature에 main이 병합됨 (원하지 않는 방향)
# 올바름: main에서 feature를 merge
git checkout main
git merge feature-login
merge의 방향: "현재 브랜치에 대상 브랜치를 합친다"는 것을 기억하세요.
실수 2: 충돌 상태에서 다른 작업
# 충돌 해결 없이 다른 명령어 사용
git merge feature-conflict # 충돌 발생
git checkout main # 에러: 충돌 해결 먼저!
# 올바른 순서
# 1. 충돌 해결
# 2. git add 파일명
# 3. git commit
실수 3: merge 취소를 모름
# 충돌 해결이 어렵거나 잘못된 merge면 취소 가능
git merge --abort # merge 전 상태로 돌아감
심화 학습
merge 전략 선택
# fast-forward만 허용 (불가능하면 에러)
git merge --ff-only feature-branch
# 항상 merge commit 생성
git merge --no-ff feature-branch
# squash: feature의 모든 커밋을 하나로 합쳐서 staging에 넣기
git merge --squash feature-branch
git commit -m "feat: feature 기능 추가 (squash)"
팀에서 일관된 머지 방식을 정해두면 히스토리 관리가 쉬워집니다.
git log --all --graph --decorate 활용
# 가장 상세한 그래프 보기
git log --all --graph --oneline --decorate
# 날짜도 포함
git log --all --graph --format="%h %ai %s"
# 특정 브랜치 비교
git log main..feature-login # feature-login에만 있는 커밋
git log feature-login..main # main에만 있는 커밋
- 저장소를 만들고 main에 커밋 2개를 만드세요.
feature-page브랜치를 만들고 커밋 2개를 추가하세요.- main으로 돌아와
git merge feature-page로 fast-forward 병합하세요. git log --oneline --graph --all로 히스토리를 확인하세요.- 새
feature-nav브랜치를 만들고 main에도 새 커밋을 추가한 후,git merge --no-ff feature-nav로 merge commit을 만들고 히스토리를 비교하세요.
Q1. git merge --no-ff 옵션의 역할은?
- A) 충돌을 무시하고 강제로 병합
- B) fast-forward가 가능해도 merge commit을 만든다
- C) 브랜치를 삭제하면서 병합
- D) 병합 전에 확인을 요청