전체 목록
GitMedium#47

Git의 cherry-pick, stash, reflog 명령어를 설명해주세요.

#Git#명령어#협업
힌트

특정 커밋 선택, 임시 저장, 히스토리 복구 기능입니다.

정답 및 해설

Git의 cherry-pick, stash, reflog 명령어를 설명해주세요.

Git에는 일상적인 add/commit/push 외에도 실수를 복구하거나 유연하게 작업을 관리할 수 있는 강력한 명령어들이 있습니다. cherry-pick은 특정 커밋만 선택적으로 가져오는 기능, stash는 작업 중인 변경사항을 임시 저장하는 기능, reflog는 Git의 "타임머신"으로 실수로 삭제된 커밋도 복구할 수 있는 기능입니다.

cherry-pick

개념

cherry-pick은 다른 브랜치의 특정 커밋 하나(또는 여러 개)를 현재 브랜치에 적용하는 명령어입니다. merge나 rebase처럼 전체 브랜치를 통합하는 것이 아니라, 원하는 커밋만 골라서 가져올 수 있습니다.

# 기본 사용법
git cherry-pick <커밋_해시>

# 커밋 해시 확인
git log --oneline feature-branch
# a1b2c3d feat: 결제 버그 수정
# e4f5g6h feat: 장바구니 기능 추가
# i7j8k9l feat: 상품 목록 구현

# main 브랜치에서 특정 커밋만 가져오기
git checkout main
git cherry-pick a1b2c3d  # 결제 버그 수정만 가져옴

여러 커밋 cherry-pick

# 여러 커밋을 개별적으로 지정
git cherry-pick a1b2c3d e4f5g6h

# 연속된 커밋 범위 (A는 포함 안 됨, B는 포함됨)
git cherry-pick A..B

# 연속된 커밋 범위 (A, B 모두 포함)
git cherry-pick A^..B

# 예시: develop 브랜치의 최근 3개 커밋 가져오기
git log --oneline develop
# 9f8e7d6 fix: 로그인 토큰 갱신 오류 수정
# 5c4b3a2 fix: 회원가입 이메일 검증 개선
# 1h2i3j4 feat: 소셜 로그인 추가

git cherry-pick 5c4b3a2^..9f8e7d6  # 이메일 검증 + 토큰 수정 가져오기

cherry-pick 옵션

# 커밋하지 않고 스테이징 상태로 유지
git cherry-pick --no-commit a1b2c3d
# 이후 내용 수정 후 직접 커밋 가능

# 충돌 발생 시 처리
git cherry-pick a1b2c3d
# CONFLICT (content): Merge conflict in src/payment.js

# 1. 충돌 해결 후
git add src/payment.js
git cherry-pick --continue

# 2. 취소 (cherry-pick 전 상태로)
git cherry-pick --abort

# 3. 충돌 무시하고 진행
git cherry-pick --skip

# 커밋 메시지 수정 (-e: edit)
git cherry-pick -e a1b2c3d  # 에디터에서 메시지 수정 가능

cherry-pick 실전 시나리오

# 시나리오 1: 핫픽스를 여러 브랜치에 적용
# main에 긴급 버그 수정 후 release/1.0 브랜치에도 적용

git checkout main
git commit -m "fix: 결제 오류 수정" # 커밋 해시: abc123

git checkout release/1.0
git cherry-pick abc123  # release에도 동일 수정 적용

git checkout release/2.0
git cherry-pick abc123  # 다른 릴리즈에도 적용

# 시나리오 2: 잘못된 브랜치에 커밋한 경우
git checkout wrong-branch
git commit -m "feat: 새 기능" # 커밋 해시: def456

# 올바른 브랜치로 이동 후 cherry-pick
git checkout correct-branch
git cherry-pick def456

# wrong-branch에서 커밋 되돌리기
git checkout wrong-branch
git revert def456  # 또는 git reset HEAD~1 (push 전)

# 시나리오 3: develop의 특정 기능만 main에 반영 (긴급)
git log --oneline develop
# 3e4f5g6 test: 단위 테스트 추가 (아직 불완전)
# 1a2b3c4 feat: 빠른 검색 기능 (완성, 긴급 반영 필요)
# 9z8y7x6 feat: 추천 알고리즘 (개발 중)

git checkout main
git cherry-pick 1a2b3c4  # 빠른 검색만 main에 반영

stash

개념

stash는 작업 중인 변경사항(추적된 파일의 수정사항, staged 변경사항)을 임시로 저장하고 작업 디렉토리를 깨끗한 상태로 만드는 명령어입니다. 스택(stack) 구조로 동작합니다.

# 기본 사용법
git stash           # 현재 변경사항 stash (추적된 파일만)
git stash pop       # 가장 최근 stash 적용 후 stash 목록에서 제거
git stash apply     # 가장 최근 stash 적용 (stash 목록에 유지)
git stash list      # stash 목록 조회
git stash drop      # 가장 최근 stash 삭제
git stash clear     # 모든 stash 삭제

stash 상세 사용법

# stash 저장 시 메시지 추가
git stash push -m "로그인 폼 작업 중"
git stash push -m "결제 모듈 리팩토링"

# stash 목록 확인
git stash list
# stash@{0}: On main: 결제 모듈 리팩토링
# stash@{1}: On feature/login: 로그인 폼 작업 중
# stash@{2}: WIP on develop: 상품 페이지 수정

# 특정 stash 적용
git stash apply stash@{1}   # 인덱스로 지정
git stash pop stash@{1}     # 적용 후 삭제

# 특정 stash 내용 확인
git stash show stash@{0}         # 변경된 파일 목록
git stash show -p stash@{0}      # diff 내용까지 확인

# 추적되지 않은 새 파일도 stash
git stash push --include-untracked
git stash push -u  # 축약형

# gitignore 파일도 포함
git stash push --all
git stash push -a

# 일부 파일만 stash
git stash push -m "일부 변경사항" src/components/Login.tsx src/api/auth.ts

# stash를 새 브랜치로 꺼내기
git stash branch new-feature-branch stash@{0}
# stash 내용이 새 브랜치에 적용되고 stash는 삭제됨

stash 실전 시나리오

# 시나리오 1: 브랜치 전환이 필요할 때
# A 기능 개발 중 긴급 버그 신고 접수

git status
# M  src/components/ProductList.tsx  ← 작업 중
# M  src/api/products.ts              ← 작업 중

git stash push -m "상품 목록 페이지네이션 작업 중"

# 긴급 버그 수정
git checkout hotfix/payment-error
# ... 버그 수정 ...
git commit -m "fix: 결제 금액 계산 오류 수정"
git checkout main
git merge hotfix/payment-error

# 원래 작업으로 복귀
git checkout feature/product-pagination
git stash pop

# 시나리오 2: pull 전 임시 저장
# 원격에 새 변경사항이 있을 때

git stash push -m "현재 작업 임시 저장"
git pull origin develop
git stash pop
# 충돌이 있을 수 있음 - 해결 후 작업 계속

# 시나리오 3: stash 충돌 처리
git stash pop
# CONFLICT: 충돌 발생

# 충돌 해결 후
git add .
# stash pop 중 충돌 시 stash는 자동 삭제 안 됨
git stash drop  # 수동으로 stash 삭제

reflog

개념

reflog(reference log)는 HEAD와 브랜치 포인터가 이동한 모든 기록을 저장합니다. reset, rebase, checkout 등으로 커밋을 "잃어버린" 것처럼 보여도 reflog를 통해 복구할 수 있습니다. 기본적으로 90일간 보관됩니다.

# HEAD의 이동 기록 조회
git reflog

# 출력 예시:
# a1b2c3d (HEAD -> main) HEAD@{0}: commit: feat: 새 기능 추가
# e4f5g6h HEAD@{1}: reset: moving to HEAD~1
# i7j8k9l HEAD@{2}: commit: fix: 버그 수정
# m1n2o3p HEAD@{3}: checkout: moving from develop to main
# q4r5s6t HEAD@{4}: commit: docs: README 업데이트

# 특정 브랜치의 reflog
git reflog show feature/my-branch

# 날짜 기준으로 확인
git reflog --date=iso

실수 복구 시나리오

# 시나리오 1: git reset --hard로 커밋 삭제한 경우

# 실수로 최근 커밋들을 hard reset
git reset --hard HEAD~3  # 최근 3개 커밋 삭제됨!

# reflog로 삭제된 커밋 해시 확인
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# d4e5f6g HEAD@{1}: commit: feat: 세 번째 기능
# h7i8j9k HEAD@{2}: commit: feat: 두 번째 기능
# l1m2n3o HEAD@{3}: commit: feat: 첫 번째 기능  ← 이전 HEAD

# 이전 상태로 복구
git reset --hard d4e5f6g  # reset 전 HEAD로 복구

# 시나리오 2: 브랜치를 실수로 삭제한 경우

git branch -D feature/important-work  # 실수로 브랜치 삭제!

# reflog에서 삭제된 브랜치의 마지막 커밋 찾기
git reflog
# a1b2c3d HEAD@{0}: checkout: moving from feature/important-work to main
# b2c3d4e HEAD@{1}: commit: feat: 중요한 기능 완성
# ...

# 브랜치 복구
git checkout -b feature/important-work b2c3d4e

# 시나리오 3: 잘못된 rebase 후 복구

git rebase main  # 예상치 못한 충돌 등으로 작업이 망가짐

# rebase 전 상태 찾기
git reflog
# a1b2c3d HEAD@{0}: rebase finished: refs/heads/feature onto abc
# e4f5g6h HEAD@{1}: rebase: feat: 기능 C
# i7j8k9l HEAD@{2}: rebase: feat: 기능 B
# m1n2o3p HEAD@{3}: rebase (start): checkout main
# q4r5s6t HEAD@{4}: commit: feat: 기능 C  ← rebase 전 상태

# rebase 전으로 복구
git reset --hard q4r5s6t

# 시나리오 4: 특정 시간대로 복구
git checkout 'HEAD@{2 hours ago}'  # 2시간 전 HEAD 상태로
git checkout 'HEAD@{yesterday}'    # 어제 HEAD 상태로

reflog 활용 팁

# 분리된 HEAD 상태에서 작업 후 브랜치 잃어버린 경우
git checkout abc123  # detached HEAD에서 작업
git commit -m "작업 내용"  # 커밋 해시: def456

git checkout main  # 다른 브랜치로 이동 (def456 커밋이 브랜치에 없음!)

# reflog에서 찾기
git reflog
# 현재 HEAD: main
# def456 HEAD@{1}: commit: 작업 내용 ← 이 커밋!

# 새 브랜치로 복구
git branch recovered-work def456

# orphan 커밋 찾기 (어느 브랜치에도 속하지 않는)
git fsck --unreachable
# unreachable commit def456...

# reflog의 기간 설정 (기본 90일)
git config gc.reflogExpire "180 days"
git config gc.reflogExpireUnreachable "90 days"

세 명령어 조합 실전 예시

# 복잡한 시나리오: 잘못된 브랜치에서 작업 후 올바른 브랜치로 이전

# 1. main에서 실수로 직접 작업
git checkout main
# ... 여러 파일 수정 ...
git add .
git commit -m "feat: 신규 기능 구현" # 커밋 해시: abc123
git commit -m "fix: 버그 수정"        # 커밋 해시: def456

# 2. 이 커밋들을 feature 브랜치로 이전
git checkout -b feature/new-feature  # 새 브랜치 생성
# (이미 커밋이 포함되어 있음)

# 3. main을 이전 상태로 되돌리기
git checkout main
git reset --hard HEAD~2  # 2개 커밋 제거

# 4. 만약 reset 후 후회가 된다면 reflog로 복구
git reflog
git reset --hard <이전_HEAD_해시>

# 다른 브랜치의 특정 커밋만 급하게 가져와야 할 때
git stash push -m "현재 작업 임시 저장"  # 현재 작업 stash
git cherry-pick abc123 def456            # 필요한 커밋들 cherry-pick
git stash pop                             # 임시 저장한 작업 복원

정리 표

명령어주요 역할핵심 명령주요 사용 시나리오
cherry-pick특정 커밋만 선택 적용git cherry-pick <hash>핫픽스 여러 브랜치 적용, 잘못된 브랜치 커밋 이전
stash변경사항 임시 저장git stash / git stash pop긴급 브랜치 전환, pull 전 임시 보관
reflogHEAD 이동 이력 조회git refloghard reset 복구, 삭제된 브랜치 복구