40のコミットから犯人を見つける
한국어 원문으로 표시합니다.
목표
"예전엔 됐는데요" 에서 커밋 하나를 특정합니다. 그리고 자동화가 끝나는 자리를 직접 만납니다.
환경
/root/repo 에 빈 저장소가 하나 있습니다. 이력은 직접 만듭니다 — 아래를
그대로 붙여 넣으세요. 이 부분은 과제가 아니라 준비입니다.
cd /root/repo && mkdir -p app
i=1
while [ "$i" -le 40 ]; do
if [ "$i" -eq 22 ]; then printf '#!/bin/sh
echo $(( 100 +
' > app/calc.sh
elif [ "$i" -lt 23 ]; then printf '#!/bin/sh
echo 100
' > app/calc.sh
else printf '#!/bin/sh
echo 250
' > app/calc.sh; fi
echo "note $i" > app/notes.txt
git add -A && git commit -qm "change $i"
i=$((i + 1))
done
printf 'timeout=3
retries=2
' > app/settings.ini
git add -A && git commit -qm "설정값 도입"
printf ' timeout=3
retries=2
' > app/settings.ini
git add -A && git commit -qm "포매터 실행"
git tag -f v1 "$(git rev-list --reverse HEAD | sed -n '2p')"
mkdir -p /root/arch
app/calc.sh 는 원래 100 을 내야 합니다. 지금은 250 을 냅니다. 그리고
중간 어딘가에 문법이 깨져 실행조차 안 되는 커밋이 하나 있습니다.
만들 것
/root/check.sh 판정 스크립트 — 종료코드로 good/bad/skip 을 말한다
/root/wrong-check.sh 125 를 일부러 뺀 것 (4단계에서 비교용)
/root/arch/bisect.txt bisect run 의 결과
/root/arch/wrong.txt 125 를 뺐을 때의 결과
/root/arch/culprit.txt 범인과 배제 근거
/root/arch/pickaxe.txt log -S 로 찾은 결과
/root/arch/blame.txt blame 과 blame -w 의 차이
/root/arch/report.md 정리
판정 스크립트를 저장소 밖에 두는 이유가 있습니다. 저장소 안에 두면 bisect 가 커밋을 옮겨 다닐 때 그 스크립트까지 함께 바뀝니다.
단계
- 이력과 기준 태그를 만듭니다.
/root/check.sh를 씁니다. 종료코드 세 가지를 정확히 구분해야 합니다.git bisect start HEAD v1뒤에git bisect run sh /root/check.sh. 결과를 그대로bisect.txt에 옮깁니다. 하나로 안 좁혀집니다.- 125 를 뺀
wrong-check.sh로 같은 이등분을 다시 돌립니다. 이번에는 확신 있게 답을 냅니다 — 그리고 그 답은 틀렸습니다. 둘을 나란히 적으세요. - 두 후보 중 어느 쪽이 범인인지
git show로 확인하고, 다른 쪽을 왜 배제했는지까지 적습니다. git log -S로 같은 답을 한 번에 냅니다.app/settings.ini에git blame과git blame -w를 각각 돌려 봅니다.- 네 도구를 언제 쓰는지 정리합니다.
참고
3단계에서 화면에 The first bad commit could be any of: 가 뜨면 제대로 온
것입니다. 실패가 아니라 정직한 답입니다 — 판정할 수 없었던 커밋이 남아
있으면 bisect 는 거기까지만 말할 수 있습니다. 4단계와 비교해 보면, 틀린
답을 확신 있게 내는 쪽보다 이쪽이 낫다는 것이 분명해집니다.
이력과 기준점 만들기
이력과 기준 태그를 만듭니다.
지시문의 준비 블록을 그대로 돌립니다. v1 태그는 '이때는 됐다' 는 기준점이고, 이등분은 good 하나와 bad 하나가 있어야 시작합니다.
종료코드로 말하는 판정 스크립트
/root/check.sh 를 씁니다. 종료코드 세 가지를 정확히 구분해야 합니다.
0 은 good, 1~124 는 bad, 125 는 판정 불가(skip) 입니다. 실행조차 안 되는 커밋을 bad 로 접으면 이등분이 엉뚱한 곳으로 수렴합니다. sh -n 으로 문법만 먼저 봅니다.
이등분을 돌리면 어디까지 좁혀지나
git bisect start HEAD v1 뒤에 git bisect run sh /root/check.sh.
결과를 그대로 bisect.txt 에 옮깁니다. 하나로 안 좁혀집니다.
git bisect start HEAD v1 로 시작하고 git bisect run sh /root/check.sh 를 돌립니다. 끝나면 반드시 git bisect reset 으로 정리하세요. 결과를 요약하지 말고 화면에 나온 대로 옮기세요.
125 를 빼면 무슨 일이 생기나
125 를 뺀 wrong-check.sh 로 같은 이등분을 다시 돌립니다. 이번에는
확신 있게 답을 냅니다 — 그리고 그 답은 틀렸습니다. 둘을 나란히 적으세요.
판정 불가에도 1(bad)을 돌려주는 스크립트를 따로 만들어 같은 이등분을 다시 돌립니다. 이번에는 하나를 확신 있게 지목하는데, 그 답이 틀렸습니다. 두 결과를 나란히 적으세요.
마지막 한 걸음은 사람이 정한다
두 후보 중 어느 쪽이 범인인지 git show 로 확인하고, 다른 쪽을 왜
배제했는지까지 적습니다.
후보가 둘이면 git show <해시>:app/calc.sh 로 각각 무엇이 들어 있는지 봅니다. 배제한 쪽의 근거까지 적어야 다음 사람이 다시 확인하지 않습니다.
무엇을 찾는지 알 때의 지름길
git log -S 로 같은 답을 한 번에 냅니다.
git log -S <문자열> 은 그 문자열이 추가되거나 삭제된 커밋만 냅니다. 이등분을 여섯 번 돌 필요 없이 한 번에 나옵니다 — 다만 무엇을 찾을지 이미 알아야 쓸 수 있습니다.
포매터 커밋을 지나 값을 정한 커밋으로
app/settings.ini 에 git blame 과 git blame -w 를 각각 돌려 봅니다.
git blame app/settings.ini 와 git blame -w app/settings.ini 를 각각 돌려 보세요. 앞의 것은 들여쓰기만 바꾼 커밋을 가리키고, -w 는 그것을 건너뜁니다. 두 해시를 나란히 적으세요.
언제 무엇을 먼저 쓰는가
네 도구를 언제 쓰는지 정리합니다.
세 도구가 답하는 질문이 다릅니다. 그리고 이번에 직접 겪은 것 — 자동화가 어디서 끝나는지 — 도 함께 적으세요.