내가 모든 머신에서 돌리는 git 설정을 공개한다, 전역 설정 11개와 alias 20개, 각각이 특정한 일상적 짜증을 없애기 위해 존재한다. 전부 가져다 써도 좋다.
얼마 전, 동료가 내가 새 브랜치를 푸시하는 걸 보고 나를 멈춰 세웠다.
"잠깐, 그게 어떻게 그냥 됐어?
--set-upstream안 붙였는데?"
설정 한 줄이었다. 그는 몇 년째 그 우회법을 손으로 치고 있었다.
그 대화가 이 글을 쓰게 된 이유다. 대부분의 개발자는 git을 공장 초기값으로 쓴다, 그리고 git의 공장 초기값은 당신을 위해 설계된 게 아니다. 호환성을 위해 설계됐다. git은 한 번 당신을 놀라게 하느니 천 번 방해하는 쪽을 택한다.
그래서 모두가 조용히 같은 세금을 낸다. 이제는 대부분 눈치도 못 챈 채로.
fatal: The current branch has no upstream branch, 하도 많이 쳐서 근육 기억이 된 해결법- 평범한 pull이 머지를 해버려서 히스토리를 더럽히는 머지 커밋
- 맥 프로젝트마다
git status에 나타나는.DS_Store - 다섯 번째로 똑같은 리베이스 충돌을 또 푸는 일
- 개인 이메일로 회사 저장소에 커밋하는 일
이 모든 것은 git이 이미 갖고 있지만 꺼둔, 한 줄짜리 해결법이다.

지금부터 소개할 건 내 실제 설정이다, 모든 머신에 나와 함께 다니는 그 설정. git이 지원하는 모든 걸 덤프해놓은 게 아니다. 여기 있는 한 줄 한 줄은 실제 짜증을 없애서 자리를 얻었다. 2막 구성이다. 먼저 전역 설정, git의 동작 자체를 어디서나 바꾸니까. 그다음 alias, 그걸 얼마나 빠르게 다룰 수 있는지를 바꾸니까.
🪪 1막: 아이덴티티, 당신을 곤란하게 만들 수 있는 설정
결과가 따르는 설정부터 시작하자.
git config --global user.name "Paurush Rai"
git config --global user.email "personal@email.com"당연한 얘기다. 하지만 대부분의 개발자가 절대 설정하지 않는 부분이 있다. 자동 업무/개인 라우팅이다.
나는 회사 코드를 ~/Developer/company/ 아래에 둔다. ~/.gitconfig에 블록 하나면 된다.
[includeIf "gitdir:~/Developer/company/"]
path = ~/.gitconfig-work그리고 ~/.gitconfig-work에는 딱 하나만 들어 있다.
[user]
email = work@company.com
이제 아이덴티티는 저장소가 어디 있는지로 결정된다. company/ 안에 뭐든 클론하면 → 자동으로 업무 이메일. 그 외 모든 곳은 → 개인 이메일. 기억할 필요도, 저장소별 설정도, 회사 감사 로그에 개인 이메일이 찍히는 일도 없다, 주말 사이드 프로젝트에 업무 이메일이 찍히는 일도 없다.
(work 파일에 없는 걸 눈여겨보라. 이름이다. includeIf는 전역 설정 위에 레이어로 얹히므로, work 파일은 달라지는 부분, 이메일, 만 선언한다. 나머지는 전부 상속된다.)
이게 보이는 것보다 중요한 이유는, 커밋 이메일이 영구적이기 때문이다. 잘못된 아이덴티티로 푸시하면 영원히 히스토리에 남는다. GitHub는 그 이메일을 소유한 계정에게 커밋을 귀속시킨다. 아이덴티티는 저장소의 속성이어야지, 마지막으로 기억해서 입력한 무언가의 속성이어서는 안 된다.
⚙️ 1막 계속: 동작 설정들
11줄. 각각 특정하고 반복되는 짜증 하나를 없앤다.
"no upstream branch"를 다시는 보지 않기
git config --global push.autoSetupRemote true모든 개발자가 외워버린 그 에러, fatal: The current branch has no upstream branch, 는 git이 당연한 일을 해도 되는지 허락을 구하기 위해 존재한다. 이 설정은 그 허락을 영구적으로 내준다. 새 브랜치에서의 첫 푸시가 그냥 된다. (이게 내 동료를 말문 막히게 한 그 줄이다.)
뜻하지 않은 머지 커밋 생성 멈추기
git config --global pull.rebase true기본값 git pull은 fetch + merge다, 그래서 "Merge branch 'main' of github.com..." 같은 커밋들이 히스토리를 도배하게 된다. 이 설정을 하면 pull이 도착한 것 위에 로컬 커밋을 재생(replay)한다. 선형적인 히스토리, 노이즈 없음.
새 저장소가 main에서 시작하게 만들기
git config --global init.defaultBranch main이게 없으면 모든 git init이 master에서 시작하고 나중에 이름을 바꿔야 한다. 한 줄이면 다시는 신경 쓸 필요 없다.
죽은 브랜치 쌓아두지 않기
git config --global fetch.prune true리모트에서 브랜치가 삭제되면(대부분 머지된 PR), 로컬에 남은 origin/그브랜치 사본은 영원히 남아있는다. 이 설정은 fetch할 때마다 오래된 참조를 자동으로 지운다. 브랜치 목록이 계속 정직하게 유지된다.
.DS_Store를 모든 저장소에서 영원히 추방하기
git config --global core.excludesfile ~/.gitignore_global
echo ".DS_Store" > ~/.gitignore_global전역 ignore 파일은 각 저장소 자체의 .gitignore 위에 추가로 적용된다. macOS 부산물, 에디터 스왑 파일 등을 프로젝트마다가 아니라 머신 전체에서 한 번에 처리한다.
충돌 해결의 삼위일체
git config --global merge.conflictstyle zdiff3
git config --global rerere.enabled true
git config --global rebase.autostash true함께 작동하며 충돌 해결을 바꿔놓는 설정 세 가지다.
zdiff3는 충돌 마커에 세 번째 섹션을 더한다, 양쪽이 시작한 원본 텍스트다. 표준 마커는 "내 것"과 "상대 것"만 보여주고 의도를 추측하게 만든다. 공통 조상을 보는 순간, 추측이 읽기로 바뀐다.

rerere("reuse recorded resolution")는 11개 중 숨은 히트작이다. git이 당신이 각 충돌을 어떻게 해결했는지 기록하고, 같은 충돌이 다시 나타나면(리베이스는 충돌을 반복하는 걸 좋아한다) 그 해결법을 자동으로 재생한다. 다섯 번째로 똑같은 충돌은 스스로 해결된다.
autostash는 "cannot rebase: you have unstaged changes" 춤을 없애준다. git이 작업 내용을 스태시하고, 리베이스하고, 복원한다. 하던 일이 그대로 남는다.
삶의 질, 두 줄
git config --global diff.algorithm histogram
git config --global branch.sort -committerdateHistogram은 기본값인 Myers 알고리즘보다 의미 있게 더 깨끗한 diff를 만든다. git이 관련 없는 줄들을 짝지어버리는 뒤섞인 블록이 줄어든다. 그리고 브랜치를 최근 활동순으로 정렬하면 git branch가 알파벳순 고고학이 아니라 실제로 작업 중인 것을 먼저 보여준다.
⌨️ 2막: alias들
설정은 git의 동작을 바꾼다. alias는 당신의 속도를 바꾼다. 나는 엄선한 20개를 유지한다, 무엇을 아껴주는지에 따라 그룹으로 묶었다.

일상 주력들
st = status -sb
cm = commit -m
sw = switch
co = checkout
amend = commit --amend --no-editstatus -sb는 짚고 넘어갈 만하다. 두 줄짜리 status다. 브랜치 상태와 변경된 파일, 몇 년 전부터 안 읽는 조언 문단은 없다.
명확하게 보기
lg = log --oneline --graph --decorate -20
last = log -1 HEAD --stat
d = diff
ds = diff --staged
whoami = !echo "$(git config user.name) <$(git config user.email)>"git lg는 나의 "지금 어디쯤"이다, 최근 20개 커밋을 그래프로 그려준다. 그리고 git whoami는 1막의 마무리를 짓는다. 어떤 저장소에서든 실행하면 다음 커밋에 서명할 아이덴티티가 정확히 뭔지 출력한다. company/ 안에서는 업무 이메일을, 그 외에서는 개인 이메일을 보여준다. 명령어 한 번으로 검증된 신뢰.
겁 없이 되돌리기
unstage = restore --staged
undo = reset --soft HEAD~1
wip = !git add -A && git commit -m "wip: checkpoint"undo는 아무것도 파괴하지 않고 커밋을 취소한다. 변경 사항은 다시 스테이징으로 돌아간다. wip는 위험한 수술 전에 모든 걸 스냅샷으로 남긴다. 대부분의 git 불안은 안전하게 되돌아가는 법을 모르는 데서 온다. 이것들이 바로 그 안전한 길이다.
프로 기술들
pf = push --force-with-lease
fixup = commit --fixup
ri = rebase -i --autosquash
cleanup = !git branch --merged | grep -vE "^\*|main|master" | xargs git branch -dpf가 중요한 것이다. --force-with-lease는 리모트가 내가 마지막으로 본 상태 그대로일 때만 강제 푸시한다, 그 사이 도착한 동료의 작업을 물리적으로 덮어쓸 수 없다는 뜻이다. 순수한 --force가 더 나은 선택인 상황은 없다. 이걸 alias로 만들어두면 내 손가락은 위험한 버전을 절대 치지 않는다.
fixup과 ri 조합은 깨끗한 히스토리 워크플로다. 이전 커밋을 겨냥한 수정 커밋을 만들고, 인터랙티브 리베이스 한 번이면 모든 걸 자동으로 접어 넣는다.
🎯 이걸 영구적으로 만드는 부분
여기 설정에 관한 진실이 있다. 만드는 건 쉽다. 유지하는 게 진짜 문제다. 지금 쓰는 머신에만 있는 설정은, 다음 머신에서 기억을 더듬어 다시 만들어야 할 설정이다.
내 ~/.gitconfig는 그냥 파일이 아니다. 변경이 일어나는 순간 자동으로 커밋하고 푸시하는 dotfiles 저장소로의 심링크다. 최근에 alias 하나를 추가했더니, 백그라운드 에이전트가 내가 테스트를 끝내기도 전에 그걸 GitHub에 커밋해놓았다.
(심링크와 launchd 에이전트로 이뤄진 그 시스템, cron도, 서드파티 도구도 없이, 은 따로 정리해둔 별도의 이야기다.)
결과는 이렇다. 아이덴티티 라우팅과 alias 20개를 포함한 이 설정 전체가, install.sh 하나로 어느 머신에든 안착한다. 한 번 설정하면 영원히 유지된다.
📋 전체 내용, 복사-붙여넣기 가능
# 아이덴티티 (여기 수정!)
git config --global user.name "Your Name"
git config --global user.email "you@personal.com"
# 동작
git config --global init.defaultBranch main
git config --global push.autoSetupRemote true
git config --global pull.rebase true
git config --global fetch.prune true
git config --global core.excludesfile ~/.gitignore_global
echo ".DS_Store" > ~/.gitignore_global
git config --global merge.conflictstyle zdiff3
git config --global rerere.enabled true
git config --global rebase.autostash true
git config --global diff.algorithm histogram
git config --global branch.sort -committerdate
# alias
git config --global alias.st "status -sb"
git config --global alias.cm "commit -m"
git config --global alias.sw "switch"
git config --global alias.co "checkout"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.lg "log --oneline --graph --decorate -20"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.d "diff"
git config --global alias.ds "diff --staged"
git config --global alias.unstage "restore --staged"
git config --global alias.undo "reset --soft HEAD~1"
git config --global alias.wip '!git add -A && git commit -m "wip: checkpoint"'
git config --global alias.pf "push --force-with-lease"
git config --global alias.fixup "commit --fixup"
git config --global alias.ri "rebase -i --autosquash"
git config --global alias.cleanup '!git branch --merged | grep -vE "^\*|main|master" | xargs git branch -d'
git config --global alias.root "rev-parse --show-toplevel"
git config --global alias.aliases "config --get-regexp ^alias"
git config --global alias.whoami '!echo "$(git config user.name) <$(git config user.email)>"'거기에 업무 코드를 전용 디렉토리에 둔다면 아이덴티티 라우팅까지.
git config --global includeIf."gitdir:~/Developer/company/".path ~/.gitconfig-work
printf '[user]\n email = you@work.com\n' > ~/.gitconfig-work교훈
이 설정들 하나하나는 단독으로는 인상적이지 않다. 그게 핵심이다.
git의 기본값은 아무도 놀라게 하지 않는 걸 최적화한다. 그 결과 모두를 방해한다. 위의 각 줄은 반복되는 방해 하나를 침묵으로 바꾼다, 그리고 침묵은 쌓인다. 앞으로 만질 모든 저장소에 걸쳐 아낀 몇 초와 피한 잘못된 이메일 커밋을 곱해보면, 이 30분짜리 설정 작업은 도구에 쓸 수 있는 시간 중 가장 레버리지 높은 축에 든다.
내 동료는 그걸 본 바로 그날 오후에 push.autoSetupRemote를 설정했다. 몇 년째 치고 있던 우회법이 한 줄로 사라졌다, 그게 선택 사항이었다는 걸 깨닫는 데 필요했던 건, 누군가 그 세금을 내지 않는 걸 보는 것뿐이었다.
이 글을 그 순간의 문서화된 버전이라고 생각해달라. cat ~/.gitconfig를 쳐서 지금 뭐가 들어 있는지 보라.
기본값은 호환성을 위한 것이다. 설정은 당신을 위한 것이다.
당신의 gitconfig엔 뭐가 있나
나는 늘 다음 한 줄짜리 해결법을 찾아다닌다. 자리를 얻어낸 설정이나 alias가 있다면, 그게 바로 내가 지금 것들의 절반을 찾아낸 방법이다.


