'프로그래밍/Git'에 해당되는 글 2건

  1. 2015.07.13 Git & Gerrit & Jenkins 설치하기
  2. 2014.01.19 Git 에 대한 설명

1. git 설치하기

# apt-get install git-core git-review

# adduser gerrit

# mkdir -p /git_repo

# chown -R gerrit.gerrit /git_repo

# sudo mkdir -p /git_review

chown -R gerrit.gerrit /git_review

# git init --bare /git_repo/paas.git


2. gerrit 다운로드

https://gerrit-releases.storage.googleapis.com/index.html


3. mysql 설치

# mysql -uroot -p

mysql> CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'secret';

mysql> CREATE DATABASE reviewdb;

mysql> ALTER DATABASE reviewdb charset=utf8;

mysql> GRANT ALL ON reviewdb.* TO 'gerrit'@'localhost';

mysql> FLUSH PRIVILEGES;



4. apache2 설치

$ sudo apt-get install apache2 apache2-utils libapache2-mod-proxy-html libxml2-dev

$ sudo a2enmod proxy_http

$ sudo a2enmod proxy

$ sudo service apache2 restart


# sudo vi /etc/apache2/sites-available/gerrit.conf

<VirtualHost *:8080>

  ServerName localhost

  ProxyRequests Off

  ProxyVia Off

  ProxyPreserveHost On


  <Proxy *>

    Order deny,allow

    Allow from all

  </Proxy>


  <Location /login/>

    AuthType Basic

    AuthName "Gerrit Code Review"

    Require valid-user

    AuthUserFile /git_review/etc/passwords

  </Location>


  AllowEncodedSlashes On

  ProxyPass / http://127.0.0.1:8081/

  ProxyPassReverse / http://127.0.0.1:8081/                #외부 SSO 검증에 기반한 HTTP 인증

#  RequestHeader set REMOTE-USER %{REMOTE_USER} #외부 SSO 검증에 기반한 HTTP 인증

</VirtualHost>


$ cd /etc/apache2/sites-available

$ sudo a2ensite gerrit.conf

$ sudo vi /etc/apache2/ports.conf

Listen 8080


$ sudo service apache2 restart




5. gerrit site 설치

# apt-get install openjdk-7-jdk


# oracle java 를 설치하는 방법

# add-apt-repository ppa:webupd8team/java

# apt-get udpate

# apt-get install oracle-java7-installer



# su - gerrit

$ cd /git_review

$ cp /home/stack/Downloads/gerrit-2.11.3.war .

$ java -jar gerrit-2.11.3.war init -d /git_review

 *** Git Repositories

*** 


Location of Git repositories   [git]: /git_repo


*** SQL Database

*** 


Database server type           [h2]: mysql


Gerrit Code Review is not shipped with MySQL Connector/J 5.1.21

**  This library is required for your configuration. **

Download and install it now [Y/n]?

Downloading http://repo2.maven.org/maven2/mysql/mysql-connector-java/5.1.21/mysql-connector-java-5.1.21.jar ... OK

Checksum mysql-connector-java-5.1.21.jar OK

Server hostname                [localhost]: 

Server port                    [(mysql default)]: 

Database name                  [reviewdb]: 

Database username              [gerrit]:

gerrit2's password            : secret


*** Index

*** 


Type                           [LUCENE/?]: 


The index must be rebuilt before starting Gerrit:

  java -jar gerrit.war reindex -d site_path


*** User Authentication

*** 


Authentication method          [OPENID/?]: http

# Get username from custom HTTP header [y/N]? y                    # 외부 SSO HTTP 인증시

# Username HTTP Header [SM_USER]: REMOTE_USER_RETURN    # 외부 SSO HTTP 인증시

SSO logout URL  : http://aa:aa@192.168.75.141:8080/


*** Review Labels

*** 


Install Verified label         [y/N]? 


*** Email Delivery

*** 


SMTP server hostname       [localhost]: smtp.gmail.com

SMTP server port               [(default)]: 465

SMTP encryption                [NONE/?]: SSL

SMTP username                 [gerrit]: skanddh@gmail.com


*** Container Process

*** 


Run as                         [gerrit]: 

Java runtime                   [/usr/local/jdk1.8.0_31/jre]: 

Copy gerrit-2.11.3.war to /git_review/bin/gerrit.war [Y/n]? 

Copying gerrit-2.11.3.war to /git_review/bin/gerrit.war


*** SSH Daemon

*** 


Listen on address              [*]: 

Listen on port                 [29418]: 


Gerrit Code Review is not shipped with Bouncy Castle Crypto SSL v151

  If available, Gerrit can take advantage of features

  in the library, but will also function without it.

Download and install it now [Y/n]? N


*** HTTP Daemon

*** 


Behind reverse proxy           [y/N]? y

Proxy uses SSL (https://)      [y/N]? 

Subdirectory on proxy server   [/]: 

Listen on address              [*]: 127.0.0.1        # reverse 이기 때문에

Listen on port                 [8081]: 

Canonical URL                  [http://127.0.0.1/]:


java -jar bin/gerrit.war reindex -d /git_review


htpasswd -c /git_review/etc/passwords skanddh

# service apache2 restart



6. start/stop Daemon

$ /git_review/bin/gerrit.sh restart

$ /git_review/bin/gerrit.sh start

$ /git_review/bin/gerrit.sh stop


$ sudo ln -snf /git_review/bin/gerrit.sh /etc/init.d/gerrit.sh

$ sudo ln -snf /etc/init.d/gerrit.sh /etc/rc3.d/S90gerrit



[ HTTPS 활성화 ]

$ vi gerrit.conf

[httpd]

         listenUrl = proxy-https://127.0.0.1:8081/


$ vi /etc/httpd/conf/httpd.conf

LoadModule ssl_module modules/mod_ssl.so

LoadModule mod_proxy modules/mod_proxy.so

<VirtualHost _default_:443>

SSLEngine on

SSLProtocol all -SSLv2

SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW

SSLCertificateFile /etc/pki/tls/certs/server.crt

SSLCertifacteKeyFile /etc/pki/tls/private/server.key

SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt

ProxyPass / http://127.0.0.1:8081/

ProxyPassReverse / http://127.0.0.1:8081/

</VirtualHost>


인증서 생성

$ sudo mkdir -p /etc/pki/tls/private

$ sudo mkdir -p /etc/pki/tls/certs

$ sudo openssl req -x509 -days 3650 \

-nodes -newkey rsa:2048 \

-keyout /etc/pki/tls/private/server.key -keyform pem \

-out /etc/pki/tls/certs/server.crt -outform pem



-----

Country Name (2 letter code) [AU]:KO

State or Province Name (full name) [Some-State]:Seoul

Locality Name (eg, city) []:Seoul

Organization Name (eg, company) [Internet Widgits Pty Ltd]:MyCompany

Organizational Unit Name (eg, section) []:

Common Name (e.g. server FQDN or YOUR name) []:myhost.mycompany.com

Email Address []:admin@myhost.mycompany.com

$ cd /etc/pki/tls/certs

sudo cp server.crt server-chain.crt



user.email 과 user.name 등록

$ git config user.name "Seungkyu Ahn"

$ git config user.email "skanddh@gmail.com"


password 등록

git config credential.helper cache                             # default 15분 저장

$ git config credential.helper 'cache --timeout=3600'      # 1시간 저장


커밋 메세지 hook 설치

curl -Lo .git/hooks/commit-msg http://localhost:8080/tools/hooks/commit-msg

$ chmod +x .git/hooks/commit-msg


review (gerrit remote url 등록)

git remote add gerrit http://localhost:8080/hello-project


# 서버 프로젝트에 미리 등록해서 clone 시 다운받을 수 있도록 함

$ vi .gitreview


[gerrit]

host=localhost

port=8080

project=hello-project

defaultbranch=master


$ git checkout -b bug/1

수정1

$ git add

$ git commit

$ git review

수정2

$ git add

$ git commit --amend

$ git review



review (직접하는 방법)

$ git checkout -b bug/1

수정1

$ git add

$ git commit

git push origin HEAD:refs/for/master%topic=bug/1



[ Jenkins 설치 ]

jenkins tomcat 의 webapps 디렉토리에 다운로드

# adduser jenkins

# chown -R jenkins.jenkins apache-tomcat-8.0.26

# su - jenkins


http://jenkins-ci.org/

$ cd /usr/local/apache-tomcat-8.0.26/webapps

$ wget http://updates.jenkins-ci.org/download/war/1.580.1/jenkins.war

wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war                       # 최신 버전


tomcat 포트 및 URIEndoing 변경

$ vi /usr/local/apache-tomcat-8.0.26/conf/server.xml


<Connector port="7070" protocol="HTTP/1.1"

           connectionTimeout="20000"

           redirectPort="8443"

           URIEncoding="UTF-8" />


/usr/local/apache-tomcat-8.0.26/bin/startup.sh


jenkins 접속

http://192.168.75.141:7070/jenkins/


웹화면에서 보안 설정

(좌측메뉴) Jenkins 관리

Configure Global Security

  - Enable security

  - Security Realm : Jenkins’ own user database

  - Authorization : Matrix-based security

  - User/group to add: admin


저장 후 admin 계정으로 가입



[ Jenkins 연동 ]

1. 젠킨스 플러그인 설치

1. Jenkins Git Client plugin

2. Jenkins Git Plugin : 젠킨스와 깃을 연동

3. Jenkins Gerrit Trigger plugin : 게릿 변경시 패치 세트를 가져와 빌드하고 점수를 남김

4. Hudson Gerrit plugin : 깃 플러그인 설정을 가능


2. 게릿 트리거 플러그인

1. HTTP/S Canonical URL: 게릿의 변경 및 패치 세트를 가리키는 URL

2. SSH 접속 : 게릿에 연결하여 게릿으로부터의 이벤트를 감지


jenkins를 띄운 사용자로 ssh 키 생성 및 게릿에 젠킨스가 사용할 배치 사용자를 생성

jenkins 계정으로 jenkins 를 실행하면 아래 내부 사용자 생성이 필요없음

사용자가 다르면 게릿의 관리자 계정으로 create-account 명령을 실행해서 내부 사용자를 생성

$ skanddh 계정으로 로그인

ssh-keygen -t rsa

ssh -p 29418 skanddh@192.168.75.141


# skanddh 가 gerrit 의 관리자 계정이어야 하며 skanddh 계정으로 실행

$ sudo cat /home/jenkins/.ssh/id_rsa.pub | \

ssh -p 29418 skanddh@192.168.75.141 gerrit create-account \

--group "'Non-Interactive Users'" --full-name Jenkins \

--email jenkins@localhost.com \ --ssh-key - jenkins


All-Projects에 있는 Non-Interactive Users 그룹에 아래의 권한이 있는지 확인

1. Stream events 권한이 있으면 게릿의 변경 발생을 원격으로 감지

2. refs/* 에 Read 권한이 있으면 gerrit 저장소의 변경 사항을 읽고 clone 가능

3. refs/heads/* 에 대한 Label Code-Review(Verified) -1..+1 권한이 잇으면 변경에 대해 점수 부여 가능


게릿 트리거 플러그인 설정

jenkins url 접속

http://192.168.75.141:7070/jenkins/gerrit-trigger


1. URL 과 SSH 연결을 설정

    Name : Gerrit

    Hostname : 192.168.75.141

    Frontend URL : http://192.168.75.141:8080

    SSH Port : 29418

    Username : jenkins

    E-mail : jenkins@localhost.com

    SSH Keyfile : /home/jenkins/.ssh/id_rsa

    SSH Keyfile Password :


2. Test Connection 으로 테스트

3. 설정 페이지 맨 아래 Start/Stop 버튼으로 젠킨스 재시작


jenkins url 접속

http://192.168.75.141:7070/jenkins/gerrit_manual_trigger

Query 입력 란에 status:open 입력 -> Search 버튼 클릭

http://192.168.75.141:8080/#q/status:open,n,z 페이지에서 리뷰 대기 중인 변경 확인


게릿 트리거 설정

게릿 트리거 실행 조건을 SCM polling(또는 다른 트리거 정책)에서 Gerrit Event 로 변경

게릿 트리거 설정 부분에서 Advanced 버튼으로 게릿 조건을 지정


깃 플러그인 설정 (Hudson Gerrit plugin 을 설치해야 나옴)

깃 플러그인에서 게릿의 ref-spec 다음에 추가

Advanced 버튼 클릭 하여 깃 저장소 설정 변경

1. $GERRIT_REFSPEC 을 복제해야 할 깃 refspec 으로 지정

2. $GERRIT_PATCHSET_REVISION을 빌드할 깃 브랜치로 지정

3. 트리거 방식을 Gerrit trigger로 지정


아래 두가지 활성화

1. Wipe out workspace : 작업 공간 비우기

2. Use shallow clone : 얕은 복제 사용





Posted by Kubernetes Korea co-leader seungkyua@gmail.com

댓글을 달아 주세요

1. git 에서 제일 큰 파일사이즈 찾아내서 없애기

$ git count-objects -v    (size-pack 항목이 Packfile 의 크기임)

$ git verify-pack -v .git/objects/pack/pack-3f8c0...bb.idx | sort -k 3 -n | tail -3  (3번째 항목이 파일 크기임)

$ git rev-list --objects --all | grep 7a9eb2fb   (blob(파일)의 이름을 찾는다)

$ git log --pretty=oneline -- git.tbz2   (git.tbz2 파일의 커밋을 찾는다.)

$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch git.tbz2' 6df7640^..  (6df76 이후의 커밋을 모두 재작성 된다)

$ rm -Rf .git/refs/original   (filter-branch 실행의 상태를 삭제)

$ rm -Rf .git/logs/     (Reflog 삭제)

$ git gc


2. 데이터 복구

$ git log --pretty=oneline

$ git reset --hard 1a410e   (이전 커밋으로 복구)

$ git log -g   혹은   git reflog    (HEAD 가 바뀔 때 마다 저장된 기록을 확인)

$ git branch recover-branch ab1afef   (recover-branch를 만듬)

$ git log --pretty-oneline recover-branch

$ git fsck --full   (길잃은 객체를 찾아 줌,  히스토리 중 2번째 칼럼에서 commit 을 찾음)


0. Git 설정

- Windows Git 설치

   http://code.google.com/p/msysgit


- Git config 적용 순서

   1. $ git config

       .git/config

   2. $ git config --global

       ~git/.gitconfig

   3. $ git config --system

       /etc/gitconfig


- Git config 로 조회될 때 중복되어 있으면 나중에 나오는 값이 적용된다.

   $ git config --list


- config 값이 중복될 때 어떤 값이 적용되는지 볼 수 있음

   $ git config <Key이름>


- config 관련 help 보기

   $ git help config


1. 상태 변화

    ※ 신규파일이든 수정한 파일이든 모두 add 해야 한다.

       생성하거나 수정하고 나서 git add 명령으로 추가하지 않은 파일은 commit 하지 않는다.

- 신규 파일을 생성한 경우 : Untracked (Untracked files)

- 신규 파일을 add 한 경우 : Untracked -> Tracked and Staged (Changed to be committed)

   $ git add <file or directory>


- 기존 파일을 수정한 경우 : Tracked and Unstage (Changed but not updated)

- 기존 수정된 파일을 add 한 경우 : Tracked and Unstage -> Staged (Changed to be committed)

   $ git add <file or directory>


- 상태의 변화

   (Untracked : 신규)              (Tracked)

   (Tracked    : 수정)              (Tracked)

    Unstage               -->         Staged          -->         Committed         -->        Remote Repository

                           (git add)                  (git commit)                      (git push)

 

2. 상태 변경 내용 보기

- Staged 와 Unstage (워킹 디렉토리) 상태의 변경 내용 보기

   $ git diff


- Staged 와 Committed (저장소) 상태의 변경 내용 보기

   $ git diff --staged


3. Staged 상태 커밋하기

$ git commit


4. Staging Area 파일 삭제하기

- 워킹디렉토리 파일을 삭제하고 Staged 파일을 삭제한다.

   $ rm <file>

   $ git rm <file>


- Staged 와 워킹 디렉토리 파일 모두 삭제한다.

  $ git rm <file or directory>


- Staged 파일은 삭제하고 워킹 디렉토리 파일은 남겨둔다.

   $ git rm --staged(or cached) <file or directory>


5. 커밋 히스토리 조회하기

- log는 히스토리를 보여주며 -p는 diff 결과를 보여주고 -2는 최근 두 개의 결과만 보여준다

   $ git log -p -2


- since 나 until 을 사용할 수 있다.

   $ git log --since=2.weeks

   $ git log --since="2013-01-01"


- author 나 grep 을 사용할 수 있다.

   $ git log --author="stephen" --grep="local storage"


- 로그를 옵션으로 볼 수 있다.

   $ git log --pretty=oneline (short, full, fuller)


- 로그를 특정 포맷으로 볼 수 있다. (h:hash, an: author name, ad: author date, s: subject)

   $ git log --pretty=format:"%h %s" --graph


- 수정한 파일, 추가된 줄, 삭제된 줄만 표시

   $ git log --shortstat


- 수정된 파일의 목록과 파일을 추가한 것인지, 수정한 것인지, 삭제한 것인지 보여준다

   $ git log --name-status


- HEAD 가 가리켰던 커밋을 조회하기

   $ git reflog

   $ git log -g master


- master 에는 없지만 local-storage 에는 있는 커밋 조회

   $ git log master..local-storage


- 리모트 저장소 master 에는 없고 현재의 브랜치에만 있는 커밋

   $ git log origin/master..HEAD


- git log graph 로 보기

   $ vi ~/.gitconfig


[alias]

lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)\

%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)\

- %an%C(reset)%C(bold yellow)%d%C(reset)' --all

lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)\

%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)\

%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)\

- %an%C(reset)' --all

lg = !"git lg1"


6. 되돌리기

- 커밋 한 후 빠진 파일을 다시 포함시키거나, 커밋 메세지를 수정할 때, 커밋을 합치고 싶을 때 마지막 커밋으로 모두 덮어쓴다.

   $ git commit --amend


- Staged 상태 파일을 Unstage (워킹 디렉토리에만 수정된 상태)로 되돌리기

   $ git reset HEAD <file>


- Unstage (워킹 디렉토리에만 수정된 상태)를 Commit (저장소)와 동일한 초기 상태로 되돌리기

   $ git checkout -- <file>


- 커밋을 취소하기 

   $ git reset HEAD^            최종 커밋을 취소, 워킹 디렉토리를 보존

   $ git reset HEAD~2          마지막 2개의 커밋을 취소, 워킹 디렉토리를 보존

   $ git reset --hard HEAD~2 마지막 2개의 커밋을 취소, 워킹 디렉토리와 Staged 모두 취소

   $ git reset --hard ORIG_HEAD    머지한 것을 커밋했을 때 취소

   $ git revert HEAD        커밋을 push 했을 때 HEAD에 변경한 내용을 취소하는 커밋 추가 


7. 리모트 저장소

- 리모트 저장소 리스트 보기, Clone 으로 가져온 저장소의 이름은 origin 으로 자동으로 붙혀진다.

   $ git remote -v


- 리모트 저장소 추가하기

   $ git remote add <리모드 저장소 별명> <URL>


- 리모트 저장소의 브랜치 포인터를 로컬로 가져오기

   fetch 는 실제 데이터를 가져오는 것이 아니라 리모트 브랜치에 대한 모든 정보를 가져와 포인터가 생기는 것

   $ git fetch <리모트 저장소 별명>


- 리모트 저장소 Branch 의 데이터를 가져와서 자동으로 로컬에 머지하기

   $ git pull <리모트 저장소 별명> <리모트 브랜치 이름>:<로컬 브랜치 이름>


- 리모트 저장소에 브랜치 데이터를 올리기

   $ git push <리모트 저장소 별명> <로컬 브랜치 이름>:<리모트 저장소 브랜치 이름>

   $ git push


- 로컬의 local-storage 브랜치를 리모트 저장소 origin 에 storage 브랜치라는 이름으로 올리기

   $ git push origin local-storage:storage


- 리모트 저장소 상세보기

   push 할 때 와 pull 할 때 어떤 브랜치로 작동하는지 보여준다.

   $ git remote show <리모트 저장소 별명>


- 리모트 저장소 이름 바꾸기

   $ git remote rename <현재 이름> <바꿀 이름>


- 리모트 저장소 삭제하기

   $ git remote rm <리모트 저장소 별명>


8. 태그

- 태그 조회하기

   $ git tag


- 1.x 버전의 태그를 조회하기

   $ git tag -l '1.*'


- 태그 1.1 붙히기

   $ git tag 1.1


- 특정 commit 에 대해서 태그 붙히기

   $ git tag -a 1.1 <체크섬>


- 리모트 저장소에 태그 데이터 올리기

   $ git push <리모트 저장소 별명> <태그 이름>


- 리모트 저장소에 모든 태그를 올리기

   $ git push <리모트 저장소 별명> --tags


9. 브랜치

- HEAD 는 현재 작업하는 로컬 branch 를 가리키는 포인터


- 브랜치 생성하기

   $ git branch local-storage


- 생성한 브랜치로 이동하기

   $ git checkout local-storage


- 브랜치를 만들고 생성한 브랜치로 이동하기

   $ git checkout -b local-storage


- checkout 으로 브랜치를 바꾸면 워킹 디렉토리의 파일들을 추가, 삭제, 수정하여 이전 스냅샷으로 돌린다.


- 브랜치 삭제하기 (Merge 하지 않은 브랜치는 삭제되지 않음)

   $ git branch -d local-storage


- 브랜치 삭제하기 (Merge 하지 않은 브랜치도 강제로 삭제하기)

   $ git branch -D local-storage


- 브랜치 리스트 조회하기

   $ git branch


- 브랜치 리스트에 마지막 커밋 메세지까지 조회하기

   $ git branch -v


- 현재의 브랜치에 Merge 하지 않은 브랜치 보기

   $ git branch --no-merged


- 리모트 저장소의 브랜치를 로컬 브랜치로 Tracking 하여 생성하기 (둘 중 하나를 사용)

   $ git checkout -b local-storage origin/local-storage

   $ git checkout --track origin/local-storage


- 리모트 브랜치 삭제하기 (리모트의 local-storage 라는 브랜치 삭제)

   $ git push origin :local-storage


9. Merge 하기

- Merge 하기 위해서는 중심이 되는 브랜치로 이동 후 합친다. (여기서 master 에 local-storage를 합치기)

   $ git checkout master

   $ git merge local-storage


- 리모트 저장소의 브랜치를 로컬의 현재 브랜치에 합치기 (여기서는 master 에 리모트 local-storage를 합치기)

   $ git checkout master

   $ git merge origin/local-storage


- Merge 시 Conflict 발생하면 상태를 확인

   $ git status


- 리모트 저장소의 master 가 수정되었을 때 로컬 master 와 Merge 하기

   $ git fetch origin

   $ git merge origin/master


- 브랜치 (local-storage)를 만든 후 리모트 저장소의 master 가 변경되고, 브랜치에서 개발 한 후 리모트 master 와 브랜치 commit 차이를 보고 Merge 하기 ( Merge 는 브랜치든 master든 둘 중 아무거나 먼저한다.)

   $ git fetch origin

   $ git log --no-merges origin/master ^local-storage


   $ git checkout master

   $ git merge local-storage

   $ git merge origin/master 


10. Rebase 하기

      ※ 리모트 저장소에 Push 한 내용을 Rebase 하면 안된다.

- Rebase 하기 위해서는 개발한 브랜치로 이동 후 중심이 되는 브랜치로 rebase 한다.

  (여기서는 local-branch 에서 개발하는 중간에 master 에 최신 내용들을 local-branch 에 합친다)

   $ git checkout local-branch

   $ git rebase master


- Master 에서 브랜치 (local-storage)를 만들고, 또 여기서 추가로 브랜치 (storage)를 만들어 storage 브랜치를 먼저 rebase 하고 local-storage 를 rebase 한다.

   $ git rebase --onto master local-storage storage

   $ git checkout master

   $ git merge storage


   $ git rebase master local-storage

   $ git checkout master

   $ git merge local-storage








Posted by Kubernetes Korea co-leader seungkyua@gmail.com
TAG git

댓글을 달아 주세요