git 튜토리얼 메뉴얼 페이지

여기 공식 한글 문서가 있더군요. 여기 페이지보다 git 공식 한글 문서를 보시길 추천합니다.

개요

이 튜토리얼은 프로젝트를 임포트하고, 수정하고, 수정한 결과를 다른 개발자들과 공유하는 방법을 설명한다.

최신 버젼의 소스를 테스트하기 위해서 라든지 git으로 프로젝트를 패치해 오는 것에만 관심이 있는 경우, Git User's Manual의 첫 두 장을 읽어 보는 편이 좋을 것이다.

먼저, 명령어에 대한 문서를 보려면 man또는 git help를 사용한다. 예를 들어 git log --graph라는 명령의 도움말을 보려면:

  $man git-log

또는:

  $ git help log

라고 하면 된다.

git help를 사용할 경우에는 자신이 지정한 메뉴얼 뷰어로 보는 것이 가능하다. 자세한 내용은 get-help 메뉴얼을 참고한다.

git 으로 작업을 하기 전에 자신에 대한 정보(이름과 email)를 미리 설정에 두는 것이 좋다. 아래와 같이 하면 된다.

$git config --global user.name "여기에 이름을..."
$git config --global user.email you@yourdomain.example.com

새 프로젝트 임포트하기

처음으로 작성한 project.tar.gz라는 프로젝트 압축파일이 있다고 하자. 아래와 같이 git 버젼관리로 임포트해서 버젼 관리를 시작할 수 있다.

$tar xzf project.tar.gz
$cd project
$git init

압축을 풀고 그 디렉토로로 가서 git을 초기화하였다.
아래와 같이 결과가 출력될 것이다.

Initialized empty Git repository in .git/

(.git/ 디렉토리에 빈 Git 리파지토리가 초기화 되었습니다)
즉, 작업 디렉토리를 초기화 한 것이다. ".git"라는 디렉토리가 생성된 것을 볼 수 있을 것이다.
다음은 git-add를 사용해서 현재 디렉토리(.)의 모든 내용을 스넵샷으로 저장한다.

$git add .

현재의 스넵샷이  "index"라고 하는 임시 스테이지 영역에 저장되었다. 이제 git-commit을 이용해서 index의 내용을 리파지토리에 영구 저장할 수 있다.

$git commit

이를 실행하면 커밋 메세지 입력을 요구할 것이다. 이렇게 프로젝트의 첫 벗째 버젼을 git에 저장하였다.

변경을 가하기

몇몇 파일을 수정하고, index에 저장하려면 아래와 같이 한다.

$git add file1 file2 file3

이제 커밋을 할 수 있는 상태가 되었다. 이 때 git-diff명령에 --cached 옵션을 사용해서 commit할 내용의 미리보기를 해 보자.
 
$git diff -cached

(--cached 옵션을 지정하지 않으면, 현재 디렉토리와 index사이의 차이점이 표시된다.) 

git-status 명령을 사용해서 현재상태를 확인할 수 있다.

$git status
# On branch master
# Changes to be committed:
#   (user "get reset HEAD <file>..." to unstage)
#
#        modified:   file1
#        modified:   file2
#        modified:   file3
#

더 수정할 것이 있다면 수정을 하고, 그 내용을 index에 적용한 다음에 마지막으로 커밋을 한다.

$git commit

마찬가지로 커밋에 대한 간단한 설명을 입력하는 프롬프트가 표시될 것이다.
git-add 를 생략할 수 있는 방법도 있다.

$git commit -a 

를 실행하면 수정된 파일들을 자동으로 인식해서 index에 넣고 커밋까지 한번에 해준다. 단 새로 생성된 파일은 자동으로 add되지 않는다.

커밋 메세지에 대해서: 필수로 입력해야 하는 것은 아니지만, 첫 번째 줄에 수정 내용에 대한 전체적인 요약을 50문자 이내로 기술하고 한 줄을 띄우고 상세한 내용을 기술하는 것이 좋다. 예를 들어, 커밋의 내용을 email로 보내 주는 툴들은 첫 번째 라인을 제목으로 사용하고 나머지를 본문으로 사용한다.

Git은 파일자체를 트렉킹하는 것이 아니고 내용을 트렉킹한다. 

많은 버젼관리 시스템들은 새로운 파일을 버젼관리에 추가하기 위해 add 명령을 사용한다. 반면에 git의 add명령은 더 심플하면서 좀 더 강한 기능을 가진다. git-add는 새로운 파일의 추가에도 사용하지만 이미 있는 파일들중에서 수정된 파일의 내용을 index에 저장할 때도 사용된다 (영어로 스냅샷을 스테이징한다라고 표현). commit을 하면 이 index에 저장된 스냅샷이 commit되게 된다.

프로젝트 히스토리 보기

변경 내용의 히스토리를 열람하려면 

$git log

명령을 사용한다. 모든 스텝별 변경사항을 보려면 

$git log -p

를 실행한다.

때로는 요약한 내용으로 보는 것이 편할 때가 있다. 이럴 때 아래와 같이 실행한다.

$git log --stat --summary

브랜치 관리하기

하나의 git 리파지포리에 복수 개의 브랜치를 유지할 수 있다. 새로운 브랜치 "experimental"을 생성하려면 

$git branch experimental

이라고 실행한다. 이제 

$git branch 

라고 실행하면 아래와 같이 브란치의 리스트를 표시할 것이다.

  experimental
* master

"experimental" 브랜치가 방금 생성한 브랜치이다. "master" 브랜치는 처음에 자동으로 생성되는 기본 브란치이다. * 마크는 현재 사용중인 브랜치를 가리킨다.

$git checkout experiment

라고 실행해서 experimental 브랜치로 스위치할 수 있다. 파일을 수정해서 커밋을 한 다음 master 브랜치로 되돌아가 보자.

(파일 수정)
$git commit -a
$git checkout master

방금 수정했던 내용들이 다 사라졌을 것이다. 왜냐하면 experimental 브랜치에 수정을 커밋한 다음에 master 브랜치로 스위치 해서 돌아 왔기 때문이다.
master에도 수정을 가해보자.

(파일 수정)
$git commit -a

이제 두개의 브랜치는 각각 서로 다른 수정분들을 가지고 있다. experimental에 가했던 수정을 master로 병합하려면 아래와 같이 실행한다.

$git merge experimental

각 수정본들이 충돌하지 않았다면 이제 커밋을 할 수 있는 상태가 된다(병합 결과를 master에 커밋할 수 있는 상태). 만약 충돌하는 부분이 있을 경우, 해당 파일내에 충돌내용을 표기하는 마크가 남을 것이다.

$git diff

명령으로 그 마크를 확인해 볼 수 있다. 충돌한 부분을 해결했다면 아래와 같이 

$git commit -a

실행해서 병합 + 충돌 해결분을 커밋해서 병합을 완료한다. 마지막으로 

$gitk

를 실행하면 히스토리를 멋있는 그래픽으로 표시할 것이다.
이제 experimental은 삭제해도 된다.

$git branch -d experimental

아직 병합이 완료가 되지 않은 경우에는 삭제할 수 없다. 만약 강제로 삭제하고 싶다면 아래의 예와 같이 -D 옵션을 사용한다.

$git branch -D crazy-idea

브랜치는 부하도 적고 사용하기도 쉽기 때문에 무언가를 실험해 보고 싶은 경우 좋은 방법이 된다.

git으로 협업하기

Alice가 /home/alice/project 에 git 리파지토리가 있는 상태로 새로운 프로젝트를 시작했고, 그리고 같은 머신에 홈디렉토리를 가지는 Bob이 도움을 주고 싶어한다고 하자.

Bob은 

bob$ git clone /home/alice/project myrepo

라는 명령으로 myrepo라는 새로운 디렉토리에 Alice의 리파지토리를 복제해 온다. clone은 원래의 프로젝트의 히스토리까지 모두 자신의 것으로 복제해 온다.
Bob은 이제 파일들을 수정하고 커밋한다.

(파일 수정)
bob$git commit -a
(필요에 따라서 반복)

작업이 끝난 때에 Bob은 Allice에게 /home/bob/myrepo로 부터 수정내용을 가지고 가라고 한다. Alice는 아래와 같이 실행한다.

alice$cd /home/alice/project
alice$git pull /home/bob/myrepo master

이 명령은 Bob의 master 브랜치를 Alice의 현재 브랜치에 병합한다. 이 사이에 Alice가 내용을 수정해서 충돌이 발생했다면 충돌한 부분들을 직접 고쳐야할 것이다.

pull 명령은 2가지의 일을 한다. 원격의 브랜치로 부터 변경된 내용을 가지고 오기(fetch)와 현재 브랜치에 병합하기(merge)이다.

일반적으로 Alice는 pull을 하기 전에 현재의 변경내용을 커밋을 할 것이다. 만약 Bob의 작업내용이 Alice의 작업과 충돌한 경우 Alice는 현재의 작업디렉토리와 index를 사용해서 충돌을 해결하려고 할 것이지만 커밋하기 전의 변경내용들과 섞여버려서 방해가 될 것이기 때문이다. (실제로 git은 내용은 fetch해 오지만 merge는 실패하게 된다. 이런 경우 Alice는 어떤 방법으로라도 작업 디렉토리에 가했던 변경내용을 없애고 다시 pull을 실행해야 할 것이다.)  

Alice는 fetch 명령을 사용해서 실제로 Bob의 작업 내용을 merge하기 전에 확인해 볼 수 있다. 이를 위해  FETCH_HEAD라는 심볼을 사용한다.

alice$ git fetch /home/bob/myrepo master
alice$ git log -p HEAD..FETCH_HEAD

fetch 명령은 로컬에 아직 commit되지 않은 내용이 있더라도 안전하다. HEAD..FETCH_HEAD 범위 지정 표기는 FETCH_HEAD까지의 경위에서 HEAD까지의 경위을 뺀 범위를 의미한다. alice는 이미 HEAD까지의 경위를 알고 있고 Bob이 FETCH_HEAD의 상태가 되기까지의 경위(즉 아직 Alice의 시야에 없던 변화들)를 살펴보고 있는 것이다.

Alice는 Bob이 가한 변경을 시각적으로 확인해 보려면 아래와 같이 실행할 것이다.

alice$ gitk HEAD..FETCH_HEAD

git log에서 보았던 ..(점 둘) 범위 지정 표기를 사용하고 있다.
Alice는 Bob의 변경 내용 뿐만 아니라 자신의 변경 내용까지 한 번에 확인하고 싶을 때도 있을 것이다. 이럴 때 ..(점 둘)대신에 ...(점 셋)을 사용한다.

alice$ gitk HEAD...FETCH_HEAD

여기서 정의한 범위는 "각각의 경위를 모두 표시하되, 공통 경위는 제외하라"이다.
범위 지정 표기는 git log와 gitk 양쪽 모두에 사용할 수 있다는 점도 잊지 말자.

Bob이 변경한 내용을 살펴본 후 Alice는 급하게 pull하지 않아도 되겠다고 판단했다면 그냥 작업을 계속 진행한다. Bob이 변경한 내용에 지금 바로 필요한 내용이 있다면 현재 작업중인 내용을 잠시 피신(git-stash) 시키고 pull을 한 다음에 다시 작업하던 내용을 복귀(git-unstash) 시켜서 최신의 상태가 되도록 할 것이다.

작고 잘 짜여진 그룹에서 작업중이라면 특정 repository들과 반복해서 상호작용을 하게 될 것이다. 이런 repository를 간편하게 지정할 수 있도록 remote repository로 별명을 지을 수 있다.

alice$ git remote add bob /home/bob/myrepo

이제, Alice는 pull의 첫 부분인 git-fetch 명령을 아래와 같이 실행할 수 있다.

alice$git fetch bob

길게 적어서 fetch해 왔던 경우와는 다르게,  git-remote 명령으로 정했던 별명으로를 fetch를 실행한 경우, fetch해 온 내용은 remote 트랙킹 브랜치에 저장된다. 이 경우에는 bob/master 가 된다. 아래와 같이 실행하면

alice$git log -p master..bob/master

Alice의 master 브랜치로 부터 브랜치해 간 이래 변경된 내용을 보여주게 된다.
내용을 확인 했다면 Alice는 아래와 같은 명령으로 merge를 실행할 것이다.

alice$git merge bob/master

merge 명령 대신에 Alice의 remote 트랙킹 브랜치로 부터 pull해오는 방법도 있다.

alice$git pull . remotes/bob/master

git pull은 항상 현재의 브랜치로 merge하게 된다.
나중에, Bob은 아래의 명령으로 Alice의 변경내용을 pull해 온다.

bob$git pull

Bob은 굳이 Alice의 repository를 명시하지 않아도 됨에 주의한다. Bob이 Alice의 repository를 clone했을 때 git은 그녀의 repository 위치를 repository 설정에 저장해 두었기 때문이다.

bob$git config --get remote.origin.url
/home/alice/project

git-clone에 의해서 생성된 설정 내용은 git config -l 명령으로 확인 가능하다. git-config man 페이지를 참조하면 각 옵션들의 의미를 알아 볼 수 있다.

git은 당시의 Alice의 master 브랜치를 origin/master라는 이름으로 저장해 둔다.

bob$ git branch -r
origine/master

이후, Bob이 다른 호스트에서 작업하게 되었다면 ssh를 통해서 여전히 clone이나 pull이 가능하다.

bob$ git clone alice.org:/home/alice/project myrepo

이외에도 git용 프로토콜이나 rsync, http 등도 사용할 수 있다. 자세한 내용은 git-pull을 참고한다.
git은 CVS유사모드로도 사용할 수도 있다. 이 모드는 중앙 repository를 두고 여러 유저가 변경을 push한다. 자세한 내용은 git-push나 gitcvs-migration을 참고한다.

내력조회하기

git 내력이란 밀접한 commit들의 연속들을 의미한다. 앞에서 이미 git-log를 명령으로 commit들의 일람을 확인해 보았었다. 각 엔트리의 첫번째 라인은 커밋의 이름임을 주목하자.

$git log
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
Author: Junio C Hamano <junkio@cox.net>
Date:   Tue May 16 17:18:22 2006 -0700

merge-base: Clarify the comments on post processing

이 이름으로 git-show명령을 사용해서 해당 commit의 상세를 볼 수 있다.

$git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7

commit을 지정하는 방법은 이름말고도 더 있다. 이름의 앞 부분만 지정해도 (중복되지 않을 만큼 긴) 된다.

$git show c82a22c39c     #이름의 앞부분의 어느정도만 적어도 된다.
$git show HEAD                #현재 브랜치의 마지막
$git show experimental    #experimental 브랜치

모든 commit은 일반적으로 하나의 parent commit을 가진다. parent commit이란 프로젝트의 바로 앞의 상태를 가리킨다.

$git show HEAD^      #HEAD의 parent
$git show HEAD^^    #HEAD의 parent의 parent
$git show HEAD~4   #HEAD로 부터 4단계위의 parent

merge commit의 경우에는 하나 이상의 parent를 가질 수도 있다.

$git show HEAD^1     #HEAD의 첫번째 상위 parent(HEAD^와 동일)
$git show HEAD^2     #HEAD의 두번째 상위 parent

commit명에 별칭을 붙이는 것도 가능하다. 아래와 같이 실행하면 

$git tag v2.5 1b2e1d63ff

1b2e1d63ff 대신에 v2.5라는 이름을 사용할 수 있게 된다. 이 이름을 타인과도 공유 하려면 (예를 들어, 릴리즈 번호라던지) tag 객체를 생성해야 하고, sign할 필요가 있을 수도 있다. 자세한 내용은 git-tag를 참고한다.

commit명을 인수로 받는 모든 git 명령에 이 이름을 사용할 수 있다.

$git diff v2.5 HEAD            #현재 HEAD와 v2.5와 비교
$git branch stable v2.5     #v2.5로부터 새로운 브랜치 "stable"을 브랜치한다.
$git reset --hard HEAD^   #현재 브랜치와 작업 디렉토리를 HEAD^상태로 되돌린다.

마지막 명령문은 주의해야 한다. 작업디렉토리의 변경내용뿐만 아니라 HEAD^이후의 모든 commit들까지 리셋된다. 리셋된 commit들이 다른 브랜치에도 존재하지 않다면 완전히 소실되는 것이 된다. 또한 다른 개발자들이 사용하고 있는 공개 브랜치를 reset하지 않도록 한다. reset할 경우 merge한 내용들까지 history에서 사라지게 되는 결과를 낳게 된다. 만약 push했던 내용을 되돌리고 싶다면 git-reset 대신에 git-revert를 사용하도록 한다.

git-grep 명령은 프로젝트의 모든 버젼으로부터 문자열 검색을 할 때 사용한다.

$git grep "hello" v2.5

는 v2.5에서 hello라는 문자열을 검색한다.
commit명을 생략하면 현재 디렉토리의 모든 파일에서 검색을 실행할 것이다.

git grep "hello"

는 git이 현재 tracking하고 있는 파일들에서 검색을 실행한다.
git의 많은 명령들을 commit들을 인수로 받는데 commit들을 지정할 수 있는 여러 방법이 있다. 여기 git-log의 예제가 있다.

$git log v2.5..v2.6      #v2.5와 v2.6사이의 commit들
$git log v2.5..              #v2.5이래의 모든 commit들
$git log --since="2 weeks ago"    #2주 전 이래의 모든 commit들
$git log v2.5.. Makefile       #v2.5이래에 Makefile에 변경을 가했던 모든 commit들

git-log에 지정할 수 있는 commit의 범위의 시작과 끝은 상속관계에 관계없이 지정할 수도 있다. 예를 들면, "stable-release"와 "master"라는 브랜치가 예전에 분기되었다고 하면 

$git log stable..experimental 

는 experimental 브랜치에는 존재하고 stable 브랜치에는 존재하지 않는 commit들을 출력한다. 반면에

$git log experimental..stable

는 stable 브랜치에는 존재하고 experimental에는 존재하지 않는 commit들을 출력한다.

git-log 명령에는 약점도 존재한다. commit의 이력을 일람으로만 출력한다는 점이다. 만약 이력에 분기되었다가 다시 merge되었다면 git-log가 출력하는 commit들의 순서는 의미가 없어지게 된다.

복수의 공헌자들이 작업하는 대부분의 프로젝트들(리눅스 커널등)은 많은 merge를 가지고 있다. 이럴 경우 gitk는 이력을 시각화하는데 더 유용할 것이다. 예를 들면

$gitk --since="2 weeks age" drivers/

는 최근 2주간의 drivers 디렉토리에 가한 commit들을 열람할 수 있게 한다. (주: gitk의 폰트 크기를 조절하려면 컨트롤키를 누를 상태에서 +나 -키를 누른다)

마지막으로, 파일명을 인수로 받는 git 명령의 경우, 파일명 앞에 commit명을 지정할 수 있다.

$git diff v2.5:Makefile HEAD:Makefile.in

는 v2.5의 Makefile과 HEAD의 Makefile.in의 차이를 출력한다.
마찬가지로 git-show라면

$git show v2.5:Makefile

라는 식으로 사용가능하다.

다음 단계

이 튜토리얼만으로도 프로젝트의 분산 리비젼관리를 하는 것에는 충분할 것이다. git의 깊이와 힘을 완전히 이해하기 위해서는 2개의 간단한 기본적인 원리에 대해서 이해해야 한다.

- 객체 데이터베이스  : 프로젝트( 파일, 디렉토리, commit들)의 이력을 저장하는 시스템
- index 파일 : 디렉토리의 상태를 캐싱하는 파일. commit을 생성하거나 작업 디렉토리를 check out하거나 merge에 관련된 여러 트리를 저장하는 곳

이 튜토리얼의 Part 2에서는 객체 데이터베이스와 index 파일과 그외 여러가지들에 대해서 설명한다.
Part 2로 바로 진행하고 싶지 않다면 여기 흥미로운 읽을만한 것들이 있다.

git-format-patch(1), git-am(1): These convert series of git commits into emailed patches, and vice versa, useful for projects such as the Linux kernel which rely heavily on emailed patches.

git-bisect(1): When there is a regression in your project, one way to track down the bug is by searching through the history to find the exact commit that's to blame. Git bisect can help you perform a binary search for that commit. It is smart enough to perform a close-to-optimal search even in the case of complex non-linear history with lots of merged branches.

gitworkflows(7): Gives an overview of recommended workflows.


gitcvs-migration(7): Git for CVS users.


원문: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html
번역: 허 련호(airless@funit.net)
2009/04 생성, 2011/08 수정


Comments