← 홈Setup Tip

Setup Tip

Setup Tip — 조상 커밋은 과거의 브랜치 헤드가 아니다

“이 브랜치가 어제 어디를 가리키고 있었나”를 날짜 필터가 붙은 커밋 목록으로 답하면, 나중에 머지되어 조상이 된 남의 커밋이 그 자리에 올라온다. 헤드는 목록의 첫 줄이 아니라 first-parent 체인을 거슬러 얻는 값이다.

문제

정기 보고를 자동화하면 거의 반드시 “창 시작 시점에 이 브랜치의 헤드는 무엇이었나”라는 질문이 생긴다. 창 안에서 얼마나 움직였는지를 말하려면 시작점이 필요하기 때문이다. 가장 손에 잡히는 답은 커밋 목록 API에 브랜치와 until=<창 시작>을 넘기고 첫 줄을 읽는 것이다. 답이 즉시 나오고, 형식도 커밋 해시라서 맞아 보인다. 그러나 이 값은 틀릴 수 있고, 틀렸을 때 조용히 그럴듯하다.

그 질의는 다른 질문에 답하고 있다

브랜치와 시간 상한을 준 커밋 목록은 “지금 이 브랜치의 조상 중, 그 시각 이전 타임스탬프를 가진 가장 최신 커밋”을 돌려준다. 여기서 기준이 되는 그래프는 *지금*의 그래프다. 창이 끝난 뒤 다른 브랜치가 머지되면, 그 브랜치에서 며칠 전에 작성된 커밋들이 한꺼번에 조상 집합으로 들어온다. 그중 하나는 타임스탬프가 창 시작 직전일 수 있고, 그러면 그 커밋이 첫 줄로 올라온다. 그 시각에 브랜치가 그 커밋을 가리킨 적은 한 번도 없는데도.

조상과 ref는 다른 개념이다

조상 관계는 “도달 가능한가”이고 헤드는 “이름표가 어디에 붙어 있었는가”이다. 머지는 도달 가능성을 과거 방향으로 넓히지만, 과거의 이름표 위치를 바꾸지는 않는다. 커밋 타임스탬프가 작성 시점을 기록할 뿐 편입 시점을 기록하지 않기 때문에, 날짜 정렬은 이 둘을 구분하지 못한다. 그래서 이 오류는 리베이스가 잦은 저장소, 장수 기능 브랜치, 릴리즈 머지가 있는 곳에서 특히 자주 터진다.

first-parent를 거슬러 올라간다

고쳐 쓰는 방법은 단순하다. 창 이후의 확실한 헤드에서 출발해 first-parent 링크만 따라 뒤로 걷고, 커밋 시각이 창 시작보다 앞서는 첫 커밋에서 멈춘다. first-parent 체인은 그 브랜치가 실제로 통과한 궤적이다. 머지로 딸려 온 사이드 브랜치 커밋은 second parent 쪽에 있어 이 경로에 끼어들지 못한다. 별도 권한도, 새 저장소 접근도 필요 없다.

손 측정과 한 번 맞춰 본다

새 구현을 믿기 전에, 이미 다른 방법으로 같은 창을 측정한 값이 있다면 반드시 맞춰 본다. 두 값이 갈릴 때 “자동화 쪽이 맞겠지”라고 두지 않는다. 이 종류의 결함은 정확히 그 가정 아래에서 몇 주씩 산다. 일치가 확인되면, 창 안에 병합이 하나도 없는 조용한 구간도 함께 돌려 본다. 그 구간에서는 두 방식의 답이 같으므로, 한쪽만 틀리면 바로 드러난다.

회귀를 고정한다

고친 뒤에는 실제로 사고를 낸 모양을 그대로 재현하는 테스트를 남긴다. 창이 끝난 뒤에 머지된, 작성 시각이 창 시작 직전인 사이드 브랜치 커밋 하나. 그 커밋을 반환하면 실패해야 한다. 이런 그래프 결함은 눈으로 다시 찾기 어렵고, 같은 형태로 조용히 재발한다.

완료 기준

보고서의 시작 헤드가 first-parent 주행으로 나왔고, 독립적인 손 측정과 한 번 일치했으며, 머지 뒤에도 과거 헤드를 다시 계산할 수 있음을 확인하는 테스트가 있을 때 완료다. 그 전까지 그 해시는 측정값이 아니라 추정값이다.