distributed git

1. workflow in distributed environment

git은 매우 유연한 구조의 분산형 버전관리 시스템이다. 즉, git에서는 각 개발자의 저장소가 하나의 노드 (= 분산형) 또는 중앙집중형일 수 있다.

모든 개발자는 다른 개발자의 저장소에 작업 내용을 전송하거나, 다른 개발자들이 참여할 수 있도록 자신이 운영하는 저장소 위치를 공개할 수 있다. 이러한 특징은 프로젝트나 팀이 코드를 운영할 때 다양한 워크플로를 만들 수 있도록 해준다.

git은 매우 유연함을 가지고 있기 때문에 저장소를 운영하는 측면에서 다양한 방식을 가질 수 있다. 이는 운영자가 상황에 따라 잘 조합하여야 원하는 저장소를 만들 수 있다는 것을 의미한다.

1.1 중앙집중식 워크플로

중앙집중식 협업 모델은 한 가지밖에 없다. 중앙저장소는 딱 하나 있고 변경 사항은 모두 이 중앙저장소에 집중된다. 개발자는 이 중앙저장소를 중심으로 작업한다.

          +---------------------------+
          |     shared repository     |
          +---------------------------+
             ^          ^          ^
             |          |          |
      +------+          +          +------+
      |                 |                 |
      V                 V                 V
+-----------+     +-----------+     +-----------+
| developer |     | developer |     | developer |
+-----------+     +-----------+     +-----------+

중앙집중식 모델에서 개발자 두명이 중앙저장소를 Clone 하고 각자 수정을 한다고 가정하는 것을 예로들어 보자. A 개발자가 자신이 한 작업을 commit 하고, 서버에 push 한다. 다른 개발자 B 는 자신의 작업을 commit, push 하기전에 A 개발자가 한 일을 marge 해야 한다. A 개발자가 먼저 commit, push 한 내용을 덮어쓰지 않기 위함이다. 이러한 방식은 subversion 으로 대표되는 중앙집중식 버전 관리 시스템에서 자연스럽게 이용되는 방식이다.

소규모 프로젝트이거나 중앙집중화 방식에 완전히 적응한 상황이라면 중앙집중식 워크플로에 따라 git을 도입하여 사용할 수 있다. 중앙저장소를 하나 만들고 개발자 모두에게 push 권한을 주는 것이다. git은 한 개발자가 다른 개발자의 작업 내용을 덮어쓰도록 허용하지 않는다. 개발자 A, 개발자 B 가 동시에 같은 부분을 수정된다고 예를 들어보자. A 가 먼저 작업을 끝내고 수정한 내용을 서버로 push 한다. 동시에 B 도 작업을 끝내고 수정한 내용을 서버로 push 하려 하지만 서버는 이를 받지 않는다. 서버에는 A 가 수정한 내용이 추가되었기 때문에 push 하기 전에 fetch 로 받아서 merge 한 후 push 할 수 있다. 이는 개발자에게 이미 익숙한 개념이다.

소규모에서만 할 수 있는 것이 아니라 git이 제공하는 브렌치 관리 모델을 사용하면 대규모 프로젝트에서도 사용 가능하다.

1.2 Integration-Manager 워크플로

git에서는 리모트 저장소를 여러개 운영할 수 있다. 다른 개발자는 읽기만 가능하고 자신은 쓰기도 가능한 공개 저장소를 만드는 워크플로를 만들 수 있다. 이 워크플로에는 보통 프로젝트를 대표하는 공식저장소가 있다. 기여자는 우선 공식저장소를 하나 clone 하고 수정(자신의 작업)하고 자신의 저장소에 push 한다. 그 다음에 프로젝트 Integration-Manager 에게 새 저장소에서 pull 하라고 요청한다. 그러면 Integration-Manager 는 기여자의 저장소를 리모트 저장소로 등록하고, 로컬에서 기여물을 테스트하고, 프로젝트 메인 브렌치에 merge 하고, 그 내용을 다시 프로젝트 메인 저장소에 push 한다. 이런 과정은 아래와 같다.

  1. 프로젝트 Integration-Manager는 프로젝트 메인 저장소에 push 한다.
  2. 프로젝트 기여자는 메인 저장소를 clone 하고 수정한다.
  3. 기여자는 자신의 저장소에 push 하고 Integration-Manager 가 접근할 수 있도록 공개한다.
  4. 기여자는 Integration-Manager 에게 변경사항을 적용해 줄 것을 메일로 요청한다.
  5. Integration-Manager 는 기여자의 저장소를 리모트 저장소로 등록하고 수정사항을 merge 하여 테스트한다.
  6. Integration-Manager 는 merge 한 사항을 메인 저장소에 push 한다.
+-----------+          +---------+          +---------+
| blessed   |          |developer|          |developer|
|repository | ----+    | public  |    +---- | public  |
+-----------+     |    +---------+    |     +---------+
   ^       \      |      |     ^      |             ^
   |        \     +------|-----|------|-------+     |
   |    +----\-----------+     |      |       |     |
   |    |     +-----------+    |      |       |     |
   |    |          +------|----|------+       |     |
   |    V          |      V    |              V     |
+-----------+ <----+   +---------+          +---------+
|integration|          |developer|          |developer|
|  manager  |          | private |          | private |
+-----------+          +---------+          +---------+

이 방식은 github 나 gitlab 같은 사이트를 통해 주로 사용하는 방식으로 프로젝트를 fork 하고 수정사항을 반영하여 다시 모두에게 공개하기 좋은 구조로 되어있다. 이 방식의 장점은 기여자와 Integration-Manager가 각자의 사정에 맞춰 프로젝트를 유지할 수 있다는 점이다. 기여자는 자신의 저장소와 브랜치에서 수정 작업을 계속해 나갈 수 있고 수정사항이 프로젝트에 반영되도록 기다릴 필요가 없다. 관리자는 여유를 가지고 기여자가 pushcommit 을 적절한 시점에 merge 한다.

1.3 Dictator and Lieutenants 워크플로

저장소를 여러개 운영하는 방식을 변형한 구조이다. 수백 명의 개발자가 참여하는 아주 큰 프로젝트를 운영할 때 사용하는 방식으로 Linux 커널 프로젝트가 대표적이다. 여러 명의 Integration-Manager가 저장소에서 자신이 맡은 부분만을 담당하는데 이들을 Lieutenants 라고 한다. 모든 Lieutenant는 최종 관리자 아래에 있으며 이 최종 관리자를 Benevolent Dictator 라고 부른다. Benevolent Dictator는 Lieutenant의 저장소를 가져와 공식 저장소에 push 하고 모든 프로젝트 참여자는 이 공식 저장소에서 반드시 pull 해야 한다. 이러한 워크플로는 아래와 같다.

  1. 개발자는 코드를 수정하고 master 브랜치를 기준으로 자신의 토픽 브랜치를 rebase 한다. 여기서 master 브랜치란 공식 저장소의 브랜치를 말한다.
  2. Lieutenant들은 개발자들의 수정사항을 자신이 관리하는 master 브랜치에 merge 한다.
  3. Dictator는 Lieutenant의 master 브랜치를 자신의 master 브랜치로 merge 한다.
  4. Dictator는 자신의 master 브랜치를 push 하며 다른 모든 개발자는 Dictator의 master 브랜치를 기준으로 rebase 한다.
+----------+ ------------------------------> +-----------+
| dictator |      +------------------------- |blessed    |
+----------+      |            +------------ | repository|
  ^      ^        |            |             +-----------+
  |      |        |            |                 | | |
  |      +--------|------+     |        +--------+ | |
  |               |      |     V        |          | |
+----------+ <----+   +----------+      |          | |
|lieutenant|          |lieutenant| <----|-----+    | |
+----------+          +----------+      |     |    | |
  ^      ^        +---------------------+     |    | |
  |      |        |            +--------------|----+ |
  |      +--------|-----+      |              |      |
  |               |     |      V              |      V
+----------+ <----+   +----------+          +----------+
|developer |          |developer |          |developer |
|   public |          |   public |          |   public |
+----------+          +----------+          +----------+

일반적이지 않지만 깊은 계층 구조를 가지는 환경이나 규모가 큰 프로젝트에서는 매우 쓸모 있다. 프로젝트 리더가 모든 코드를 통합하기 전에 코드를 부분부분 통합하도록 여러 명의 Lieutenant에게 위임한다.