모든 내용은 https://git-scm.com/book를 참고하여 만들었습니다.
Pull Request 놓고 감 놓고 배 놓기
Pull Request가 오면 프로젝트 소유자는 변경 점이 무엇인지 확인한 후, Merge 혹은 거절하거나 코멘트를 달 수 있다. 소유자가 아이디어 자체를 마음에 들어 한다면 빛을 보기까지 좀 더 공을 들여야 한다.
이런 소통을 이메일로 하는 워크플로는 분산 환경에서의 Git에 설명했었다. GitHub에서는 온라인에서 한다. 프로젝트 소유자는 unified diff 형식의 변경사항을 검토하고 즉각 해당 라인에 코멘트를 달 수 있다.
Figure 93. Pull Request의 코드에 코멘트 달기
관리자가 코멘트를 달면 Pull Request를 만든 사람에게 알림이 간다. 실제로는 저장소를 ‘Watch’하는 사람 모두에게 알림이 간다. 알림 정책은 설정할 수 있지만, 다음에 검토한다. 알림을 받는 Tony가 이메일 알림을 켜놨다면 이메일 알림도 받는다.
Figure 94. 이메일 알림으로 온 코멘트
누구나 Pull Request에 코멘트를 달 수 있다. Pull Request 토론 페이지를 보면 프로젝트 소유자가 코드에 코멘트를 달거나 Pull Request 자체에 코멘트를 달면서 토론하는 것을 보여 준다. 코드 코멘트도 맥락을 이루어 커뮤니케이션 할 수 있다.
Figure 95. Pull Request 토론 페이지
이 토론을 보고 기여자는 자신이 무엇을 해야 자신의 코드가 받아들여질지 알 수 있다. 다행히 매우 직관적이다. 만약 이 일을 이메일로 하고자 한다면 관련 커밋을 다시 말아서 메일링 리스트에 다시 보내야 한다.
하지만, GitHub에서는 해당 토픽 브랜치에 이어서 커밋하고 Push 하면 된다. 최종 Pull Request에서 Push로 업데이트한 PR의 코드를 보면 예전 코드에 달렸던 코멘트는 나오지 않는다.
추가된 커밋으로 인해 코드가 수정되었기 때문이다.
기존 PR에 이어서 Push를 하면 알림이 가지 않는다. 그래서 Tony는 자신이 작업한 내용을 코멘트로 남겼다. 그러면 프로젝트 소유자는 무슨 일이 있었는지 쉽게 알 수 있다.
Figure 96. 최종 Pull Request
꼭 짚고 넘어가야 할 것이 있다. 이 Pull Request의 “Files Changed” 탭을 클릭하면 “unified” diff를 볼 수 있다. 이 Pull Request가 주 브랜치에 Merge 되면 어떻게 달라지는지 보여준다. git diff 명령을 빌어 표현하자면 git diff master…<branch>
와 같은 명령이 실행되는 거고 <branch>
는 Pull Request의 브랜치를 의미한다. 무슨 내용인지 확인하기에서 자세히 설명한다.
그 외 알아두면 좋은 것은 GitHub은 Pull Request가 Merge 될 수 있는지 검사해서 서버에서 Merge 할 수 있도록 Merge 버튼을 제공한다.
이 버튼은 저장소에 쓰기 권한이 있는 사람만 볼 수 있고 이 버튼으로 Merge 하면 Merge 커밋이 생긴다(Trivial Merge). “fast-forward” Merge가 가능할 때도 “non-fast-forwrd” 로 Merge 한다.
로컬에 Pull Request 브랜치를 당겨와서 Merge 해도 된다. master 브랜치에 Merge 해서 GitHub에 Push 하면 자동으로 해당 Pull Request가 닫힌다.
이런 방식이 대부분의 GitHub 프로젝트가 사용하는 기본 워크플로다. 토픽 브랜치를 만들고 Pull Request를 연다. 거기서 토론을 계속 하고 그 브랜치에 커밋을 하기도 한다. 마지막에는 Merge하고 Request를 닫는다.
Note
Fork는 옵션 한 저장소의 두 브랜치를 두고도 Pull Request를 열 수 있다. 한 저장소에 쓰기 권한이 있는 동료 둘이서 어떤 기능을 추가하려고 하고 있다면 토픽 브랜치를 만들고 Push 한다. 그리고 나서 같은 저장소의 master 브랜치에 대해 Pull Request를 만들어 코드 리뷰와 토론을 시작한다. Fork는 필수가 아니다.
Pull Request 팁
GitHub에서 프로젝트에 기여하는 방법 중 가장 기본적인 방법을 살펴봤다. Pull Request를 사용할 때 도움이 되는 유용한 팁을 몇 가지 살펴보자.
Patch를 Pull Request로 보내기
보통 프로젝트에서는 Pull Request의 Patch가 완벽하고 큐처럼 꼭 순서대로 적용돼야 한다고 생각하지 않는다. 메일링 리스트를 사용하던 프로젝트에서는 Patch 순서가 의미가 있다고 생각한다. GitHub의 Pull Request는 어떤 주제를 두고 논의하는 자리다. 논의가 다 무르익으면 Merge 한다.
이 차이는 매우 중요하다. 일반적으로 처음부터 완벽한 코드를 보낼 수 없어서 메일링 리스트로 Patch를 보낼 일은 별로 없다. Pull Request는 초기부터 프로젝트 관리자와 소통할 수 있도록 해주기 때문에 혼자 답을 찾는 게 아니라 커뮤니티에서 함께 찾을 수 있다. 누군가 Pull Request를 열면 관리자와 커뮤니티는 어떻게 수정하는 게 좋을지 의견을 낸다. Patch를 처음부터 다시 전체를 작성하지 않아도 된다. 수정한 만큼만 해당 브랜치에 커밋하고 하던 일과 대화를 계속 해 나가면 된다.
최종 Pull Request로 돌아가서 다시 보면 기여자가 커밋을 Rebase 하거나 Pull Request를 다시 열지 않았다는 것을 확인할 수 있다. 그냥 기존 브랜치에 좀 더 커밋하고 Push 했을 뿐이다. 나중에 시간이 지나서 이 Pull Request를 다시 읽으면 왜 이런 방향으로 결정했는지에 대한 맥락을 쉽게 알 수 있다. 웹사이트에서 “Merge” 버튼을 누르면 Merge 커밋을 일부러 남기겠다는 뜻이 된다. 이 Merge 커밋에는 Pull Request 정보가 들어가기 때문에 필요하면 언제든지 맥락을 확인할 수 있다.
Pull Request를 최신으로 업데이트하기
Pull Request가 만든 지 오래됐거나 깨끗하게 Merge 되지 않으면 메인테이너가 쉽게 Merge 할 수 있게 수정한다. GitHub은 자동으로 Merge 할 수 있는 Pull Request인지 아닌지 Pull Request 페이지 하단에서 알려준다.
Figure 97. 깨끗하게 Merge 할 수 없는 Pull Request
깨끗하게 Merge 할 수 없는 Pull Request 같은 메시지를 보면 해당 브랜치를 고쳐서 녹색으로 만든다. 메인테이너가 고치지 않아도 되도록 한다.
이 문제를 해결하는 방법은 두 가지가 있다. 대상 브랜치(보통은 master
브랜치)를 기준으로 Rebase 하는 방법이 있고 대상 브랜치를 Pull Request 브랜치에 Merge 하는 방법이 있다.
GitHub을 사용하는 개발자는 대부분 후자를 고른다. 앞서 살펴봤던 것과 같은 이유다. Rebase 하면 히스토리는 깨끗해지지만 훨씬 더 어렵고 에러 나기 쉽다.
Pull Request가 Merge 될 수 있도록 대상 브랜치를 Merge 하려면 먼저 원 저장소를 리모트로 추가한다. 그리고 나서 Fetch 하고 그 저장소의 대상 브랜치를 해당 토픽 브랜치에 Merge 한다. 문제를 해결하고 그 브랜치에 도로 Push 한다.
“tonychacon” 예제에 이 워크플로를 적용해보자. 원저자가 뭔가 수정을 했는데 Pull Request와 충돌이 난다. 여기부터 살펴보자.
$ git remote add upstream https://github.com/schacon/blink (1)
$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From https://github.com/schacon/blink
* [new branch] master -> upstream/master
$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.
$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
into slower-blink
$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
ef4725c..3c8d735 slower-blink -> slow-blink
- 원 저장소를 “upstream” 이라는 이름의 리모트로 추가한다
- 리모트에서 최신 데이터를 Fetch 한다
- 대상 브랜치를 토픽 브랜치에 Merge 한다
- 충돌을 해결한다
- 동일한 토픽 브랜치에 도로 Push 한다
이렇게 하면 Pull Request는 자동으로 업데이트되고 깨끗하게 Merge 할 수 있는지 재확인된다.
Figure 98. 깨끗하게 Merge 할 수 있는 Pull Request.
연속성은 Git의 장기 중 하나다. 오랫동안 무엇인가 만들고 있다면 최신으로 유지하기 위해 대상 브랜치를 쉽게 Merge 해 올 수 있다. 다 마칠 때까지 하고 또 하고 할 수 있다. Merge 할 때 발생하는 충돌만 해결하면 되고 지속적으로 개발 프로세스를 관리할 수 있다.
브랜치를 꼭 깨끗하게 유지하고 싶어서 Rebase 해야 한다고 생각한다면 이미 열어 놓은 Pull Request에 대고 Push 하지 말아야 한다. 그럼 이 브랜치를 가져다 Merge 해 놓은 사람들은 Rebase 의 위험성에 설명했듯이 충격에 빠질 것이다. 대신 브랜치를 새로 만들어 Push 한다. 그리고 Pull Request도 새로 여는데 원 Pull Request가 뭔지 알 수 있도록 참조를 달고 원래 것은 닫는다.
참조
그럼 바로 “어떻게 Pull Request를 참조시키지?” 라는 의문이 들겠지만, 방법은 매우 많다. GitHub에 쓰기 가능한 곳 어디에서나 참조를 달 수 있다.
먼저 Issue와 Pull Request를 서로 참조시키는 방법부터 살펴보자. 모든 Pull Request와 Issue에는 프로젝트 내에서 유일한 번호를 하나 할당한다. 예를 들어, 3인 Pull Request와 #3인 Issue는 동시에 있을 수 없다. <num>
과 같은 형태로 코멘트가나 설명에 Pull Request와 Issue를 참조시킬 수 있다. 이 방법은 단일 프로젝트 범위에서만 유효하다. Fork 저장소의 Issue나 Pull Request를 참조시키려고 한다면 username#<num>
라고 쓰고 아예 다른 저장소면 username/repo#<num>
라고 써야 한다.
설명을 위해 이미 브랜치를 Rebase 했고 Pull Request를 새로 만들었다고 하자. 그럼 예전 Pull Request가 뭔지 알 수 있도록 새것에서 예전 것을 참조하게 해보고 Pull Request의 상호 참조 편집.같이 Fork 한 저장소의 이슈나 아예 다른 저장소의 이슈도 참조하게 해보자.
Figure 99. Pull Request의 상호 참조 편집.
이 Pull Request를 보내면 Pull Request의 상호 참조.처럼 보인다.
Figure 100. Pull Request의 상호 참조.
GitHub URL을 전부 입력해도 딱 필요한 만큼으로 줄어든다.
그리고 원래 있던 Pull Request를 닫으면 새 Pull Request에는 기존 Pull Request가 닫혔다고 언급된다. GitHub은 Pull Request 타임라인에 트랙백 이벤트를 자동으로 만든다. 그래서 이 Pull Request에 방문하는 사람은 예전 Pull Request가 닫혔는지 알 수 있고 그 링크가 있어서 바로 클릭해서 예전 것을 볼 수 있다. 이 링크는 닫은 Pull Request의 트랙백처럼 생겼다.
Figure 101. 닫은 Pull Request의 트랙백
이슈뿐만 아니라 커밋의 SHA도 참조할 수 있다. 40자 SHA를 적으면 GitHub은 자동으로 해당 커밋에 링크를 걸어 준다. Fork 저장소나 아예 다른 저장소의 커밋도 이슈와 동일한 방식으로 링크시킬 수 있다.
GitHub Flavored Markdown
다른 이슈를 링크하는 것은 GitHub 글쓰기의 첫걸음에 불과하다. “GitHub Flavored Markdown” 이라는 형식으로 이슈나 Pull Request의 설명, 코멘트, 코드 주석 등에서 글을 쓸 수 있다. Markdown 형식으로 글을 쓰면 그냥 텍스트로 쓴 글이지만 형식을 갖춰 미끈하고 아름답게 렌더링된다.
GitHub Flavored Markdown 예제.는 Markdown으로 쓴 글이 어떻게 렌더링되는지 보여준다.
Figure 102. GitHub Flavored Markdown 예제.
GitHub Flavored Markdown
GitHub Flavored Markdown(이하 GFM)은 기본 Markdown을 확장했다. GFM은 Pull Request나 이슈 등의 글을 쓸 때 매우 유용하다.
타스크 리스트
GFM이 확장한 것 기능 중 타스크 리스트가 있는데 Pull Request에서 사용하면 좋다. 간단히 말해서 타스크 리스트는 완료했다고 표시할 수 있는 체크박스의 목록이다. 이슈나 Pull Request에서 다 했다고 표기하고 싶을 때 사용한다.
타스크 리스트는 아래와 같이 사용한다.:
- [X] Write the code
- [ ] Write all the tests
- [ ] Document the code
이 타스크 리스트를 이슈나 Pull Request에 사용하면 타스크 리스트.처럼 렌더링된다.
Figure 103. 타스크 리스트.
Pull Request를 Merge 하기 전에 꼭 처리해야 하는 일의 목록을 표현할 때 타스크 리스트를 사용한다. Markdown을 직접 고치지 않고 체크박스만 클릭해도 해당 타스크가 완료됐다고 업데이트되기 때문에 상당히 좋은 기능이다.
GitHub은 이슈나 Pull Requests에 있는 타스크 리스트를 집계해서 목록 화면에서 보여준다. 예를 들어, 타스크들이 정리된 Pull Request가 있으면 Pull Request 요약 페이지에서 얼마나 진행됐는지 볼 수 있다. 그래서 Pull Request를 타스크 여러 개로 쪼개 두면 그 브랜치가 얼마나 진행됐는지 알기 쉽다. Pull Request 목록 화면에서 보여주는 타스크 현황.를 보자.
Figure 104. Pull Request 목록 화면에서 보여주는 타스크 현황.
Pull Request부터 열어 두고 일을 하면 해당 기능이 얼마나 진행됐는지 쉽게 알 수 있다.
코드 조각
코멘트에 코드 조각도 넣을 수 있다. 실제로 구현해서 브랜치에 커밋하기 전에 뭔가 아이디어를 코드로 표현해 볼 때 좋다. 그 외에도 단순히 코드 예제를 보여주기 위해서 사용하거나 해당 Pull Request에서 구현한 것이 무엇인지 보여줄 때도 사용한다.
백틱으로 된 “Fence” 안에 코드 조각을 넣는다.
java
for(int i=0 ; i < 5 ; i++)
{
System.out.println("i is : " + i);
}
코드 조각에 언어 이름을 쓰면 GitHub은 구문강조(Syntax Highlight)도 해준다. 구문강조로 미끈해진 코드.는 언어 이름을 넣어서 구문 강조된 결과다.
Figure 105. 구문강조로 미끈해진 코드.
인용
아주 긴 글에서 딱 한 부분만 집어서 논의하고 싶을 때 > 문자로 해당 부분을 인용하고 그 밑에 코멘트를 단다. 이 방법은 매우 흔히 사용하는 방법이라, 상당히 유용하고, 단축키도 지원한다. 인용하고 싶은 텍스트를 선택하고 r
키를 누르면 바로 코멘트 상자에 해당 텍스트가 인용된다.
아래와 같이 인용한다.
> Whether 'tis Nobler in the mind to suffer
> The Slings and Arrows of outrageous Fortune,
How big are these slings and in particular, these arrows?
이 텍스트는 인용 예제.처럼 렌더링된다.
Figure 106. 인용 예제.
Emoji
마지막으로 소개하는 것은 글에 Emoji를 넣을 수 있다는 것이다. Emoji는 GitHub 이슈나 Pull Request에서 정말 많이 사용된다. GitHub은 Emoji를 쉽게 사용할 수 있도록 돕는다. 코멘트를 쓸 때 : 문자로 Emoji 입력을 시작하면 선택해서 자동완성할 수 있도록 Emoji 목록을 보여준다.
Figure 107. Emoji 자동완성.
Emoji는 :<name>:
형식으로 생겼다. 아래 예제를 보자.
I :eyes: that :bug: and I :cold_sweat:.
:trophy: for :microscope: it.
:+1: and :sparkles: on this :ship:, it's :fire::poop:!
:clap::tada::panda_face:
렌더링되면 Emoji를 많이 쓴 글.처럼 보인다.
Figure 108. Emoji를 많이 쓴 글.
Emoji는 정보 전달하는 데도 좋지만 얼마나 재밌고 기쁜지 같은 표현도 가능하다.
Note
Emoji 문자를 사용하는 웹 서비스가 정말 많다. 어떤 Emoji 문자가 있는지 쉽게 찾아볼 수 있는 치트시트가 있어서 두고두고 참고할 수 있다.
http://www.emoji-cheat-sheet.com
이미지
GitHub이 제공하는 글에 이미지를 포함시키는 기능은 기술적으로 GFM이 아니지만 엄청 유용하다. Markdown 형식으로 이미지를 첨부하고 싶을 때 일반적인 방법으로는 이미지를 올리고 그 URL을 찾아서 일일이 입력해야 하는데 번거롭다. GitHub에서는 그냥 이미지를 바로 Drag-and-Drop으로 붙여 넣을 수 있다.
Figure 109. 끌어다 놓기로 이미지 자동 붙이기.
끌어다 놓기로 이미지 자동 붙이기.로 돌아가서 보면 Text Area 위에 “Parsed As Markdown” 이라는 표시를 볼 수 있다. 그 링크를 클릭하면 GitHub에서 Markdown을 어떻게 사용하는지 알려주는 치트시트를 보여준다.