LabHub
배우기 러닝패스 코스

Git in Practice

Finding the Culprit Among 40 Commits

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

"예전엔 됐는데요" 에서 커밋 하나를 특정합니다. 그리고 자동화가 끝나는 자리를 직접 만납니다.

환경

/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 가 커밋을 옮겨 다닐 때 그 스크립트까지 함께 바뀝니다.

단계

  1. 이력과 기준 태그를 만듭니다.
  2. /root/check.sh 를 씁니다. 종료코드 세 가지를 정확히 구분해야 합니다.
  3. git bisect start HEAD v1 뒤에 git bisect run sh /root/check.sh. 결과를 그대로 bisect.txt 에 옮깁니다. 하나로 안 좁혀집니다.
  4. 125 를 뺀 wrong-check.sh 로 같은 이등분을 다시 돌립니다. 이번에는 확신 있게 답을 냅니다 — 그리고 그 답은 틀렸습니다. 둘을 나란히 적으세요.
  5. 두 후보 중 어느 쪽이 범인인지 git show 로 확인하고, 다른 쪽을 왜 배제했는지까지 적습니다.
  6. git log -S 로 같은 답을 한 번에 냅니다.
  7. app/settings.inigit blamegit blame -w 를 각각 돌려 봅니다.
  8. 네 도구를 언제 쓰는지 정리합니다.

참고

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.inigit blamegit blame -w 를 각각 돌려 봅니다.

git blame app/settings.inigit blame -w app/settings.ini 를 각각 돌려 보세요. 앞의 것은 들여쓰기만 바꾼 커밋을 가리키고, -w 는 그것을 건너뜁니다. 두 해시를 나란히 적으세요.

언제 무엇을 먼저 쓰는가

네 도구를 언제 쓰는지 정리합니다.

세 도구가 답하는 질문이 다릅니다. 그리고 이번에 직접 겪은 것 — 자동화가 어디서 끝나는지 — 도 함께 적으세요.