Git 고급 사용법 — rebase, cherry-pick, bisect 실전

게시일: 2025년 6월 6일 · 14분 읽기

Git reset --hard로 3일치 작업을 날린 후부터

3년 전 일이다. 급하게 코드를 수정해야 한다고 생각해서 git reset --hard origin/main을 쳤다. 잠깐, 내 로컬 브랜치에서? 네, 거기서. 결과? 3일 동안 작업한 코드가 모두 사라졌다.

그때 리더에게 보고하면서 "Git reflog가 구해줬다"고 했다. 그리고 그 이후로 Git의 고급 기능들을 제대로 배우기 시작했다. 이젠 저 정도의 실수로 당황하지 않는다.

Interactive Rebase로 커밋 정리하기

깔끔한 커밋 히스토리는 유지보수의 핵심이다. Interactive rebase는 이를 위한 가장 강력한 도구다.

<?xml version="1.0"?>
# 최근 5개 커밋을 정리하기
$ git rebase -i HEAD~5

# 이 명령어를 실행하면 에디터가 열린다:
pick 1a2b3c4 Initial commit
pick 5d6e7f8 Add user model
pick 9g0h1i2 Add user tests
pick 3j4k5l6 Fix typo
pick 7m8n9o0 Update user model

# 이제 이 커밋들을 수정할 수 있다
# 명령어:
#   pick   = 커밋 유지
#   reword = 커밋 메시지 수정
#   squash = 이전 커밋에 합치기
#   fixup  = 이전 커밋에 합치기 (메시지 제거)
#   drop   = 커밋 삭제

보통 나는 이렇게 한다:

<?xml version="1.0"?>
pick 1a2b3c4 Initial commit
pick 5d6e7f8 Add user model
squash 9g0h1i2 Add user tests
fixup 3j4k5l6 Fix typo
squash 7m8n9o0 Update user model

이렇게 하면 최종적으로 2개의 커밋만 남는다. "Add user model"과 그 테스트, 그리고 "Update user model"이 하나로 합쳐진다. 히스토리가 훨씬 깔끔해진다.

Rebase의 실전 워크플로우

팀 프로젝트에선 rebase 전략이 중요하다. 우리 팀이 쓰는 패턴을 소개하겠다.

<?xml version="1.0"?>
# 1. feature 브랜치 생성
$ git checkout -b feature/user-auth

# 2. 작업하면서 커밋 (로컬에서는 자유롭게)
$ git commit -m "WIP: user auth implementation"
$ git commit -m "WIP: add login form"
$ git commit -m "Add authentication"

# 3. 메인 브랜치 최신화
$ git fetch origin
$ git rebase origin/main

# 4. 로컬 커밋 정리
$ git rebase -i origin/main

# 에디터:
pick 1a2b3c4 WIP: user auth implementation
squash 5d6e7f8 WIP: add login form
reword 9g0h1i2 Add authentication

# 5. Reword에서 메시지 수정
# "Add user authentication with login form"

# 6. 푸시
$ git push origin feature/user-auth --force-with-lease

--force-with-lease--force보다 안전하다. 다른 누군가가 같은 브랜치에 푸시했으면 실패하니까.

Cherry-pick으로 선택적 커밋 적용

긴급 핫픽스를 배포해야 할 때 cherry-pick이 유용하다.

<?xml version="1.0"?>
# develop 브랜치에서 버그 수정
$ git checkout develop
$ git commit -m "Fix: critical bug in payment processing"
# 커밋 해시: a1b2c3d

# 지금 바로 main 브랜치에도 적용해야 함
$ git checkout main
$ git cherry-pick a1b2c3d

# 완료! main에도 같은 커밋이 적용됨

복수의 커밋을 cherry-pick할 수도 있다:

<?xml version="1.0"?>
# 연속된 커밋들
$ git cherry-pick a1b2c3d^..9z8y7x6

# 또는 개별적으로
$ git cherry-pick a1b2c3d e4f5g6h i7j8k9l

주의할 점: cherry-pick은 기본적으로 새로운 커밋을 만든다. 원래 커밋과는 다른 해시를 가진다.

Git Bisect로 버그 찾기

언제부터 버그가 생겼는지 찾아야 할 때가 있다. 수백 개의 커밋을 하나하나 확인할 순 없다. 이때 bisect를 쓴다.

<?xml version="1.0"?>
# 1. Bisect 시작
$ git bisect start

# 2. 현재 커밋을 "bad" (버그 있음)로 표시
$ git bisect bad

# 3. 버그가 없는 커밋을 찾아서 "good"으로 표시
$ git bisect good v1.0.0

# Git이 중간값을 체크아웃함
# 현재 커밋에서 테스트
$ npm test

# 4. 버그가 있으면
$ git bisect bad

# 버그가 없으면
$ git bisect good

# 5. 반복. Git이 이진 탐색으로 빠르게 찾음

# 6. 완료
$ git bisect reset

만약 자동화하고 싶다면:

<?xml version="1.0"?>
# 테스트 스크립트를 실행해서 자동으로 bisect
$ git bisect start
$ git bisect bad
$ git bisect good v1.0.0

# 테스트 명령어 실행 (0 = good, 1 = bad)
$ git bisect run npm test

# Git이 자동으로 good/bad를 판단하고 진행

우리 프로젝트에서 버그를 찾은 사례:

<?xml version="1.0"?>
# 성능 저하가 발생했는데 정확한 원인을 모름
$ git bisect start
$ git bisect bad HEAD  # 현재: 느림
$ git bisect good v2.1.0  # 2주 전: 빠름

# Git이 500개의 커밋을 이진 탐색
# 약 9번의 검사로 원인 커밋 찾음

# Bisect run으로 자동화
$ git bisect run ./measure-performance.sh

# 결과: "Add caching layer for user profiles" 커밋이 버그 원인

Reflog로 데이터 복구하기

우리가 처음 이야기한 git reset --hard 사건이 여기다.

<?xml version="1.0"?>
# 내가 실수로 한 일
$ git reset --hard origin/main  # 3일치 작업 사라짐

# 하지만 reflog는 모든 HEAD 이동 기록을 가지고 있다
$ git reflog

# 출력:
# abc1234 (HEAD -> main) HEAD@{0}: reset: moving to origin/main
# def5678 HEAD@{1}: commit: Add user authentication
# ghi9012 HEAD@{2}: commit: Add login form
# jkl3456 HEAD@{3}: commit: WIP: user auth

# 내 작업은 ghi9012에 있다!
$ git reset --hard ghi9012

# 또는 특정 커밋으로
$ git checkout def5678

이 명령어가 내 작업을 구해줬다. reflog는 최소 30일간 데이터를 유지한다.

고급 패턴들

몇 가지 더 유용한 패턴들:

<?xml version="1.0"?>
# 1. Rebase 중 충돌 해결
$ git rebase main
# 충돌 발생
$ vim src/file.ts  # 수정
$ git add src/file.ts
$ git rebase --continue

# 2. 잘못된 rebase 취소
$ git rebase --abort

# 3. 여러 브랜치를 하나로 (squash merge)
$ git merge feature/branch --squash
$ git commit -m "Merge feature"

# 4. 특정 커밋의 변경사항만 역적용
$ git revert abc1234
# 새로운 커밋이 생김 (히스토리 유지)

# 5. 커밋 메시지 수정
$ git commit --amend -m "New message"
$ git push --force-with-lease

# 6. 이전 버전으로 돌아가기 (히스토리 유지)
$ git revert abc1234..def5678
# 해당 범위의 커밋들을 역적용하는 새 커밋들 생성

팀 환경에서의 주의사항

Git의 고급 기능들은 강력하지만, 팀 환경에서 조심해야 한다.

마무리

Git의 고급 기능들은 처음엔 복잡해 보인다. 하지만 실전에서 써보면 정말 강력하다. 깔끔한 히스토리, 빠른 버그 찾기, 안전한 복구 — 이 모든 게 가능하다.

그리고 가장 중요한 건 git reflog를 기억하는 것이다. 이게 있으면 웬만한 실수는 복구할 수 있다.

iL
ian.lab

실무 개발자입니다. 현장에서 겪은 문제와 해결 과정을 기록합니다. 오류 제보는 연락처로 보내주세요.