Pro git - 7.12

Updated:

Pro git - Bundle

1. Bundle

  • Git 데이터를 네트워크를 거쳐 전송하는 일반적인 방법(HTTP, SSH등) 외에 Bundle이라는 것이 있다.

  • 예를 들어 네트워크가 불통인데 변경사항을 동료에게 보낼 때
  • 출장을 나갔는데 보안상의 이유로 로컬 네트워크에 접속하지 못할 때
  • 통신 인터페이스 장비가 고장났을 때, 갑자기 공용 서버에 접근하지 못할 때
  • 누군가에게 수정사항을 이메일로 보내야 하는데 40개 씩이나 되는 커밋을 format-patch 로 보내고 싶지 않을 때

  • git bundle 명령은 보통 git push 명령으로 올려 보낼 모든 것을 감싸서 한 바이너리 파일로 만든다.
  • 이 파일을 이메일로 보내거나 USB로 다른 사람에게 보내서 다른 저장소에 풀어서(Unbundle) 사용한다.

  • 간단한 예제를 보자. 이 저장소에는 커밋 두 개가 있다.
$ git log
commit 9a466c572fe88b195efd356c3f2bbeccdb504102
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:10 2010 -0800

    second commit

commit b1ec3248f39900d2a406049d762aa68e9641be25
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:01 2010 -0800

    first commit
  • 이 저장소를 다른 사람에게 통째로 보내고 싶은데 그 사람의 저장소에 Push 할 권한이 없거나, 그냥 Push 하고 싶지 않을 때, git bundle create 명령으로 Bundle을 만들 수 있다.
$ git bundle create repo.bundle HEAD master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 441 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
  • 이렇게 repo.bundle 이라는 이름의 파일을 생성할 수 있다.
  • 이 파일에는 이 저장소의 master 브랜치를 다시 만드는 데 필요한 모든 정보가 다 들어 있다.
  • bundle 명령으로 모든 Refs를 포함하거나 Bundle에 포함할 특정 구간의 커밋을 지정할 수 있다.
  • 이 Bundle을 다른 곳에서 Clone 하려면 위의 명령처럼 HEAD Refs를 포함해야 한다.

  • repo.bundle 파일을 다른 사람에게 이메일로 전송하거나 USB 드라이브에 담아서 나갈 수 있다.

  • 혹은 repo.bundle 파일을 일할 곳으로 어떻게든 보내놓으면 이 Bundle 파일을 마치 URL에서 가져온 것처럼 Clone 해서 사용할 수 있다.
$ git clone repo.bundle repo
Cloning into 'repo'...
...
$ cd repo
$ git log --oneline
9a466c5 second commit
b1ec324 first commit
  • Bundle 파일에 HEAD Refs를 포함하지 않으려면 -b master 옵션을 써주거나 포함시킬 브랜치를 지정해줘야 한다.
  • 그렇지 않으면 Git은 어떤 브랜치로 Checkout 할지 알 수 없다.

  • 이제 새 커밋 세 개를 추가해서 채운 저장소를 다시 원래 Bundle을 만들었던 저장소로 USB든 메일이든 Bundle로 보내 새 커밋을 옮겨보자.
$ git log --oneline
71b84da last commit - second repo
c99cf5b fourth commit - second repo
7011d3d third commit - second repo
9a466c5 second commit
b1ec324 first commit
  • 먼저 Bundle 파일에 추가시킬 커밋의 범위를 정해야 한다.
  • 전송할 최소한의 데이터를 알아서 인식하는 네트워크 프로토콜과는 달리 Bundle 명령을 사용할 때는 수동으로 지정해야 한다.
  • 전체 저장소를 Bundle 파일로 만들 수도 있지만, 차이점만 Bundle로 묶는 게 좋다.
  • 예제에서는 로컬에서 만든 세 개의 커밋만 묶는다.

  • 우선 차이점을 찾아내야 Bundle 파일을 만들 수 있다.
  • 숫자를 사용하여 커밋의 범위를 지정할 수 있다.
  • 원래 Clone 한 브랜치인 master에는 없던 세 개의 커밋을 얻어내려면 origin/master..master 또는 master ^origin/master 파라미터를 쓰면 된다.
  • log 명령으로 시험해볼 수 있다.
$ git log --oneline master ^origin/master
71b84da last commit - second repo
c99cf5b fourth commit - second repo
7011d3d third commit - second repo
  • 이제 Bundle 파일에 포함할 커밋을 얻었으니 묶어보자.
  • git bundle create 명령에 Bundle 파일의 이름과 묶어 넣을 커밋의 범위를 지정한다.
$ git bundle create commits.bundle master ^9a466c5
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 775 bytes, done.
Total 9 (delta 0), reused 0 (delta 0)
  • 이제 디렉토리에 commits.bundle 파일이 생겼다. 이 파일을 동료에게 보내면 원래의 저장소에 일이 얼마나 진행되었든 간에 파일 내용을 적용할 수 있다.

  • 이 Bundle 파일을 동료가 받았으면 원래 저장소에 적용하기 전에 무엇이 들어 있는지 살펴볼 수 있다.
  • 우선 bundle verify 명령으로 파일이 올바른 Git Bundle인가, 제대로 적용하는 데 필요한 모든 히스토리가 현재 저장소에 있는가 확인한다.
$ git bundle verify ../commits.bundle
The bundle contains 1 ref
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
The bundle requires these 1 ref
9a466c572fe88b195efd356c3f2bbeccdb504102 second commit
../commits.bundle is okay
  • 만약 앞에서 Bundle 파일을 만들 때 커밋 세 개로 만들지 않고 마지막 두 커밋으로만 Bundle 파일을 만들면 커밋이 모자라기 때문에 최초에 Bundle을 만들었던 저장소에 새 Bundle 파일을 합칠 수 없다.
  • 이런 문제를 verify 명령으로 확인할 수 있다.
$ git bundle verify ../commits-bad.bundle
error: Repository lacks these prerequisite commits:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 third commit - second repo
  • 제대로 만든 Bundle 파일이라면 커밋을 가져와서 최초 저장소에 합칠 수 있다.
  • 데이터를 가져올 Bundle 파일에 어떤 브랜치를 포함하고 있는지 살펴보려면 아래와 같은 명령으로 확인할 수 있다.
$ git bundle list-heads ../commits.bundle
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
  • 앞에서 verify 명령을 실행했을 때도 브랜치 정보를 확인할 수 있다.
  • 여기서 중요하게 짚을 부분은 fetch 명령이나 pull 명령으로 가져올 대상이 되는 브랜치를 Bundle 파일에서 확인하는 것이다.
  • 예를 들어 Bundle 파일의 master 브랜치를 작업하는 저장소의 other-master 브랜치로 가져오는 명령은 아래와 같이 실행한다.
$ git fetch ../commits.bundle master:other-master
From ../commits.bundle
 * [new branch]      master     -> other-master
  • 이런 식으로 작업하던 저장소의 master 브랜치에 어떤 작업을 했든 상관없이 Bundle 파일로부터 커밋을 독립적으로 other-master 브랜치로 가져올 수 있다.
$ git log --oneline --decorate --graph --all
* 8255d41 (HEAD, master) third commit - first repo
| * 71b84da (other-master) last commit - second repo
| * c99cf5b fourth commit - second repo
| * 7011d3d third commit - second repo
|/
* 9a466c5 second commit
* b1ec324 first commit
  • git bundle 명령으로 데이터를 전송할 네트워크 상황이 여의치 않거나 쉽게 공유할 수 있는 저장소를 준비하기 어려울 때도 히스토리를 쉽게 공유할 수 있다.