Git 설정하기

1장에서 설명했지만, 제일 먼저 해야 하는 것은 git config 명령으로 이름과 e-mail 주소를 설정하는 것이다:

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

이렇게 설정하는 몇 가지 중요한 것을 더 배우게 될 것이다.

아주 기초적인 설정은 1장에서도 설명했지만, 이번 장에서 다시 한 번 복습한다. Git은 내장된 기본 규칙을 따르지만, 설정 파일에 들어 있는 것이 있다면 그에 따른다. Git은 먼저 /etc/gitconfig 파일을 찾는다. 이 파일은 해당 시스템에 있는 모든 사용자와 저장소에 적용되는 설정 파일이다. git config 명령에 --system 옵션을 주면 이 파일을 사용한다.

다음으로 ~/.gitconfig 파일을 찾는다. 이 파일은 해당 사용자에게만 적용되는 설정 파일이다. --global 옵션을 주면 Git은 이 파일을 사용한다.

마지막으로 현재 작업 중인 저장소의 Git 디렉토리에 있는 .git/config 파일을 찾는다. 이 파일은 해당 저장소에만 적용된다. 각 설정 파일에 중복된 설정이 있으면 설명한 순서대로 덮어쓴다. 예를 들어 .git/config/etc/gitconfig에 같은 설정이 들어 있다면 .git/config에 있는 설정을 사용한다. 설정 파일은 손으로 직접 편집해도 되지만 보통 git config 명령을 사용하는 것이 더 편하다.

클라이언트 설정

설정은 클라이언트와 서버로 나누어 볼 수 있다. 대부분은 개인작업 환경과 관련된 클라이언트 설정이다. Git에는 설정거리가 매우 많은데, 여기서는 Workflow를 관리하는 데 필요한 것과 잘 사용하는 것만 설명한다. 한 번도 겪어 보지 못할 상황에서나 유용한 옵션까지 포함하면 설정거리가 너무 많다. Git 버전마다 옵션이 조금씩 다른데, 다음과 같이 실행하면 설치한 버전에서 사용할 수 있는 옵션을 모두 볼 수 있다.

$ git config --help

어떤 옵션을 사용할 수 있는지 git config의 매뉴얼 페이지에 자세히 설명돼 있다.

core.editor

Git은 편집기를 설정하지 않았거나 설정된 편집기를 찾을 수 없으면 Vi를 실행한다. 커밋할 때나 tag 메시지를 편집할 때 설정된 편집기를 실행시켜 준다. code.editor 설정으로 편집기를 설정한다:

$ git config --global core.editor emacs

이렇게 설정하면 메시지를 편집할 때 환경변수에 설정된 편집기 말고 항상 Emacs를 실행해준다.

commit.template

커밋할 때 Git이 보여주는 커밋 메시지는 이 옵션에 설정한 템플릿 파일이다. 예를 들어 $HOME/.gitmessage.txt 파일을 다음과 같이 만든다:

subject line

what happened

[ticket: X]

이 파일을 commit.template에 설정하면 Git은 git commit 명령이 실행해주는 편집기에 이 메시지를 기본으로 넣어준다.

$ git config --global commit.template $HOME/.gitmessage.txt
$ git commit

그러면 commit할 때 자동으로 편집기에 다음과 같은 메시지를 채워준다:

subject line

what happened

[ticket: X]
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   lib/test.rb
#
~
~
".git/COMMIT_EDITMSG" 14L, 297C

소속 팀에서 커밋 메시지 규칙을 정하면 그 규칙을 템플릿 파일로 만든다. 그리고 Git이 그 파일을 사용하도록 설정하면 규칙을 좀 더 쉽게 따를 수 있다.

core.pager

Git은 logdiff같은 명령의 메시지를 출력할 때 페이지로 나누어 보여준다. 기본으로 사용하는 명령은 less이나 more를 더 좋아하면 more라고 설정한다. 혹은 페이지를 나누고 싶지 않으면 빈 문자열로 설정한다:

$ git config --global core.pager ''

이제 명령을 실행하면 Git은 길든지 짧든지 결과를 한 번에 다 보여 준다.

user.signingkey

2장에서 설명했던 Annotated Tag를 만들 때 사용하는 GPG 키를 설정해 둘 수 있다. GPG 키를 다음과 같이 설정해 두면 서명할 때 편하다:

$ git config --global user.signingkey <gpg-key-id>

git tag 명령을 실행할 때 굳이 키를 명시하지 않고도 서명할 수 있다:

$ git tag -s <tag-name>

core.excludesfile

Git이 무시하는 untracked 파일은 .gitignore에 해당 패턴을 적으면 된다고 2장에서 설명했다. 해당 패턴의 파일은 git add 명령으로 추가해도 Stage되지 않는다. .gitignore 파일을 저장소 밖에 두고 관리하고 싶으면 core.excludesfile에 해당 파일의 경로를 설정한다. 이 파일을 작성하는 방법은 .gitignore 파일을 작성하는 방법과 같다. 그리고 core.excludesfile에 설정한 파일과 저장소 안에 있는 .gitignore 파일은 둘 다 사용된다.

help.autocorrect

이 옵션은 Git 1.6.1 버전부터 사용할 수 있다. 명령어를 잘못 입력하면 Git 1.6에서는 메시지를 다음과 같이 보여 준다:

$ git com
git: 'com' is not a git-command. See 'git --help'.

Did you mean this?
     commit

그러나 help.autocorrect를 1로 설정한 상태에서 명령어를 잘못 입력하면 Git은 자동으로 그 명령어를 찾아서 실행해준다. 단, 비슷한 명령어가 딱 하나 있을 때에만 실행된다.

컬러 터미널

사람이 쉽게 인식할 수 있도록 터미널에 결과를 컬러로 출력할 수 있다. 터미널 컬러와 관련된 옵션이 많아 꼼꼼하게 설정할 수 있다.

color.ui

color.ui를 true로 설정하면 Git은 결과를 자동으로 색칠한다. 물론 무엇을 어떻게 색칠할지 꼼꼼하게 설정할 수 있지만, 이 옵션을 켜기만 해도 그냥 기본 컬러로 터미널이 칠해진다.

$ git config --global color.ui true

이 옵션을 켜면 Git은 터미널에 컬러로 결과를 출력한다. 이 값을 false로 설정하면 절대 컬러로 출력하지 않는다. 결과를 파일로 리다이렉트하거나 다른 프로그램으로 보낼(Piping. 파이프라인)때도 그렇다. 이 설정은 1.5.5 버전에 이르러 추가됐고 예전 버전을 사용하고 있다면 해당 요소마다 직접 컬러를 지정해주어야 한다.

color.ui = always라고 설정하면 결과를 리다이렉트할 때에도 컬러 코드가 출력된다. 이렇게까지 설정해야 하는 경우는 매우 드물다. 대신 Git 명령에 --color 옵션을 주고 어떻게 출력할지 그때그때 정해줄 수 있다. 보통은 color.ui = true 만으로도 충분하다.

color.*

좀 더 꼼꼼하게 컬러를 설정하거나 예전 버전이라서 color.ui 옵션을 사용할 수 없으면 다음과 같이 종류별로 설정할 수 있다. 모두 true, false, always 중 하나를 고를 수 있다:

color.branch
color.diff
color.interactive
color.status

또한, 각 옵션의 컬러를 직접 지정할 수도 있다. 다음과 같이 설정하면 diff 명령에서 meta 정보의 foreground는 blue, background는 black, text는 bold로 바꿀 수 있다:

$ git config --global color.diff.meta “blue black bold”

컬러는 normal, black, red, green, yellow, blue, magenta, cyan, white 중에서 고를 수 있고 bold 같은 text 속성은 bold, dim, ul, blink, reverse 중에서 고를 수 있다.

git config Manpage를 보면 어떤 설정거리가 있는지 자세히 나온다.

다른 Merge, Diff 도구 사용하기

Git에 들어 있는 diff 말고 다른 도구로 바꿀 수 있다. GUI 기반의 화려한 것으로 바꿔서 좀 더 편리하게 충돌을 해결할 수 있다. 여기서는 Perforce의 Merge 도구인 P4Merge로 설정하는 것을 보여준다. P4Merge는 무료인데다 꽤 괜찮다.

P4Merge는 중요 플랫폼을 모두 지원하기 때문에 웬만한 환경이면 사용할 수 있다. 여기서는 Mac과 Linux 시스템에 설치하는 것을 보여준다. Windows에서 사용하려면 /usr/local/bin 경로만 Windows에서의 경로로 바꿔주면 된다.

다음 페이지에서 P4Merge를 내려받는다.

http://www.perforce.com/perforce/downloads/component.html

먼저 P4Merge에 쓸 wrapper 스크립트를 만든다. 필자는 Mac 사용자라서 Mac 경로를 사용한다. 어떤 시스템이든 p4merge 명령이 설치된 경로를 사용하면 된다. extMerge라는 Merge용 Wrapper 스크립트를 만들고 이 스크립트로 넘어오는 모든 인자를 p4merge 프로그램으로 넘긴다:

$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/p4merge.app/Contents/MacOS/p4merge $*

그리고 diff용 Wrapper도 만든다. 이 스크립트로 넘어오는 인자는 총 7개지만 그 중 2개만 Merge Wrapper로 넘긴다. Git이 diff 프로그램에 넘겨주는 인자는 다음과 같다:

path old-file old-hex old-mode new-file new-hex new-mode

이 중에서 old-filenew-file 만 사용할 것이기 때문에 wrapper script를 만들어야 한다:

$ cat /usr/local/bin/extDiff 
#!/bin/sh
[ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"

이 두 스크립트에 실행 권한을 부여한다:

$ sudo chmod +x /usr/local/bin/extMerge 
$ sudo chmod +x /usr/local/bin/extDiff

Git config 파일에 이 스크립트를 모두 추가해야 한다. 설정해야 하는 옵션이 좀 많다. merge.tool로 무슨 merge 도구를 사용할지, mergetool.*.cmd로 실제로 어떻게 명령어를 실행할지, mergetool.trustExitCode로 merge 도구가 반환하는 exit 코드가 merge의 성공여부를 나타내는지, diff.external은 diff할 때 실행할 명령어가 무엇인지를 설정할 때 사용한다. 모두 git config 명령으로 설정한다:

$ git config --global merge.tool extMerge
$ git config --global mergetool.extMerge.cmd \
    'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
$ git config --global mergetool.trustExitCode false
$ git config --global diff.external extDiff

~/.gitconfig/ 파일을 직접 편집해도 된다:

[merge]
  tool = extMerge
[mergetool "extMerge"]
  cmd = extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
  trustExitCode = false
[diff]
  external = extDiff

설정을 완료하고 나서 다음과 같이 diff 명령어를 실행해본다:

$ git diff 32d1776b1^ 32d1776b1

diff 결과가 터미널에 출력되는 대신 P4Merge가 실행된다. 그리고 그림 7-1처럼 그 프로그램 안에서 보여준다:

7-1. P4Merge.
7-1. P4Merge.

브랜치를 Merge할 때 충돌이 나면 git mergetool 명령을 실행한다. 이 명령을 실행하면 GUI 도구로 충돌을 해결할 수 있도록 P4Merge를 실행해준다.

wrapper를 만들어 설정하면 다른 diff, merge 도구로 바꾸기 쉬워진다. 예를 들어, KDiff3를 사용하도록 extDiff와 extMerge 스크립트를 수정한다:

$ cat /usr/local/bin/extMerge
#!/bin/sh   
/Applications/kdiff3.app/Contents/MacOS/kdiff3 $*

이제부터 Git은 diff 결과를 보여주거나 충돌을 해결할 때 KDiff3 도구를 사용한다.

어떤 Merge 도구는 Git에 미리 cmd 설정이 들어 있다. 그래서 cmd 설정 없이 사용할 수 있는 것도 있다. kdiff3, opendiff, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff는 cmd 설정 없이 merge 도구로 사용할 수 있다. diff 도구로는 다른 것을 사용하지만, merge 도구로는 KDiff3를 사용하고 싶은 경우에는 kdiff3 명령을 실행경로로 넣고 다음과 같이 설정하기만 하면 된다:

$ git config --global merge.tool kdiff3

extMerge와 extDiff 파일을 만들지 않으면 KDiff3를 Merge 도구로 사용하고 Git에 원래 들어 있던 것을 diff 도구로 사용한다.

소스 포맷과 공백

협업할 때 겪는 소스 포맷(Formatting)과 공백 문제는 미묘하고 난해하다. 동료 사이에 사용하는 플랫폼이 다를 때는 특히 더 심하다. 다른 사람이 보내온 Patch는 공백 문자가 미묘하게 다를 확률이 높다. 편집기가 몰래 공백문자를 추가해 버릴 수도 있고 크로스-플랫폼 프로젝트에서 Windows 개발자가 줄 끝에 CR(Carriage-Return) 문자를 추가해 버렸을 수도 있다. Git에는 이 이슈를 돕는 몇 가지 설정이 있다.

core.autocrlf

Windows에서 개발하는 동료와 함께 일하면 줄 바꿈(New Line) 문자 문제가 생긴다. 윈도우는 줄 바꿈 문자로 CR(Carriage-Return)과 LF(Line Feed) 문자를 둘 다 사용하지만, Mac과 Linux는 LF 문자만 사용한다. 아무것도 아닌 것 같지만, 크로스 플랫폼 프로젝트에서는 꽤 성가신 문제다.

Git은 커밋할 때 자동으로 CRLF를 LF로 변환해주고 반대로 Checkout할 때 LF를 CRLF로 변환해 주는 기능이 있다. core.autocrlf 설정으로 이 기능을 켤 수 있다. Windows에서 이 값을 true로 설정하면 Checkout할 때 LF 문자가 CRLR 문자로 변환된다:

$ git config --global core.autocrlf true

줄 바꿈 문자로 LF를 사용하는 Linux와 Mac에서는 Checkout할 때 Git이 LF를 CRLF로 변환할 필요가 없다. 게다가 우연히 CRLF가 들어간 파일이 저장소에 들어 있어도 Git이 알아서 고쳐주면 좋을 것이다. core.autocrlf 값을 input으로 설정하면 커밋할 때만 CRLF를 LF로 변환한다:

$ git config --global core.autocrlf input

이 설정을 이용하면 Windows에서는 CRLF를 사용하고 Mac, Linux, 저장소에서는 LF를 사용할 수 있다.

Windows 플랫폼에서만 개발한다면 이 기능을 끌 수 있다. 이 옵션을 false라고 설정하면 CR 문자도 저장소에도 저장된다:

$ git config --global core.autocrlf false

core.whitespace

Git에는 공백 문자를 다루는 방법으로 네 가지가 미리 정의돼 있다. 두 가지는 기본적으로 켜져 있지만 끌 수 있고 나머지 두 가지는 꺼져 있지만 켤 수 있다.

먼저 기본적으로 켜져 있는 것을 살펴보자. trailing-space는 각 줄 끝에 공백이 있는지 찾고 space-before-tab은 모든 줄 처음에 tab보다 공백이 먼저 나오는지 찾는다.

기본적으로 꺼져 있는 나머지 두 개는 indent-with-non-tabcr-at-eol이다. intent-with-non-tab은 tab이 아니라 공백 8자 이상으로 시작하는 줄이 있는지 찾고 cr-at-eol은 줄 끝에 CR 문자가 있어도 괜찮다고 Git에 알리는 것이다.

core.whitespace 옵션으로 이 네 가지 방법을 켜고 끌 수 있다. 설정에서 해당 옵션을 빼버리거나 이름이 -로 시작하면 기능이 꺼진다. 예를 들어, 다른 건 다 켜고 cr-at-eol 옵션만 끄려면 다음과 같이 설정한다:

$ git config --global core.whitespace \
    trailing-space,space-before-tab,indent-with-non-tab

git diff 명령을 실행할 때 Git은 이 설정에 따라 검사하고 쉽게 수정해서 커밋할 수 있도록 컬러로 표시해준다. git apply 명령으로 Patch를 적용할 때도 이 설정을 이용할 수 있다. Patch가 설정한 공백문자 정책에 들어맞는지 확인하려면 다음과 같이 명령어를 실행한다:

$ git apply --whitespace=warn <patch>

아니면 Git이 자동으로 고치도록 할 수 있다:

$ git apply --whitespace=fix <patch>

이 옵션은 git rebase 명령에서도 사용할 수 있다. 공백 문제가 있는 커밋을 서버로 Push하기 전에 --whitespace=fix 옵션을 주고 Rebase하면 Git은 다시 Patch를 적용하면서 공백을 설정한 대로 고친다.

서버 설정

서버 설정은 많지 않지만, 꼭 짚고 넘어가야 하는 것이 몇 개 있다.

receive.fsckObjects

Git은 Push할 때 기본적으로 개체를 검증하지(check for consistency) 않는다. 하지만, Push할 때마다 각 개체가 SHA-1 체크섬에 맞는지, 가리키는 개체가 괜찮은지 검사하게 할 수 있다. 개체를 점검하는 것은 상대적으로 느려서 Push하는 시간이 늘어난다. 얼마나 늘어나는지는 저장소 크기와 Push하는 양에 달렸다. receive.fsckOBjects 값을 true로 설정하면 Push할 때마다 Git이 검증한다.

$ git config --system receive.fsckObjects true

이렇게 설정하면 문제 있는 클라이언트가 잘못된 데이터를 Push하지 못하도록 Git은 Push마다 검증한다.

receive.denyNonFastForwards

이미 Push한 커밋을 Rebase했을 때 다시 Push하지 못하게 할 수 있다. 또 리모트 브랜치가 가리키는 커밋을 모르는 브랜치를 그 리모트 브랜치로 Push하지 못하게도 할 수 있다. 보통은 이런 정책이 좋지만 git push 명령에 -f 옵션을 주면 강제로 Push할 수 있다.

하지만, 강제로 Push하지 못하게 할 수도 있다. receive.denyNonFastForwards 옵션으로 Fast-forward로 Push할 수 없는 브랜치는 아예 Push하지 못하게 할 수 있다:

$ git config --system receive.denyNonFastForwards true

사용자마다 다른 정책을 적용하고 싶다면 서버 훅을 사용해야 한다. 서버의 receive 훅으로 할 수 있고 이 훅도 이 장에서 설명한다.

receive.denyDeletes

receive.denyNonFastForwards와 비슷한 정책으로 receive.denyDeletes라는 것이 있다. 이 설정을 켜면 브랜치를 삭제하는 Push가 거절된다. Git 1.6.1부터 receive.denyDeletes를 사용할 수 있다:

$ git config --system receive.denyDeletes true

이제 브랜치나 Tag를 삭제하는 Push는 거절된다. 아무도 삭제할 수 없다. 리모트 브랜치를 삭제하려면 직접 손으로 server의 ref 파일을 삭제해야 한다. 그리고 ACL로 사용자마다 다른 정책을 적용시킬 수도 방법도 있다. 이 방법은 이 장 끝 부분에서 다룬다.