2012년 5월 13일 일요일

AOSP에 컨트리뷰션 하기

AOSP에 컨트리뷰션 하기


Author : 박성재

이 문서는 Google korea 후원, Google developers group 연합 주최 DevFestX Korea 2012 행사에서 발표했던 내용을 재정리한 문서입니다.
이 저작물은 크리에이티브 커먼즈 [저작자표시-동일조건변경허락 2.0 대한민국 라이선스]에 따라 이용할 수 있습니다.





민주주의는 피를 먹고 자라고, 오픈소스 소프트웨어는 컨트리뷰션을 먹고 자란다고 하죠.
AOSP에 컨트리뷰션 하는 방법과, AOSP가 어떻게 구성되어 관리되고 있는지를 간단히 정리해 볼까 합니다.



What is AOSP


AOSP는 Android Open Source Project 의 약자입니다. 안드로이드는 원래 오픈소스 아닌가? 하는 혼란이 있을 수 있겠습니다.
구글에서 관리하는 안드로이드 소스트리는 크게 두개로 나눌 수 있습니다. 하나는 AOSP, 나머지 하나는 구글 내부 소스 트리입니다.
AOSP는 소스트리는 모든 사람에게 공개되어 있고, 외부의 컨트리뷰션을 받습니다. 일반적으로 안드로이드 소스 코드라고 하면 이 소스 트리를 의미하죠.
하지만, 구글 내부 소스 트리는 외부로부터의 접근이 차단되어 있습니다.
AOSP는 커뮤니티와 컨트리뷰터 중심으로 주로 maintenance에 치중한 개발이 이루어지고, 구글 내부 소스 트리에서는 구글 내부 안드로이드 개발팀에 의해 차기 버전을 위한 개발이 이루어집니다.
필요하다 판단될 때 AOSP의 변경 사항들이 구글 내부 소스 트리로 머지되고, 차기 버전의 개발이 완료되어 릴리즈 될 때, 구글 내부 소스 트리의 차기 버전 소스코드가 AOSP 소스트리에 머지되어 세상에 공개됩니다.
그러면 제조사나 하비스트 등 Android 소스가 필요한 사람은 AOSP에서 해당 태그의 소스를 받아 사용하고, 기여도 하는 거죠.
안드로이드가 역사상 가장 폐쇄적인 오픈소스 소프트웨어라는 악명을 받는 이유기도 하죠.






How AOSP managed



Android is an open source software stack



안드로이드는 오픈소스 소프트웨어의 스택, 즉 더미입니다. 여러개의 개별적이고 독립된 오픈소스 소프트웨어가 모여 구성되고, 동작하는 하나의 거대한 시스템이라는 말이죠.
이런 오픈소스 소프트웨어는 일부는 안드로이드 팀에 의해, 일부는 외부에서 개발되었고, 개발되고 있습니다. 개별 소프트웨어 개발은 개별 프로젝트로 진행됩니다. 현재 플랫폼은 총 261개의 프로젝트로 구성되어 있습니다.
이렇게 거대한 규모의 소프트웨어의 소스코드를 어떻게 관리할 것이고, 어떻게 작업 디렉토리를 구성할 것인가도 큰 이슈입니다. 이를 위해, AOSP는 세개의 도구를 사용합니다.





git


먼저, git 입니다. AOSP를 구성하는 모든 소프트웨어 개발 프로젝트는 git 을 이용해 버전 컨트롤 됩니다. 모든 프로젝트가 git을 이용해 버전 컨트롤 되기 때문에, 모든 개별 프로젝트에 동일한 인터페이스(git)를 이용해 관리가 가능하다는 장점이 있습니다. 소스 코드를 다운로드 받으신 후, 개별 프로젝트, 예를 들어 packages/apps/Launcher/ 에 들어가 보면, .git 디렉토리가 존재함을 확인 할 수 있습니다. .git 디렉토리는 git 에 의해 버전컨트롤 되면서 필요한 모든 데이터가 저장되는 디렉토리로, 그 존재는 git을 이용해 버전 컨트롤 되고 있다는 증거입니다.





repo


개별 프로젝트가 모두 git를 사용하기로 함에 따라 각 프로젝트의 버전 관리 방법을 통일한 건 좋지만, 261개나 되는 프로젝트를 git 만을 이용해 관리하려면 어지간한 부담이 아닐 수 없습니다. 이런 부담은 스크립트를 이용해 자동화 할 수 있겠지만, 어차피 그럴 거라면 주로 사용하는 기능을 간단히 처리해주는 간단한 프로그램을 하나 만드는 게 낫겠죠.
그런 목적으로, 더불어 안드로이드 만이 아니라, 안드로이드와 비슷하게 소프트웨어 스택으로 구성되는 거대 개발 프로젝트를 관리하기 위한 목적으로 구글은 repo 라는 도구를 만들게 되었습니다.
repo는 git 로 관리되는 프로젝트들로 구성된 거대 프로젝트를 하나의 단일 프로젝트처럼 간단히 관리하기 위해 사용되는 도구로, xml 포맷의 매니페스트를 이용해 거대 프로젝트를 구성하는 다수 프로젝트의 원격 소스 리파지토리와 리뷰 서버 주소, 작업 디렉토리에 해당 프로젝트를 어떻게 위치할 것인지 등을 명시합니다.





gerrit


컨트리뷰션을 받는 데는 여러 방법이 있죠.
AOSP는 컨트리뷰션을 받기 위한 별도의 리뷰용 소스트리 리파지토리를 만들어, 그리로 푸시를 허용하고, 각 푸시된 변경 사항에 대해 리뷰를 거쳐 AOSP로 해당 변경 사항을 받아들일 것인지를 결정하는 방법을 사용합니다.
이런 목적으로, AOSP는 git 으로 관리되는 프로젝트의 리뷰 시스템을 웹 기반으로 제공하는, gerrit 이라는 툴을 사용합니다. 모든 패치는 리뷰 서버로 업로드 되고, gerrit을 통해 리뷰를 거쳐 검증된 패치는 AOSP에 머지됩니다.



Contribution


이제 컨트리뷰션에 대해서 이야기 해 보죠.
컨트리뷰션, 말 그대로 기여한다는 이야기죠. 기여의 방법에는 여러가지가 있겠습니다. 먼저, 해당 프로젝트의 결과물을 사용해 주는 것이 가장 기본적이고 중요한 기여죠. 안드로이드 단말을 사용하고 계시다면, 여러분은 훌륭한 AOSP 컨트리뷰터 입니다.
다음으로 문서나 언어셋의 번역을 도울 수 있겠습니다. klutzy 님과 dalinaum 님이 주축이 되어, 국내의 개발자들이 동참, 컨트리뷰션을 통해 안드로이드 디자인 가이드 페이지를 번역 하기도 했죠(http://klutzy.github.com/android-design-ko/).
그리고, 새로운 기능이나 버그의 수정을 하고, 해당 수정 내역을 메인 소스트리에 기여하는 방법이 있죠.
모두 다 중요하고 소중한 기여 방법입니다만, 오늘은 세번째, 소스 코드 기여에 대해서 이야기 해 보려 합니다.





contributor’s life cycle


컨트리뷰터의 라이프 사이클을 통해 간단히 컨트리뷰션 프로세스를 짚어보면 다음과 같습니다.


0. gerrit 리뷰 서버에 접근 가능하도록 설정하고, 소스 코드를 다운로드 받는 준비 과정을 거칩니다.
1. 어떤 컨트리뷰션을 할 것인지 목표를 설정합니다.
2. 해당 목표를 위해 소스 코드에 변경 사항을 만듭니다.
3. 만든 변경 사항을 gerrit 리뷰서버로 업로드 합니다.
4. gerrit 리뷰 서버에서 리뷰를 받습니다.
4-1. 리뷰 결과, 특정 부분에 문제가 있거나, 수정 방향을 변경할 것을 요청받게 되면, 해당 요청에 맞게 변경사항을 수정하고 다시 업로드, 리뷰를 받습니다.
4-2. 리뷰 결과, 성공적으로 변경 사항이 머지 되었거나, 해당 변경 사항을 사용하지 않기로 결정된다면, 다음 컨트리뷰션을 위해 새로운 컨트리뷰션 목표를 찾습니다.





Sign on gerrit


패치를 리뷰 서버에 보내려면 gerrit 에 접근 가능하도록 설정을 해야 합니다.
https://android-review.googlesource.com 으로 접속하시면 되구요. 구글 계정으로 곧바로 로그인 할 수 있습니다.
여기서 컨트리뷰터 약정에 사인하고, 리뷰서버에 git로 push하기 위한 패스워드를 얻으면 됩니다.
먼저, Settings > agreements > New contributor agreement 메뉴로 들어갑니다.


개인 컨트리뷰터 또는 기업 컨트리뷰터의 약정을 볼 수 있습니다.
기업 컨트리뷰터 약정의 경우 약정서를 프린트, 직접 사인하고 이메일이나 팩스, 또는 우편으로 구글에 보낸 후 승인을 기다려야 합니다.
개인 컨트리뷰터는 온라인에서 곧바로 사인 할 수 있습니다.
컨트리뷰터 약정은 한번 작성하면 취소할 수 있는 방법이 현재까지는 알려진 게 없기 때문에(이호민 님께서 알려 주셨습니다), 실수로 기업 컨트리뷰터 약정을 선택하거나 하진 맙시다.


약정 내용을 잘 읽으신 후, 맨 아래의 입력란에 I AGREE 라고 직접 입력하시고, Submit Agreement 버튼만 누르면 약정 서약이 완료됩니다.
우편 주소, 국가, 전화번호, 팩스 번호 등도 물어보지만, 입력하지 않아도 됩니다.



(2013.10.15 업데이트)
이제 Contact Information으로 들어가 정보를 입력해 줍니다.
이 글을 처음 작성하던 때(2012년 초)에는 이걸 굳이 넣지 않아도 문제가 없었는데, 최근엔 꼭 입력되어야 하도록 바뀐 모양입니다. 입력하지 않으면 다음과 같은 에러 메세지가 난다고 하네요. 한정일 님께서 알려주셨습니다(감사해요!).
fatal: remote error: Individual contributor agreement requires current contact information.
팩스 번호까지 있는데, 딱히 직접 전화해서 본인확인하거나 하지는 않으니 적당한 번호로 입력해 주시면 될 것 같습니다.

다음으로, Settings > HTTP password > Obtain password > 액세스 허용을 해주시면, 리뷰서버의 git 리파지토리에 접근하기 위한 로그인 name과 패스워드를 얻을 수 있습니다. 매번 패스워드를 입력하지 않아도 되도록, ~/.netrc 파일에 해당 내용들을 추가해 두는 게 편합니다.



Download source


이제, 전체 소스를 다운로드 받아야겠습니다.
물론 빌드 머신과 적합한 도구들이 있어야겠죠. 우분투 10.04를 기본적으로 추천 합니다.
이건 정말 쉽습니다. 먼저, repo를 다운받고, 사용할 수 있도록 설정해 줍니다.
$ mkdir ~/bin
$ PATH=~/bin:$PATH
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
$ chmod a+x ~/bin/repo

소스 코드를 다운받을 디렉토리를 만들고(WORKING_DIRECTORY 는 원하시는 디렉토리 이름으로 대체하시면 됩니다. 예를 들면 my_droid_source 처럼요),
$ mkdir WORKING_DIRECTORY
$ cd WORKING_DIRECTORY

마지막으로, 다음의 두개 명령만 입력하시면 만사 해결입니다.
$ repo init -u https://android.googlesource.com/platform/manifest
$ repo sync





What repo internally do


repo init과 repo sync 가 어떤 마법을 부리는 건지 자세히 알아 보겠습니다.
단순히 컨트리뷰션 하는 것에만 관심이 있으시다면 이 부분은 건너 뛰셔도 됩니다. 몰라도 컨트리뷰션 하는데는 아무 문제 없는 내용입니다.





repo init


repo init는 git init가 .git 디렉토리를 만들고 리파지토리 기본 설정을 하듯이, repo init는 .repo 디렉토리를 만들어 프로젝트의 기본 설정을 합니다.
먼저, google code의 repo 공식 리파지토리로부터 최신 버전의 repo를 얻어와 .repo/repo/ 디렉토리 아래에 저장합니다.
다음으로, -u 옵션을 통해 받은 url 로부터 리포 매니페스트를 얻어 옵니다. 리포 매니페스트는 현재 구성할 프로젝트가 어떠한 개별적, 독립적 서브 프로젝트들로 구성되며, 각 서브 프로젝트들이 소스 코드를 다운로드 받아올 리모트 리파지토리의 주소, 변경사항을 업로드 할 리뷰 서버의 주소 등의 설정과, 각 서브 프로젝트들을 어떤 모습으로 작업 디렉토리에 위치시켜 작업 디렉토리의 모습을 구성할 것인가를 결정합니다.
위에서 우리가 -u 옵션을 통해 넘긴 주소, https://android.googlesource.com/platform/manifest 는 AOSP의 플랫폼 리포 매니페스트의 리파지토리 주소입니다. git clone 을 통해서도 해당 주소로부터 매니페스트를 얻어와 확인 할 수 있습니다.
다운로드 받은 매니페스트는 repo/manifests/ 디렉토리 아래 저장되며, 별다른 설정을 하지 않는다면, 이 중 default.xml 을 매니페스트로 사용하는데, 이를 위해 .repo/manifest.xml 이라는 심볼릭 링크를 만들어 .repo/manifests/default.xml을 가리키게 합니다.




repo manifest


manifest 파일의 내용을 간단히 살펴 보겠습니다.

remote, default, project 눈에 띕니다.

remote는 여러개의 프로젝트들이 사용할 원격 git 리파지토리 설정입니다.
이 값이 각 프로젝트의 git remote 설정이 됩니다.
name 속성은 해당 remote 엘리먼트를 가리키기 위해 사용되구요.
fetch 속성은 각 프로젝트들이 소스 코드를 다운로드 할 원격 리파지토리 URL의 prefix 입니다. 여기에 각 프로젝트의 name 속성값이 뒤에 붙어서 최종 fetch URL이 됩니다.
예를 들어, 이 경우,  fetch 속성값은 상대경로로 한단계 상위 경로를 의미하는 .. 이므로, https://android.googlesource.com/platform/ 에서 한단계 상위 경로인 https://android.googlesource.com/ 이 되며, 그림에 보이는 build 프로젝트가 소스코드를 다운로드 할 원격 리파지토리의 주소는 그 뒤에 build 프로젝트의 name 값이 붙어서 https://android.googlesource.com/platform/build 가 됩니다.

review 속성은 gerrit 서버의 주소입니다. 추후, 모든 upload는 여기로 업로드 됩니다.

default는 별도로 프로젝트별로 설정되어 있지 않은 경우 기본값으로 사용할 remote와 revision, 즉 브랜치 값을 제공합니다.
이 경우, aosp remote 설정을 따를 것이며, master 브랜치를 사용할 겁니다.

다음으로 project 엘리먼트는 직관적이죠? name 은 프로젝트의 이름이자 리모트에서 설정한 fetch 에 이어져서 fetch 할 프로젝트 리파지토리 주소가 되구요. path 는 작업 디렉토리 내에서 위치할 경로를 의미합니다.
copyfile은 말 그대로 작업디렉토리 구성 시에 복사할 파일을 의미해서, 이 경우는 작업디렉토리에서 make만 입력하면 곧바로 빌드가 돌아갈 수 있도록 준비하고 있죠.





repo sync


다음으로 repo sync 인데요.
repo sync를 하게 되면, 위에서 설명한대로 매니페스트를 읽어서 각 프로젝트의 소스코드를 다운로드 하고, 각 프로젝트의 git config 설정을 합니다.
예를 들어 framework/base 프로젝트의 config 파일을 보면, remote 로 aosp가 추가되어 있고, url과 projectname, review 값이 매니페스트에서 지정한대로 저장되어 있는 것을 볼 수 있습니다.


한가지 재미있는 점은, .git 디렉토리는 .repo/projects/ 아래에 저장되고, 작업디렉토리에 있는 .git 디렉토리는 .repo/projects/ 아래 git 디렉토리들의 심볼릭 링크라는 점입니다.

git가 실제 데이터의 내용과 이력 등의 데이터를 .git 디렉토리 아래에 저장하고, 최신 버전의 소스 코드를 거기서 가져와 작업 디렉토리를 구성하듯, repo 또한 실제 각 프로젝트의 데이터 내용과 이력 등의 데이터를 가진 .git 디렉토리는 .repo/projects 디렉토리 아래에 저장해 두고, 최신 버전의 소스 코드를 거기서 가져와 작업 디렉토리를 구성하는 것입니다.
마지막으로, commit-msg와 pre-auto-gc git hook을 설치합니다.
git hook은 git 에서 commit, push 등의 주요 커맨드가 실행될 때마다, 실행 전, 또는 후에 자동으로 실행하는 실행파일(셸 스크립트 또는 파이썬, 루비, 펄 프로그램 등이 될 수 있습니다)로, .git/hooks/ 아래에 특정 이름으로 저장함으로써 설치됩니다.
이 중 commit-msg는 매 커밋 때마다 사용자가 커밋 메세지를 작성한 이후마다 실행되어 중요한 역할을 하는데, 잠시 후 gerrit에서 빠꾸먹었을 때의 요령에서 다시 설명하겠습니다.

이제, 컨트리뷰션을 위한 준비 단계는 모두 끝났습니다.





Find goal


준비는 모두 끝났으니, 컨트리뷰션할 꺼리를 찾아야겠죠. 시작이 반이라고, 저는 이게 가장 어려운 것 같습니다.





Bug fix


구글은 외부에서 안드로이드에 이슈 레포팅을 할 수 있도록 http://code.google.com/p/android/issues/list 를 제공합니다. 여기서 남들이 수정하지 않은 이슈를 찾는 것도 하나의 방법이구요.
스스로 발견한 버그가 있다면, 그걸 수정해 보는 것도 좋은 방법이겠습니다.





Implement new feature


새로운 기능을 제안하고, 구현해서 컨트리뷰션 하는 것도 좋은 생각인데, 그런 목적을 위해 android-contrib 라는 구글 그룹스가 있습니다.
갑자기 패치를 업로드 하기보다는, 여기서 미리 기능을 제안하고, 결론을 얻은 후에 새로운 기능을 구현하고 push 하는 게 좋습니다.
아무래도 AOSP는 명확한 버그 수정 이외의 패치에 대해서는 조금 신중하게 접근하는 분위기가 있어 해당 변경을 하지 않기를 원하는 방향으로 토론이 마무리 되는 경우도 많습니다.
안드로이드는 역사상 가장 폐쇄적인 오픈 소스니까요. ;)





Make changes


목표를 찾았다면, 목표를 이뤄야겠죠. 우리 개발자는 코드로 목표를 이루죠 ;)
진정한 프로그래머는 어떻게 소스 코드를 작성하는 가에 대해서는 많은 논쟁이 있죠. 다음의 링크는 그런 논쟁의 전형적 예입니다. "진정한 프로그래머"
각자 편한 방법을 이용해 소스 코드를 수정합니다. 이맥스가 되었든 vi가 되었든 이클립스가 되었든 염력이 되었든 우리의 취향은 존중받아야 하니까요.


지켜야 할 것은, topic 브랜치를 이용할 것. 그리고 코딩 컨벤션 두가지 뿐입니다.
토픽 브랜치는 브랜칭 비용이 굉장히 싼 git의 특성에 따라 상당히 유용하게 사용되는 개념으로, 매번의 변경사항마다 별도의 브랜치를 만들어서, 여러개의 이슈를 해결할 때 수정을 동시에 진행하기 쉽게 해주고, 머지해야 하는 remote 리파지토리의 매니저가 손쉽게 머지/테스트를 할 수 있게 해주는 장점이 있죠.
repo는 upload 명령 시에, 새로 생성된 브랜치를 인식해서 업로드할 내용이 있는지 없는지를 판단합니다. 때문에, 토픽 브랜치를 만들지 않고 변경 사항을 만들면, repo upload 명령 시, repo는 업로드 할 변경 사항이 없다고 판단합니다. 때문에, 반드시 topic 브랜치를 생성 해야 합니다.
repo start <branch name> <project path>로 토픽 브랜치를 생성합니다. 예를 들어, frameworks/base 프로젝트 아래에 있는 WindowManagerService.java 에 있는 버그를 수정하려 한다면,
$ repo start fix_bug_on_windowmanagerservice frameworks/base
정도가 되겠죠.
repo start 명령은 단순히 해당 프로젝트에 인자로 넣어준 브랜치 이름의 브랜치를 하나 더 만듭니다.
지켜야 할 것 두번째는, 코딩 컨벤션인데요. 기본적으로 java 코딩 컨벤션을 따르고, 몇가지 더 있습니다만, 이 부분은 구글의 공식 문서를 보시는 게 좋겠습니다.
http://s.android.com/source/code-style.html





Upload patch


이제, 패치를 업로드 해야겠죠. 안드로이드는 git로 관리되는 프로젝트의 집합으로 이루어진 만큼, git를 사용하는 여타 프로젝트와 비슷합니다. 터미널을 열고 다음 명령만 입력해 주시면 됩니다.
$ cd </path/of/project/you/made/changes>
$ git add <edited files>
$ git commit -s
$ repo upload

</path/of/project/you/made/changes> 부분은 업로드할 변경사항을 가한 프로젝트의 경로로 변경해야 합니다. AOSP 소스트리를 /home/geek/aosp/ 아래에 저장했고, frameworks/base를 수정했다면, cd /home/geek/aosp/frameworks/base 가 되겠죠.
<edited files> 부분은 변경사항을 가한 파일들의 경로로 변경해야 합니다.

커맨드가 하는 일은, 수정한 파일을 staging 하고(git add), 로컬 리파지토리에 커밋합니다.(git commit -s)
마지막으로, repo upload 명령을 입력하면, repo가 현재 생성된 topic 브랜치를 검색하고, 수정된 내용이 있으면 해당 수정 내용을 보여주며 업로드 할 것인지 물어봅니다. yes 를 선택하면, 수정 내용이 리뷰 서버로 올라가고, 이후 리뷰 프로세스가 이루어질 웹페이지 주소를 표시해 줍니다.
결국, repo upload가 하는 일은
$ git push \

https://android-review.googlesource.com/p/<project name> \

HEAD:/refs/for/master
와 같습니다.




Get review


upload를 성공한 경우 repo가 던져주는, 리뷰 프로세스가 이루어질 웹페이지 주소를 웹브라우저를 이용해 로그인 하면 다음과 같은 화면을 볼 수 있습니다.


이제 리뷰를 통과해야겠죠. gerrit에서의 리뷰는 verification과 approval 을 받으면 패스되어 머지됩니다.
verification 은 AOSP와 구글 내부 소스 트리에 conflict 없이 잘 머지 되며, 빌드 또한 정상적으로 된다는 의미입니다.
approval은, 변경사항이 합리적이고 훌륭하므로, 머지 되어도 좋다는 의미입니다.
각각 자격을 가진 verifier 와 approver가 줄 수 있구요. 어떤 자격이 없더라도, 누구라도, 심지어 패치를 올린 본인도 review는 할 수 있고, 이에 따라 +1, -1을 줄 수 있습니다. 하지만, 암만 +1, -1 줘봤자 verification, approval이 안되면 소용 없습니다.
때문에, 올바른 verifier, approver를 선정하는게 중요한데요. Add reviewer 버튼을 이용해 이들을 추가할 수 있습니다.
gerrit의 merge에 성공한 패치를 참고해서, 또는 git log를 이용해 적합한 리뷰어를 추가하는 게 가장 좋겠구요.
가장 유명한 verifier는 AOSP를 총괄 관리하는 JBQ(https://plus.google.com/u/0/112218872649456413744/posts)입니다.





Upload a replacement patch


한방에 리뷰 패스하고 머지되면 모양새도 나고 통쾌하고 좋겠지만, 리뷰 결과가 항상 좋을 수야 없죠. 그런 경우는 흔치 않습니다.
코딩 컨벤션 또는 수정 방법에 대한 견해 차이 등으로 이런 부분은 수정하는게 좋겠다는 리뷰를 많이 받게 됩니다. 이런 경우, 해당 리뷰 결과를 참고해서 패치를 다시 만들어 올려야 하는데요. 기존 패치를 취소하고 gerrit에 새로이 올리는 건 히스토리 관리에 있어 좋을 게 없겠죠. 기존 올린 패치에 change set 2, 3, 4... 와 같이 연결해서 변경 패치를 올릴 수 있습니다.
이를 위해선, 기존과 동일하게 git add; git commit; repo upload; 명령을 이용하면 됩니다. 다만, 소스 코드를 수정한 이후에, 이번에는 git commit --amend 로 입력해야 합니다.





How gerrit know replacement patch


gerrit은 어떻게 특정 패치가 다른 특정 패치의 후속 변경 패치임을 알까요? git에 대한 이해가 없으시거나, 원치 않으신다면 이 부분은 건너뛰셔도 좋습니다. 이 부분은 몰라도 컨트리뷰션 하는데 전혀 문제 없습니다.
답은 Change-ID 입니다.
gerrit 서버에 올라간 패치들의 커밋 메세지를 보면 모두 공통적으로 Change-ID 라는 값을 가지고 있는 걸 확인 할 수 있고, Change set 2, 3, 4 와 같이 연결되는 변경패치들은 모두 같은 Change-ID 값을 가지는 걸 알 수 있습니다. git commit --amend 시, 커밋 메세지에 이미 Change-ID 항목이 적혀 있음을 볼 수 있습니다.
예를 들어, frameworks/base 아래에서 $ git log --no-merges 로 로그를 보면 다음과 같이 모두 Change-ID 값을 갖습니다.



gerrit 서버는 이 값을 가지고 어떤 특정 패치가 다른 특정 패치의 후속 변경 패치임을 알아챕니다. 이 값은 누가, 언제 만들어 커밋 메세지에 집어 넣은 걸까요?





commit-msg git hook


답은, repo sync의 실제 동작에서 이야기한, commit-msg git hook 입니다.
이건 repo가 각 프로젝트에 설치를 해놓은 git hook 으로, 셸 스크립트로 작성 되어 있습니다.
commit 을 할 때마다 실행되며, 최초 커밋 시에 Change-ID를 생성해 두고, 이후 commit-amend 시에는 최초 생성된 Change-ID를 계속해서 사용합니다.
Chage-ID는 tree, parent, author, commiter 정보와 최초 사용자가 입력한 커밋 메세지로 구성한 텍스트를 SHA-1으로 hashing 한 값에, 앞에 I를 붙인 문자열입니다.


즉, 아래와 같은 문자열을 SHA-1 으로 hashing 한 값에, 앞에 I를 붙인 문자열이 됩니다.
tree 8bc51ccbccd3f90c131b1acd747bf869c2d569d4
parent f26d9d5884339dc450831dd453bbb77b4e77d9e5
author SeongJae Park <sj38.park@gmail.com> 1336637344 +0900
committer SeongJae Park <sj38.park@gmail.com> 1336637344 +0900

Test for devFest
보통 한번에 머지되는 경우도 있긴 하지만, 대부분 여러 차례의 체인지셋을 올리게 되는 것 같구요. 다양한 리뷰 의견에서 많이 배우기도 하는 것 같습니다.





Summary

앞서 봤던 전체 프로세스 그림을 실제 사용되는 커맨드와 함께 정리해 봤습니다.
한번 더 그림을 보면서 전체 프로세스를 재정리 해 봅시다.





Now, you can change the world


이제 여러분은 AOSP에 컨트리뷰션을 할 수 있습니다.
이건 정말 멋진 겁니다.("오랜 프로그래머로부터의 조언" 에서 인용 했습니다)



gource 라는 프로그램을 이용해, 안드로이드가 개발 시작되었을 때부터 아이스크림 샌드위치가 릴리즈 될 때까지 있었던 변경 내역을 영상으로 만든 것입니다. 끝까지 볼 필요는 없지만, 한번 보시길 권합니다.

댓글 없음:

댓글 쓰기