DaleSchool

히스토리 탐색과 고급 기능

입문25분

학습 목표

  • git log의 고급 검색 옵션을 활용할 수 있다
  • git blame으로 코드 변경 이력을 추적할 수 있다
  • git bisect로 버그를 도입한 커밋을 찾을 수 있다
  • git cherry-pick으로 특정 커밋만 가져올 수 있다

동작하는 코드

예제 1: git log 고급 검색

히스토리가 있는 저장소를 준비합니다:

git init
echo "v1" > app.py && git add app.py && git commit -m "feat: 초기 앱"
echo "login" >> app.py && git add app.py && git commit -m "feat: 로그인 기능 추가"
echo "bug fix" >> app.py && git add app.py && git commit -m "fix: 로그인 버그 수정"
echo "profile" >> app.py && git add app.py && git commit -m "feat: 프로필 페이지 추가"
echo "style" >> app.py && git add app.py && git commit -m "style: 코드 포맷 정리"

커밋 메시지로 검색:

git log --grep="feat" --oneline

출력:

e4f5g6h feat: 프로필 페이지 추가
b2c3d4e feat: 로그인 기능 추가
a1b2c3d feat: 초기 앱

특정 내용 변경을 포함한 커밋 찾기:

# "login"이 추가되거나 삭제된 커밋
git log -S "login" --oneline

출력:

c3d4e5f fix: 로그인 버그 수정
b2c3d4e feat: 로그인 기능 추가

예제 2: 작성자로 검색

git log --author="홍길동" --oneline

# 날짜 범위
git log --after="2024-01-01" --before="2024-02-01" --oneline

# 특정 파일의 변경 히스토리
git log --oneline app.py

예제 3: git blame

어느 줄이 누가 언제 작성했는지:

git blame app.py

출력:

a1b2c3d (홍길동 2024-01-15 10:30:01 +0900 1) v1
b2c3d4e (홍길동 2024-01-15 10:31:00 +0900 2) login
c3d4e5f (김철수 2024-01-16 09:00:00 +0900 3) bug fix
e4f5g6h (홍길동 2024-01-17 14:00:00 +0900 4) profile

각 줄마다:

  • 커밋 해시 (앞 8자리)
  • 작성자
  • 날짜/시간
  • 줄 번호
  • 내용

직접 수정하기

git log -S: 특정 코드 변경 추적

"이 코드가 언제 추가됐지?"를 찾을 때:

# "getUserById"가 추가되거나 삭제된 커밋
git log -S "getUserById" --oneline

# 변경 내용까지 보기
git log -S "getUserById" -p

# 정규식으로 검색
git log -G "user.*[Bb]y[Ii]d" --oneline

git blame 활용

# 특정 줄 범위만 (10~20줄)
git blame -L 10,20 app.py

# 더 짧은 해시로
git blame --abbrev=7 app.py

# 공백 변경 무시
git blame -w app.py

blame 결과로 히스토리 탐색:

git blame app.py
# c3d4e5f (김철수 ...) 3) bug fix

# 해당 커밋 상세 보기
git show c3d4e5f

# 해당 커밋 전의 blame 보기
git blame c3d4e5f~1 app.py

git cherry-pick: 특정 커밋만 가져오기

다른 브랜치의 특정 커밋만 현재 브랜치에 적용:

git checkout -b feature-a
echo "기능 A" >> app.py && git add app.py && git commit -m "feat: 기능 A 추가"
echo "버그 수정 A" >> app.py && git add app.py && git commit -m "fix: 기능 A 버그 수정"

git checkout main
git log --oneline --all

출력:

f1g2h3i (feature-a) fix: 기능 A 버그 수정
e0f1g2h (feature-a) feat: 기능 A 추가
a1b2c3d (HEAD -> main) style: 코드 포맷 정리
...

버그 수정만 main에 가져오기:

git cherry-pick f1g2h3i   # fix 커밋만 선택
git log --oneline

출력:

j0k1l2m (HEAD -> main) fix: 기능 A 버그 수정  ← 새 해시로 복사됨
a1b2c3d style: 코드 포맷 정리

cherry-pick은 커밋을 복사합니다. 원본 커밋은 그대로 있고 새 해시로 현재 브랜치에 추가됩니다.

여러 커밋 cherry-pick:

# 커밋 2개 선택
git cherry-pick a1b2c3d e4f5g6h

# 범위로 선택 (a부터 b까지, a 제외)
git cherry-pick a1b2c3d..e4f5g6h

# 범위로 선택 (a 포함)
git cherry-pick a1b2c3d^..e4f5g6h

git bisect: 버그 도입 커밋 찾기

"언젠가부터 이 기능이 안 되는데 어떤 커밋에서 깨졌지?"

# bisect 시작
git bisect start
git bisect bad HEAD           # 현재: 버그 있음
git bisect good a1b2c3d      # 이 커밋은 정상이었음

# Git이 중간 커밋을 자동 체크아웃
# 테스트 후 결과 보고
git bisect good   # 이 커밋은 정상
# 또는
git bisect bad    # 이 커밋에 버그 있음

# 몇 번 반복하면 Git이 범인 커밋을 찾아줌
# 완료 후 원래 상태로
git bisect reset

"왜?" — 히스토리 탐색이 필요한 상황

| 상황 | 도구 | | -------------------------------- | ------------------- | | "언제 이 기능이 추가됐지?" | git log -S "코드" | | "이 줄 누가 작성했지?" | git blame | | "언제부터 버그가 생겼지?" | git bisect | | "저 브랜치의 특정 커밋만 필요해" | git cherry-pick | | "특정 작업자의 커밋만 보고 싶어" | git log --author |

실전 디버깅 시나리오

# 1. 버그 발견: 특정 함수가 잘못 동작
# 2. blame으로 마지막 수정자 확인
git blame -L 45,60 src/auth.js

# 3. 해당 커밋 확인
git show c3d4e5f

# 4. 그 커밋 전후 비교
git diff c3d4e5f~1 c3d4e5f

# 5. 언제부터인지 bisect로 범위 좁히기
git bisect start
git bisect bad HEAD
git bisect good v1.0.0

흔한 실수

실수 1: cherry-pick 충돌 처리 방법 모름

git cherry-pick f1g2h3i   # 충돌 발생
# CONFLICT: ...

# 충돌 해결 후
git add 파일명
git cherry-pick --continue

# 또는 취소
git cherry-pick --abort

실수 2: git log -S와 --grep 혼동

# --grep: 커밋 메시지 검색
git log --grep="로그인"   # 메시지에 "로그인"이 있는 커밋

# -S: 코드 변경 내용 검색
git log -S "login"        # 코드에 "login"이 추가/삭제된 커밋

실수 3: blame으로 사람 탓하기

git blame의 목적은 코드 문맥을 이해하는 것이지, 잘못한 사람을 찾는 게 아닙니다. 그 사람이 왜 그렇게 작성했는지 커밋 메시지와 PR을 찾아보세요.

심화 학습

git log --format 커스터마이징
# 커밋 해시, 작성자, 날짜, 메시지
git log --format="%h %an %ar %s"

# 예쁜 색상 출력
git log --format="%C(yellow)%h%C(reset) %C(blue)%an%C(reset) %s"

# alias로 등록
git config --global alias.lg "log --format='%C(yellow)%h%C(reset) %C(blue)%an%C(reset) %ar %s' --graph --all"
git lg
git shortlog: 기여자 요약
# 기여자별 커밋 수 요약
git shortlog -sn

# 특정 기간
git shortlog -sn --after="2024-01-01"

출력:

    45  홍길동
    23  김철수
    12  이영희

오픈소스 프로젝트 기여자 목록이나 팀 활동 현황 파악에 유용합니다.

git stash vs cherry-pick vs patch

브랜치 간 코드를 이동하는 방법:

# cherry-pick: 특정 커밋 복사
git cherry-pick 커밋해시

# patch: 파일로 변경 사항 추출 후 적용
git format-patch -1 커밋해시
git am 0001-커밋메시지.patch

# 임시로 파일만 복사
git show 커밋해시:경로/파일.txt > 복사본.txt
  1. 여러 커밋을 만들고 git log --grep="feat" --oneline으로 feat 커밋만 검색하세요.
  2. git log -S "특정코드" --oneline으로 특정 코드가 변경된 커밋을 찾아보세요.
  3. git blame 파일명으로 각 줄의 작성자와 커밋을 확인하세요.
  4. feature 브랜치를 만들고 커밋 2개를 추가한 후, git cherry-pick 커밋해시로 main에 하나만 가져오세요.
  5. git bisect를 시뮬레이션해보세요 (여러 커밋 후 good/bad를 번갈아 지정하며 범인 커밋 찾기).

Q1. git log -S "getUserById"git log --grep="getUserById"의 차이는?

  • A) -S는 작성자 검색, --grep은 내용 검색
  • B) -S는 코드에서 해당 텍스트가 추가/삭제된 커밋 검색, --grep은 커밋 메시지 검색
  • C) -S는 빠른 검색, --grep은 정확한 검색
  • D) 두 명령어는 동일한 결과를 보여줌