Skip to content
isdnetworks
Go back

저장소마다 커밋 작성자가 달랐다

누가 고쳤는지 보려고 이력을 봤는데 이름이 여러 가지였다.

Table of contents

Open Table of contents

증상 — 작성자가 여러 가지였다

작성자를 뽑아 봤다.

$ git log --format='%an <%ae>' | sort -u
root <root@web01.localdomain>
ubuntu <ubuntu@ip-10-0-0-31>
개발자 <dev@example.com>
jenkins <jenkins@jenkins>

rootubuntujenkins 까지 같은 사람이 낸 커밋이 서버마다 다른 이름으로 남아 있었다.

git blame 으로 한 줄씩 볼 때도 같은 사람이 여럿으로 갈려서 누가 무엇을 했는지를 이력으로 못 따라갔다.

원인 — 값이 오는 세 층

user.email 은 설정에서 오고 없으면 자동으로 채워진다.

$ git config --show-origin --get user.email
(없음)

어느 층에도 없으니 시스템 사용자와 호스트 이름을 붙여 root@web01.localdomain 이 됐다.

설정은 세 층이다.

system   /etc/gitconfig
global   ~/.gitconfig
local    <저장소>/.git/config

localglobal 을 덮으므로 어느 층에서 온 값인지 봐야 한다.

서버에서 root 로 작업하면 그 계정의 설정을 보는데 거기에 아무것도 없으니 호스트 이름으로 만들어진 값이 그대로 남았다.

jenkins 로 남은 것도 같은 경로였다. 사람이 아니라 계정이 남는 셈이라 누가 했는지는 그 줄만 봐서는 알 수 없다.

값이 없을 때 오류를 내지 않고 조용히 채우는 것이 문제의 시작이었다.

제약 — 하나로 맞추는 것이 답은 아니다

전부 한 값으로 통일하려다 걸렸다.

회사 저장소   회사 주소
개인 저장소   개인 주소

한 사람이 둘을 오가는데 global 에 하나만 두면 반대쪽에 잘못된 주소가 박힌다.

경로별로 갈리게 뒀다.

# ~/.gitconfig
[user]
    name = ...
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
    path = ~/.gitconfig-personal

includeIf 로 폴더가 갈리니 저장소마다 손으로 설정하지 않아도 된다.

통일이 목적이 아니라 의도한 값이 들어가는 것이 목적이었고 그 목적으로 보면 하나로 맞추는 것은 답이 아니었다.

사외 협업 저장소처럼 다른 주소를 써야 하는 사정도 있었다. 그런 자리를 local 로 지정해 두면 그 저장소에서만 다른 값이 걸린다.

설정 — 없으면 멈추게

기본값으로 조용히 채워지는 것을 막았다.

$ git config --global user.useConfigOnly true

useConfigOnly 를 켜면 설정이 없을 때 커밋이 실패한다.

fatal: no email was given and auto-detection is disabled

호스트 이름으로 만들어 채우지 않고 그 자리에서 멈춘다.

조용히 채워지면 몇 달 뒤 이력을 볼 때야 드러나고 그때는 이미 수백 건이 섞인 뒤다. 새로 환경을 만든 사람이 첫 커밋에서 알아채는 편이 훨씬 쌌다.

멈추는 것이 불편해 보이지만 그 불편은 한 번뿐이다. 조용히 지나간 것을 나중에 되돌리는 쪽이 몇 배로 비싸다.

조치 — 서버 커밋 경로 제거

서버 계정으로 남은 커밋이 왜 있는지 봤다.

급한 수정을 서버에서 하고 커밋했다
배포 스크립트가 설정 파일을 고치고 커밋했다

앞은 규칙으로 막았고 뒤는 generated.php 를 커밋할 이유가 없었다.

# .gitignore
/config/generated.php
/public/build/
/REVISION

배포가 만드는 것을 추적에서 뺐다.

추적하면 배포마다 변경이 생기고 그 변경이 다음 배포를 막는다. 막히면 사람이 서버에서 커밋하게 되므로 결국 같은 자리로 돌아온다.

경로 자체를 없애야 그 계정으로 남는 커밋이 안 생긴다.

검증 — 이미 섞인 것과 앞으로

과거 커밋의 작성자는 고치지 않기로 했다.

고치면 모든 커밋 식별자가 바뀌고 이미 여럿이 받아 간 상태라 그 기준이 어긋난다.

# .mailmap
개발자 <dev@example.com>  <root@web01.localdomain>
개발자 <dev@example.com>  <ubuntu@ip-10-0-0-31>

.mailmap 을 두어 보는 쪽에서 묶이게 했다.

$ git shortlog -sne
   412  개발자 <dev@example.com>
    38  jenkins <jenkins@jenkins>

shortlog 의 집계만 맞고 이력 자체는 그대로다.

새로 들어오는 것은 받는 쪽에서 걸렀다.

#!/bin/sh
# pre-receive
while read old new ref; do
    for c in $(git rev-list "$old..$new"); do
        email=$(git log -1 --format='%ae' "$c")
        case "$email" in
            *@example.com) ;;
            *) echo "허용되지 않은 작성자: $email ($c)"; exit 1 ;;
        esac
    done
done

각자 local 설정에 기대면 언젠가 어긋나므로 pre-receive 에서 막는다.

일부러 다른 주소로 커밋해 밀어 보고 거부되는지 확인했고 그것을 보고 나서야 그 훅을 믿을 수 있었다.

통과만 보고 작동한다고 판단하면 아무것도 안 거르는 훅을 그대로 두게 된다. 거르는 장치는 거르는 것을 봐야 검증이 끝난다.

정리


Share this post on:

Previous Post
상태에 따라 부를 API가 다르다
Next Post
한 대만 켜는 것과 한 대에만 나가는 것은 달랐다