Git 내부 명령어와 워크플로우 시각적 이해

Git은 분산 버전 관리 시스템으로서 내부적으로 작업 디렉터리, 스테이징 영역(인덱스), 그리고 커밋 히스토리라는 세 가지 주요 공간을 사용한다. 이 글에서는 각 영역의 역할과 이들을 오가는 명령어의 흐름을 시각적으로 풀어내고, 브랜치 병합과 리베이스의 차이점까지 도식화하여 설명한다.

Git의 세 가지 데이터 공간

Git은 프로젝트 상태를 크게 세 가지 공간에 나누어 관리한다. 이 구조를 이해하면 명령어의 효과를 정확히 예측할 수 있다.

1. 작업 디렉터리 (Working Directory)

실제로 코드를 편집하는 파일 시스템 공간이다. 여기서 수정된 파일은 ‘추적 대상’(tracked)이거나 ‘추적되지 않은’(untracked) 상태일 수 있다. Git은 이 디렉터리에서 변경 사항을 감지할 뿐, 자동으로 버전 관리를 수행하지 않는다.

2. 스테이징 영역 (Staging Area / Index)

커밋으로 확정하기 전에 원하는 변경 사항만 골라 담을 수 있는 중간 버퍼이다. .git/index 파일에 이진 형태로 저장되며, 파일의 해시와 메타정보를 들고 있다. ‘쇼핑 카트’에 비유할 수 있다.

3. 커밋 히스토리 (Commit History)

.git/objects 디렉터리에 SHA-1 해시로 식별되는 불변 객체들(blob, tree, commit)로 구성된 영구 기록이다. 모든 커밋은 프로젝트의 완전한 스냅숏을 가리키며, 부모 커밋을 참조하는 방향성 비순환 그래프로 연결된다.

기본 명령어의 흐름 시각화

git add: 작업 디렉터리 → 스테이징 영역

수정된 파일을 스테이징 영역으로 옮겨 커밋 후보로 만든다. 부분 스테이징이나 패치 단위 추가도 가능하다.

# 특정 파일만 스테이징
git add src/app.py

# 현재 디렉터리 내 모든 변경 사항 추가
git add .

# hunk 단위 대화식 추가
git add -p

git commit: 스테이징 영역 → 커밋 히스토리

스테이징된 내용을 묶어 하나의 커밋 객체를 생성한다. 커밋 메시지는 일관된 규칙을 따르면 히스토리 탐색이 쉬워진다.

구분접두어설명예시
새 기능feat:새로운 기능 추가feat: OAuth 로그인 구현
버그 수정fix:문제 해결fix: 결제 금액 소수점 오류 수정
문서docs:문서 변경docs: API 엔드포인트 설명 보강
리팩터링refactor:동작 변경 없는 코드 개선refactor: 인증 모듈 함수 분리
테스트test:테스트 코드test: 사용자 생성 단위 테스트 추가
# 메시지와 함께 커밋
git commit -m "feat: 검색 자동완성 추가"

# 추적 대상 파일의 변경을 한 번에 스테이징 후 커밋
git commit -am "fix: 레이아웃 겹침 수정"

git status: 상태 점검

작업 디렉터리와 스테이징 영역 사이의 파일 상태를 보여준다. 커밋 전 상태 확인에 필수적이다.

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   config.yaml

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
        modified:   src/main.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        temp.log

브랜치 병합과 리베이스의 내부 동작

브랜치 생성과 전환

브랜치는 특정 커밋을 가리키는 가벼운 포인터일 뿐이다. 새 브랜치를 만들고 전환하는 비용은 거의 없다.

# feature/user-auth 브랜치 생성 후 전환
git checkout -b feature/user-auth

# 기존 브랜치로 이동
git checkout main

# 로컬 브랜치 삭제
git branch -d feature/user-auth

Fast-forward 병합

main 분기에 새로운 커밋이 없고, 대상 브랜치가 main의 직접 후속인 경우 병합 커밋 없이 포인터만 앞으로 이동한다. 이력이 선형으로 유지된다.

git checkout main
git merge feature/user-auth
# Fast-forward 병합이 자동 수행됨

3-way 병합

두 브랜치 모두 독립적인 커밋을 갖고 있을 때, 공통 조상 커밋을 기준으로 양쪽 변경을 하나로 합친 병합 커밋이 생성된다. 이때 충돌이 발생하면 수동 해결이 필요하다.

git merge feature/payment
# 충돌 발생 시
git status                # 충돌 파일 확인
# 충돌 해결 후
git add .
git commit -m "Merge branch 'feature/payment'"

리베이스(Rebase)

한 브랜치의 커밋들을 다른 브랜치의 최신 커밋 뒤로 재배치한다. 결과적으로 마치 브랜치를 새로 딴 것처럼 선형 이력이 만들어진다. 각 커밋은 새 해시값을 얻기 때문에 공유된 브랜치에서는 사용을 주의해야 한다.

# feature/payment-gateway 브랜치에서
git rebase main

# 충돌 시 수정 후 계속
git add .
git rebase --continue

# 리베이스 중단
git rebase --abort

대화식 리베이스

최근 몇 개의 커밋을 재정렬, 합치기, 메시지 수정 등을 할 수 있다.

git rebase -i HEAD~4
# 편집기에서 명령어 선택
pick a1b2c3d 결제 모듈 초안
fixup d4e5f6h 초안 수정
reword g7h8i9j API 연동 완료

병합 vs 리베이스 비교

특성병합 (Merge)리베이스 (Rebase)
이력 형태분기와 병합 커밋이 보존된 가지 구조커밋이 재배치되어 단일 선형 이력
커밋 해시기존 커밋의 해시 유지, 병합 커밋 추가모든 커밋의 해시가 변경됨
충돌 해결병합 시점에 한 번 수행커밋마다 순차적으로 해결 가능
이력 추적어떤 브랜치에서 작업했는지 명확개별 커밋의 소속 브랜치 정보 소멸
주요 용도공개 브랜치 통합, 팀 협업개인 브랜치 정리, 풀 리퀘스트 전 이력 정돈
안전성공유 브랜치에서도 안전공유 브랜치에서는 피해야 함 (force push 유발)

팀 정책에 따라 두 방식을 혼용하는 것이 일반적이다. 예를 들어 main에는 병합만 사용하고, 피처 브랜치는 git rebase main으로 최신 상태를 반영한 뒤 병합하는 전략이 깔끔한 이력을 만든다.

태그: Git staging-area commit branching merging

9월 19일 13:31에 게시됨