SPACE LOGBOOK

OCEAN LOGBOOK

3-2. 브랜치를 실제로 움직여 보면서 Git의 역사를 관리하는 법

2026.08.31

3-2. 브랜치를 실제로 움직여 보면서 Git의 역사를 관리하는 법

2026.08.31

Git 활용 3-2: 브랜치와 HEAD, 병합을 실제로 이해하기

3-1편에서 GitHub와 로컬 저장소를 연결했다면, 이번에는 Git 활용의 핵심인 브랜치를 다룬다.

단순히 git branch, git switch, git merge의 사용법만 외우지 않고, 실제 명령과 출력 결과를 하나씩 해석하면서 브랜치의 정체를 찾아간다.

1. 첫 커밋과 100644에서 시작한 의문

첫 커밋을 만들면 다음과 비슷한 결과가 나타날 수 있다.

[master (root-commit) 9c95c25] first
 1 file changed, 1 insertion(+)
 create mode 100644 branch.htm

여기서 9c95c25가 커밋의 짧은 ID다. 반면 100644는 커밋 ID가 아니다.

100644 = 일반적인 비실행 파일을 나타내는 Git 파일 모드
따라서 어떤 자료에서 100644를 “마지막 커밋 ID”라고 설명한다면 그 부분은 잘못된 설명이다.

2. 해시와 커밋 ID

전체 커밋 ID는 다음처럼 길다.

9c95c252913286bbf95a76ac0205f2e119173c52

해시는 데이터를 일정한 계산 방식으로 처리해 그 데이터를 식별할 수 있는 값으로 만드는 것이다. 쉽게 말해 디지털 지문과 비슷하다.

커밋 객체의 정보
       ↓ hash
9c95c252913286bbf95a76ac0205f2e119173c52

Git 로그에서는 전체 값을 매번 보여줄 필요가 없으므로 9c95c25처럼 앞부분만 줄여 표시할 수 있다.

3. 브랜치를 만들면 무엇이 생길까?

git branch footer
질문: 브랜치를 만들면 프로젝트 복사본이나 새로운 스냅샷이 하나 생기는 것일까?

아니다. 새 커밋도 생기지 않는다. 현재 커밋을 가리키는 새로운 이름표가 하나 생긴다.

       master
         ↓
     9c95c25
         ↑
       footer
브랜치 = 특정 커밋을 가리키는 ‘움직이는 이름표/포인터’

이 문장이 브랜치를 이해하는 가장 중요한 기준이다.

4. ‘branch 명령을 단독 실행’한다는 말

터미널에서 다음처럼 입력하면:

$ branch
bash: branch: command not found

오류가 난다. branch라는 독립된 Bash 명령이 있는 것이 아니기 때문이다.

어떤 자료에서 “브랜치 명령어를 단독으로 실행한다”라고 표현했다면 실제 뜻은 다음처럼 git branch 뒤에 추가 옵션이나 브랜치 이름을 붙이지 않는다는 뜻이다.

git branch

5. 별표(*)는 무엇을 뜻할까?

$ git branch
  footer
* master

*는 “이 브랜치가 주 브랜치다”라는 뜻이 아니다. 현재 체크아웃되어 있는 브랜치, 즉 HEAD가 붙어 있는 브랜치라는 뜻이다.

6. checkout과 switch

git checkout footer

예전부터 사용하던 방식이다. 최근에는 브랜치 이동의 의미를 더 분명하게 한 다음 명령을 사용할 수 있다.

git switch footer

브랜치 이동이라는 목적에서는 결과가 같다.

작업기존 명령역할이 분리된 명령
브랜치 이동git checkout footergit switch footer
생성+이동git checkout -b footergit switch -c footer
파일 복원git checkout -- filegit restore file

7. HEAD는 현재 작업 위치를 알리는 깃발

footer로 이동했다면:

9c95c25
↑      ↑
master footer
       ↑
      HEAD 🚩
HEAD = 현재 내가 어느 브랜치에서 작업 중인지를 알려주는 깃발

엄밀하게는 일반적인 상태에서 HEAD는 현재 브랜치를 가리키고, 그 브랜치가 다시 커밋을 가리킨다.

HEAD → footer → 9c95c25

8. footer에서 새 커밋을 만들면?

git commit -m "second commit on branch"

실제 예에서는 다음 커밋이 만들어졌다.

[footer 7d0f9d6] second commit on branch

그러면:

9c95c25 ── 7d0f9d6
   ↑           ↑
 master      footer
               ↑
              HEAD 🚩

HEAD가 독립적으로 뛰어간 것이라기보다 현재 브랜치 footer가 새 커밋으로 이동했고, HEAD는 계속 footer를 가리키고 있는 것이다. 그래서 브랜치를 “움직이는 포인터”라고 부르는 이유가 여기서 드러난다.

9. rev-parse로 브랜치의 정체를 직접 확인하기

git rev-parse footer

실제 결과:

7d0f9d6f2ea653f29cae77e67f09424217a716c1

rev는 revision, parse는 해석한다는 의미로 이해할 수 있다. 즉 footer라는 Git의 참조 표현이 실제로 어떤 객체 ID로 해석되는지 보여준다.

footer
  ↓ rev-parse
7d0f9d6f2ea653f29cae77e67f09424217a716c1

이 명령을 직접 실행해보면 “브랜치가 특정 커밋을 가리킨다”는 설명이 추상적인 비유가 아니라 실제 Git의 동작임을 확인할 수 있다.

10. git log –graph –all을 읽어보자

실제 로그가 다음과 같다고 하자.

* commit 7d0f9d6... (HEAD -> footer)
|     second commit on branch
|
* commit 9c95c25... (master)
      first

이것을 그림으로 번역하면:

HEAD → footer → 7d0f9d6
                 │
                 ↓
               9c95c25 ← master

즉 “HEAD는 여기, master는 여기”라는 별도 표가 있는 것이 아니라 각 커밋 옆에 붙은 이름표를 보고 현재 구조를 읽는 것이다.

11. 가장 유용했던 로그 명령

git log --oneline --graph --decorate --all

외울 때는 다음처럼 말로 풀어보면 쉽다.

git log를

--oneline   → 한 줄로
--graph     → 그래프로
--decorate  → 이름표로 장식해서
--all       → 모두 보여줘

--decorate가 바로 (HEAD -> footer), (master) 같은 이름표를 붙여주는 옵션이다.

12. 왜 –all이 없으면 footer가 안 보일 수 있을까?

master에 서서 현재 브랜치에서 도달 가능한 역사만 보면 footer에만 존재하는 커밋은 나타나지 않을 수 있다.

git log --oneline --graph --decorate

반면:

git log --oneline --graph --decorate --all

은 다른 브랜치가 가리키는 역사까지 함께 보여주므로 전체 지도를 보기 좋다.

13. master..footer는 무엇을 뜻할까?

git log master..footer

이것은 두 브랜치를 그림으로 비교한다는 뜻이 아니라 footer에서는 도달할 수 있지만 master에서는 도달할 수 없는 커밋을 보여달라는 뜻이다.

현재 구조가:

9c95c25 ── 7d0f9d6
   ↑           ↑
 master      footer

라면 결과는 7d0f9d6뿐이다. 반대로:

git log footer..master

는 비어 있을 수 있다. master의 커밋은 이미 footer 역사 안에 포함되어 있기 때문이다.

14. 파일 내용의 차이는 log가 아니라 diff

git diff master footer

이 명령은 두 브랜치가 가리키는 상태의 파일 내용 차이를 확인하는 데 사용할 수 있다. log는 커밋 역사, diff는 내용 차이라는 구분이 중요하다.

15. 커밋하지 않은 내용이 있으면 switch가 안 될까?

흔한 설명: “워킹 트리에 커밋하지 않은 변경이 있으면 브랜치를 변경할 수 없다.” 정말 항상 그럴까?

정확히는 아니다. Git은 브랜치를 전환하면서 현재의 미커밋 변경을 덮어써 잃어버릴 위험이 있는지를 본다.

미커밋 변경 있음
       ↓
브랜치 전환 시도
       ↓
변경을 안전하게 유지 가능?
   ├─ YES → 이동 가능할 수 있음
   └─ NO  → Git이 이동 차단

중요한 것은 아직 커밋하지 않은 수정은 특정 브랜치의 커밋 역사에 고정된 것이 아니라는 점이다. 실험 결과를 그 브랜치의 역사로 확실히 남기려면 커밋해야 한다.

16. master와 footer가 다른 커밋을 가리키면 문제일까?

* 7d0f9d6 (HEAD -> footer)
* 9c95c25 (master)

이것은 “Git에 문제가 생겼다”는 표시가 아니다. 브랜치를 만들고 footer에서 새 작업을 했으니 당연히 서로 다른 커밋을 가리키는 정상적인 상태다.

이제 사람이 판단한다.

footer 작업이 좋다 → master에 merge
footer 작업이 싫다 → master로 돌아가고 footer 삭제
계속 실험한다      → footer에서 계속 commit

17. merge는 마지막 커밋 하나만 가져오는 것이 아니다

footer에서 한 번 더 커밋했다고 하자.

9c95c25 ── 7d0f9d6 ── ABC1234
   ↑                     ↑
 master                footer
                         ↑
                        HEAD

master로 이동해:

git switch master
git merge footer

를 실행한다고 해서 ABC1234 하나만 떼어오는 것이 아니다.

master가 이미 가진 9c95c25는 공통 역사이고, master에 없었던 다음 역사까지 이어지는 footer의 역사가 master의 역사에 포함된다.

7d0f9d6 ── ABC1234
merge = 마지막 커밋 하나를 가져오는 것이 아니라 브랜치의 역사를 합치는 것

18. Fast-forward가 눈앞에서 이해되는 순간

master가 9c95c25에서 따로 발전하지 않았다면:

9c95c25 ── 7d0f9d6 ── ABC1234
   ↑                     ↑
 master                footer

master 포인터를 그냥 ABC1234까지 앞으로 옮기면 된다.

9c95c25 ── 7d0f9d6 ── ABC1234
                         ↑
                  master, footer
                         ↑
                        HEAD

새로운 병합 커밋이 없어도 된다. 이것이 Fast-forward다.

19. master도 따로 발전했다면?

          D ── E  ← footer
         /
A ── B ── C
         \
          F ── G  ← master

이제 master를 단순히 E로 옮길 수 없다. 두 작업 흐름을 합쳐야 하므로 일반적인 병합에서는 새로운 merge commit이 만들어질 수 있다.

          D ── E
         /      \
A ── B ── C      M
         \      /
          F ── G

같은 부분을 서로 다르게 수정했다면 충돌(conflict)이 발생할 수도 있다.

20. 하나의 커밋만 골라 가져오고 싶다면?

이 경우 merge와 다른 개념이 필요하다.

git cherry-pick ABC1234
merge
→ 브랜치의 역사를 합친다

cherry-pick
→ 특정 커밋의 변경을 골라 가져온다

21. diff를 다시 정확히 정리하기

HEAD / 마지막 커밋
        ↕
Staging Area
        ↕
Working Tree
명령비교
git diffIndex ↔ Working Tree
git diff --stagedHEAD ↔ Index
git diff HEADHEAD ↔ 현재 작업 전체
git diff HEAD~1 HEAD직전 커밋 ↔ 최신 커밋
git diff HEAD는 “이전 커밋과 최신 커밋 비교”가 아니다. 이 구분을 잘못 설명한 자료를 볼 때 주의해야 한다.

22. commit -v도 구조로 이해하자

git commit -v

커밋 메시지 편집 화면에서 staged diff를 참고용으로 보여준다. 그 diff가 커밋 메시지에 들어가는 것도 아니고 Git이 단순히 diff 파일만 저장하는 것도 아니다. Git은 프로젝트 상태를 스냅샷 중심으로 기록하고 필요할 때 스냅샷 사이의 차이를 계산한다.

23. 실전 활용: 안전한 main과 실험용 브랜치

안정적으로 동작하는 웹앱에 새로운 효과를 시험한다고 해보자.

git switch -c twinkle

main을 그대로 보존한 채 twinkle에서 코드를 수정하고 테스트하고 커밋한다.

main → 안정 버전
   \
    twinkle → 실험 커밋들

성공하면:

git switch main
git merge twinkle

실패하면 main으로 돌아가 실험 브랜치를 삭제할 수 있다.

git switch main
git branch -D twinkle

이것이 브랜치가 실제 개발에서 강력한 이유다. 안정된 역사를 보존하면서 별도의 작업 흐름에서 자유롭게 실험할 수 있다.

24. 결국 Git의 화면은 이렇게 읽는다

                     HEAD 🚩
                       ↓
                     footer
                       ↓
A ───── B ───── C ───── D
              ↑
            master

여기서:

A, B, C, D = 커밋(스냅샷)

master = C를 가리키는 움직이는 이름표

footer = D를 가리키는 움직이는 이름표

HEAD = 현재 작업 브랜치 footer를 가리키는 깃발

새 커밋 E를 만들면 footer가 E로 움직인다.

                     HEAD 🚩
                       ↓
                     footer
                       ↓
A ───── B ───── C ───── D ───── E
              ↑
            master

3-2편 핵심

Git 브랜치를 이해할 때 가장 중요한 문장은 두 개다.

브랜치 = 특정 커밋을 가리키는
         '움직이는 이름표/포인터'

HEAD = 현재 작업 위치를 알려주는 깃발

이 두 문장을 중심으로 로그를 읽으면 branch, switch, checkout, rev-parse, merge가 따로 외워야 할 명령이 아니라 하나의 구조로 연결된다.

“지금 HEAD는 어느 브랜치에 붙어 있는가? 그 브랜치는 어느 커밋을 가리키는가? 다음 명령을 실행하면 어떤 포인터가 어디로 움직이는가?” 이 질문을 할 수 있게 되면 Git의 활용이 훨씬 선명해진다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 기록은 여기까지지만,
생각은 아직 이동 중이다