본문 바로가기

Sorry Architecture

Git

깃(Git)은 버전관리체계(Version Control System)이며, 소포트웨어(Software)개발 또는 버전관리 작업을 위한 위한 도구이다. 깃은 2005년 리누스 토발즈(Linus Torvalds) 가 리눅스 커널(Linux Kernel) 개발 협업을 위해 만들었다.
 
역사
리눅스 커널은 굉장히 규모가 큰 오픈소스 과제(Opensource project)다. 리눅스 커널개발의 대부분 기간 동안(1991-2002) 패치(Patch)와 아카이브(Archive)로만 관리했다. 2002년에 리눅스 커널은 비트키퍼(BitKeeper)라는 상용 분산형 버전관리체계(DVCS: Distributed Version Control System)를 사용했다. 그러다가 2005년에 비트키퍼와 관계가 나빠지면서 리눅스 협회(Linux Community)와 리누스 토발즈가 자체 도구를 만들게 되었다. 깃은 비트키퍼를 사용하면서 배운 교훈을 기초로 아래와 같은 목표를 세웠다.

  • 빠른 속도
  • 단순한 구조
  • 비선형적인 개발 (수 천 개의 동시 다발적인 브랜치)
  • 완벽한 분산
  • Linux 커널 같은 대형 프로젝트에도 유용할 것 (속도나 데이터 크기 면에서)

깃은 2005년 탄생하고 나서 여전히 초기 목표를 그대로 유지하고 있다. 그러면서도 사용하기 쉽게 진화하고 성숙했다.
 
기초 지식
1) 세 가지 상태
깃을 잘 사용하기 위해서 반드시 짚고 넘어가야할 부분을 설명한다. 깃은 파일(File)을 Committed, Modified, Staged 의 세 가지 상태로 관리한다. 'Commited'란 자료가 깃의 로컬 데이터베이스(Local Database)에 안전하게 저장되었다는 것을 의미한다. 'Modified'는 수정한 파일을 아직 깃의 로컬 데이터베이스에 저장하지 않았다는 것을 뜻한다. Staged란 현재 수정한 파일을 곧 커밋(Commit) 할 것이라고 표시한 상태를 말한다. 이 세가지 상태는 깃의 세 가지 단계와 연결되어 있다. 작업 디렉토리(Directory), 스테이징(Staging area), 저장소 이렇게 세 가지 단계를 이루고 있다.
깃으로 작업하는 과정은 기본적으로 다음과 같다.

  • Committed 란 데이터가 로컬 데이터베이스에 안전하게 저장됐다는 것을 의미한다.
  • Modified는 수정한 파일을 아직 로컬 데이터베이스에 커밋하지 않은 것을 말한다.
  • Staged란 현재 수정한 파일을 곧 커밋할 것이라고 표시한 상태를 의미한다.

3 States (https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)

2) Snapshots, not differences
깃이 다른 버전관리체계들과 가장 다른 점은 정보를 저장하는 방식이다. 대부분의 다른 버전관리 도구들은 파일의 변경을 기록한 목록을 저장하는 방식으로 동작한다. CVS, Subversion, Perforce, Bazaar 등이 여기에 해당하며 시간이 지나면서 변경점이 발생할 때마다 계속 변경된 내용을 담은 파일을 유지해야한다.
 

Delta (https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)

그러나 깃은 이런 방식과 다르게 동작한다. 깃은 리눅스의 파일 체계를 모방한 방식으로 변경내용을 기록하고 관리한다. 사용자가 커밋을 수행할 때, 깃은 마치 그 당시를 사진 찍어두는 것처럼 변경내용을 저장하고 변경이 없는 부분은 참조만 걸어둔다. 효율성을 위해서 변경이 없는 파일 다시 저장하기 않고 원래의 파일에 참조만 생성한다. 깃은 이러한 정보들을 스냅샷의 연속으로 생각한다. 아래 그림은 이러한 개념을 설명한 것이다.
 

Snapshots (https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)

앞 서 이야기했듯이, 깃은 모든 변경점을 스냅샷으로 관리한다. 그렇기기 때문에 브랜치를 만들거나, 브랜치사이를 옮겨다니거나 브랜치를 없애는 것이 쉽고 빠르다. 이러한 특징은 지속적 통합(Continuous Integration)과 같은 애자일(Agile) 개발방법에 아주 적합하다. 그 이유는 기존 버전관리체계의 브랜치방식과 비교해 보면 알 수 있다. 보통 브랜치를 만들고 거기서 작업을 하다보면 중앙에서 관리하는 공간 (main, trunk, master 등)과 내용이 달라지는데, 기존 버전관리도구에서는 달라진 형상을 맞춰주기 위해서 항상 모든 파일을 비교했다. 이것은 매우 고통스러웠는데, 모든 코드를 1:1로 다 비교하는 것이었다. 그나마 지속적 통합을 적용하면 매일매일 조금씩 병합하기 때문에 비교해야 하는 대상이 줄어서 조금은 나았다. 그러나 깃은 쉽게 브랜치를 만들고 바로바로 저장해 둘 수 있다. 브랜치들 사이를 넘나들거나 병합하는 것이 간단하고 빠르다. 또한 브랜치를 병합할 때 스냅샷을 기준으로 처리하기 때문에 충돌처리하는 것도 깔끔하다. 그래서 시간이 지나더라도 마스터(Master) 브랜치의 변화게 쉽게 발을 맞출 수 있다. 깃은 브랜치를 쉽게 다룰 수 있고 쉽게 병합할 수 있기 때문에 (하루에도 여러 번일지라도) 브랜치를 자주 만들고 자주 병합하는 것을 권장한다. 그래서 지속적 통합의점진적인 개선 방식과 깃의 철학은 맥락을 같이한다. 기존의 버전관리체계를 이용해도 지속적 통합을 할 수 있다. 그러나 깃을 이용하면 더 쉽게 된다. 궁합이 잘 맞는다.
 
깃은 스냅샷을 기반으로 동작하기 때문에 파일의 내용을 일일이 비교하지 않는다. 버전 충돌이 생겼을 경우 여러 정보를 이용해서 충돌처리를 해야할 대상을 최소한으로 가려낸다. 추측해보자면 커밋번호, 타임스탬프 등의 정보를 이용할 것이다. 그렇게 충돌이 일어난 파일을 추려내는데, 이 경우 한 줄씩 직접 비교하는 것이 아니라 파일 전체를 비교한다. 예를 들어 연속된 3줄의 코드 중에서 앞 뒤 한 줄씩만 바꾼 것이 버전충돌이 되었을 경우를 생각해보자. 깃에서는 충돌을 풀기 위해서 충돌이 일어난 각 버전의 파일을 보여주는데, 3줄을 한 단위로 묶어서 어느 쪽 파일을 선택할 것인지 묻는 것을 볼 수 있다. 이 때 사용자는 한 줄씩 비교하면서 버전충돌을 해결해도 되고, 깃이 제안한대로 둘 중 한 쪽의 손을 들어줘도 된다. 아래 코드는 가상으로 작성한 것이며 실제 일부러 버전충돌을 만드는 것은 쉽지 않다. 경험을 통해 얻는 내용을 바탕으로 비슷한 예시를 만들었다.

If you have questions,
<<<<<<< HEAD
open an issue
in github project
andd
=======
create a ticket
in github project
and
>>>>>>> branch

다른 버전관리체계도 비슷하겠지만, 깃은 뒤로 돌아가는 것을 권장하지 않는다. 작업을 하다가 문제가 생겨서 다시 예전의 내용으로 돌아가고 싶어하는 경우가 많겠지만, 그것은 별로 좋은 생각이 아니다. 예전 작업물을 하나씩 모두 비교해 가면서 편집해야 한다. 오히려 작업하던 브랜치를 없애버리고 다시 시작하는 편이 낫다. 그래서 깃은 계속 점진적으로 개선해가는 방식을 제안한다. 작은 브랜치들을 여러 개 만들고 작업한 즉시 병합하는 것이다. 그러면 문제가 생겼을 때 파악해야 하는 범위가 작아지기 때문에 다루기 쉬워진다. 그래서 문제가 생기면 모든 소스(Source Code)를 이전 상태로 되돌리는 것이 아니라 브랜치를 만들고 고치고 병합하면서 개선하는 것이다. 마치 위키(Wiki, 공동 문서 편집기)를 사용해서 1개의 최종 결과물을 함께 만들어 가는 것과 비슷하다.

Branches (https://git-scm.com/about)

3) Nearly every operation is local
깃은 사용자 개인의 저장소에 여러 개의 브랜치를 생성하고 관리하는 방식을 권장한다. 스냅샷 특징에서 설명한 것과 같이, 브랜치를 만들고 변경하고 병합하고 제거하는 작업들이 불과 몇 초만에 가능할 정도로 쉽고 간단하다. 그래서 작은 개선사항이라도 브랜치를 생성하고 거기서 작업한 다음 다른 브랜치에 병합하는 전략을 가지고 있다. 깃의 이러한 전략은 작은 기능 또는 작업을 통하여 지속적으로 개선하는 애자일 철학과 궁합이 좋다. 또한 깃은 분산구조방식(DVCS: Distributed Version Control System)이기 때문에, 원격 저장소와 사용자 저장소는 완전히 분리 되어있고 원격 저장소와 사용자 저장소를 마치 브랜치처럼 다룰 수 있다. 게다가 다른 사용자 저장소도 마찬가지 방식으로 다룰 수 있기 떄문에, 동시에 여러 사용자가 변경작업을 수행하는 규모가 큰 과제에도 장점을 가진다. 따라서 사용자(개발자)는 다른 사용자와의 충돌할 것은 걱정할 필요없다. 자신의 공간에서 여러 개의 브랜치를 마음 껏 생성해서 작업할 수 있다. 또한 동시에 협업이 필요한 것은 다른 사용자의 저장소와 브랜치 병합방식으로 처리할 수 있다.
 

Distributed Version Control System (https://git-scm.com/about/distributed)

 
깃이 많은 개발자들에게 사랑받기 전까지, 많은 개발자들이 서브버전(Subversion)을 애용했다. 분산방식보다 개념이 확실하고 단순하기 때문에 처음 버전관리를 처음 접하는 경우에도 큰 어려움 없이 접근할 수 있었다. 항상 중앙에서 가져오고 가져온 곳에 결과물을 도로 갖다 놓으면 되는 구조였기 때문에 이해하기 쉬운 까닭이었다. 그러나 과제의 규모가 커지면서 중앙집중방식(CVCS: Centralized Version Control System)은 문제점이 나타나기 시작했다. 개발자가 늘어나거나 파일이 증가하게 되면 브랜치를 생성하고 병합하기 어려워졌다. 브랜치를 만들고 관리하는 것이 불가능하지는 않지만 난이도가 높아지게 되었다. 특히 작은 코드를 바꾸었더라도 다른 사람이 다른 파일을 변경했다면 거의 모든 파일에서 버전 충돌이 발생했다. 그래서 몇몇 기법들이 나타나기 시작했다. 몇몇 개발자는 이러한 상황에 대응하기 위해 쉘터(Shelter) 등의 임시 공간을 두거나 자신만의 브랜치를 따로 만들고 그 곳을 작업공간으로 활용했다. 브랜치를 만드는 것이 나쁜 것은 아니다. 그러나 중앙집중식 버전관리체계에서는 브랜치 작업을 공동작업공간(Trunk)에 병합할 때, 폭탄과 같은 큰 충격/충돌이 발생할 수 밖에 없었다. 이러한 상황을 폭탄 병합(Bumb Merge; 대량의 작업결과를 일시에 병합하는 것)이라고 부른다. 폭탄 병합을 경험해 본 사용자(개발자)라면 폭판 병합이 얼마나 수고가 많이 드는 작업인 지 알 것이다. 폭탄 병합은 실수나 부작용이 발생할 가능성이 높기 때문에 조심스러우면서도 신경을 많이 써야 하기 때문이다. 폭탄 병합해보고 나면, 깃이 왜 좋은 지 알 수 있다.
 

Centralized Version Control System (https://git-scm.com/about/distributed)

 


여기까지는 깃의 역사와 목적, 그리고 개념에 대해 이야기했다. 여기서는 깃에서 자주 사용할 명령어에 대해 간단하게 다룬다. 깃은 다양한 기능을 제공하며, 사용자의 숙련도에 따라서 복잡하고 현란하게 사용할 수 있다. 그러나 가장 좋은 도구는 쉽고 간편하지만 막강한 기능이 있는 것이라고 생각한다. 그래서 정말 특수하거나 예외적인 경우를 제외한다면 checkout, add, commit, push, pull 정도만 사용해도 문제 없다고 본다. 실제로 일을 하면서도 cherry pick, rebase, revert, reset, merge, remote, init, clone, branch 등을 사용하긴 하지만 대부분은 앞에서 말한 기본기를 제일 많이 활용하고 있다. 
 
CLI (Command Line Interface)
clone
Clone 명령은 remote 저장소의 내용을 local 저장소에 그대로 가져오는 명령이다.

$ git clone https://github.com/projectnew/starthere.git


config
Git에서 활용할 정보를 설정한다. 다음의 예시는 사용자 이름과 메일 계정을 등록해 두는 명령이다.

$ git config --global user.name "YOUR NAME"
$ git config --global user.email "YOUR EMAIL ADRRESS"

add
작업(편집)한 내용을 stage에 반영한다. Stage에 올려둔 내용만 commit을 할 수 있다.

$ git add new-file

rm
지정한 file을 Git 의 관리 범위에서 제거한다.

$ git rm remove-file

commit
선택한 file 들을 저장소에 보관한다.

$ git commit -a                                                 # or
$ git commit -m "commit message"

 

'Sorry Architecture' 카테고리의 다른 글

Asynchronous Code Review  (0) 2019.06.10
Jikji Code  (0) 2019.06.08
Continuous Integration  (0) 2019.06.06
Continuous Delivery  (0) 2019.06.06
About Pull Requests  (0) 2019.06.01