본문으로 건너뛰기 개발자가 알아야 할 Git 필수 명령어·팁 모음 | 매일 쓰는 실전 패턴

개발자가 알아야 할 Git 필수 명령어·팁 모음 | 매일 쓰는 실전 패턴

개발자가 알아야 할 Git 필수 명령어·팁 모음 | 매일 쓰는 실전 패턴

이 글의 핵심

매일 실무에서 자주 쓰는 Git 명령어와 패턴을 정리한 치트시트입니다. alias 설정, stash 활용, cherry-pick, log 필터링, reflog로 실수 복구, 커밋 메시지 컨벤션, .gitignore 패턴, 브랜치 네이밍, PR 올리기 전 정리까지 복사해서 바로 쓸 수 있게 구성했습니다.

들어가며: “그 명령어 뭐였더라”를 줄이는 글

Git을 어느 정도 써 본 개발자라면 add, commit, push, pull, branch, merge 정도는 손에 익어 있을 것입니다. 문제는 그 다음 단계입니다. stash를 언제 써야 효율적인지, 커밋 로그를 어떻게 필터링해야 원하는 커밋을 빨리 찾는지, 실수로 브랜치를 날렸을 때 어떻게 복구하는지는 매뉴얼을 정독한다고 몸에 붙는 것이 아니라, 반복해서 겪어봐야 익숙해지는 영역입니다.

이 글은 Git 내부 구조나 개념 설명을 목표로 하지 않습니다. pkglog에는 이미 객체 모델과 머지 내부 동작을 다루는 Git 완벽 가이드, 설치부터 기본 흐름을 다루는 Git 기초 입문, 브랜치 전략과 협업 문화를 다루는 Git 워크플로우 가이드가 있습니다. 대신 이 글은 매일 터미널 앞에서 실제로 타이핑하는 명령어와, 그 명령어를 왜 그렇게 쓰는지에 집중합니다.

대상 독자

  • 기본 Git 흐름은 알지만 alias·stash·cherry-pick 같은 생산성 도구를 아직 습관화하지 못한 개발자
  • 사고(브랜치 삭제, 커밋 날림, 잘못된 파일 커밋 등)가 났을 때 당황하지 않고 복구하고 싶은 개발자
  • 팀 컨벤션(커밋 메시지, 브랜치 이름, .gitignore)을 정리하고 싶은 리드/시니어

이 글을 읽으면

  • 매일 반복 타이핑하는 명령어를 alias로 줄일 수 있습니다.
  • stash와 cherry-pick으로 “지금 하던 작업을 잠깐 치워두고 다른 작업”을 깔끔하게 처리할 수 있습니다.
  • git log를 필터링해서 원하는 커밋·변경 이력을 빠르게 찾을 수 있습니다.
  • reflog로 실수를 복구하는 절차를 익혀 웬만한 사고에는 당황하지 않게 됩니다.

깊이 있는 리베이스 시나리오, reset 3단계(soft/mixed/hard)의 정확한 동작 원리, submodule, 파일 이름 변경 추적 같은 주제는 각각 전용 글(인터랙티브 리베이스, reset·revert 가이드)이 있으므로 여기서는 “언제 필요한지”만 짚고 링크로 넘기겠습니다.


1. Alias — 손가락이 기억하는 단축 명령어

가장 먼저 손봐야 할 것은 alias입니다. 하루에 수십 번 치는 명령어를 매번 전체 이름으로 입력하는 것은 시간 낭비이자 오타의 원인입니다. Git은 git config로 전역 alias를 등록할 수 있고, 이렇게 등록한 alias는 ~/.gitconfig 파일의 [alias] 섹션에 저장됩니다.

# 상태 확인 단축
git config --global alias.st "status -sb"

# 로그를 그래프로, 한 줄로, 최근 20개
git config --global alias.lg "log --oneline --graph --decorate -20"

# 마지막 커밋 메시지 수정 없이 내용만 추가
git config --global alias.amend "commit --amend --no-edit"

# 스테이징 취소 (add 되돌리기)
git config --global alias.unstage "restore --staged"

# 현재 브랜치 이름만 출력
git config --global alias.current "rev-parse --abbrev-ref HEAD"

# 방금 만든 커밋 하나 취소 (스테이징은 유지)
git config --global alias.undo "reset --soft HEAD~1"

등록 후에는 git st, git lg, git amend처럼 짧게 사용합니다. ~/.gitconfig를 직접 열어서 아래처럼 한 번에 관리하는 방법도 있습니다.

[alias]
    st = status -sb
    lg = log --oneline --graph --decorate -20
    amend = commit --amend --no-edit
    unstage = restore --staged
    current = rev-parse --abbrev-ref HEAD
    undo = reset --soft HEAD~1
    last = log -1 HEAD --stat

alias를 만들 때의 원칙은 하나입니다. 하루에 세 번 이상 치는 명령어라면 alias 후보입니다. 반대로 자주 안 쓰는 옵션 조합을 alias로 만들면 오히려 “이 alias가 뭐였지”를 검색하는 시간이 더 들 수 있으므로, 처음에는 위 목록 정도로 시작해서 필요에 따라 조금씩 늘려가는 것을 권장합니다. 셸 자체의 alias(alias gs='git status' 같은 bash/zsh alias)와 병행해도 좋지만, git config alias는 팀원과 .gitconfig 스니펫을 공유하기 쉽다는 장점이 있습니다.


2. git log 필터링 — 원하는 커밋을 빨리 찾기

git log를 옵션 없이 그대로 쓰면 정보량이 너무 많아서 오히려 원하는 걸 찾기 어렵습니다. 실무에서는 상황별로 옵션을 조합해서 씁니다.

그래프로 브랜치 흐름 보기

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

--graph는 커밋 간의 부모-자식 관계를 아스키 아트로 그려주고, --decorate는 각 커밋에 어떤 브랜치·태그가 붙어 있는지 보여줍니다. --all을 붙이면 현재 체크아웃한 브랜치뿐 아니라 로컬에 있는 모든 브랜치의 커밋을 함께 그려주므로, “내 브랜치가 main에서 얼마나 벌어졌는지”를 눈으로 확인할 때 유용합니다. 위에서 만든 git lg alias가 바로 이 조합의 축약형입니다.

특정 파일의 변경 이력 추적

# 파일별 커밋 이력
git log --follow -- src/utils/parser.ts

# 파일 이름이 바뀐 경우에도 추적 (--follow가 핵심)
git log --follow --stat -- src/utils/parser.ts

--follow는 파일 이름이 변경(rename)된 이력까지 추적합니다. --follow 없이 그냥 git log -- <파일>만 쓰면, 파일이 리네임된 시점 이전의 커밋은 보이지 않아서 “분명 예전에 이 로직이 있었는데 로그에 안 나온다”는 혼란을 겪기 쉽습니다. 파일 리네임 자체을 다루는 좀 더 깊은 내용은 git mv·파일 이름 변경 가이드에서 다룹니다.

실제 코드 변경 내용까지 확인 — git log -p

# 커밋 메시지뿐 아니라 diff까지 함께
git log -p -3 -- src/api/auth.ts

# 특정 함수/문자열이 추가되거나 삭제된 커밋만 찾기 (pickaxe)
git log -S "refreshToken" --oneline
git log -G "refreshToken\(" --oneline

git log -p는 커밋 메시지 아래에 실제 diff를 함께 보여줍니다. “이 함수가 언제, 왜 바뀌었는지”를 코드 리뷰 없이 혼자 추적할 때 유용합니다. -S"문자열"(pickaxe 검색)은 해당 문자열이 추가되거나 삭제된 횟수가 변한 커밋만 찾아주고, -G"정규식"은 diff 내용에 정규식이 매치되는 커밋을 찾아줍니다. 둘의 차이는 미묘한데, -S는 문자열의 등장 횟수 변화(예: 0번→1번)를 기준으로 찾고, -G는 정규식이 diff 어딘가에 매치되기만 하면 됩니다. 함수 이름처럼 정확히 몇 번 나왔는지가 중요하면 -S, 패턴 자체를 찾고 싶으면 -G가 더 적합합니다.

작성자·기간·경로로 좁히기

git log --author="kim" --since="2 weeks ago" --oneline
git log --since="2026-08-01" --until="2026-08-31" -- src/server/
git shortlog -sn --since="1 month ago"   # 최근 한 달 기여자별 커밋 수

git shortlog -sn은 커밋 수를 작성자별로 요약해줍니다. 스프린트 회고나 코드 오너십을 파악할 때 자주 씁니다.

git blame — 이 줄은 누가, 왜 바꿨나

# 파일의 각 줄이 어느 커밋에서 왔는지
git blame -L 40,60 src/server/router.ts

# 공백/포맷 변경 커밋은 건너뛰고 "진짜 의미 있는" 커밋 찾기
git blame -w -C src/server/router.ts

git blame은 특정 줄이 언제 누구에 의해 추가됐는지 알려주지만, 함정이 있습니다. 코드 포맷터(prettier 등)가 한 번 전체 파일을 정리하는 커밋을 만들면, 그 이후로는 blame이 전부 포맷터 커밋만 가리키게 됩니다. 이때 -w(공백 변경 무시)와 -C(코드 이동·복사 추적)를 함께 쓰면 실제 로직을 바꾼 커밋까지 더 잘 거슬러 올라갈 수 있습니다. 그래도 안 되면 .git-blame-ignore-revs 파일에 포맷팅 전용 커밋 해시를 등록하고 git config blame.ignoreRevsFile .git-blame-ignore-revs로 아예 blame 대상에서 제외하는 방법도 있습니다.


3. stash 활용 패턴 — 하던 작업을 깔끔하게 치워두기

작업 중 급하게 다른 브랜치로 넘어가야 하는데, 지금 변경사항을 커밋하기엔 아직 미완성인 경우가 흔합니다. 이럴 때 커밋 대신 stash를 씁니다.

# 가장 기본적인 사용
git stash push -m "로그인 폼 검증 로직 작업 중"

# untracked 파일(새로 만든 파일)까지 포함
git stash push -u -m "새 컴포넌트 포함"

# 특정 파일만 stash
git stash push -m "부분 저장" -- src/components/LoginForm.tsx

# 목록 확인
git stash list

# 최근 stash 복원 후 목록에서 제거
git stash pop

# 목록에는 남기고 복원만 (여러 브랜치에 같은 stash를 적용하고 싶을 때)
git stash apply stash@{1}

stash push-m으로 메시지를 남기는 습관을 들이면, git stash list가 길어졌을 때 “이게 뭐였지”를 겪지 않습니다. -u 옵션은 자주 놓치는 부분인데, 기본 git stash아직 add하지 않은, 그리고 이미 추적 중인 파일의 변경만 저장하고 새로 만든(untracked) 파일은 저장하지 않습니다. 새 파일까지 포함해서 통째로 치워두려면 반드시 -u(또는 --include-untracked)를 붙여야 합니다.

stash를 브랜치로 승격하기

작업하다 보면 “어, 이거 지금 브랜치가 아니라 새 브랜치에서 했어야 했는데”를 깨달을 때가 있습니다. 이미 stash에 넣어둔 상태라면 아래처럼 새 브랜치를 만들면서 바로 적용할 수 있습니다.

git stash branch feature/login-validation stash@{0}

이 명령은 stash를 만들었던 시점의 커밋을 기준으로 새 브랜치를 만들고, 거기에 stash 내용을 적용한 뒤 stash 목록에서 제거합니다. 잘못된 브랜치에서 작업을 시작했을 때 되돌리는 가장 빠른 방법입니다.

stash 충돌이 났을 때

git stash pop 도중 충돌이 나면 stash는 자동으로 삭제되지 않고 목록에 남아 있습니다. 당황해서 같은 stash를 다시 pop하려다 오류를 겪는 경우가 많은데, 충돌을 해결하고 git add로 스테이징한 뒤에는 git stash drop으로 수동으로 지워야 합니다. 반대로 충돌 해결에 자신이 없다면 git checkout --theirsgit merge --abort에 해당하는 안전한 되돌리기는 없으므로, 급하면 git reset --merge로 stash 적용 이전 상태로 되돌린 뒤 다시 시도하는 편이 안전합니다.


4. cherry-pick — 특정 커밋만 골라서 가져오기

브랜치 전체를 머지하지 않고 커밋 하나(또는 몇 개)만 다른 브랜치로 옮기고 싶을 때 씁니다. 대표적으로 hotfix를 여러 릴리스 브랜치에 동시에 반영해야 할 때 사용합니다.

# 커밋 하나 가져오기
git cherry-pick a1b2c3d

# 커밋 여러 개를 순서대로
git cherry-pick a1b2c3d e4f5g6h

# 커밋 범위 (a1b2c3d는 제외, e4f5g6h까지 포함)
git cherry-pick a1b2c3d..e4f5g6h

# 실제로 커밋을 만들지 않고 워킹 디렉터리에만 반영 (직접 리뷰 후 커밋하고 싶을 때)
git cherry-pick -n a1b2c3d

# 원본 커밋 해시를 메시지에 남기기 (어디서 왔는지 추적 가능)
git cherry-pick -x a1b2c3d

-x 옵션은 실무에서 특히 유용합니다. hotfix를 release/1.2release/1.3 양쪽에 cherry-pick했을 때, 나중에 “이 커밋이 원래 어디서 온 건지” git log만 봐도 알 수 있도록 커밋 메시지 끝에 (cherry picked from commit a1b2c3d...) 문구를 자동으로 남겨줍니다.

흔한 함정: cherry-pick도 결국 내부적으로 병합과 비슷한 방식으로 diff를 적용하기 때문에 충돌이 날 수 있습니다. 충돌이 나면 git status로 어떤 파일이 문제인지 확인하고, 해결 후 git cherry-pick --continue로 이어가거나, 포기하려면 git cherry-pick --abort로 원래 상태로 되돌립니다. 또한 이미 머지된 브랜치에서 다시 cherry-pick하면 “empty commit”(변경사항이 이미 반영돼 있어 diff가 없는 경우) 오류가 날 수 있는데, 이때는 --allow-empty로 강제 진행하거나 --skip으로 건너뛰면 됩니다.


5. reflog — 실수를 되돌리는 마지막 안전망

git reset --hard를 잘못 실행했거나, 브랜치를 삭제했거나, 리베이스 도중 커밋이 사라진 것처럼 보일 때 가장 먼저 확인해야 할 것이 git reflog입니다. reflog는 로컬 저장소에서 HEAD와 각 브랜치가 가리켰던 위치의 이력을 시간 순으로 기록합니다. 커밋 자체가 브랜치나 태그에서 더 이상 참조되지 않더라도, Git은 기본적으로 최소 90일(만료되지 않은 reflog 항목 기준) 동안 해당 객체를 가비지 컬렉션에서 보호합니다.

# HEAD의 이동 이력 확인
git reflog

# 특정 브랜치의 이력만
git reflog show feature/payment

# 잃어버린 커밋을 찾았다면 그 지점으로 되돌리기
git reset --hard HEAD@{3}

# 되돌리지 않고 그 커밋 내용만 새 브랜치로 복구
git branch recovered-work HEAD@{3}

실전 시나리오 1 — 브랜치를 실수로 삭제했을 때

git branch -D feature/payment   # 실수로 삭제!

# reflog에서 해당 브랜치가 마지막으로 가리키던 커밋 찾기
git reflog | grep "feature/payment"

# 찾은 해시로 브랜치 재생성
git branch feature/payment a1b2c3d

브랜치를 삭제해도 커밋 자체가 사라지는 것은 아닙니다. 다만 어떤 브랜치·태그도 가리키지 않는 상태(dangling commit)가 되므로, git log에는 더 이상 보이지 않을 뿐입니다. reflog에는 해당 브랜치가 마지막으로 가리켰던 커밋 해시가 남아 있으므로 이를 찾아 새 브랜치를 만들면 그대로 복구됩니다.

실전 시나리오 2 — git reset --hard로 커밋을 날렸을 때

git reflog
# 예시 출력
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# e4f5g6h HEAD@{1}: commit: 결제 검증 로직 추가
# ...

git reset --hard HEAD@{1}   # reset 실행 직전 상태로 복구

reset --hard는 작업 디렉터리까지 강제로 덮어쓰기 때문에 위험한 명령어로 알려져 있지만, 적어도 커밋했던 내용은 reflog를 통해 대부분 복구할 수 있습니다. reset·revert의 정확한 3단계 동작 원리와 언제 어떤 것을 써야 하는지는 Git reset·revert 완벽 가이드에서 더 깊게 다룹니다.

reflog의 한계는 분명히 알아둘 필요가 있습니다. reflog는 로컬 저장소에만 존재하며 원격 저장소(GitHub 등)에는 없습니다. 또한 git add조차 하지 않은, 즉 한 번도 Git 객체로 스냅샷된 적 없는 워킹 디렉터리의 변경사항은 reflog에도 남지 않으므로 복구가 불가능합니다. 이 때문에 “불안하면 일단 커밋해두고 나중에 정리한다”는 습관이 실전에서는 상당히 유효한 안전장치가 됩니다.


6. 커밋 메시지 컨벤션 — 짧게 정리

커밋 메시지 컨벤션과 브랜치 전략은 Git 워크플로우 가이드에서 팀 규모별로 자세히 비교하므로, 여기서는 매일 쓰는 실무 규칙만 짧게 정리합니다. 가장 널리 쓰이는 형식은 Conventional Commits입니다.

<타입>(<범위>): <요약>

<본문 — 무엇을, 왜 바꿨는지. "어떻게"는 코드가 말해줌>

<footer — 이슈 번호, breaking change 등>
git commit -m "fix(auth): 리프레시 토큰 만료 시 무한 재시도 버그 수정" \
  -m "만료된 토큰으로 재발급 요청 시 401을 받아도 재시도 큐에 다시 쌓이던 문제.
백오프 카운터를 요청 헤더가 아닌 별도 상태로 분리해 해결." \
  -m "Fixes: #482"

자주 쓰는 타입은 feat(기능 추가), fix(버그 수정), refactor(동작 변화 없는 구조 개선), docs, test, chore(빌드/설정 등)입니다. 여기서 실무적으로 중요한 것은 요약은 “무엇을 했다”가 아니라 “이 커밋을 적용하면 어떤 효과가 있다”를 명령형으로 쓰는 것입니다. “버그 수정함” 같은 과거형보다 “버그 수정” 또는 영어라면 “Fix bug”처럼 명령형으로 쓰면, git revertgit cherry-pick 메시지와 자연스럽게 이어집니다.


7. .gitignore 실전 패턴

.gitignore는 처음 프로젝트를 세팅할 때만 잠깐 만지고 잊어버리기 쉽지만, 실무에서는 몇 가지 패턴을 알아두면 훨씬 편해집니다.

# 특정 확장자 전체 무시
*.log
*.tmp

# 특정 디렉터리는 어디에 있든 무시 (선행 슬래시 없음)
node_modules/
dist/

# 루트의 특정 파일만 무시 (하위 디렉터리의 동명 파일은 무시 안 함)
/config/local.json

# 무시하되, 예외 하나만 포함하고 싶을 때
logs/*
!logs/.gitkeep

# 이미 커밋된 파일을 뒤늦게 .gitignore에 추가한 경우 (반드시 추적 해제 필요)
git rm -r --cached .env

가장 많이 실수하는 부분은 마지막 줄입니다. .gitignore에 파일을 추가한다고 해서 이미 커밋되어 추적(tracked) 중인 파일이 자동으로 무시되지는 않습니다. .env나 빌드 산출물을 나중에 .gitignore에 넣었는데도 git status에 계속 보인다면, git rm --cached로 인덱스에서만 제거(로컬 파일은 유지)한 뒤 커밋해야 비로소 무시가 적용됩니다.

패턴 우선순위도 헷갈리기 쉬운데, 나중에 나온 규칙이 앞의 규칙을 덮어씁니다. 그래서 logs/*로 전체를 무시한 뒤 !logs/.gitkeep으로 예외를 뒤에 적어야 그 파일만 추적됩니다. 순서를 바꾸면 예외가 적용되지 않습니다. 여러 프로젝트에서 반복해서 쓰는 패턴(OS 캐시 파일, 에디터 설정 등)은 전역 .gitignore로 빼두면 매번 프로젝트마다 추가할 필요가 없습니다.

git config --global core.excludesfile ~/.gitignore_global

8. 브랜치 네이밍 규칙

브랜치 이름 규칙은 사소해 보이지만, 저장소에 브랜치가 수백 개 쌓이면 검색·필터링(git branch --list "feature/*")에 큰 차이를 만듭니다. 실무에서 흔히 쓰는 패턴입니다.

feature/payment-retry-logic     # 새 기능
fix/login-token-refresh         # 버그 수정
hotfix/prod-null-pointer        # 프로덕션 긴급 수정
chore/upgrade-node-20           # 빌드/설정 변경
refactor/extract-auth-service   # 리팩터링
JIRA-1234-invoice-export        # 이슈 트래커 번호를 앞에 붙이는 팀도 흔함

<타입>/<설명> 형식은 커밋 메시지 컨벤션과 자연스럽게 짝을 이루고, git branch --list "fix/*"처럼 접두사로 필터링할 수 있다는 실용적인 이점이 있습니다. 이슈 트래커(Jira, Linear 등)를 쓰는 팀이라면 이슈 번호를 브랜치 이름에 포함시켜 PR과 이슈를 자동으로 연결하는 경우도 많습니다. 어떤 방식을 택하든 팀 안에서 일관되게 지키는 것이 이름 자체의 “정답”보다 훨씬 중요합니다.


9. PR 올리기 전 정리하기

작업하면서 쌓인 “WIP”, “오타 수정”, “다시 오타 수정” 같은 커밋을 그대로 PR에 올리면 리뷰어가 히스토리를 읽기 어렵습니다. PR을 열기 직전에 아래 흐름으로 정리하는 것이 일반적입니다.

# main 대비 몇 개 커밋이 쌓였는지 먼저 확인
git log --oneline main..HEAD

# 최근 5개 커밋을 대화형으로 정리 (스쿼시/재정렬/메시지 수정)
git rebase -i HEAD~5

# main의 최신 변경을 반영하면서 내 커밋을 그 위에 재배치
git fetch origin
git rebase origin/main

# 이미 원격에 push한 브랜치라면 force-with-lease로 안전하게 갱신
git push --force-with-lease

여기서 rebase -i의 각 명령(pick/squash/fixup/reword/drop)이 정확히 어떻게 동작하는지, 충돌이 여러 커밋에 걸쳐 반복될 때 어떻게 처리하는지는 Git 인터랙티브 리베이스 실전 가이드에서 단계별 예시로 다룹니다. 이 글에서 강조하고 싶은 실무 규칙은 두 가지입니다.

첫째, --force가 아니라 --force-with-lease를 씁니다. --force는 원격 브랜치를 무조건 덮어쓰지만, --force-with-lease는 “내가 마지막으로 알고 있던 원격 상태와 실제 원격 상태가 같을 때만” 덮어씁니다. 팀원이 같은 브랜치에 커밋을 push한 것을 모르고 강제 push하면, --force는 그 커밋을 조용히 날려버리지만 --force-with-lease는 오류를 내며 막아줍니다.

둘째, 공유 브랜치(main, develop 등)는 절대 rebase하지 않습니다. rebase는 커밋 해시를 새로 만드는 작업이므로, 이미 다른 사람이 받아서 작업 중인 브랜치를 rebase하면 그 사람 쪽에서 대규모 충돌이 발생합니다. 개인 feature 브랜치를 정리할 때만 rebase를 쓰고, 공유 브랜치에 반영이 끝난 뒤에는 건드리지 않는 것이 원칙입니다.


10. 자주 하는 실수와 복구법 모음

아래는 실무에서 반복적으로 마주치는 상황과 그에 대한 대응입니다.

커밋 메시지에 오타를 냈는데 아직 push 전이다

git commit --amend -m "올바른 메시지"

아직 원격에 올리지 않았다면 안전합니다. 이미 push한 커밋을 amend했다면 이후 push는 반드시 --force-with-lease가 필요합니다.

커밋에 파일을 하나 빼먹었다

git add forgotten-file.ts
git commit --amend --no-edit

새 커밋을 또 만들지 않고 방금 커밋에 합쳐 넣습니다. 앞서 만든 git amend alias가 바로 이 패턴입니다.

다른 브랜치인 줄 알고 작업했는데 사실 main이었다

# 아직 커밋 전이라면
git stash
git switch feature/correct-branch
git stash pop

# 이미 커밋까지 했다면
git branch feature/correct-branch   # 현재 위치에 새 브랜치 생성
git reset --hard origin/main         # main을 원격 상태로 되돌림
git switch feature/correct-branch    # 커밋은 새 브랜치에 그대로 남아 있음

민감한 파일(.env, 키 파일)을 실수로 커밋했다

git rm --cached .env
echo ".env" >> .gitignore
git commit -m "chore: .env 추적 해제"

이미 push까지 됐다면 단순히 다음 커밋에서 삭제하는 것만으로는 부족합니다. 과거 히스토리에 값이 남아 있으므로, 이미 유출된 것으로 간주하고 해당 키·비밀번호를 즉시 폐기·재발급하는 것이 최우선입니다. 히스토리 자체에서 완전히 제거하려면 git filter-repo(공식적으로 filter-branch보다 권장되는 도구)나 BFG Repo-Cleaner 같은 별도 도구가 필요하며, 이는 이 글의 범위를 넘어서는 별도 주제입니다.

merge 도중 마음이 바뀌어 그냥 취소하고 싶다

git merge --abort

충돌 마커가 잔뜩 생긴 상태에서 당황하지 말고, 아직 merge 커밋을 만들지 않았다면 --abort로 merge 시작 이전 상태로 완전히 되돌아갈 수 있습니다. rebase 도중이라면 같은 역할을 git rebase --abort가 합니다.

detached HEAD 상태에서 뭘 해야 할지 모르겠다

# 지금 위치에서 작업을 남기고 싶다면 브랜치로 승격
git switch -c temp-branch-from-detached

# 그냥 원래 브랜치로 돌아가고 싶다면
git switch main

과거 커밋을 체크아웃하거나 git checkout <해시>를 실행하면 어떤 브랜치도 아닌 상태(detached HEAD)가 됩니다. 이 상태에서 커밋을 하면 그 커밋들은 어떤 브랜치에도 속하지 않으므로, 나중에 다른 브랜치로 이동하면 reflog 없이는 찾기 어려워집니다. detached HEAD에서 의미 있는 작업을 했다면 반드시 이동하기 전에 브랜치로 승격해두어야 합니다.


마무리

이 글에서 다룬 패턴들의 공통점은 하나입니다. Git은 대부분의 실수를 되돌릴 수 있는 도구라는 것입니다. reflog가 존재하는 한, 그리고 최소한 git addgit commit으로 한 번이라도 스냅샷을 남긴 상태라면, 웬만한 사고는 침착하게 명령어 몇 개로 복구됩니다. 반대로 말하면 한 번도 스냅샷되지 않은 변경사항은 Git이 보호해주지 않으므로, 불안한 순간에는 일단 커밋(또는 최소한 stash)해두는 습관이 가장 확실한 보험입니다.

이 치트시트에 담긴 명령어를 한 번에 다 외울 필요는 없습니다. alias 몇 개부터 등록해두고, 실제로 stash 충돌이나 커밋 유실 같은 상황을 마주쳤을 때 이 글로 돌아와 해당 섹션만 다시 찾아 쓰는 방식을 권장합니다. 더 깊은 내부 동작이 궁금하다면 Git 완벽 가이드로, 팀 협업 전략이 궁금하다면 Git 워크플로우 가이드로 이어서 읽어보시기 바랍니다.

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.