모든 내용은 https://git-scm.com/book를 참고하여 만들었습니다.
프로젝트 관리하기
효율적으로 기여하는 방법뿐만 아니라 효율적으로 운영하는 방법도 알아야 한다. 언젠가는 단순히 프로젝트에 기여하는 것이 아니라 프로젝트를 직접 운영해야 할 수도 있다.
프로젝트를 운영하는 것은 크게 두 가지로 이루어진다.
하나는 format-patch 명령으로 생성한 Patch를 이메일로 받아서 프로젝트에 Patch를 적용하는 것이다.
다른 하나는 프로젝트의 다른 리모트 저장소로부터 변경 내용을 Merge 하는 것이다. 저장소를 아주 깔끔하고 정돈된 상태로 운영하고 Patch를 적용하거나 수정사항을 확인하기 쉬운 상태로 유지하려면 좋은 운영 방식을 터득해야 한다. 좋은 운영 방식은 다른 사람들이 이해하기 쉽고 프로젝트가 오랫동안 운영돼도 흐트러짐이 없어야 한다.
토픽 브랜치에서 일하기
메인 브랜치에 통합하기 전에 임시로 토픽 브랜치를 하나 만들고 거기에 통합해 보고 나서 다시 메인 브랜치에 통합하는 것이 좋다.
이렇게 하면 Patch를 적용할 때 이리저리 수정해 보기도 하고 좀 더 고민해 봐야 하면 Patch를 적용해둔 채로 나중으로 미룰 수도 있다.
무슨 Patch인지 브랜치 이름에 간단히 적어주면 다른 작업을 하다가 나중에 이 브랜치로 돌아왔을 때 기억해내기 훨씬 수월하다.
프로젝트 관리자라면 이런 토픽 브랜치의 이름을 잘 지어야 한다.
예를 들어 sc 라는 사람이 작업한 Patch라면 sc/ruby_client
처럼 앞에 닉네임을 붙여서 브랜치를 만들 수 있다. master
브랜치에서 새 토픽 브랜치를 아래와 같이 만든다.
$ git branch sc/ruby_client master
checkout -b
명령으로 브랜치를 만들고 Checkout까지 한 번에 할 수 있다.
$ git checkout -b sc/ruby_client master
이렇게 토픽 브랜치를 만들고 받은 Patch를 적용해보고 적용한 내용을 다시 Long-Running 브랜치로 Merge 한다.
이메일로 받은 Patch를 적용하기
이메일로 받은 Patch를 프로젝트에 적용하기 전에 우선 토픽 브랜치에 Patch를 적용한다. Patch를 적용하는 방법은 git apply
명령을 사용하는 것과 git am
명령을 사용하는 것 두 가지가 있다.
apply 명령을 사용하는 방법
git diff
나 Unix의 diff
명령(다음 절에서 다루겠지만 추천하지 않는 방법)으로 만든 Patch 파일을 적용할 때는 git apply
명령을 사용한다. Patch 파일이 /tmp/patch-ruby-client.patch
라고 하면 아래와 같은 명령으로 Patch를 적용할 수 있다.
$ git apply /tmp/patch-ruby-client.patch
위 명령을 실행하면 Patch 파일 내용에 따라 현재 디렉토리의 파일들을 변경한다. 위 명령은 patch -p1
명령과 거의 같다. 하지만, 이 명령이 patch
명령보다 훨씬 더 꼼꼼하게 비교한다. git diff
로 생성한 Patch 파일에 파일을 추가하거나, 파일을 삭제하고, 파일의 이름을 변경하는 내용이 들어 있으면 그대로 적용된다. 이런 것은 patch
명령으로 할 수 없다. 그리고 git apply
는 모두 적용, 아니면 모두 취소
모델을 사용하기 때문에 Patch를 적용하는 데 실패하면 Patch를 적용하기 이전 상태로 전부 되돌려 놓는다.
patch
명령은 여러 파일에 적용하다가 중간에 실패하면 거기서 그대로 중단하기 때문에 깔끔하지 못하다. git apply
는 patch
명령보다 훨씬 보수적이다.
이 명령은 자동으로 커밋해 주지 않기 때문에 변경된 파일을 직접 Staging Area에 추가하고 커밋해야 한다.
실제로 Patch를 적용하기 전에 Patch가 잘 적용되는지 한 번 시험해보려면 git apply --check
명령을 사용한다.
$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
화면에 아무 내용도 뜨지 않으면 Patch가 깔끔하게 적용됐다는 것이다. 이 명령은 Patch를 적용해 보고 에러가 발생하면 0이 아닌 값을 반환하기 때문에 쉘 스크립트에서도 사용할 수 있다.
am 명령을 사용하는 방법
프로젝트 기여자가 Git의 format-patch
명령을 잘 사용하면 관리자의 작업은 훨씬 쉬워진다. format-patch
명령으로 만든 Patch 파일은 기여자의 정보와 커밋 정보가 포함되어 있기 때문이다. 그래서 기여자가 diff
보다 format-patch
를 사용하도록 권해야 한다. git apply
는 기존 Patch 파일에만 사용한다.
format-patch
명령으로 생성한 Patch 파일은 git am
명령으로 적용한다(am
은 “apply a series of patches from a mailbox”의 약자다). git am
은 mbox
파일을 읽어 그 안에 이메일 메시지가 한 개가 있든 여러 개가 있든 처리할 수 있다. mbox
파일은 간단한 텍스트 파일이고 그 내용은 아래와 같다.
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function
Limit log functionality to the first 20
이 내용은 format-patch
명령으로 생성한 파일의 앞부분이다. 이 파일은 mbox
이메일 형식이다. 받은 메일이 git send-email
로 만든 메일이라면 mbox
형식으로 저장하고 이 mbox
파일을 git am
명령으로 적용한다. 사용하는 메일 클라이언트가 여러 이메일을 mbox
파일 하나로 저장할 수 있다면 메일 여러 개를 한 번에 Patch 할 수 있다.
이메일로 받은 것이 아니라 git format-patch
명령으로 만든 이슈 트래킹 시스템 같은데 올라온 파일이라면 먼저 내려받고서 git am
명령으로 Patch 한다.
$ git am 0001-limit-log-function.patch
Applying: add limit to log function
Patch가 성공하면 자동으로 새로운 커밋이 하나 만들어진다. 이메일의 From 과 Date 에서 저자 정보가, 이메일의 제목과 메시지에서 커밋 메시지가 추출돼 사용된다.
예를 들어 위의 mbox
예제 파일을 적용해서 생성되는 커밋은 아래와 같다.
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author: Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit: Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700
add limit to log function
Limit log functionality to the first 20
Commit 부분의 커밋 정보는 누가 언제 Patch 했는지 알려 준다. Author 정보는 실제로 누가 언제 Patch 파일을 만들었는지 알려 준다.
Patch에 실패할 수도 있다. 보통 Patch가 생성된 시점보다 해당 브랜치가 너무 업데이트 됐을 때나 아직 적용되지 않은 다른 Patch가 필요한 경우에 일어난다. 이러면 git am
명령은 Patch를 중단하고 사용자에게 어떻게 처리할지 물어온다.
$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
성공적으로 Patch 하지 못하면 git
은 Merge 나 Rebase 의 경우처럼 문제를 일으킨 파일에 충돌 표시를 해 놓는다. Merge 나 Rebase 할 때 충돌을 해결하는 것처럼 Patch의 충돌도 해결할 수 있다. 충돌한 파일을 열어서 충돌 부분을 수정하고 나서 Staging Area에 추가하고 git am --resolved
명령을 입력한다.
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem
충돌이 났을 때 Git에게 좀 더 머리를 써서 Patch를 적용하도록 하려면 -3
옵션을 사용한다. 이 옵션은 Git에게 3-way Patch를 적용해 보라고 하는 것이다. Patch가 어느 시점에서 갈라져 나온 것인지 알 수 없기 때문에 이 옵션은 기본적으로 비활성화돼 있다. 하지만 같은 프로젝트의 커밋이라면 기본 옵션보다 훨씬 똑똑하게 충돌 상황을 해결한다.
$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
위의 경우에서 -3
옵션이 없었으면 충돌이 났을 것이다. -3
옵션이 있어서 충돌 없이 깨끗하게 Patch가 적용됐다.
하나의 mbox
파일에 들어 있는 여러 Patch를 적용할 때 am
명령의 대화형 방식을 사용할 수 있다. 이 방식을 사용하면 Patch를 적용할 때마다 묻는다.
$ git am -3 -i mbox
Commit Body is:
--------------------------
seeing if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
이 옵션은 Patch를 여러 개 적용할 때 유용하다. 적용하려는 Patch의 내용을 미리 꼭 기억해두지 않아도 되고 적용하기 전에 이미 적용된 Patch인지 알 수 있다.
모든 Patch를 토픽 브랜치에 적용하고 커밋까지 마치면 Long-Running 브랜치에 어떻게 통합할지를 결정해야 한다.