How to Git (1) - Introduction
Subversion에서 Git으로 처음 넘어올 때 개념과 사용 방법이 달라서 많은 삽질을 했었는데, 관련 개념과 사용법에 대해 정리해보려고 한다.
Git 이란?
Git은 분산 버전 관리 시스템이다. 간단하게 말하면 분산된 환경에 소스 코드를 저장해서 관리하게 도와주는 시스템이다.
Git은 최초에 Linux 커널로 부터 시작했기 때문에, Linux 문법을 많이 따르는 편이고 기본적으로 제공되는 Git Bash 역시 Linux를 다룰 줄 알면 사용하기 편하다.
Git의 장단점
Git은 원격(Remote) 리파지토리가 있지만 로컬(Local)에도 리파지토리가 있다. 이렇게 구성되어 있기 때문에 분산 환경이라고 부른다.
이 부분 때문에 Subversion을 사용하던 사용자들은 (나포함) 헷갈리는 듯 하다.
이렇게 구성되어있기 때문에 매번 원격 리파지토리에 저장하지 않아도 되며, 로컬에 커밋을 저장하게 되므로 빠르고, 네트워크가 끊겨도 계속 작업이 가능하다는 장점이 있다.
그리고 브랜치를 굉장히 쉽게 만들고 활용할 수 있는 구조로 되어있고, 요즘 가장 많이 사용하는 SCM 이므로 레퍼런스가 많다는 장점이 있다.
Git의 가장 큰 단점으로는 초보자가 이해하기 어렵다는 것이다.
Git 클라이언트
Git을 사용하기 위해서는 Git 클라이언트 프로그램을 사용해야한다. 대표적으로는 Git을 설치 시에 같이 설치되는 Git Bash, Git GUI가 있고, Third Party 프로그램인 Tortoise Git, 또는 Sourcetree같은 툴이 있다. 앞으로의 서술할 내용에는 Git Bash 기준으로 설명한다.
Git 호스팅 서버
원격 저장소를 쉽고 편하게 관리하기 위해서 서버를 띄우게 되는데, 이 원격 Git을 웹으로 호스팅하게 도와주는 툴로는 널리 알려진 GitHub, GitLab, Bitbucket 등이 있다.
Repository
Local Repository
앞서 말한 Local Repository는 내 PC에 있다고 생각하면 된다. 소스를 수정하고 Commit하는 것은 Local Repository에 수정한 소스를 업데이트하는 행동이다. Local Repository는 내 PC에 있으므로 이 내용을 Push하지 않는 이상 다른 사람들의 소스코드에는 영향을 끼치지 않는 것이다.
Remote Repository
Local Repository를 Push하면 Remote Repository에 Local Repository에 Commit된 내용이 반영되게 된다. 반대로 Pull이나 Clone을 하게 되면 Remote의 내용을 Local에 내려받게 되는 것이다.
Subversion과의 차이점
Subversion은 Git과 다르게 Central Repository (중앙 리파지토리)방식을 취하고 있다. 이 경우 내가 Commit을 잘못 하면 바로 Central Repository의 내용을 수정하게 되므로 상대적으로 위험도가 높다.
Local Repository
Local Repository의 Working Tree, Index, HEAD 에 대해서 알아보자.
Working Tree (Working Directory)
실제 사용자가 소스를 수정하는 폴더를 Working Directory 또는 Working Tree라고 부른다.
파일을 수정하고 Git에 Add(추가)하는 액션을 하면 이 파일은 Staged한 상태가 된다. Staged Area에 추가된 파일은 내가 커밋할 준비가 된 파일/폴더이다. 이
Index
위의 과정을 통해 Staged Area에 추가된 파일/폴더를 Index라고도 부른다. 다음에 커밋할 Working Tree를 저장해 놓은 것이다. Staging된 파일들의 상태의 사진 이라고 생각해도 된다.
HEAD
이렇게 Staging된 파일들을 Commit을 하게 되면 최종적으로 지금 사용자가 사용하는 Branch의 HEAD값이 바뀌게 된다.
HEAD는 보통 Branch의 마지막 커밋 번호를 가지고 있다. 커밋을 하면 40자리 해쉬값 (ex. 313483058eec67d04407d212d20ba35c23c73059) 이 생성되는데 이를 커밋 번호, 또는 커밋 ID라고 부른다. 보통 앞에서 6자리 정도만으로 이 ID를 지칭하기도 한다.
만약 빈 폴더에 Checkout으로 Branch를 변경하게 되면 그 Branch의 HEAD가 바라보는 파일들을 받게 되는 것이다.
Status
Commit을 하는 과정 중 파일의 상태가 어떻게 변경되는지 알아보자.
Git에서는 파일의 상태를 4가지로 나눈다. 예를 들어, README.md라는 파일을 생성했다고 가정해보자.
Untracked
Untracked라는 상태의 파일은 아직 Git에 추가하지 않은 파일이다. 이 상태로는 아무리 git commit
을 해도 리파지토리가 변경되지 않는다.
Staged
이 파일을 git add README.md
라는 명령어로 Git에 추가하게 되면 이 파일의 상태는 Staged상태가 된다. 앞서 설명했던 Index가 변경되었을 것이다.
이후에 git commit
으로 커밋을 완료했다고 생각해보자. 이제 이 branch의 HEAD는 이 커밋으로 변경되었을 것이다.
Unmodified
커밋이 완료된 후 이 파일의 상태는 어떠할까? 커밋후 아무런 수정이 일어나지 않았으므로 이 파일의 상태는 Unmodified상태이다. 즉 Local의 파일이 Repository의 파일과 동일함을 나타낸다.
이제 이 파일을 수정했다고 생각해보자.
Modified
이 파일은 한번 커밋된 이력이 있고 (Tracking 되는 상태), 리파지토리의 파일과 비교했을 때 다른 점이 있기 때문에 Modified상태가 된다. 이제 add도 할 수 있고 commit도 할 수 있는 상태가 된 것이다.
만약 이 파일을 더 이상 리파지토리에서 유지하고 싶지 않은 경우, git rm
명령어로 파일을 tracking하지 않게 제거할 수 있다. 이렇게 된 파일은 Tracking상태가 아니므로 Untracking상태라고 할 수 있다.
Branch
Git의 브랜치에 대해 알아보자. 쉬운 브랜칭은 Git의 장점이므로 잘 활용할 수 있으면 좋을 것이다.
Branch는 리파지토리의 커밋 중 하나를 가리킨다. 리파지토리를 생성하면 기본이 되는 브랜치는 master일 것이다. 아마 개발을 위해 dev 브랜치를 만들게 될 것이다.
예를 들어, master 브랜치가 fe1edb라는 커밋을 HEAD로 가지고 있다고 생각해보자. master 브랜치를 clone 받게 되면 fe1edb의 상태의 Git 리파지토리를 내려받은 것이다. 여기서 dev 브랜치를 생성하고, 무언가를 수정한 다음에 dev 브랜치에 ed2cac라는 커밋을 생성하고 push 했다고 하자. 즉 dev 브랜치의 HEAD는 ed2cac라는 커밋이 된 것이다.
이후에 dev 브랜치를 clone받은 사용자는 ed2cac라는 커밋을 HEAD로 가지는 리파지토리를 내려받게 될 것이다. 만약 master 브랜치를 clone받는다면 fe1edb라는 커밋을 HEAD로 가지는 리파지토리를 내려받은 것이다.
Branch Model
보통 많이 사용하고, 추천하는 Git Branch 모델은 다음과 같다.
- Master Branch
- Dev Branch
먼저, 이렇게 메인이 되는 브랜치가 두 가지 있다. Master 브랜치는 Git 리파지토리를 생성하면 기본적으로 생성되는 브랜치이다. 주로 이 브랜치에 커밋되는 코드들은 최종 릴리즈 또는 출시될 코드들의 모음이다. 코드 리뷰나 테스트가 완료된 코드이고 실제 사용자들에게 공개되어도 아무 문제가 없는 코드들의 모음이라는 것이다.
주로 개발 중에는 Dev 브랜치를 많이 사용하게 되는데, 최종 변경사항들을 반영한 코드들이다. 이 브랜치는 Master브랜치로 부터 checkout되어 생성된다. 이 두 가지의 메인 브랜치는 안정적으로 수행되는 코드들이다.
개발 중에는 서버를 여러 대 사용하게 되는데, 주로 Master 브랜치의 코드를 운영 서버에 반영하고, Dev 브랜치에 커밋된 코드는 개발 서버에 반영되게 설정을 하게 될 것이다.
- Feature Branch
- Hotfix Branch
- Release Branch
남은 3가지의 브랜치는 필요에 따라 사용하게 되는 브랜치이다. Git 에서는 개발한 코드를 바로 Dev 브랜치에 Push하기 보다는, Feature 브랜치를 생성하는 것을 추천한다. 주로 Dev 브랜치를 base로 생성하며, 브랜치의 이름은 개발하는 기능 번호나 이슈 이름 등으로 네이밍 한다. (ex. feature-something-1234) 해당 Feature에 대한 개발이 끝나게 되면 이 브랜치를 Dev 브랜치에 merge하게 된다.
Hotfix 브랜치는 운영 중 급하게 수정할 사항이 있을 경우, 수정사항을 반영하는 브랜치이다. 주로 Master 브랜치에서 생성하게 되며 수정 후 Master로 merge한다.
Release브랜치는 Dev브랜치를 Master브랜치로 바로 merge하기 불안한 경우, Dev에서 안정적인 버전에 대한 브랜치를 생성해서 테스트 후 Master 브랜치로 merge할 때 사용한다. 주로 검증서버, 테스트서버로 불리는 서버들에 연계되어 있다.
Pull Request : Code Review
GitHub에서는 브랜치를 merge할 때 바로 merge하지 않고 리파지토리의 컨트리뷰터들에게 리뷰를 요청할 수 있는 기능을 제공한다. 종종 master 브랜치의 경우에는 임의로 반영되는 것을 막기 위해 브랜치 설정에서 Review 를 반드시 받게 하는 등의 설정이 되어있다.
GitHub에서 Pull Request라는 메뉴를 클릭하면 어느 브랜치에서 어디로 merge할 지 선택하는 화면이 나온다.
- Base에는 Merge가 일어날 브랜치
- Compare에는 Merge 될 브랜치
를 선택한다. 예를 들어, Dev 브랜치를 Master 브랜치로 merge하고 싶다면, Base를 Master, Compare를 Dev로 선택하면 된다. 이 부분이 가끔 헷갈린다..
그리고 Pull Request 내용을 작성하고, 내 코드를 리뷰할 Reviewer를 선택하면 된다. 브랜치 설정이 ‘Require pull requests reviews before merging’이 된 경우에는 이 Reviewer들의 Approval이 있어야 최종적으로 Merge할 수 있게 된다.
다음 번 포스트에서는 실제 사용하는 Git Command를 다뤄보기로 한다.