모든 내용은 https://git-scm.com/book를 참고하여 만들었습니다.
비공개 소규모 팀
두세 명으로 이루어진 비공개 프로젝트가 가장 간단한 프로젝트일 것이다. 비공개
라고 함은 소스코드가 공개되지 않은 것을 말하는 것이지 외부에서 접근할 수 없는 것을 말하지 않는다. 모든 개발자는 공유하는 저장소에 쓰기 권한이 있어야 한다.
이런 환경에서는 보통 Subversion 같은 중앙집중형 버전 관리 시스템에서 사용하던 방식을 사용한다.
물론 Git이 가진 오프라인 커밋 기능이나 브랜치 Merge 기능을 이용하긴 하지만 크게 다르지 않다. 가장 큰 차이점은 서버가 아닌 클라이언트 쪽에서 Merge 한다는 점이다. 두 개발자가 저장소를 공유하는 시나리오를 살펴보자. 개발자인 John은 저장소를 Clone 하고 파일을 수정하고 나서 로컬에 커밋한다 (예제에서 Git이 출력하는 메시지 중 일부는 …
으로 줄이고 생략했다).
# John's Machine
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'remove invalid default value'
[master 738ee87] remove invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
개발자인 Jessica도 저장소를 Clone 하고 나서 파일을 하나 새로 추가하고 커밋한다.
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
Jessica는 서버에 커밋을 Push 한다.
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
John도 서버로 커밋을 Push 하려고 한다.
# John's Machine
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
Jessica의 Push는 성공했지만, John의 커밋은 서버에서 거절된다. Subversion을 사용했던 사람은 이 부분을 이해하는 것이 중요하다. 같은 파일을 수정한 것도 아닌데 왜 Push가 거절되는 걸까?
Subversion에서는 서로 다른 파일을 수정하는 이런 Merge 작업은 자동으로 서버가 처리한다. 하지만, Git은 로컬에서 먼저 Merge 해야 한다. John은 Push 하기 전에 Jessica가 수정한 커밋을 Fetch 하고 Merge 한다.
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
Fetch 하고 나면 John의 로컬 저장소는 아래와 같이 된다.
Figure 58. Fetch 하고 난 John의 저장소.
John은 Jessica가 저장소로 Push 했던 코드를 로컬 저장소에 가져왔다. 하지만, Push 하기 전에 Fetch 한 브랜치를 Merge 해야 한다.
$ git merge origin/master
Merge made by recursive.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
Merge가 잘 이루어지면 John의 브랜치는 아래와 같은 상태가 된다.
Figure 59. origin/master 브랜치를 Merge 하고 난 후 John의 저장소.
John은 Merge 하고 나서 자신이 작업한 코드가 제대로 동작하는지 확인한다. 그 후에 공유하는 저장소에 Push 한다.
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
이제 John의 저장소는 아래와 같이 되었다.
Figure 60. Push 하고 난 후 John의 저장소.
동시에 Jessica는 토픽 브랜치를 하나 만든다. issue54 브랜치를 만들고 세 번에 걸쳐서 커밋한다. 아직 John의 커밋을 Fetch 하지 않은 상황이기 때문에 아래와 같은 상황이 된다.
Figure 61. Jessica의 토픽 브랜치.
Jessica는 John의 작업을 적용하려면 Fetch 해야 한다.
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
위 명령으로 John이 Push 한 커밋을 모두 내려받는다. 그러면 Jessica의 저장소는 아래와 같은 상태가 된다.
Figure 62. John의 커밋을 Fetch 한 후 Jessica의 저장소.
이제 orgin/master
와 Merge 할 차례다. Jessica는 토픽 브랜치에서의 작업을 마치고 어떤 내용이 Merge 되는지 git log
명령으로 확인한다.
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date: Fri May 29 16:01:27 2009 -0700
remove invalid default value
issue54..origin/master
문법은 히스토리를 검색할 때 뒤의 브랜치(origin/master)에 속한 커밋 중 앞의 브랜치(issue54)에 속하지 않은 커밋을 검색하는 문법이다. 자세한 내용은 범위로 커밋 가리키기에서 다룬다.
앞의 명령에 따라 히스토리를 검색한 결과 John이 생성하고 Jessica가 Merge 하지 않은 커밋을 하나 찾았다. origin/master
브랜치를 Merge 하게 되면 검색된 커밋 하나가 로컬 작업에 Merge 될 것이다.
Merge 할 내용을 확인한 Jessica는 자신이 작업한 내용과 John이 Push 한 작업(origin/master)을 master 브랜치에 Merge 하고 Push 한다. 모든 내용을 합치기 전에 우선 master
브랜치를 Checkout 한다.
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
origin/master
, issue54
모두 Upstream 브랜치이기 때문에 둘 중에 무엇을 먼저 Merge 하든 상관이 없다. 물론 어떤 것을 먼저 Merge 하느냐에 따라 히스토리 순서는 달라지지만, 최종 결과는 똑같다. Jessica는 먼저 issue54 브랜치를 Merge 한다.
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
보다시피 Fast-forward Merge 이기 때문에 별 문제 없이 실행된다. 다음은 John의 커밋(origin/master)을 Merge 한다.
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
위와 같이 Merge가 잘 되면 그림 아래와 같은 상태가 된다.
Figure 63. John의 커밋을 Merge 후 Jessica의 저장소.
origin/master
브랜치가 Jessica의 master
브랜치로 나아갈(reachable) 수 있기 때문에 Push는 성공한다(물론 John이 그 사이에 Push 하지 않았다면).
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
두 개발자의 커밋을 성공적으로 Merge 하고 나면 결과는 아래와 같다.
Figure 64. Jessica가 서버로 Push 하고 난 후의 저장소.
매우 간단한 상황의 예제를 살펴보았다. 토픽 브랜치에서 수정하고 로컬의 master
브랜치에 Merge 한다. 작업한 내용을 프로젝트의 공유 저장소에 Push 하고자 할 때는 우선 origin/master
브랜치를 Fetch 하고 Merge 한다. 그리고 나서 Merge 한 결과를 다시 서버로 Push 한다. 이런 워크플로가 일반적이며 아래와 같이 나타낼 수 있다.
Figure 65. 여러 개발자가 Git을 사용하는 워크플로.