infra
Platform

모듈 맵

[SW Eng] Git 기본 흐름 — 커밋·브랜치·머지를 PM·인프라의 언어로

0 / 38 완료

펼치기
0 / 38 완료0%

PM·SRE를 위한 소프트웨어 엔지니어링 · 12 / 38

[SW Eng] Git 기본 흐름 — 커밋·브랜치·머지를 PM·인프라의 언어로

Git이 무엇을 추적하는지, 커밋·브랜치·머지·충돌·PR이 무슨 뜻인지 코드를 짜지 않는 사람의 관점에서 정리합니다

🚨INCIDENT ALERT
HIGH

배포 후 장애가 났습니다. 인프라 담당이 묻습니다. "이번 배포에 뭐가 들어갔죠?" 개발자가 "main에 머지된 PR 다섯 개요"라고 답합니다. PM은 "어제 그 기능 롤백되면 다른 작업도 사라지나요?"가 궁금합니다. 커밋·브랜치·머지·PR이라는 단어의 의미와 관계를 모르면, 이 대화에서 아무 판단도 내릴 수 없습니다. Git은 개발자만의 도구가 아니라, 변경을 추적하는 팀 전체의 기록 시스템입니다.

이번 챕터에서 배울 것
  • 1커밋이 "변경의 스냅샷이자 추적 단위"임을 설명할 수 있다
  • 2브랜치가 "독립 작업 공간"이고 머지로 합쳐짐을 설명할 수 있다
  • 3머지 충돌이 왜 생기고 왜 정상인지 설명할 수 있다
  • 4git log/blame으로 장애·릴리스 범위를 조사할 수 있다

Git이 추적하는 것 — 변경의 역사

Git 커밋 히스토리 — 스냅샷의 연결확대

위 그림처럼 각 커밋은 고유 해시·메시지·작성자가 담긴 스냅샷으로 이어지고, 피처 브랜치는 메인에서 분기해 작업 후 머지 커밋으로 다시 합쳐진다.

💡개념

커밋: 누가·언제·왜 바꿨는가의 스냅샷

Git은 파일의 변경 이력을 추적하는 시스템입니다. 핵심 단위가 커밋입니다.

TEXT
커밋 = 변경 스냅샷 + 메시지 + 작성자 + 시각 + 고유 해시

a3f4b1c  feat: 결제 카드 연동 추가        (홍길동, 2시간 전)
9f8e7d6  fix: 중복 결제 방지              (김철수, 어제)
2c1b0a9  docs: API 문서 업데이트          (이영희, 2일 전)

각 커밋은 고유 해시(a3f4b1c)로 식별되며, 이력이 사슬처럼 이어집니다. 그래서:

  • 되돌리기: 문제가 된 커밋만 되돌릴 수 있다(revert).
  • 추적: "이 줄을 누가 언제 왜 바꿨나"를 blame으로 확인.
  • 릴리스 범위: "이번 배포 = 이 커밋부터 저 커밋까지"로 정의.

커밋은 배포가 아닙니다 — 기록입니다. 배포는 그 기록 중 특정 시점을 프로덕션에 반영하는 별개 행위입니다(CI/CD 파이프라인).

git commit이 실제로 저장하는 것 — 작업 트리에서 객체까지

💡개념

git commit 한 번이 만드는 것 — blob·tree·commit 객체 5단계

"커밋은 스냅샷"이라고 배웠지만, 정작 git commit을 치는 순간 Git이 디스크에 무엇을 만드는지는 잘 보이지 않습니다. 이 내부를 알면 "왜 add를 안 하면 커밋에 안 담기나", "커밋이 diff가 아니라 스냅샷이라는 게 무슨 뜻인가", "브랜치를 바꿔도 왜 파일이 안 사라지나" 같은 의문이 한 번에 풀립니다. Git은 파일을 .git/objects 안에 내용 기반(SHA-1 해시) 으로 저장하는 작은 객체 데이터베이스이고, 커밋은 그 위에 얹힌 세 종류 객체(blob·tree·commit)의 조합입니다.

TEXT
[작업 트리]  payment.js 를 수정              (아직 Git은 모름 = modified)
   │
   ① git add payment.js
   │    → 파일 내용을 blob 객체로 압축 저장 (이름 = 내용의 SHA-1 해시)
   │    → 인덱스(staging area)에 "payment.js = 이 blob 해시" 등록
   │
   ② git commit
   │    ↓ 커밋 시점의 인덱스 전체를 읽어서
   │  ③ tree 객체 생성   (디렉터리 스냅샷: 어떤 경로 = 어떤 blob·하위 tree)
   │  ④ commit 객체 생성 (root tree + 부모 커밋 + 작성자 + 시각 + 메시지)
   │    → tree·commit 모두 SHA-1 해시로 .git/objects 에 저장
   │
   ⑤ HEAD가 가리키는 브랜치(refs/heads/main)를 새 commit 해시로 이동
   ▼
[.git]  a3f4b1c(commit) ─→ tree ─→ blob(payment.js), blob(...), ...
        = 그 순간 프로젝트 전체의 완전한 스냅샷 (직전 커밋과의 diff가 아님)

각 단계가 하는 일과, 여기서 막히거나 오해하면 생기는 증상:

단계하는 일여기서 막히거나 오해하면
① 작업 트리 수정에디터로 파일을 바꾼 상태. 아직 Git 객체는 하나도 안 생김git statusChanges not staged로 뜸 — 여기서 멈추면 커밋에 아무것도 안 담김
② git add(스테이징)그 시점 파일 내용을 blob으로 저장하고 인덱스에 등록. add 이후 파일을 또 고치면 그 변경은 다시 add해야 커밋에 들어감add 뒤 수정분을 안 올리면 "커밋했는데 방금 고친 게 빠졌다" — 인덱스에 찍힌 시점이 커밋됨
③ tree 객체 생성인덱스를 디렉터리 구조 스냅샷으로 굳힘. 폴더마다 하위 tree, 파일마다 blob을 가리킴내용이 같은 파일은 같은 blob 해시를 공유 — 그래서 커밋마다 전체를 스냅샷해도 저장소가 폭발하지 않음
④ commit 객체 생성tree 해시 + 부모 커밋 해시 + author·committer + 메시지를 묶어 저장. 이 전체를 해시한 값이 커밋 IDgit config user.name/email 미설정 시 Please tell me who you are로 커밋 자체가 실패
⑤ 브랜치 포인터 이동브랜치는 커밋 해시 하나를 적어둔 파일일 뿐. HEAD→브랜치→커밋으로 따라감detached HEAD면 브랜치가 안 따라와, 만든 커밋이 어느 브랜치에도 안 붙어 나중에 "사라진 것처럼" 보임

커밋은 "직전 대비 바뀐 줄(diff)"을 저장하는 게 아니라, 그 순간 전체 파일 트리를 가리키는 스냅샷입니다(diff는 두 커밋의 tree를 비교해 그때그때 계산). 브랜치를 바꿔도 파일이 안 사라지는 이유는 각 커밋이 자기 완결적 스냅샷을 들고 있기 때문이고, 브랜치 전환은 그 스냅샷으로 작업 트리를 바꿔치기하는 것뿐입니다. 이 내부는 git cat-file로 직접 열어볼 수 있습니다.

로컬 터미널
git cat-file -t a3f4b1c     # 객체 종류 확인 → commit
git cat-file -p a3f4b1c     # 커밋 내용: tree 해시·parent·author·메시지
git cat-file -p <tree해시>   # 그 커밋의 디렉터리 스냅샷: 경로별 blob 목록

브랜치와 머지 — 평행 작업과 합치기

브랜치 전략 — Feature Branch 협업 흐름확대

위 그림처럼 feature 브랜치 생성 → 커밋 → PR 열기 → 리뷰·수정 → main 머지의 5단계 흐름이 다수 협업자가 충돌 없이 병렬 작업하는 표준 패턴이다.

💡개념

브랜치: 메인을 지키며 따로 일하는 공간

여러 명이 한 코드베이스에서 동시에 일하려면, 서로의 미완성 작업이 섞이면 안 됩니다. 브랜치가 이를 해결합니다.

feature 브랜치 흐름 — main에서 feature/pay 브랜치를 따 독립적으로 커밋(메인 영향 없음)하고, 완성되면 PR 리뷰를 거쳐 main에 머지확대

  1. main에서 feature 브랜치를 딴다
  2. 거기서 자유롭게 커밋(메인 영향 없음)
  3. 완성되면 PR(Pull Request)로 리뷰 후 main에 머지
  • 브랜치: 독립된 평행 작업선. 망쳐도 main은 안전.
  • 머지(merge): 브랜치의 변경을 main에 합치는 것.
  • PR(Pull Request): "내 브랜치를 main에 합쳐 주세요" 요청 + 리뷰·테스트 게이트(PR과 코드 리뷰).

PM 관점: "그 기능 롤백하면 다른 작업도 사라지나요?" → 브랜치/커밋이 분리돼 있으면 그 PR의 커밋만 되돌리면 됩니다. 이것이 브랜치 전략(브랜치 전략)이 중요한 이유입니다.

히스토리로 장애 조사하기 — 직접 해보기

Git 히스토리 — 장애 조사·릴리스 범위 도구확대

위 그림처럼 git log로 릴리스 범위를, git blame으로 특정 줄의 변경자를, git bisect로 버그 도입 커밋을, git revert로 안전한 롤백을 수행할 수 있다.

Git 작업 판단 — 어떤 상황에 어떤 명령인가
이번 배포에 무엇이 들어갔나git log v1.1.0..v1.2.0 — 두 태그 사이 커밋 범위. 장애 조사 1순위(범위 좁히기). '무엇이 바뀌었나'부터
이 버그가 언제 들어왔나git bisect — 정상/버그 커밋을 주면 이진 탐색으로 도입 커밋 특정. 수백 커밋도 몇 단계에 좁힘. '언제부터 깨졌나'
이 줄을 누가·왜 바꿨나git blame <file> → 커밋 해시 → git show로 그 커밋 맥락. 비난이 아니라 '의도 파악'용. '코드의 사연을 읽는다'
배포한 변경을 되돌려야 함이미 공유된 커밋은 git revert(되돌리는 새 커밋) — 히스토리 보존, 안전. git reset(히스토리 삭제)은 공유 브랜치에서 금지. '공유된 건 revert'
커밋이 거대하게 뭉쳐 일부만 못 되돌림작고 논리적 단위로 커밋하는 습관이 롤백 유연성을 만든다. 한 커밋=한 의미. '큰 커밋은 미래의 롤백을 막는다'
main에 직접 커밋 vs 브랜치기능/수정은 브랜치에서 → PR → 머지. main 직접 커밋은 리뷰·CI를 건너뛰어 위험. '메인은 항상 배포 가능 상태로 지킨다'
1git log로 '이번 배포에 무엇이 들어갔나' 확인

코드를 못 짜도 Git 히스토리는 강력한 조사 도구입니다. 두 릴리스 사이의 변경, 특정 줄의 변경자를 확인합니다.

로컬 터미널
# 이전 릴리스(v1.1.0)부터 현재(v1.2.0)까지 들어간 커밋 = 이번 배포 범위
git log --oneline v1.1.0..v1.2.0

# 특정 파일의 특정 줄을 누가 언제 바꿨나 (장애 의심 코드 추적)
git blame -L 40,50 payment/service.js

# 어제 이후 결제 관련 변경만
git log --since="yesterday" --oneline -- payment/
OUTPUT
$ git log --oneline v1.1.0..v1.2.0
a3f4b1c feat: 결제 카드 연동 추가      ← 장애 시각과 겹침? 1순위 의심
9f8e7d6 fix: 중복 결제 방지
2c1b0a9 chore: 의존성 업데이트
→ 이번 배포에 3개 커밋. 장애가 결제 쪽이면 a3f4b1c부터 본다
git log --oneline v1.1.0..v1.2.0
🔍실행 후 확인할 것
  • v1.1.0..v1.2.0 범위의 커밋 목록이 곧 "이번 배포에 들어간 변경" — 장애 영역과 겹치는 커밋부터 의심한다. 목록이 비면 잘못된 태그 범위
  • git blame으로 의심 줄의 변경 커밋·작성자를 보고 → 그 커밋 메시지/PR에서 "왜 이렇게 바꿨나" 맥락을 찾는다(작성자에게 직접 물을 근거)
  • 한 커밋에 수십 개 파일이 섞여 있으면(거대 커밋) 추적이 어렵다 → 팀에 "작은 커밋·작은 PR"을 요청하는 근거
  • 롤백 결정 시 git revert <커밋>은 해당 변경만 되돌린다 — 다른 작업은 보존. "다 사라지나요?"의 답은 보통 No(분리돼 있으면)

상황: 장애 후 "방금 배포한 결제 변경만 되돌려 주세요"라고 요청했지만, 개발자가 "그 커밋에 결제·알림·리팩터링이 다 섞여 있어 결제만 되돌리면 다른 것도 깨져요"라고 답합니다.

원인: 하나의 커밋/PR에 여러 관심사가 뭉쳐 있습니다. Git의 되돌리기는 커밋 단위라, 한 커밋에 여러 변경이 섞이면 선택적 롤백이 어렵습니다. 이는 코드 문제가 아니라 '변경을 작게 나누지 않은' 협업 습관의 문제입니다.

진단:

로컬 터미널
git show --stat <커밋>    # 이 커밋이 건드린 파일 범위 확인
# 결제·알림·설정이 한 커밋에 다 있으면 = 뭉친 커밋

해결: 단기적으론 전체 배포를 이전 태그로 롤백(git checkout v1.1.0 기반 재배포)하고 결제만 다시 분리 작업합니다. 근본적으론 팀에 "한 PR = 한 관심사" 원칙을 요청합니다(PR과 코드 리뷰). 작은 커밋·작은 PR은 리뷰 품질뿐 아니라 장애 시 선택적 롤백을 가능하게 하는 운영 안전장치입니다 — 인프라/SRE가 강하게 요구할 명분이 있습니다.

심화 — 히스토리는 저절로 조사 도구가 되지 않는다

💡개념

심화: 조사 가능한 히스토리는 팀이 만든다 — 히스토리 위생

git log·blame이 강력한 조사 도구라고 배웠지만, 실제 팀 저장소를 열면 fix, wip 커밋 수백 개에 막히는 경우가 많습니다. 도구는 기록을 보여줄 뿐, 기록의 품질은 팀의 습관이 만듭니다 — 이것이 '히스토리 위생'입니다.

  • 머지 방식이 히스토리의 모양을 정합니다: PR 전체를 커밋 하나로 뭉쳐 합치는 squash 머지는 log가 깔끔해 릴리스 범위 파악이 쉬운 대신, PR 내부의 세부 커밋(어느 단계에서 무엇이 바뀌었나)이 사라집니다. 머지 커밋 방식은 전부 보존하는 대신 log가 복잡해집니다. 어느 쪽이든 팀이 알고 선택해야지, 사람마다 다르게 섞이면 히스토리가 조사 불가능해집니다.
  • blame은 '마지막으로 만진 사람'만 보여줍니다: 전체 코드 포맷팅이나 대량 리팩터링 커밋이 한 번 지나가면, 수천 줄의 blame이 전부 그 커밋을 가리켜 진짜 의도가 담긴 커밋이 가려집니다. 포맷팅류 커밋은 로직 변경과 분리해서 올리고, 그 해시를 blame 무시 목록(.git-blame-ignore-revs)에 등록하는 것이 성숙한 팀의 관례입니다.
  • 공유된 히스토리는 불변이어야 합니다: force push는 이미 공유한 기록을 다시 쓰는 행위라, 어제 조사 기준점으로 삼은 커밋·태그가 오늘 사라질 수 있습니다. main 같은 공유 브랜치에 force push 차단(보호 규칙)을 거는 것은 인프라가 챙길 기본 안전장치입니다.
  • 강제 수준은 팀 규모의 함수입니다: 2~3명 팀은 구두 합의로 충분하지만, 10명이 넘으면 습관에 맡긴 컨벤션은 반드시 무너집니다 — 커밋 메시지 형식 검사와 머지 방식을 저장소 설정·CI로 강제해야 하는 시점이 옵니다.

히스토리 위생은 취향 문제가 아니라 미래의 장애 조사 비용입니다. "6개월 뒤의 우리가 이 log만 보고 사고 원인을 좁힐 수 있는가"가 판단 기준입니다.

상황: 장애로 기능 PR을 revert했다가, 개발자가 같은 브랜치에서 버그를 고쳐 다시 main에 머지했습니다. PR 상태는 Merged, CI 초록, 배포도 완료. 그런데 프로덕션에 그 기능이 없습니다. 확인해 보니 이번 머지로 실제 반영된 diff가 버그 수정분 몇 줄뿐입니다.

원인: 머지는 '브랜치에는 있는데 main에는 없는 커밋'만 가져옵니다. 원래 기능 커밋들은 첫 머지 때 이미 main 히스토리에 들어갔고, revert는 그 코드를 지우는 새 커밋을 얹었을 뿐 원래 커밋을 히스토리에서 없애지 않았습니다. 그래서 재머지 때 Git은 기능 커밋들을 '이미 반영됨'으로 판단해 건너뛰고, revert가 지운 코드는 되살아나지 않습니다.

진단:

로컬 터미널
git log --oneline main | head -5        # revert 커밋과 이번 머지 커밋 확인
git branch --contains a3f4b1c           # 기능 커밋이 이미 main에 포함돼 있음
git diff v1.2.0..main -- payment/       # 실제 반영분 = 수정 몇 줄뿐, 기능 본체 없음

해결: revert 커밋을 다시 revert해 기능 코드를 먼저 되살린 뒤 수정분을 얹거나, 새 브랜치에서 기능을 다시 만들어 올립니다. 팀 차원에서는 "revert로 뺀 기능을 재출시할 때 같은 브랜치 재머지는 조용히 실패한다"를 롤백 절차에 명시하고, Merged 표시가 아니라 배포 후 기능이 실제로 있는지로 완료를 판정합니다. PM·인프라에게 주는 교훈: 'Merged = 코드 반영'이라는 가정은 revert가 한 번이라도 개입한 순간 깨질 수 있습니다.

💼
실무 맥락
현업 패턴

인프라/SRE로서 장애 대응의 첫 5분은 종종 Git 히스토리에서 시작합니다 — "이번 배포에 뭐가 들어갔지?"를 git log <이전태그>..<현재태그>로 좁히고, 의심 커밋의 PR에서 변경 의도를 읽습니다. 또한 "작은 PR·의미 있는 커밋 메시지"를 팀에 요구하는 것은 잔소리가 아니라, 선택적 롤백과 빠른 원인 추적을 가능하게 하는 운영 요구입니다. PM은 Git 히스토리로 릴리스 노트를 자동 생성하고(커밋→변경 목록), 기능의 진행을 PR 상태로 추적합니다. Git은 개발자만의 것이 아니라, 변경을 다루는 모든 역할의 공용 기록입니다.

다음 모듈에서는 이 브랜치들을 팀이 어떤 규칙으로 운영하는가 — Git Flow와 Trunk-based 같은 브랜치 전략을 다룹니다.

실전 랩으로 손에 익히기: Git 기본 흐름 실습 — 커밋·브랜치·머지, 그리고 일부러 낸 충돌을 직접 해결하며 손으로 익힙니다.

관련 모듈로 더 깊이:

지식 확인

퀴즈 — 8문제

Q1

Git에서 '커밋(commit)'이 의미하는 것은?

Q2

브랜치(branch)를 쓰는 이유로 가장 적절한 것은?

Q3

'머지 충돌(merge conflict)'이 발생하는 상황은?

Q4

PM/인프라가 Git 히스토리(로그·blame)를 활용하는 대표적 상황은?

Q5

커밋 메시지를 '무엇을 왜 바꿨는지' 명확히 쓰면 나중에 어떤 이점이 있나?

Q6

여러 명이 같은 코드베이스에서 동시에 다른 기능을 개발할 때 브랜치가 주는 이점은?

Q7

[심화] 대량 코드 포맷팅 커밋이 한 번 지나간 뒤 git blame이 조사 도구로서 약해지는 이유와 대응으로 옳은 것은?

Q8

[심화] 장애로 revert했던 기능을 같은 브랜치에서 버그만 고쳐 다시 main에 머지했다. PR은 Merged, CI 초록, 배포 완료인데 프로덕션에 그 기능이 없다. 원인과 처방으로 옳은 것은?

0 / 8 답변

🧪 실습으로 확인하기

Git 기본 흐름 — 커밋·브랜치·머지·충돌을 손으로

초급

로컬에 저장소를 만들어 커밋을 쌓고, 브랜치를 갈라 작업한 뒤 머지하고, 일부러 충돌을 내서 해결한다. PR·리뷰·CI를 이해하기 위한 손의 기초를 만든다.

45📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요