삼덕회계법인공인회계사 이철

Git Commands 가이드: 개념부터 실전 명령어까지

Git이란?

Git은 분산 버전 관리 시스템(DVCS)이다. 코드의 변경 이력을 추적하고, 여러 사람이 동시에 작업할 수 있게 해준다.

핵심 특징:

  • 분산형: 각 개발자가 전체 히스토리의 복사본을 로컬에 가지고 있음
  • 빠름: 대부분의 작업이 로컬에서 이루어짐
  • 브랜치: 가볍고 빠른 브랜치 생성/전환/병합

공식 문서: Git Reference


기본 개념

Working Directory / Staging Area / Repository

Working Directory  →  Staging Area (Index)  →  Local Repository  →  Remote Repository
      (작업 공간)        (git add)               (git commit)         (git push)
  • Working Directory: 실제 파일을 편집하는 공간
  • Staging Area (Index): 다음 커밋에 포함시킬 변경사항을 모아두는 공간
  • Local Repository: 커밋된 히스토리가 저장되는 .git 디렉토리
  • Remote Repository: GitHub, GitLab 등 원격 저장소

파일의 상태

Untracked → Staged → Committed
              ↑         ↓
           Modified ←───┘
  • Untracked: Git이 추적하지 않는 새 파일
  • Modified: 추적 중인 파일이 변경된 상태
  • Staged: git add로 스테이징 영역에 올린 상태
  • Committed: 로컬 저장소에 기록된 상태

HEAD는 현재 체크아웃된 커밋을 가리키는 포인터다. 보통 브랜치의 최신 커밋을 가리킨다.

HEAD → main → commit C
               commit B
               commit A

초기 설정

사용자 정보 설정

git config --global user.name "이름"
git config --global user.email "이메일"

설정 확인

git config --list
git config user.name

공식 문서: git-config


저장소 생성 및 복제

새 저장소 초기화

git init

현재 디렉토리에 .git 폴더를 생성하고 Git 저장소로 만든다.

원격 저장소 복제

git clone <url>
git clone <url> <디렉토리명>

원격 저장소를 로컬에 복사한다. origin이라는 이름으로 자동 등록된다.

공식 문서: git-init / git-clone


상태 확인 명령어

git status

git status
git status -s    # 짧은 형식

작업 디렉토리와 스테이징 영역의 상태를 보여준다.

 M file.txt      # 수정됨 (unstaged)
M  file.txt      # 수정됨 (staged)
MM file.txt      # staged 후 다시 수정됨
?? newfile.txt   # untracked
A  newfile.txt   # 새 파일 (staged)
D  deleted.txt   # 삭제됨

git log

git log                      # 전체 로그
git log --oneline            # 한 줄 요약
git log --oneline --graph    # 브랜치 그래프 포함
git log -5                   # 최근 5개 커밋
git log --author="이름"       # 특정 작성자의 커밋
git log -- path/to/file      # 특정 파일의 변경 이력

git diff

git diff                    # unstaged 변경사항
git diff --staged           # staged 변경사항
git diff HEAD               # 모든 변경사항 (staged + unstaged)
git diff branch1..branch2   # 브랜치 간 차이
git diff commit1..commit2   # 커밋 간 차이

공식 문서: git-status / git-log / git-diff


변경사항 기록

git add

git add file.txt             # 특정 파일 스테이징
git add .                    # 현재 디렉토리의 모든 변경사항
git add -A                   # 저장소 전체의 모든 변경사항
git add -p                   # 변경사항을 하나씩 확인하며 스테이징
git add *.js                 # 패턴 매칭

-p (patch) 모드는 하나의 파일 안에서도 원하는 부분만 골라 스테이징할 수 있어 유용하다.

git commit

git commit -m "커밋 메시지"
git commit -am "메시지"       # add + commit (tracked 파일만)
git commit --amend            # 직전 커밋 수정 (메시지 또는 내용)

좋은 커밋 메시지 작성법

타입: 제목 (50자 이내)

본문 (선택사항, 72자 줄바꿈)
- 왜 변경했는지
- 어떤 영향이 있는지

흔히 사용하는 타입: feat, fix, refactor, docs, test, chore

git rm / git mv

git rm file.txt              # 파일 삭제 + 스테이징
git rm --cached file.txt     # Git 추적만 제거 (파일은 보존)
git mv old.txt new.txt       # 파일 이름 변경

공식 문서: git-add / git-commit


브랜치

브랜치 기본

git branch                   # 브랜치 목록
git branch -a                # 원격 브랜치 포함
git branch <이름>             # 새 브랜치 생성
git branch -d <이름>          # 브랜치 삭제 (병합 완료된 것만)
git branch -D <이름>          # 브랜치 강제 삭제

브랜치 전환

git checkout <브랜치>          # 브랜치 전환
git checkout -b <브랜치>       # 생성 + 전환
git switch <브랜치>            # 브랜치 전환 (Git 2.23+)
git switch -c <브랜치>         # 생성 + 전환 (Git 2.23+)

git switch는 git checkout의 브랜치 전환 기능만 분리한 명령어다. 더 명확하고 안전하다.

병합 (Merge)

git merge <브랜치>             # 현재 브랜치에 대상 브랜치를 병합
git merge --no-ff <브랜치>     # fast-forward 없이 병합 커밋 생성
git merge --abort             # 충돌 시 병합 취소

Fast-forward vs Merge commit:

  • Fast-forward: 분기 없이 포인터만 앞으로 이동 (히스토리가 직선)
  • Merge commit: 두 브랜치를 합치는 별도의 커밋 생성 (--no-ff)

리베이스 (Rebase)

git rebase <브랜치>            # 현재 브랜치를 대상 브랜치 위로 재배치
git rebase -i HEAD~3          # 최근 3개 커밋을 인터랙티브 리베이스
git rebase --abort            # 리베이스 취소
git rebase --continue         # 충돌 해결 후 계속

리베이스는 히스토리를 깔끔하게 만들지만, 이미 push한 커밋은 리베이스하지 않는 것이 원칙이다.

공식 문서: git-branch / git-merge / git-rebase


원격 저장소

remote 관리

git remote -v                       # 원격 저장소 목록
git remote add origin <url>         # 원격 저장소 추가
git remote remove <이름>             # 원격 저장소 제거
git remote set-url origin <new-url> # URL 변경

push / pull / fetch

git push origin main                # 로컬 → 원격
git push -u origin main             # 업스트림 설정 + push
git push --force                    # 강제 push (주의!)

git pull                            # fetch + merge
git pull --rebase                   # fetch + rebase
git fetch                           # 원격 변경사항만 가져오기 (병합 X)
git fetch --all                     # 모든 원격의 변경사항 가져오기

pull vs fetch:

  • fetch: 원격의 변경사항을 가져오기만 함. 로컬 브랜치에 영향 없음
  • pull: fetch + merge (또는 rebase). 로컬 브랜치에 바로 반영

공식 문서: git-remote / git-push / git-pull / git-fetch


되돌리기

변경사항 취소

git checkout -- file.txt       # 특정 파일 변경사항 취소 (unstaged)
git restore file.txt           # 위와 동일 (Git 2.23+)
git restore --staged file.txt  # 스테이징 취소 (변경사항 보존)

커밋 되돌리기

git revert <commit>            # 해당 커밋을 취소하는 새 커밋 생성
git reset --soft HEAD~1        # 커밋 취소 (변경사항은 staged 상태 유지)
git reset --mixed HEAD~1       # 커밋 취소 (변경사항은 unstaged 상태)
git reset --hard HEAD~1        # 커밋 취소 + 변경사항 완전 삭제 (위험!)

revert vs reset:

  • revert: 안전함. 히스토리를 보존하면서 변경을 취소. 공유된 브랜치에서 사용
  • reset: 히스토리를 변경함. 로컬에서만 사용하는 것이 안전

공식 문서: git-restore / git-revert / git-reset


임시 저장 (Stash)

git stash                     # 현재 변경사항 임시 저장
git stash -m "메시지"          # 메시지와 함께 저장
git stash list                # stash 목록
git stash pop                 # 가장 최근 stash 적용 + 삭제
git stash apply               # stash 적용 (삭제하지 않음)
git stash drop                # 가장 최근 stash 삭제
git stash clear               # 모든 stash 삭제

브랜치를 전환해야 하는데 현재 작업을 커밋하기엔 애매할 때 유용하다.

공식 문서: git-stash


태그

git tag                       # 태그 목록
git tag v1.0.0                # 태그 생성 (lightweight)
git tag -a v1.0.0 -m "설명"   # 태그 생성 (annotated)
git push origin v1.0.0        # 특정 태그 push
git push origin --tags        # 모든 태그 push
git tag -d v1.0.0             # 로컬 태그 삭제
  • Lightweight tag: 단순 포인터 (이름만)
  • Annotated tag: 작성자, 날짜, 메시지 포함 (릴리스에 권장)

공식 문서: git-tag


기타 유용한 명령어

cherry-pick

git cherry-pick <commit>      # 특정 커밋만 현재 브랜치에 적용

blame

git blame file.txt            # 각 줄을 누가, 언제 수정했는지 확인

reflog

git reflog                    # HEAD 이동 이력 (reset으로 잃어버린 커밋 복구 시 유용)

clean

git clean -n                  # 삭제 대상 미리 보기 (dry run)
git clean -f                  # untracked 파일 삭제
git clean -fd                 # untracked 파일 + 디렉토리 삭제

공식 문서: git-cherry-pick / git-blame / git-reflog / git-clean


.gitignore

프로젝트 루트에 .gitignore 파일을 만들어 추적하지 않을 파일을 지정한다.

# 빌드 결과물
node_modules/
dist/
build/

# 환경 설정
.env
.env.local

# OS 파일
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/

# 로그
*.log

공식 문서: gitignore


자주 쓰는 조합

새 기능 개발 흐름

git switch -c feature/new-feature    # 브랜치 생성 + 전환
# ... 작업 ...
git add .
git commit -m "feat: 새 기능 추가"
git push -u origin feature/new-feature
# PR 생성 → 코드 리뷰 → 병합

충돌 해결

git merge feature-branch             # 충돌 발생
# 충돌 파일 편집 (<<<<<<, ======, >>>>>> 마커 해결)
git add .                            # 해결 완료 표시
git commit                           # 병합 커밋 생성

실수로 커밋한 파일 제거

git rm --cached secrets.env          # Git 추적만 제거 (파일은 남김)
echo "secrets.env" >> .gitignore     # .gitignore에 추가
git commit -m "chore: remove tracked secret file"

이전 커밋 상태 확인

git log --oneline                    # 커밋 해시 확인
git show <commit>                    # 해당 커밋의 변경 내용 보기
git checkout <commit> -- file.txt    # 특정 파일만 해당 커밋 상태로 복원

브랜치 병합 전략 가이드: 작업 브랜치에서 main으로

실제 개발에서 가장 많이 하는 작업 중 하나가 "작업 브랜치에서 완성된 코드를 main에 합치는 것"이다. 같은 결과를 내더라도 어떤 방식으로 합치느냐에 따라 히스토리의 모양과 추적 가능성이 크게 달라진다.

실전 시나리오: drafts → main

drafts 브랜치에서 작업하다가 완료 후 main에 반영하는 기본 흐름:

# 1. main을 최신 상태로 업데이트
git switch main
git pull origin main

# 2. drafts를 main에 병합 (방법은 아래에서 선택)
git merge drafts

# 3. (선택) 원격에 push
git push origin main

핵심은 2번 단계에서 어떤 병합 방법을 쓰느냐다. 아래에서 각 방법을 비교한다.


방법 1: Fast-Forward Merge (기본 동작)

main에 drafts 분기 이후 새로운 커밋이 없으면, Git은 단순히 포인터만 앞으로 이동한다.

git switch main
git merge drafts
# Before
main:    A --- B
                \
drafts:          C --- D

# After (fast-forward)
main:    A --- B --- C --- D
  • merge 커밋이 생성되지 않음
  • 히스토리가 완전한 직선
  • 단, drafts 브랜치가 존재했다는 기록이 남지 않음

적합한 상황: 혼자 작업하는 프로젝트, 커밋 1~2개짜리 단순 수정


방법 2: Merge Commit (--no-ff)

Fast-forward가 가능한 상황에서도 강제로 merge 커밋을 생성한다.

git switch main
git merge --no-ff drafts
# After (merge commit M 생성)
main:    A --- B --- E --- M
                \         /
drafts:          C --- D
  • "drafts 브랜치가 여기서 합쳐졌다"는 기록이 히스토리에 명확히 남음
  • git revert -m 1 <merge-commit> 한 번으로 기능 전체를 되돌릴 수 있음
  • 팀 프로젝트에서 "이 기능은 언제 main에 합쳐졌는가?"를 추적하기 좋음

--no-ff vs 기본 merge:

  • 기본 git merge: fast-forward 가능하면 FF, 불가능하면 merge 커밋 생성
  • git merge --no-ff: 항상 merge 커밋 생성

적합한 상황: 팀 프로젝트, 기능 단위 추적이 필요한 경우, Git Flow 등 브랜칭 전략 사용 시


방법 3: Squash Merge

drafts의 모든 커밋을 하나의 새 커밋으로 합쳐서 main에 적용한다.

git switch main
git merge --squash drafts
git commit -m "feat: 새 기능 추가"   # 별도로 commit 필요!
# Before
drafts:  C --- D --- E  (WIP 커밋 여러 개)

# After (squash)
main:    A --- B --- S  (C+D+E가 하나의 커밋 S로)
  • --squash는 자동 커밋하지 않음. 반드시 git commit을 별도로 실행해야 함
  • main 히스토리가 매우 깔끔해짐 (1 기능 = 1 커밋)
  • "fix typo", "WIP", "oops" 같은 중간 커밋이 main에 남지 않음
  • 커밋 메시지를 새로 정리해서 작성할 수 있음

주의: Git이 drafts가 "merge되었다"고 인식하지 않기 때문에, squash merge 후에는 반드시 브랜치를 삭제하는 것이 좋다. 삭제하지 않고 다시 merge하면 충돌이 발생할 수 있다.

적합한 상황: 실험적/임시 커밋이 많은 브랜치, PR 단위로 깔끔한 히스토리를 원할 때


방법 4: Rebase 후 Fast-Forward Merge

drafts를 main의 최신 상태 위로 "재배치"한 뒤 fast-forward merge한다.

# Step 1: drafts에서 main 위로 rebase
git switch drafts
git rebase main
# (충돌 시 해결 후 git rebase --continue)

# Step 2: main에서 fast-forward merge
git switch main
git merge drafts    # 이제 반드시 fast-forward됨
# Before
main:    A --- B --- E
                \
drafts:          C --- D

# After rebase
main:    A --- B --- E
                      \
drafts:                C' --- D'  (새로운 hash)

# After merge (fast-forward)
main:    A --- B --- E --- C' --- D'
  • 완벽한 직선형(linear) 히스토리
  • merge 커밋 없이 깔끔한 git log
  • 단, rebase는 커밋 hash를 변경함 (C → C', D → D')
  • 이미 push한 브랜치를 rebase하면 force push 필요 (git push --force-with-lease)
  • 다른 사람이 같은 브랜치에서 작업 중이면 문제 발생

적합한 상황: 개인 브랜치, 직선형 히스토리를 선호하는 경우, push 전 로컬 정리


방법 5: Cherry-Pick (선택적 반영)

브랜치 전체가 아니라 특정 커밋만 골라서 main에 적용한다.

git switch main
git cherry-pick <commit-hash>

# 여러 커밋
git cherry-pick <hash1> <hash2>

# 연속 범위 (hash1 제외, hash2까지 포함)
git cherry-pick <hash1>..<hash2>
# cherry-pick C와 E만 (D는 제외)
main:    A --- B --- C' --- E'
                \
drafts:          C --- D --- E  (원본은 그대로)
  • 원하는 커밋만 선택적으로 가져올 수 있음
  • 새로운 커밋 hash로 복사됨 (원본은 유지)
  • 많은 커밋을 cherry-pick하면 관리가 어려워지므로, merge/rebase의 대체 수단으로 쓰면 안 됨

적합한 상황: hotfix를 여러 브랜치에 적용할 때, 특정 커밋만 먼저 main에 반영해야 할 때


전략 비교

방법 merge 커밋 개별 커밋 보존 직선형 히스토리 기능 전체 revert
Fast-Forward X O O 어려움
--no-ff O O X 쉬움
Squash X X (1개로 합침) O 쉬움
Rebase + FF X O O 어려움
Cherry-Pick X 선택적 O 어려움

어떤 전략을 선택할까?

작업 브랜치를 main에 합치려 한다
│
├─ 커밋이 1~2개이고 단순한 수정?
│  └─ Fast-Forward 또는 Squash
│
├─ 팀에서 기능 단위 추적이 필요?
│  └─ git merge --no-ff
│
├─ main 히스토리를 최대한 깔끔하게?
│  ├─ 개별 커밋 이력 불필요 → Squash
│  └─ 개별 커밋 이력 필요 → Rebase 후 FF
│
├─ 특정 커밋만 선택적으로 가져와야?
│  └─ Cherry-Pick
│
└─ 혼자 작업 + 직선형 히스토리 선호?
   └─ Rebase 후 FF

GitHub PR 병합 옵션

GitHub에서 Pull Request를 merge할 때 세 가지 버튼이 표시된다. 각각 위에서 설명한 로컬 명령어와 대응된다.

Create a merge commit

git merge --no-ff와 동일한 동작.

main:  A --- B --- M
              \   /
feature:       C --- D
  • 모든 커밋이 보존됨 + merge 커밋 추가
  • 커밋 메시지에 PR 번호 자동 포함: Merge pull request #42 from ...
  • GitHub의 기본 옵션

Squash and merge

git merge --squash + git commit과 동일한 동작.

main:  A --- B --- S
  • 모든 커밋이 하나로 합쳐짐
  • GitHub UI에서 최종 커밋 메시지 편집 가능
  • PR description이 커밋 body에 자동 포함
  • merge 후 브랜치 삭제를 권장 (Git이 merge 여부를 인식하지 못하므로)

Rebase and merge

git rebase main + git merge (fast-forward)와 유사한 동작.

main:  A --- B --- C' --- D'
  • 각 커밋이 main 위에 개별적으로 추가됨
  • merge 커밋 없는 직선형 히스토리
  • 주의: GitHub은 항상 새로운 커밋 SHA를 생성함 (로컬 hash와 달라짐)

참고: GitHub 저장소 Settings > General > "Automatically delete head branches"를 체크하면 PR merge 시 브랜치가 자동 삭제된다.

공식 문서: About merge methods on GitHub


병합 전 체크리스트

실수를 줄이기 위해 병합 전에 확인할 것들.

1. 반드시 main을 최신화하라

git switch main
git pull origin main

main이 최신 상태가 아니면 원격과 로컬의 히스토리가 어긋나서 push 시 충돌이 발생한다.

2. 작업 브랜치와 main의 차이를 확인하라

git log main..drafts --oneline    # drafts에만 있는 커밋
git diff main..drafts --stat      # 변경된 파일 요약

3. main이 앞서 나간 경우 처리

작업 중에 다른 사람(또는 다른 브랜치에서의 자신)이 main에 커밋을 추가한 경우:

# 방법 A: merge로 동기화 (안전, 히스토리 복잡해짐)
git switch drafts
git merge main

# 방법 B: rebase로 동기화 (깔끔, 개인 브랜치일 때만)
git switch drafts
git rebase main

4. 병합 후 브랜치 정리

# merge된 브랜치 확인
git branch --merged main

# 로컬 브랜치 삭제
git branch -d drafts

# 원격 브랜치 삭제
git push origin --delete drafts

# 원격에서 삭제된 브랜치의 로컬 tracking 참조 정리
git fetch --prune

삭제 기준:

  • 삭제: merge 완료된 feature 브랜치, squash merge된 브랜치
  • 유지: main, develop 등 장기 브랜치, 아직 작업 중인 브랜치

알아두면 좋은 최신 기능

git rerere (충돌 해결 재사용)

git config --global rerere.enabled true

merge 충돌을 한 번 해결하면 Git이 해결 방법을 기록해두고, 같은 충돌이 다시 발생하면 자동으로 동일하게 해결한다. rebase 중 반복되는 충돌에 특히 유용하다.

--force-with-lease (안전한 Force Push)

# 위험: 원격의 모든 변경을 덮어씀
git push --force

# 안전: 원격이 예상한 상태일 때만 push
git push --force-with-lease

rebase 후 push할 때 --force 대신 --force-with-lease를 쓰면, 다른 사람이 그 사이에 push한 내용이 있을 경우 push를 거부한다.

merge-ort 전략 (Git 2.34+ 기본값)

기존 merge-recursive 전략을 대체하는 새로운 merge 엔진. 대규모 rename이 있는 merge에서 수백~수천 배의 성능 향상이 보고되었다. Git 2.34 이상이면 자동 적용되므로 별도 설정이 필요 없다.

공식 문서: git-rerere / merge strategies


참고 링크

이어서 읽을 글

그 밖의 글 전체 보기 →
그 밖의 글이재명 정부 부동산 4편: 강남과 강북의 집값, 그 사이에서2026.04.24 그 밖의 글이재명 정부 부동산 3편: 강남이 꺾이다 — 2.12 대책부터 5월 9일 전략까지2026.03.02 그 밖의 글이재명 정부 부동산 총력전: 1.29 공급대책부터 보유세 시그널까지2026.02.07