Git
Chapters ▾ 2nd Edition

8.1 Git맞춤 - Git 설정하기

지금까지 Git이 어떻게 동작하고 Git을 어떻게 사용하는지 설명했다. 이제 Git을 좀 더 쉽고 편하게 사용할 수 있도록 도와주는 도구를 살펴본다. 이 장에서는 먼저 많이 쓰이는 설정 그리고 훅 시스템을 먼저 설명한다. 그 후에 Git을 내게 맞추어(Customize) 본다. Git을 자신의 프로젝트에 맞추고 편하게 사용하자.

Git 설정하기

시작하기에서 git config 명령을 간단히 사용했었다. git config 명령으로 제일 먼저 하게 되는 작업은 이름과 이메일 주소를 설정하는 것이다.

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

여기서는 이렇게 설정하는 것 중에서 중요한 것만 몇 가지 배운다.

우선 Git은 내장된 기본 규칙 따르지만, 설정된 것이 있으면 그에 따른다는 점을 생각해두자. Git은 먼저 /etc/gitconfig 파일을 찾는다. 이 파일은 해당 시스템에 있는 모든 사용자와 모든 저장소에 적용되는 설정 파일이다. git config 명령에 --system 옵션을 주면 이 파일을 사용한다.

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

마지막으로 현재 작업 중인 저장소의 Git 디렉토리에 있는 .git/config 파일을 찾는다. 이 파일은 해당 저장소에만 적용된다. git config 명령에 --local 옵션을 적용한 것과 같다. (아무런 범위 옵션을 지정하지 않으면 Git은 기본적으로 --local 옵션을 적용한다)

각 설정 파일에 중복된 설정이 있으면 설명한 “순서대로” 덮어쓴다. 예를 들어 .git/config/etc/gitconfig 에 같은 설정이 들어 있다면 .git/config 에 있는 설정을 사용한다.

Note

설정 파일 일반적인 텍스트파일로 쉽게 고쳐 쓸 수 있다. 보통 git config 명령을 사용하는 것이 더 편하다.

클라이언트 설정

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

$ man git-config

어떤 옵션을 사용할 수 있는지 매우 자세히 설명하고 있다. http://git-scm.com/docs/git-config.html 페이지에서도 같은 내용을 볼 수 있다.

core.editor

Git은 편집기를 설정($VISUAL, $EDITOR 변수로 설정)하지 않았거나 설정한 편집기를 찾을 수 없으면 vi 를 실행한다. 커밋할 때나 태그 메시지를 편집할 때 설정한 편집기를 실행한다. code.editor 설정으로 편집기를 설정한다.

$ git config --global core.editor emacs

이렇게 설정하면 메시지를 편집할 때 환경변수에 설정한 편집기가 아니라 Emacs를 실행한다.

commit.template

커밋할 때 Git이 보여주는 커밋 메시지는 이 옵션에 설정한 템플릿 파일이다. 사용자 지정 커밋 템플릿 메시지가 주는 장점은 커밋 메시지를 작성할 때 일정한 스타일을 유지할 수 있다는 점이다.

예를 들어 ~/.gitmessage.txt 파일을 아래와 같이 만든다.

Subject line (try to keep under 50 characters)

Multi-line description of commit,
feel free to be detailed.

[Ticket: X]

커밋 메시지 템플릿을 보면 커밋 메시지를 작성할 때 제목은 일정 길이 이하로 짧게 하고(git log --oneline 으로 보기 좋게) 자세한 수정 내용은 한칸 공백 이후 서술하도록 하고 버그 트래킹 시스템이나 이슈 관리 시스템을 쓸 경우 이슈의 번호를 적도록 유도하고 있는 것을 볼 수 있다.

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

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

그러면 커밋할 때 아래와 같은 메시지를 편집기에 자동으로 채워준다.

Subject line (try to keep under 60 characters)

Multi-line description of commit,
feel free to be detailed.

[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

이 설정은 내 작업에 서명하기 에서 설명했던 Annotated Tag를 만들 때 유용하다. 사용할 GPG 키를 설정해 둘 수 있다. 아래처럼 GPG 키를 설정하면 서명할 때 편리하다.

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

git tag 명령을 실행할 때 키를 생략하고 서명할 수 있다.

$ git tag -s <tag-name>

core.excludesfile

Git에서 git add 명령으로 추적할 파일에 포함하지 않을 파일은 .gitignore 에 해당 패턴을 적으면 된다고 파일 무시하기에서 설명했다.

한 저장소 안에서뿐 아니라 어디에서라도 Git에 포함하지 않을 파일을 설정할 수 있다. 예를 들어 Mac을 쓰는 사람이라면 .DS_Store 파일을 자주 보았을 것이다. Emacs나 Vim를 쓰다 보면 이름 끝에 ~, .swp 붙여둔 임시 파일도 있다.

.gitignore 파일처럼 무시할 파일을 설정할 수 있는데 ~/.gitignore_global 파일 안에 아래 내용으로 입력해두고

*~
.*.swp
.DS_Store

git config --global core.excludesfile ~/.gitignore_global 명령으로 설정을 추가하면 더는 위와 같은 파일이 포함되지 않을 것이다.

help.autocorrect

명령어를 잘못 입력하면 Git은 메시지를 아래와 같이 보여 준다.

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

Did you mean this?
    checkout

Git은 어떤 명령을 입력하려고 했을지 추측해서 보여주긴 하지만 직접 실행하진 않는다. 그러나 help.autocorrect 를 1로 설정하면 명령어를 잘못 입력해도 Git이 자동으로 해당 명령어를 찾아서 실행해준다.

$ git chekcout master
WARNING: You called a Git command named 'chekcout', which does not exist.
Continuing under the assumption that you meant 'checkout'
in 0.1 seconds automatically...

여기서 재밌는 것은 “0.1 seconds” 이다. 사실 help.autocorrect 설정에 사용하는 값은 1/10 초 단위의 숫자를 나타낸다. 만약 50이라는 값으로 설정한다면 자동으로 고친 명령을 실행할 때 Git은 5초간 명령을 실행하지 않고 기다려줄 수 있다.

컬러 터미널

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

color.ui

Git은 기본적으로 터미널에 출력하는 결과물을 알아서 색칠하지만, 이 색칠하는 기능을 끄고 싶다면 한 가지 설정만 해 두면 된다. 아래와 같은 명령을 실행하면 더는 색칠된 결과물을 내지 않는다.

$ git config --global color.ui false

컬러 설정의 기본 값은 auto 로 터미널에 출력할 때는 색칠하지만, 결과가 리다이렉션되거나 파일로 출력되면 색칠하지 않는다.

always 로 설정하면 터미널이든 다른 출력이든 상관없이 색칠하여 내보낸다. 대개 이 값을 설정해서 사용하지 않는다. --color 옵션을 사용하면 강제로 결과를 색칠해서 내도록 할 수 있기 때문이다. 대부분은 기본 값이 낫다.

color.*

Git은 좀 더 꼼꼼하게 컬러를 설정하는 방법을 제공한다. 아래와 같은 설정들이 있다. 모두 true, false, always 중 하나를 고를 수 있다.

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

또한, 각 옵션의 컬러를 직접 지정할 수도 있다. 아래처럼 설정하면 diff 명령에서 meta 정보의 포그라운드는 blue, 백그라운드는 black, 테스트는 bold로 바뀐다.

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

컬러는 normal, black, red, green, yellow, blue, magenta, cyan, white 중에서 고를 수 있다. 텍스트 속성은 bold, dim, ul (underline), blink, reverse 중에서 고를 수 있다.

다른 Merge, Diff 도구 사용하기

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

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

먼저 https://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools 에서 P4Merge를 내려받는다. 그 후에 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 스크립트를 만든다.

$ 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.extMerge.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가 실행되어 아래처럼 Diff 결과를 보여준다.

P4Merge.
Figure 143. P4Merge.

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

Wrapper를 만들어 설정해두면 다른 Diff, Merge 도구로 바꾸기도 쉽다. 예를 들어, KDiff3를 사용하도록 extDiffextMerge 스크립트를 수정한다.

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

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

어떤 Merge 도구는 Git에 미리 설정이 들어 있다. 그래서 추가로 스크립트를 작성하거나 하는 설정 없이 사용할 수 있는 것도 있다. 아래와 같은 명령으로 확인해볼 수 있다.

$ git mergetool --tool-help
'git mergetool --tool=<tool>' may be set to one of the following:
        emerge
        gvimdiff
        gvimdiff2
        opendiff
        p4merge
        vimdiff
        vimdiff2

The following tools are valid, but not currently available:
        araxis
        bc3
        codecompare
        deltawalker
        diffmerge
        diffuse
        ecmerge
        kdiff3
        meld
        tkdiff
        tortoisemerge
        xxdiff

Some of the tools listed above only work in a windowed
environment. If run in a terminal-only session, they will fail.

Diff 도구로는 다른 것을 사용하지만, Merge 도구로는 KDiff3를 사용하고 싶은 경우에는 kdiff3 명령을 실행경로로 넣고 아래와 같이 설정하기만 하면 된다.

$ git config --global merge.tool kdiff3

extMergeextDiff 파일을 만들지 않고 이렇게 Merge 도구만 `kdiff3`로 설정하고 Diff 도구는 Git에 원래 들어 있는 것을 사용할 수 있다.

Formatting and Whitespace

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

core.autocrlf

Windows에서 개발하는 동료와 함께 일하면 라인 바꿈(New Line) 문자에 문제가 생긴다. Windows는 라인 바꿈 문자로 CR(Carriage-Return)과 LF(Line Feed) 문자를 둘 다 사용하지만, Mac과 Linux는 LF 문자만 사용한다. 아무것도 아닌 것 같지만, 크로스 플랫폼 프로젝트에서는 꽤 성가신 문제다. Windows에서 사용하는 많은 편집기가 자동으로 LF 스타일의 라인 바꿈 스타일을 CRLF로 바꾸거나 Enter 키를 입력하면 CRLF 스타일을 사용하기 때문이다.

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

$ 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에는 공백 문자를 다루는 방법으로 네 가지가 미리 정의돼 있다. 두 가지는 기본적으로 켜져 있지만 끌 수 있고 나머지 두 가지는 꺼져 있지만 켤 수 있다.

먼저 기본적으로 켜져 있는 것을 살펴보자. blank-at-eol 는 각 라인 끝에 공백이 있는지 찾고, blank-at-eof 는 파일 끝에 추가한 빈 라인이 있는지 찾고, space-before-tab 은 모든 라인에서 처음에 tab보다 공백이 먼저 나오는지 찾는다.

기본적으로 꺼져 있는 나머지 세 개는 indent-with-non-tabtab-in-indentcr-at-eol 이다. intent-with-non-tab 은 tab이 아니라 공백으로(tabwidth 설정에 영향받음) 시작하는 라인이 있는지 찾고 cr-at-eol 은 라인 끝에 CR 문자가 있어도 괜찮다고 Git에 알리는 것이다.

core.whitespace 옵션으로 이 네 가지 방법을 켜고 끌 수 있다. 설정에서 해당 옵션을 빼버리거나 이름이 - 로 시작하면 기능이 꺼진다. 예를 들어, 다른 건 다 켜고 space-before-tab 옵션만 끄려면 아래와 같이 설정한다. trailing-space 옵션은 blank-at-eol 옵션과 blank-at-eof 옵션을 의미한다.

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

또는 각 부분에 대해서 설정을 할 수도 있다.

$ git config --global core.whitespace \
    -space-before-tab,indent-with-non-tab,tab-in-indent,cr-at-eol

git diff 명령을 실행하면 Git은 이 설정에 따라 검사해서 컬러로 표시해준다. 그래서 좀 더 쉽게 검토해서 커밋할 수 있다. git apply 명령으로 Patch를 적용할 때도 이 설정을 이용할 수 있다. 아래처럼 명령어를 실행하면 해당 Patch가 공백문자 정책에 들어맞는지 확인할 수 있다.

$ git apply --whitespace=warn <patch>

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

$ git apply --whitespace=fix <patch>

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

서버 설정

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

receive.fsckObjects

Git은 Push 할 때마다 각 개체가 SHA-1 체크섬에 맞는지 잘못된 개체가 가리키고 있는지 검사하게 할 수 있다. 기본적으로 이 기능이 동작하지 않게 설정이 되어 있는데 개체를 점검하데 상당히 시간이 걸리기 때문에 Push 하는 시간이 늘어난다. 얼마나 늘어나는지는 저장소 크기와 Push 하는 양에 달렸다. receive.fsckOBjects 값을 true로 설정하면 Git이 Push 할 때마다 검증한다.

$ git config --system receive.fsckObjects true

이렇게 설정하면 Push 할 때마다 검증하기 때문에 클라이언트는 잘못된 데이터를 Push 하지 못한다.

receive.denyNonFastForwards

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

receive.denyNonFastForwards 옵션을 켜면 Fast-forward로 Push 할 수 없는 브랜치는 아예 Push 하지 못한다.

$ git config --system receive.denyNonFastForwards true

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

receive.denyDeletes

receive.denyNonFastForwards 와 비슷한 정책으로 receive.denyDeletes 라는 것이 있다. 이 설정을 켜면 브랜치를 삭제하는 Push가 거절된다.

$ git config --system receive.denyDeletes true

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