Git으로 오류 커밋을 찾고 작업을 복구하는 방법

기능이 어느 커밋부터 실패했는지 조사할 때와 사라진 커밋을 복구할 때는 필요한 명령이 다릅니다. 새 명령을 실행하기 전에 현재 브랜치, 저장하지 않은 변경, 커밋 이력을 확인합니다. 복구할 커밋이 남아 있는지 알아야 어떤 명령을 사용할지 정할 수 있습니다.
Kirtesh가 정리한 명령 목록에서 오류 커밋 탐색, 다른 브랜치 검토, 이전 커밋 복구에 쓸 명령을 골랐습니다. add와 commit으로 저장한 변경을 조사하거나 복구하는 상황에 맞춰 사용법과 주의할 조건을 설명합니다.
bisect로 좁혀가는 오류 커밋
어제의 버전은 정상인데 오늘의 버전은 실패한다면 git bisect로 두 버전 사이의 오류 커밋을 찾을 수 있습니다. Git이 중간 커밋을 선택하면 사용자는 같은 검사를 실행하고 정상인지 실패인지 알려줍니다.
git bisect start
git bisect bad
git bisect good <정상-커밋>
# 선택된 버전에서 같은 검사를 실행합니다.
git bisect good
# 실패했다면 good 대신 bad를 사용합니다.
# 원인 커밋이 나올 때까지 선택된 버전의 검사와 판정을 반복합니다.
git bisect reset
판정 기준은 탐색 중에 바꾸지 않습니다. 로그인 회귀를 찾는 중에 다른 기능의 실패까지 bad로 표시하면 원인이 다른 커밋을 지목할 수 있습니다. 해당 버전을 검사할 수 없으면 git bisect skip으로 건너뜁니다. 건너뛴 구간에 원인이 있으면 하나의 커밋으로 확정하지 못할 수도 있습니다.
검사 스크립트가 있다면 git bisect run으로 자동화할 수 있습니다. 종료 코드 0은 정상, 125는 검사할 수 없는 버전의 건너뛰기를 뜻합니다. 1~127 중 125를 제외한 코드는 실패로 판정하고, 128 이상이면 탐색을 중단합니다. 탐색을 끝내면 git bisect reset으로 시작 전 상태로 돌아갑니다.

직접 작성한 커밋 A~H 예시입니다. 같은 검사에서 A는 정상, H는 실패일 때 D의 결과에 따라 다음 조사 구간이 달라집니다.
사라진 것처럼 보이는 작업의 흔적
브랜치를 잘못 이동했거나 rebase 결과가 예상과 다르면 git reflog를 먼저 읽습니다. reflog는 로컬 저장소에서 참조가 이동한 기록입니다. 일반적인 커밋 이력에서 보이지 않는 이전 위치를 찾는 데 사용할 수 있습니다.
git reflog
git branch recover <확인한-커밋-ID>
확인한 커밋에 복구 브랜치를 붙이면 현재 작업을 덮어쓰지 않고 내용을 비교할 수 있습니다. 그 뒤 필요한 변경을 가져올지 판단합니다. 먼저 reset --hard를 실행하는 방식은 작업 폴더의 수정까지 잃을 수 있으므로 결과를 확인하기 전에 사용할 이유가 없습니다.
목록에서 이전 위치를 찾았다고 해서 앞으로도 언제든 복구할 수 있다고 생각해서는 안 됩니다. reflog는 원격 백업이 아니며 영구 보존도 아닙니다. 만료 설정과 객체 정리 상태에 따라 복구 가능성이 달라집니다. 새로 복제한 저장소에 다른 컴퓨터의 reflog가 따라오는 것도 아닙니다. 문제를 발견했을 때 현재 저장소에서 확인해야 합니다.
작업 보관과 별도 검토 폴더
작업을 잠시 보관할 때는 git stash에 이름을 붙입니다.
git stash push -m "로그인 오류 안내 작업 중"
git stash list
git stash show -p 'stash@{0}'
git stash apply 'stash@{0}'
apply는 보관 항목을 남기므로 복원 결과를 확인한 뒤 정리할 수 있습니다. 추적하지 않는 새 파일도 보관해야 하면 -u 옵션을 검토합니다. 보관 범위를 확인하지 않고 브랜치를 바꾸면 파일이 남거나 빠질 수 있습니다.
현재 작업을 그대로 두고 다른 브랜치를 열려면 worktree를 사용할 수 있습니다. worktree는 같은 저장소의 커밋 정보를 공유하면서 작업 폴더를 따로 만듭니다.
git worktree add ../project-review <검토-브랜치>
git worktree list
작업이 끝난 폴더는 수정 사항이 남았는지 확인한 후 git worktree remove로 정리합니다. 같은 브랜치를 여러 worktree에서 동시에 체크아웃하는 데는 제한이 있으므로 현재 목록부터 확인합니다.
이력을 바꾸기 전에 읽을 것
git blame은 한 줄이 어느 커밋에서 들어왔는지 찾는 출발점입니다. 작성자 이름만 보는 대신 해당 커밋의 설명과 주변 변경을 읽어야 결정 이유를 알 수 있습니다. 다른 브랜치의 특정 수정이 필요하면 cherry-pick을 검토하고, 아직 공유하지 않은 작업 이력을 정리할 때는 rebase -i를 사용할 수 있습니다.
이미 공유한 커밋을 다시 작성하면 다른 사람의 작업에도 영향을 줍니다. 실행 전에는 git status로 현재 수정을 살피고 대상 커밋과 공유 여부를 확인합니다. 복구할 위치를 찾았다면 먼저 별도 브랜치로 보존해 내용을 비교할 수 있습니다.
커밋 설명을 읽다 보면 당시에는 이해하지 못했던 변경 이유를 알 수도 있습니다. 작성자 이름과 함께 왜 코드를 바꿨는지도 확인해보세요. 자신의 변경에도 이유를 기록하면 나중에 자신이나 동료가 오류를 조사하고 이전 작업을 복구할 때 참고할 수 있습니다.