오픈소스 일기: GIT 그리고 저장소 다루기

GitHub의 오픈소스 프로젝트에 기여하려면 Fork & Pull Request를 하는것이 제일 좋다. 이렇게 하면 남의 저장소에 커밋하기 전에 내 저장소를 정리할수 있는 여유가 생기기 때문이다.

나는 반 경험 반 생계를 위해서 Apache Zeppelin이라는 오픈소스 프로젝트에 기여를 하고 있다. GIT 및 오픈소스 초보인 상태로 시작했기 때문에 남의 저장소에 PR을 보내는 과정에서 실수가 좀 있었고 이런 실수를 다시 안하기 위해서 여기에 내용을 정리한다.

내 Fork를 원본 프로젝트와 싱크하는 방법

내가 Fork한 원본 프로젝트의 내용이 바뀌었을때 내 Fork를 업데이트 해주고 시작해야 남의 소스와 충돌이 일어나는걸 미리 방지할수 있다.

제일먼저 기여하고 싶은 프로젝트를 GitHub에서 Fork한다. 하는 방법은 GitHub에서 찾아보면 잘 나와있다.

그리고 Fork한 프로젝트를 내 local에 clone한다.

git clone git@github.com:yoonjs2/zeppelin.git

이제 원본 프로젝트(보통 upstream이라 이름을 붙인다)와 내 local을 연결한다.

git remote add --track master upstream git@github.com:apache/zeppelin.git

upstream의 최신 내용을 받아온다.

git fetch upstream

upstream의 내용을 내 master에 merge한다.

git merge upstream/master

이렇게 하면 내 master가 upstream의 master와 sync된 상태가 된다.

내 Fork가 Upstream과 뭔가 싱크가 안맞을때

GitHub를 통해서 Fork를 만들어서 Upstream에 PR을 보내는 형태로 개발을 하다보면 Upstream과 Sync가 안맞는 경우가 있다. 예를들어 Upstream에는 있는 파일이 Fork에는 없는데도 branch가 up-to-date라고 뜨는등의 일이 있다. 이런 경우엔 GitHub 웹 인터페이스에서 내Fork 저장소를 삭제한 다음 다시 Upstream 저장소를 Fork를 하면된다. 이 경우 반드시 작업하던 내용은 Patch등으로 백업해 놓는것을 권장한다. 아래 링크처럼 다른 방법도 있는데 GitHub에서 손으로 지우는게 제일 깔끔하긴 했다.

http://scribu.net/blog/resetting-your-github-fork.html

내 PR을 깔끔하게 정리하는 법

upstream을 받아와서 merge하고 여기서 branch를 따서 코드를 고친 후에 이 내용을 github PR를 통해서 upstream에 반영하게 되는데 이 경우 과거의 merge히스토리가 PR에 지저분하게 붙는 경우가 있다. 이럴경우 Maintainer가 브랜치를 정리할 것을 요청하고 Reject을 놓을수도 있다. 수정된 부분만 분명히 PR에 보일수 있도록 정리해 주는게 필요하다.

여러번의 커밋을 하나로 합친다

참고: http://dogfeet.github.io/articles/2012/how-to-github.html

나처럼 커밋 습관이 잦은 사람은 여러번의 커밋을 그대로 push하는 경우가 많은데 이럴경우 PR로 변경된 내용을 다른사람이 한번에 확인하는게 어려워진다. 따라서 어차피 같은 이슈에 대한 수정이라면 가급적 하나의 커밋으로 묶어주거나 임시로 커밋한 것이 push되지 않게 정리해줄 필요가 있다.

최근 3개의 커밋을 하나로 묶고 싶다면 아래와 같이 입력한다.

git rebase -i HEAD~3

다음과 비슷하게 각 커밋에 대해서 어떻게 처리할 것인지 묻는 편집기가 뜬다.

pick df94881 Allow install to SD
pick a7323e5 README Junkyism
pick 3ead26f rm classpath from git

합칠 커밋에 대해서 squash로 고쳐준다.

pick df94881 Allow install to SD
squash a7323e5 README Junkyism
squash 3ead26f rm classpath from git

그리고 저장하고 편집기를 나가면 새 커밋 메세지를 입력받는다. 적당히 새 내용을 써주면 위 3개 커밋이 하나로 합쳐진 것을 확인할수 있다.

과거 머지 및 히스토리 기록이 붙지 않게 한다

upstream의 소스를 가져와 로컬과 merge 하다보면 새 브랜치로 PR을 보낼때 지저분하게 아래처럼 과거 기록이 붙게된다. 이러면 Committer들이 변경사항을 눈으로 확인하기가 힘들어진다.

그래서 깔끔하게, 가장 최신의 수정사항만을 upstream에 PR하려면 다음과 같이 한다.

우선 origin/master를 upstream/master과 앞의 방법대로 깔끔하게 sync해 둔다.

그리고 origin/master에서 로컬 브랜치를 따거나 하는 등으로 원하는 작업을 한다. 작업이 끝나면 로컬 커밋들을 앞의 방법대로 깔끔하게 정리한다.

이제 브랜치를 이용해서 PR을 보내보자, 나는 아래와 같이 ZEPPELIN-1299라는 PR용 브랜치를 생성했다. 주의할 것은 remote가 origin/master이 아니라 upstream/master이다. 작업한 사이에 다른사람의 변경으로 충돌이 생길수도 있으니 이전에 반드시 또 sync를 해두자.

git checkout -b ZEPPELIN-1299 upstream/master

생성한 ZEPPELIN-1299브랜치로 이동.

git checkout ZEPPELIN-1299

이제 Cherry-pick기능을 이용해서 내가 작업하고 정리해서 커밋한 내용을 GIT HASH로 ZEPPELIN-1299에 불러온다.

git cherry-pick 7923d44

불러온 내용을 GitHub에 Commit & Push한다.

이제 PR페이지를 확인하면 깔끔하게 최신 수정사항만이 반영되어 있는것을 볼수있다.