이해할 수 없는 코드 한 줄을 마주쳤을 때 “이 줄은 왜 이렇게 쓰여 있을까"라는 질문에 답하려면, 그 줄이 어느 커밋에서 왜 그렇게 바뀌었는지 알아야 한다. git blame은 파일의 모든 줄을 마지막으로 수정한 커밋에 연결해, 09장의 git log가 파일 단위로 하던 일을 줄 단위로 정밀하게 해준다.
개요
| |
| |
각 줄 앞에 붙은 짧은 해시, 작성자, 날짜는 그 줄을 마지막으로 변경한 커밋 정보다. 여러 줄이 같은 해시를 공유한다면(위 예시의 1번·3번 줄) 그 줄들이 같은 커밋에서 함께 작성됐다는 뜻이다.
기본 개념
git blame이 답하는 질문은 “이 줄을 누가 언제 마지막으로 건드렸는가"이지, “이 줄을 누가 원래 처음 작성했는가"가 아니다. 파일이 여러 번 리팩터링을 거쳤다면, 원래 작성자의 이름은 blame 결과에서 사라지고 가장 최근에 그 줄을 스쳐 지나간 사람만 남는다. 예를 들어 코드 포맷터를 전체 파일에 돌린 커밋이 있다면, 그 이후로는 실질적인 로직 작성자가 아니라 포맷터를 실행한 사람이 모든 줄의 blame 결과에 나타난다.
종류/세부
특정 커밋 이전 상태를 조회하기
blame은 기본적으로 현재 브랜치의 최신 상태를 기준으로 하지만, 특정 커밋 시점의 상태로 좁힐 수도 있다.
| |
이름 변경을 넘어 추적하기(-C, -M)
09장에서 git log --follow가 이름 변경된 파일의 이전 이력까지 추적한다고 설명했다. blame에도 유사한 옵션이 있다.
| |
-M은 같은 파일 안에서 함수 순서를 재배치했을 때 “새로 작성한 코드"로 잘못 표시되는 것을 막아준다. -C는 한 파일의 코드를 복사해 다른 파일을 새로 만들었을 때, 새 파일의 그 코드가 원래 어디서 왔는지까지 거슬러 올라간다.
특정 커밋을 건너뛰기(--ignore-rev)
대규모 서식 변경(들여쓰기 스타일 통일, 자동 포맷터 일괄 적용 등)을 한 커밋으로 실행하면, 그 이후 모든 blame 결과가 실제 로직 변경과 무관하게 그 서식 변경 커밋을 가리키게 된다. 이런 “노이즈성” 커밋을 blame 계산에서 제외할 수 있다.
| |
매번 해시를 입력하는 대신, 저장소에 무시할 커밋 목록을 파일로 관리하고 설정에 등록해두는 방법도 있다.
| |
이 파일을 저장소에 커밋해두면 팀 전체가 같은 설정을 공유할 수 있다.
주의사항·함정
blame 결과만으로 “누구 책임인가"를 판단하는 것은 위험하다: 위에서 설명했듯 blame은 마지막으로 그 줄을 건드린 사람을 보여줄 뿐, 코드 리뷰에서 함께 논의된 결정이었는지, 다른 사람의 요청으로 그렇게 수정했는지는 알려주지 않는다. 진짜 맥락이 필요하다면 blame이 가리키는 커밋의 메시지(08장에서 강조한 “왜” 설명)와 관련 Pull Request(21장)를 함께 확인해야 한다.
한 줄이 여러 번 옮겨 다니면 추적이 끊길 수 있다: -M, -C 옵션이 있어도 코드가 크게 재구성되거나 여러 파일에 걸쳐 흩어지면, Git의 휴리스틱이 원래 출처를 찾지 못하고 새로 작성된 것처럼 표시할 수 있다.
대용량 파일이나 히스토리가 긴 저장소에서 blame이 느릴 수 있다: blame은 해당 파일의 전체 히스토리를 순회하며 각 줄의 출처를 계산하므로, 오래되고 자주 수정된 파일일수록 계산 비용이 커진다. 필요한 줄 범위만 좁혀 조회하는 -L <시작>,<끝> 옵션으로 속도를 개선할 수 있다.
![Featured image of post [Git] 32. git blame — 변경 이력 추적](/post/git/git-blame-command-track-line-history/wordcloud_hu_edaf7925c8441cb2.webp)
![[Git] 30. git clean — 미추적 파일 정리](/post/git/git-clean-command-remove-untracked-files/wordcloud_hu_73da61b3df7aabd.webp)
![[Git] 31. git add -p — 부분 스테이징](/post/git/git-add-patch-mode-partial-staging/wordcloud_hu_231bb07daf2dd09e.webp)
![[Git] 32. git blame — 변경 이력 추적](/post/git/git-blame-command-track-line-history/wordcloud_hu_249514842ed0d027.webp)
![[Git] 33. git bisect — 버그 커밋 이분 탐색](/post/git/git-bisect-command-binary-search-bug-commit/wordcloud_hu_8196480e8b21de7a.webp)
![[Git] 34. Git 객체 모델 — blob/tree/commit](/post/git/git-object-model-blob-tree-commit/wordcloud_hu_bd4ee27759fca634.webp)
![[Git] 08. git commit — 커밋 작성 규칙](/post/git/git-commit-command-writing-good-commits/wordcloud_hu_b27461e0d97eff2a.webp)
![[Git] 13. git merge — 병합과 충돌 해결](/post/git/git-merge-command-and-conflict-resolution/wordcloud_hu_fd1543b374ae452a.webp)
![[Git] 15. git rebase — 히스토리 재배치](/post/git/git-rebase-command-rewriting-history/wordcloud_hu_326a73b9e72fabfc.webp)
![[Git] 16. merge vs rebase 선택 기준](/post/git/merge-vs-rebase-choosing-criteria/wordcloud_hu_2d0667c2865989d3.webp)
![[Git] 21. Fork와 Pull Request 워크플로(GitHub Flow)](/post/git/fork-pull-request-workflow-github-flow/wordcloud_hu_681908459f6099a8.webp)