분산 버전 관리 시스템(DVCS)인 Git이 나온지 벌써 몇 년이 지나 개발자들에게서 상당한 호응을 받고 있지만 난(또는 우리 회사) 본격적인 사용은 미루고 있었다. 얼마 전부터 제대로 해보자고 이것저것 해보고 있는데 이제 블로그에도 한번 정리삼아 올려보기로 한다. Git을 사용해보자 첫번째 주제로 Git 개념부터 꺼내기로 한다. 이 다음에는 기회되는 대로 Eclipse Git, Git 셸 명령, GitHub, Git 작업 패턴 등을 다뤄보기로 한다.

Git 학습
Git 학습

Git 개념 소개

Git은 자유 오픈 소스로서 분산 버전 관리 시스템이라고 하는 것이다. 많이 사용되는 버전 관리 시스템인 Subversion이나 CVS, ClearCase 등이 중앙의 저장소를 두고 개발자들이 붙어 작업하는 것과 달리 개발자 각자의 버전 관리에 방점을 두고 있으며 이에 따라 서버와의 잦은 업데이트/커밋 통신이 감소하고 개인이 임의로 소스를 브랜칭하여 시험하고 병합하는 게 간편하게 이루어지는 등의 장점이 생긴다. 반면에 새로운 개념을 다수 도입했기 때문에 기존 버전 관리에 익숙한 상태에서는 좀 공부를 해서 "패러다임 전환"을 해야만 Git을 제대로 사용할 수 있다.

다음은 Git 공식 사이트에서 밝히고 있는 장점이다.

Git 학습

Git은 다른 버전 관리 시스템보다 상대적으로 혼자 공부하기 어려우나 내가 권장하는 것은 Code School의 Git 따라하기 강의를 통해 학습하는 것이다. 이 강의를 통해 다음과 같이 git 개념을 학습할 수 있다.

위 강의를 따라하는 것에 만족 못한다면 직접 실제 환경에서 Git 명령을 쳐가며 해보려면 Git을 설치해야 하는데 Debian 계열(Ubuntu 등) 리눅스에서는 apt-get install git 명령으로, RedHat 계열(CentOS 등) 리눅스에서는 yum install git 명령으로 설치할 수 있고 윈도에서는 GitHub를 권장한다(설치 후 바탕 화면을 보면 GitShell이 있어서 똑같이 명령 행으로 실행할 수 있다). 다음에 GUI에 대해서도 다루겠지만 개념적으로는 이렇게도 공부해보길 권한다.

이제 "Git 따라하기"의 내용을 풀어보자.

Git 저장소 만들기

Git으로 버전 관리를 시작하려면 저장소를 만들어야 한다. 어디에? 로컬에!

즉, 처음부터 원격지가 필요하지 않으므로 부담없이 저장소를 만들었다가 필요 없으면 지울 수 있다. 다음과 같이 실행한다.

D:\git> mkdir myproject
D:\git> cd myproject
D:\git\myproject> git init
Initialized empty Git repository in D:/git/myproject/.git/
D:\git\myproject [master]> _

myproject라는 폴더를 만들고 그 안에 저장소를 만들었다. 저장소를 만든다는 것은 사실은 myproject 폴더 안에 .git 폴더를 만들고 초기화한다는 것이다. Linux 등에서는 ls -al로 윈도에서는 dir -force 명령으로 확인할 수 있다. 이 폴더에는 이제 .git 저장소도 있고 프로젝트 소스 파일도 들어가게 된다. 예를 들어 이 폴더에서 octocat.txt라는 파일을 작성했다고 하자. 그리고 나서 git status 명령을 실행하면 현재 상태를 알 수 있다.

D:\git\myproject [master]> touch octocat.txt
D:\git\myproject [master +1 ~0 -0 !]> git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add ..." to include in what will be committed)
#
# octocat.txt
nothing added to commit but untracked files present (use "git add" to track)

"octocat.txt"라는 빈 파일을 만들고 저장소 상태를 봤더니 버전 관리가 안되는(untracked) 파일로 나오고 있다.

변경 사항 커밋

이제 변경 사항을 버전 관리에 넣어보자(커밋). 커밋을 함으로써 우리는 언제든 과거 커밋 내용으로 돌아갈 수 있는 버전 관리의 세계로 들어가게 된다. 지금은 octocat.txt 파일을 추가했으므로 이걸 넣어보자. 다음과 같이 하면 버전 관리를 하기 전에 일단 "스테이징 영역"에 임시로 보관할 수 있다.

D:\git\myproject [master +1 ~0 -0 !]> git add octocat.txt
D:\git\myproject [master +1 ~0 -0]> git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached ..." to unstage)
#
# new file: octocat.txt
#

상태가 버전 관리 안함(untracked)에서 새 파일 커밋 예정으로 바뀌었음을 볼 수 있다. 파일 여러 가지를 한번에 추가하려면 git add *.txt와 같이 와일드 카드를 사용할 수도 있다. 또한 중간 설명에 git rm --cached <file>...와 같이 버전 관리를 안하려면 뺄 수도 있다는 내용이 나온다.

이제 커밋하려면 다음과 같이 한다. 스테이징 영역에 커밋 예정으로 추가된 파일이 모두 커밋된다.

D:\git\myproject [master +1 ~0 -0]> git commit -m "귀여운 octocat 내용 추가"
[master (root-commit) 2186143] 귀여운 octocat 내용 추가
0 files changed
create mode 100644 octocat.txt

commit의 -m 옵션은 커밋에 대한 설명을 넣겠다는 옵션이다. 이 옵션을 주지 않으면 대신 텍스트 편집기가 실행되는데 거기서도 커밋에 대한 설명을 작성하지 않으면 커밋은 취소된다. 옵션을 하나 더 알아 둘 것이 있다. 지금 예제는 "스테이징 영역"을 거쳐서 커밋하는 것이었는데 거치지 않고 바로 커밋하려면 -a 옵션을 주면 된다.

지금까지의 작업 이력을 보려면 어떻게 할까? git log가 있다.

D:\git\myproject [master]> git log
commit 7682d91a152601639d3a21a8b75fd3e4726ec984
Author: dylan
Date: Thu Apr 4 15:11:08 2013 +0900

추가로 작업한 여러 파일들

commit 2186143d6cf3685ea44a9f46adfa23e21b8db0df
Author: dylan
Date: Thu Apr 4 14:55:27 2013 +0900

귀여운 octocat 내용 추가

원격지로의 작업

이제 원격지로도 파일을 넣어 보자. 원격 컴퓨터에 커밋하는 것을 push라고 한다. 로컬에서만 버전 관리를 하다보면 컴퓨터 유실 때 문제가 크므로 원격지에 한 번씩 보관하는 것이 좋을 텐데 이때 적합한 것이 GitHub 사이트다. 개인적인 용도로 사용한다면 무료다. 독자적으로 직접 서버를 셋업하는 것도 가능하다. 이건 다음에 알아보기로 한다.

GitHub에서 myproject-remote라는 저장소를 만들어 놓은 경우 다음과 같이 원격지를 등록하고 그 원격지로 로컬 저장소를 push할 수 있다.

D:\git\myproject [master]> git remote add origin git@github.com:dylan/myproject-remote
D:\git\myproject [master]> git push -u origin master
Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (9/9), 31.38 KiB, done.
Total 9 (delta 0), reused 0 (delta 0)
To git@github.com:dylan/myproject-remote
* [new branch] master -> master
Branch master set up to track remote branch master from origin.

원격지의 이름은 "origin"이라고 정했으며(이 이름을 일반적으로 많이 사용한다) GitHub 사용자 이름은 dylan이고 원격지로 push하려는 로컬의 브랜치는 master(이미 기본적으로 작업중이던 브랜치이며 자동으로 master라는 이름이 부여된 상태)다. 즉, push 명령으로 로컬의 master 브랜치를 원격의 origin으로 push했다. 이때 -u 옵션은 매개변수들을 기억하여 다음에는 git push만 해도 되게 하라는 것이다.

원격지를 다른 사람에게도 알려주고 공유했다면 다른 사람이 작업해 push한 변경 내용을 내 로컬 저장소에 가져와 반영(병합)할 필요도 있을 것이다. 이 작업을 pull이라고 하며 origin으로 등록한 원격지에서 가져올 경우 다음과 같이 한다.

d:\git\myproject [master]> git pull origin

변경 사항 확인 및 변경 취소

만약 변경 사항이 있어서 내 로컬에 병합됐다면 diff 명령으로 확인할 수 있다. 아래는 현재 작업 중인 상태(HEAD)의 변경 내용을 확인할 수 있다. HEAD는 기본적인 값으로서 없어도 된다.

d:\git\myproject [master]> git diff HEAD
diff --git a/octocat.txt b/octocat.txt
index 7d8d808..e725ef6 100644
--- a/octocat.txt
+++ b/octocat.txt
@@ -1 +1 @@
-A Tale of Two Octocats
+A Tale of Two Octocats and an Octodog

추가적으로 git diff --staged 명령을 사용하면 스테이징 영역에 올라가 있는 변경 사항을 확인할 수 있다.

변경 내용이 마음에 들지 않을 땐 원래대로 돌려야할 것이다. 마지막으로 커밋했던 내용 상태로 어떤 파일을 돌리고 싶으면

d:\git\myproject [master]> git checkout -- octocat.txt

와 같이 한다. 이때 --는 그 뒤에는 더 이상 옵션이 없다는 표시인데 --를 붙이지 않았다면 "octocat.txt"를 브랜치로 해석할 수도 있다. (그런 브랜치가 없다면 파일로 해석된다.)

분기 및 병합

현재 작업 중인 소스들과 별개로 어떤 기능 추가나 버그 수정 작업을 하고 싶을 때 분기(브랜칭)를 하게 된다. 개발자들에 따라서는 꼭 커밋을 해야되는 시점까지 변경사항을 누적해서 한 번에 커밋하는 경우가 있는데 버전 관리의 용도상 이런 저런 변경 사항을 가지치기해서 따로따로 관리하려면 분기해서 별개로 작업하는 것이 좋다. 그래야 다른 쪽에서 작업하다가 커밋, 원복할 수도 있지 같은 분기에서 커밋하면 혼란이 올 수 있는 문제도 있기 때문이다.

D:\git\myproject [master]> git branch mybranch
D:\git\myproject [master]> git branch
* master
mybranch
D:\git\myproject [master]> git checkout mybranch
Switched to branch 'mybranch'
D:\git\myproject [myproject2]> _

git branch 뒤에 단어를 주면 해당 단어로 브랜치를 만들게 되는데 단어가 없으면 브랜치 현황을 보여준다. master와 mybranch 두 개의 브랜치가 있으며 현재는 master에서 작업하고 있음을 표시하고 있다. 또한 checkout 명령으로 현재 작업 대상 브랜치를 mybranch로 변경했다. 브랜칭과 체크아웃을 한번에 하려면 git checkout -b mybranch와 같이 해도 된다.

이제 mybranch에서 파일을 수정, 삭제 등을 작업한 후 커밋해도 master에는 영향이 없으므로 안심하고 작업할 수 있다. 작업이 끝나면 git commit -a와 같이 커밋하고 언제든 git checkout master와 같이 해서 master로 돌아갈 수 있다.

이제 다시 master로 왔는데 mybranch의 변경 사항을 병합하려면 어떻게 할까?

D:\git\myproject [master]> git merger mybranch
Updating baae6cc..8806162
Fast-forward
blue_octocat.txt | 1 -
octocat.txt | 1 -
octofamily/baby_octocat.txt | 1 -
octofamily/momma_octocat.txt | 1 -
red_octocat.txt | 1 -
5 files changed, 5 deletions(-)
delete mode 100644 blue_octocat.txt
delete mode 100644 octocat.txt
delete mode 100644 octofamily/baby_octocat.txt
delete mode 100644 octofamily/momma_octocat.txt
delete mode 100644 red_octocat.txt

이제 mybranch가 필요 없어서 삭제하려면 git branch -d mybranch와 같이 하면 된다. 만약 위와 같이 병합하지 않았는데도 삭제하려고 하면 변경 사항이 무시되는 것이므로 삭제되지 않는다. 이때는 git branch -d -f mybranch와 같이 강제로 삭제해야 한다.

마지막으로 문제. 지금까지의 작업을 원격지에 반영하려면(push)? git push를 실행한다.