포스트

[GitHub 100일 챌린지] Day 46 - Fast-Forward Merge

[GitHub 100일 챌린지] Day 46 - Fast-Forward Merge

100일 챌린지 Day 46 - 가장 간단한 브랜치 병합 방식인 Fast-Forward를 배웁니다.

배울 내용

  1. Fast-Forward 병합이란
  2. Fast-Forward 병합 실습
  3. Fast-Forward vs 일반 병합

Topic1. Fast-Forward 병합이란

Fast-Forward는 가장 단순한 형태의 브랜치 병합입니다.

Fast-Forward의 개념

“빨리 감기” 병합:

gitGraph
    commit id: "C1"
    commit id: "C2"
    branch feature
    checkout feature
    commit id: "C3"
    commit id: "C4"
    checkout main
    merge feature tag: "Fast-Forward!"

작동 원리:

1
2
3
4
5
6
병합 전:
main    → C2
feature → C2 → C3 → C4

병합 후:
main    → C2 → C3 → C4 (feature와 동일!)

Fast-Forward가 가능한 조건:

  1. main 브랜치에 새로운 커밋이 없음
  2. feature 브랜치가 main보다 앞서 있음
  3. 일직선상에 커밋이 쌓여 있음

Fast-Forward vs 병합 커밋

Fast-Forward (빨리 감기):

1
2
3
4
5
6
7
8
C1 ← C2 ← C3 ← C4
      ↑         ↑
    main      feature

병합 후:
C1 ← C2 ← C3 ← C4
                ↑
          main, feature

일반 병합 (Merge Commit):

1
2
3
4
5
      C3 ← C4
     ↗       ↘
C1 ← C2      M (병합 커밋)
      ↑       ↑
    main   merged

해보기: Fast-Forward 조건 확인

시나리오: Fast-Forward가 가능한 상황

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 프로젝트 초기화
mkdir ff-merge-demo
cd ff-merge-demo
git init

# 초기 커밋
echo "# Project" > README.md
git add README.md
git commit -m "C1: Initial commit"

echo "v1.0" > VERSION
git add VERSION
git commit -m "C2: Add version file"

# 현재 main의 위치 확인
git log --oneline
# C2 Add version file
# C1 Initial commit

# feature 브랜치 생성
git checkout -b feature/add-docs

# feature에서 작업
echo "# Documentation" > DOCS.md
git add DOCS.md
git commit -m "C3: Add documentation"

echo "# More docs" >> DOCS.md
git add DOCS.md
git commit -m "C4: Update documentation"

# 브랜치 상태 확인
git log --oneline --all --graph

# 출력:
* C4 (HEAD -> feature/add-docs) Update documentation
* C3 Add documentation
* C2 (main) Add version file
* C1 Initial commit

# main은 C2에 머물러 있음!
# feature는 C2 → C3 → C4로 일직선!
# ✅ Fast-Forward 가능!

결과

Fast-Forward 가능 조건 확인:

1
2
3
4
✅ main이 C2에 머물러 있음
✅ feature가 main의 연장선상
✅ 일직선 히스토리
✅ 충돌 없음

Fast-Forward 불가능 상황:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# main에서 새 작업 발생
git checkout main
echo "hotfix" > FIX.md
git add FIX.md
git commit -m "C5: Hotfix on main"

# 이제 구조:
#     C3 ← C4 (feature)
#    ↗
# C2
#    ↘
#     C5 (main)

# ❌ Fast-Forward 불가! (main이 움직였음)

Topic2. Fast-Forward 병합 실습

실제로 Fast-Forward 병합을 수행해봅니다.

기본 Fast-Forward 병합

병합 명령어:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# main으로 전환
git checkout main

# feature 브랜치 병합
git merge feature/add-docs

# 출력:
Updating C2..C4
Fast-forward
 DOCS.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 DOCS.md

# "Fast-forward" 메시지 확인!

병합 후 상태:

1
2
3
4
5
6
7
8
9
10
# 히스토리 확인
git log --oneline

# 출력:
C4 (HEAD -> main, feature/add-docs) Update documentation
C3 Add documentation
C2 Add version file
C1 Initial commit

# main과 feature가 같은 커밋을 가리킴!

그래픽으로 확인:

1
2
3
4
5
6
7
8
9
git log --oneline --all --graph

# 출력:
* C4 (HEAD -> main, feature/add-docs) Update documentation
* C3 Add documentation
* C2 Add version file
* C1 Initial commit

# 일직선 히스토리!

Fast-Forward 병합 과정

단계별 이해:

1단계: 병합 전:

1
2
main    → C2
feature → C2 → C3 → C4

2단계: git merge 실행:

1
2
git checkout main
git merge feature/add-docs

3단계: main 포인터만 이동:

1
main    → C2 → C3 → C4 ← feature

4단계: 완료:

1
main, feature → C2 → C3 → C4

해보기: 완전한 Fast-Forward 워크플로우

실무 시나리오: 새 기능 개발 및 병합

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# === 초기 설정 ===
mkdir login-feature
cd login-feature
git init

echo "# MyApp v1.0" > README.md
git add README.md
git commit -m "Initial release"

# === 기능 브랜치 생성 ===
git checkout -b feature/login

# === 로그인 기능 개발 ===
# 커밋 1: HTML
cat > login.html << 'EOF'
<!DOCTYPE html>
<html>
<body>
  <form id="loginForm">
    <input type="email" name="email" placeholder="Email">
    <input type="password" name="password" placeholder="Password">
    <button type="submit">Login</button>
  </form>
</body>
</html>
EOF

git add login.html
git commit -m "feat: Add login HTML form"

# 커밋 2: JavaScript
cat > login.js << 'EOF'
async function handleLogin(email, password) {
  const response = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  return response.json();
}
EOF

git add login.js
git commit -m "feat: Add login JavaScript logic"

# 커밋 3: CSS
cat > login.css << 'EOF'
#loginForm {
  max-width: 400px;
  margin: 50px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}
EOF

git add login.css
git commit -m "style: Add login form styles"

# === 병합 전 확인 ===
# main 상태 확인
git checkout main
git log --oneline

# 출력:
# a1b2c3d Initial release

# feature 상태 확인
git checkout feature/login
git log --oneline

# 출력:
# d4e5f6g style: Add login form styles
# c3d4e5f feat: Add login JavaScript logic
# b2c3d4e feat: Add login HTML form
# a1b2c3d Initial release

# === Fast-Forward 병합 ===
git checkout main
git merge feature/login

# 출력:
Updating a1b2c3d..d4e5f6g
Fast-forward
 login.css  | 6 ++++++
 login.html | 9 +++++++++
 login.js   | 8 ++++++++
 3 files changed, 23 insertions(+)

# === 병합 후 확인 ===
git log --oneline --graph

# 출력:
* d4e5f6g (HEAD -> main, feature/login) style: Add login form styles
* c3d4e5f feat: Add login JavaScript logic
* b2c3d4e feat: Add login HTML form
* a1b2c3d Initial release

# === 브랜치 정리 ===
git branch -d feature/login

# 출력:
Deleted branch feature/login (was d4e5f6g).

결과

Fast-Forward 병합의 특징:

gitGraph
    commit id: "Initial"
    branch feature/login
    checkout feature/login
    commit id: "HTML form"
    commit id: "JS logic"
    commit id: "CSS styles"
    checkout main
    merge feature/login tag: "FF"

장점:

1
2
3
4
✅ 깔끔한 일직선 히스토리
✅ 별도의 병합 커밋 없음
✅ 이해하기 쉬운 구조
✅ 간단한 되돌리기

Topic3. Fast-Forward vs 일반 병합

언제 Fast-Forward를 사용하고, 언제 일반 병합을 사용하나요?

Fast-Forward 장단점

장점:

1
2
3
4
5
6
7
8
9
10
11
✅ 단순한 히스토리
   - 일직선으로 읽기 쉬움
   - git log가 깔끔함

✅ 빠른 처리
   - 포인터만 이동
   - 새 커밋 생성 안 함

✅ 쉬운 이해
   - 초보자도 이해하기 쉬움
   - 복잡도 낮음

단점:

1
2
3
4
5
6
7
❌ 브랜치 정보 손실
   - 어디서 브랜치했는지 불명확
   - 기능 단위 구분 어려움

❌ 되돌리기 어려움
   - 어떤 커밋들이 한 기능인지 파악 곤란
   - revert 시 여러 커밋 되돌려야 함

–no-ff 옵션 (병합 커밋 강제)

Fast-Forward 막고 병합 커밋 생성:

1
2
3
4
5
6
7
# 일반 Fast-Forward
git merge feature/payment
# → 포인터만 이동

# 병합 커밋 강제 생성
git merge --no-ff feature/payment
# → 병합 커밋 생성!

시각적 차이:

1
2
3
4
5
6
7
8
9
10
11
12
# Fast-Forward
* C4 (main, feature)
* C3
* C2

# --no-ff
*   M (main) Merge feature into main
|\
| * C4 (feature)
| * C3
|/
* C2

실전 사용 전략

Fast-Forward 사용 (추천):

1
2
3
4
✅ 개인 프로젝트
✅ 작은 버그 수정
✅ 문서 업데이트
✅ 단순한 기능 추가

–no-ff 사용 (추천):

1
2
3
4
✅ 팀 프로젝트
✅ 중요한 기능 개발
✅ 릴리스 브랜치
✅ 기능 단위 추적 필요

해보기: –no-ff 옵션 실습

상황: 팀 프로젝트에서 기능 단위 추적

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# === 설정 ===
mkdir team-project
cd team-project
git init

echo "# Team App" > README.md
git add README.md
git commit -m "Initial commit"

# === Feature 1: 검색 기능 ===
git checkout -b feature/search

echo "search.js" > search.js
git add search.js
git commit -m "Add search functionality"

echo "search.css" > search.css
git add search.css
git commit -m "Add search styles"

# === --no-ff로 병합 ===
git checkout main
git merge --no-ff feature/search -m "Merge feature/search into main"

# 출력:
Merge made by the 'recursive' strategy.
 search.css | 0
 search.js  | 0
 2 files changed, 0 insertions(+), 0 deletions(-)

# === Feature 2: 알림 기능 ===
git checkout -b feature/notification

echo "notify.js" > notify.js
git add notify.js
git commit -m "Add notification system"

# === --no-ff로 병합 ===
git checkout main
git merge --no-ff feature/notification -m "Merge feature/notification into main"

# === 히스토리 확인 ===
git log --oneline --graph --all

# 출력:
*   M2 (HEAD -> main) Merge feature/notification into main
|\
| * N1 (feature/notification) Add notification system
|/
*   M1 Merge feature/search into main
|\
| * S2 (feature/search) Add search styles
| * S1 Add search functionality
|/
* I1 Initial commit

# 기능 단위로 명확하게 구분됨!

기본 동작 변경

항상 –no-ff 사용하도록 설정:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 전역 설정
git config --global merge.ff false

# 또는 프로젝트별
git config merge.ff false

# 확인
git config --get merge.ff
# false

# 이제 기본적으로 병합 커밋 생성
git merge feature/new-feature
# → 자동으로 --no-ff 적용됨

설정 해제:

1
2
# 기본값으로 되돌리기
git config --global --unset merge.ff

결과

전략 비교표:

상황 Fast-Forward –no-ff
개인 프로젝트 ✅ 추천 ❌ 불필요
팀 협업 ❌ 비추천 ✅ 추천
핫픽스 ✅ 빠른 적용 ⚠️ 선택적
기능 개발 ❌ 비추천 ✅ 추천
실험적 변경 ✅ 간단 ❌ 과함
릴리스 병합 ❌ 기록 필요 ✅ 필수

권장 사항:

1
2
3
4
5
6
7
8
9
10
11
1. 팀 프로젝트
   → git config merge.ff false 설정
   → 모든 기능을 명확히 추적

2. 개인 프로젝트
   → 기본 Fast-Forward 사용
   → 중요 기능만 --no-ff

3. 오픈소스
   → --no-ff 사용
   → 기여자 기록 보존

정리

오늘 배운 내용:

1. Fast-Forward 병합:

  • 포인터만 이동하는 간단한 병합
  • 일직선 히스토리 유지
  • 조건: main이 움직이지 않아야 함

2. Fast-Forward 실습:

1
2
3
git checkout main
git merge feature/branch
# → Fast-forward 메시지 확인

3. –no-ff 옵션:

1
2
3
git merge --no-ff feature/branch
# → 병합 커밋 강제 생성
# → 기능 단위 추적 가능

4. 사용 전략:

  • 개인/단순: Fast-Forward ✅
  • 팀/중요: –no-ff ✅

다음 단계: Day 47에서 3-way 병합을 배웁니다.

완료 체크:

  • Fast-Forward 병합의 개념을 이해했다
  • Fast-Forward 병합을 수행할 수 있다
  • –no-ff 옵션의 차이를 안다
  • 상황별 병합 전략을 선택할 수 있다

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.