매일공부

[Git] GitHub push할 때마다 까먹는 커밋 이메일 교정 절차 본문

IT/작업기록

[Git] GitHub push할 때마다 까먹는 커밋 이메일 교정 절차

julian 2026. 8. 5. 14:35

왜 이게 필요한가

로컬 저장소의 user.email은 회사 이메일로 고정해서 쓰고 있다. 이 저장소가 회사 NAS 서버(dev/origin)와 GitHub 양쪽에 push되기 때문에, 커밋 작성자 이메일이 회사 계정으로 남아야 회사 서버 쪽에서 추적이 된다.

문제는 GitHub 계정에 "Block command line pushes that expose my personal email address" 설정을 켜뒀다는 것. 이 설정이 켜져 있으면, 회사 이메일이든 개인 이메일이든 GitHub이 알고 있는 "진짜 이메일"로 찍힌 커밋은 push 자체가 거부된다(email privacy restrictions). GitHub용으로는 GitHub이 발급해주는 noreply 이메일(숫자+아이디@users.noreply.github.com)을 써야 한다.

하지 말아야 할 것

로컬/전역 user.email을 noreply로 바꾸지 말 것. 그러면 회사 서버로 push할 때도 커밋이 noreply 이메일로 찍혀서, 회사 쪽에서 이 사람이 커밋한 게 맞는지 계정 추적이 깨진다. (실제로 한 번 실수로 git config --unset user.email을 했다가 바로 원복한 적이 있다.)

해야 할 것 — GitHub push 직전에만 해당 범위의 커밋 이메일을 교정

평소 작업(회사 서버로 push하는 것)은 그대로 회사 이메일로 진행하고, GitHub에 올릴 때만 그 시점에 새로 추가된 커밋들의 이메일을 git filter-branch로 임시 교정해서 push한다.

브랜치 구조

  • master: 실제 작업 브랜치. 회사 이메일로 계속 커밋. 회사 서버(dev/origin)로는 그대로 push
  • github-mirror: GitHub 업로드 전용 브랜치. 평소엔 안 건드리다가, GitHub에 올릴 때만 master를 기준으로 최신화

절차

# 0. 편집 중인 변경사항 있으면 치워두기
git status --short
git stash -u   # 변경사항 있을 때만

# 1. github 리모트 최신 상태 가져오기
git fetch github

# 2. github-mirror를 마지막으로 push된 지점(github/main)에 맞추기
git checkout github-mirror
git reset --hard github/main

# 3. 실제 새 커밋만 골라내기 — 반드시 git cherry 사용 (해시 아닌 patch 내용 비교)
git cherry github/main master
git cherry -v github/main master | grep '^+'   # "+"만 진짜 새 커밋

⚠️ git log github/main..master로 새 커밋을 판단하면 안 된다. 이메일 교정(filter-branch)을 거치면서 커밋 해시가 master와 달라지기 때문에, 단순 범위 비교는 이미 올라간 커밋까지 전부 "새 커밋"으로 착각한다. 실제로 이 방식으로 확인했다가 386개가 새 커밋으로 잡혔는데, 진짜 새 커밋은 3개뿐이었던 적이 있다. git cherry는 해시가 아니라 **커밋의 실제 변경 내용(patch-id)**을 비교하기 때문에, 이메일만 바뀌고 내용이 같은 커밋은 정확히 걸러내고 진짜 새 커밋만 잡아낸다.

# 4. 확인한 새 커밋만 오래된 순서대로 cherry-pick
git cherry-pick <해시1> <해시2> <해시3>
#   충돌 시: 파일 수정 → git add <파일> → git cherry-pick --continue
#   잘못됐으면: git cherry-pick --abort (전부 되돌림, 안전)

# 5. 새 커밋 이메일 교정 (회사 이메일 → GitHub noreply)
git log --format='%h %ae %s' github/main..github-mirror   # 회사 이메일인지 확인
git filter-branch -f --env-filter '
if [ "$GIT_AUTHOR_EMAIL" = "<회사이메일>" ] || [ "$GIT_AUTHOR_EMAIL" = "<개인이메일>" ]
then
    export GIT_AUTHOR_EMAIL="<GitHub noreply 이메일>"
fi
if [ "$GIT_COMMITTER_EMAIL" = "<회사이메일>" ] || [ "$GIT_COMMITTER_EMAIL" = "<개인이메일>" ]
then
    export GIT_COMMITTER_EMAIL="<GitHub noreply 이메일>"
fi
' -- github/main..github-mirror

# 6. noreply로 바뀌었는지 재확인 후 push
git log --format='%h %ae %s' github/main..github-mirror
git push github github-mirror:main

# 7. master로 복귀 + 편집 중이던 내용 복원
git checkout master
git rev-parse master   # 위 과정에서 master가 안 바뀌었는지 확인
git stash pop   # 0번에서 stash 했다면

주의사항

  • git config(특히 user.email)는 절대 바꾸지 않는다 — NAS 컨테이너용 회사 이메일 설정이 깨진다
  • master 브랜치 포인터는 이 절차에서 전혀 건드리지 않는다 (cherry-pick 대상이 github-mirror이지 master가 아님) — 7번에서 원래 위치인지 재확인하는 습관을 들이면 안전하다
  • 3번 단계를 건너뛰고 바로 git cherry-pick github/main..master 같은 범위 명령을 쓰면 위에서 겪은 대량 오탐(386개 vs 실제 3개)이 재현될 수 있다

자주 헷갈리는 부분들

  • git log 실행했는데 화면이 멈춘 것 같을 때: git log는 기본적으로 pager(보통 less)로 결과를 연다. 화면이 멈춘 게 아니라 pager 안에 들어가 있는 것.
    • 나가려면 q
    • 위/아래 스크롤은 방향키 또는 스페이스바
    • 검색은 /
    • pager 없이 그냥 쭉 출력만 보고 싶으면 --no-pager를 붙이면 된다: git --no-pager log --format='%h %ae %s' github/main..github-mirror
  • github-mirror, 백업 브랜치들은 삭제할 필요 없음. 로컬에만 있는 브랜치라 다음에 GitHub 올릴 때 다시 쓰면 된다. 다른 사람이 회사 서버에서 pull/clone해도 이 브랜치들은 보이지 않는다.
  • 이미 커밋된 것만 골라서 push 가능. 지금 편집 중인(unstaged) 내용은 git stash로 치워두고, 새 커밋만 git cherry로 골라서 cherry-pick한 다음 다시 꺼내 쓰면 된다.
  • 새 커밋 판별은 반드시 git cherry로. git log의 범위 비교(A..B)는 커밋 해시 기준이라, 과거에 filter-branch로 이메일을 바꾼 이력이 있으면 이미 올라간 커밋까지 전부 새 커밋으로 오탐한다. git cherry는 커밋의 실제 변경 내용을 비교하기 때문에 이 문제가 없다.
  • GitHub noreply 이메일 찾는 법: GitHub 계정 설정(Settings → Emails)에서 "Keep my email addresses private"를 켜면 숫자+아이디@users.noreply.github.com 형태로 발급된다. + 포함 전체 문자열을 그대로 써야 한다.

왜 매번 까먹는가 — 그리고 그래서 이 글을 쓴다

이 절차 자체가 "평소엔 안 쓰다가 GitHub에 올릴 때만" 필요한 작업이라 텀이 길고, 그래서 매번 명령어를 다시 찾아보게 된다. 다음엔 이 글을 보면 된다.

Comments