-
1. 使い始める
- 1.1 バージョン管理に関して
- 1.2 Git略史
- 1.3 Gitの基本
- 1.4 コマンドライン
- 1.5 Gitのインストール
- 1.6 最初のGitの構成
- 1.7 ヘルプを見る
- 1.8 まとめ
-
2. Git の基本
- 2.1 Git リポジトリの取得
- 2.2 変更内容のリポジトリへの記録
- 2.3 コミット履歴の閲覧
- 2.4 作業のやり直し
- 2.5 リモートでの作業
- 2.6 タグ
- 2.7 Git エイリアス
- 2.8 まとめ
-
3. Git のブランチ機能
- 3.1 ブランチとは
- 3.2 ブランチとマージの基本
- 3.3 ブランチの管理
- 3.4 ブランチでの作業の流れ
- 3.5 リモートブランチ
- 3.6 リベース
- 3.7 まとめ
-
4. Gitサーバー
- 4.1 プロトコル
- 4.2 サーバー用の Git の取得
- 4.3 SSH 公開鍵の作成
- 4.4 サーバーのセットアップ
- 4.5 Git デーモン
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティによる Git ホスティング
- 4.10 まとめ
-
5. Git での分散作業
- 5.1 分散作業の流れ
- 5.2 プロジェクトへの貢献
- 5.3 プロジェクトの運営
- 5.4 まとめ
-
6. GitHub
- 6.1 アカウントの準備と設定
- 6.2 プロジェクトへの貢献
- 6.3 プロジェクトのメンテナンス
- 6.4 組織の管理
- 6.5 スクリプトによる GitHub の操作
- 6.6 まとめ
-
7. Git のさまざまなツール
- 7.1 リビジョンの選択
- 7.2 対話的なステージング
- 7.3 作業の隠しかたと消しかた
- 7.4 作業内容への署名
- 7.5 検索
- 7.6 歴史の書き換え
- 7.7 リセットコマンド詳説
- 7.8 高度なマージ手法
- 7.9 Rerere
- 7.10 Git によるデバッグ
- 7.11 サブモジュール
- 7.12 バンドルファイルの作成
- 7.13 Git オブジェクトの置き換え
- 7.14 認証情報の保存
- 7.15 まとめ
-
8. Git のカスタマイズ
- 8.1 Git の設定
- 8.2 Git の属性
- 8.3 Git フック
- 8.4 Git ポリシーの実施例
- 8.5 まとめ
-
9. Gitとその他のシステムの連携
- 9.1 Git をクライアントとして使用する
- 9.2 Git へ移行する
- 9.3 まとめ
-
10. Gitの内側
- 10.1 配管(Plumbing)と磁器(Porcelain)
- 10.2 Gitオブジェクト
- 10.3 Gitの参照
- 10.4 Packfile
- 10.5 Refspec
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータリカバリ
- 10.8 環境変数
- 10.9 まとめ
-
A1. 付録 A: その他の環境でのGit
- A1.1 グラフィカルインタフェース
- A1.2 Visual StudioでGitを使う
- A1.3 EclipseでGitを使う
- A1.4 BashでGitを使う
- A1.5 ZshでGitを使う
- A1.6 PowershellでGitを使う
- A1.7 まとめ
-
A2. 付録 B: Gitをあなたのアプリケーションに組み込む
- A2.1 Gitのコマンドラインツールを使う方法
- A2.2 Libgit2を使う方法
- A2.3 JGit
-
A3. 付録 C: Gitのコマンド
- A3.1 セットアップと設定
- A3.2 プロジェクトの取得と作成
- A3.3 基本的なスナップショット
- A3.4 ブランチとマージ
- A3.5 プロジェクトの共有とアップデート
- A3.6 検査と比較
- A3.7 デバッグ
- A3.8 パッチの適用
- A3.9 メール
- A3.10 外部システム
- A3.11 システム管理
- A3.12 配管コマンド
10.6 Gitの内側 - 転送プロトコル
転送プロトコル
Gitが2つのリポジトリ間でデータを転送する方法には、主に “dumb” プロトコルと “smart” プロトコルの2つがあります。 このセクションでは、これらのプロトコルがどのように機能するのかを駆け足で見ていきます。
dumbプロトコル
HTTP経由でのリポジトリへのアクセスを読み取り専用にする場合、dumbプロトコルを使うことになると思います。
このプロトコルを “dumb” (馬鹿)と呼ぶのは、転送プロセスにおいて、サーバー側にGit専用のコードが不要だからです。フェッチのプロセスは一連のHTTP GET
リクエストです。ここで、クライアントは、サーバー上のGitリポジトリのレイアウトを仮定してよいことになっています。
注記
|
dumbプロトコルは昨今ではほとんど使用されていません。 安全性や秘匿性を保つのが難しいため、多くのGitのホスト(クラウドベースでも、オンプレミスでも)では使用が禁止されています。 一般的には、もう少し後で述べるsmartプロトコルを使用することをおすすめします。 |
simplegitライブラリにおける http-fetch
のプロセスを追ってみましょう。
$ git clone http://server/simplegit-progit.git
このコマンドは最初に info/refs
ファイルをサーバから取得します。
このファイルは update-server-info
コマンドによって出力されます。そのため、HTTPによる転送を適切に動作させるためには、このコマンドを post-receive
フック中で呼び出す必要があります。
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
いま、手元にはリモート参照とSHA-1のハッシュのリストがあります。 次に、HEADが指しているものを見て、終了時に何をチェックアウトするのかを調べます。
=> GET HEAD
ref: refs/heads/master
プロセスの完了時には、master
ブランチをチェックアウトする必要があると分かりました。
これで、参照を辿るプロセスを開始する準備ができました。
開始地点は info/refs
ファイルの中にあった ca82a6
のコミットオブジェクトなので、まずそれを取得します。
=> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
すると、オブジェクトが返ってきます。これは、サーバー上にある緩いフォーマットのオブジェクトで、それを静的なHTTP GETリクエストで取得したわけです。 このオブジェクトのzlib圧縮を解除し、ヘッダを取り除けば、コミットの内容が見られます。
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
changed the version number
もう2つ、オブジェクトを取得する必要があることが分かりました。
たった今取得したコミットが指しているコンテンツのツリーである cfda3b
と、親にあたるコミットである 085bb3
です。
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
まずは親にあたるオブジェクトを取得しました。 続いてツリーオブジェクトを取得してみましょう。
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
おっと、そのツリーオブジェクトは緩いフォーマットではサーバー上に存在しないようです。そのため404のレスポンスを受け取っています。 考えられる理由は2つあります。オブジェクトが代替のリポジトリにあるためか、またはこのリポジトリ内のpackfileに含まれているためです。 Gitはまず、代替のリポジトリの一覧を調べます。
=> GET objects/info/http-alternates
(empty file)
このGETリクエストに対して代替のURLのリストが返ってきた場合、Gitはその場所から緩いフォーマットのファイルとpackfileを探します。これは、プロジェクトがディスク上のオブジェクトを共有するために互いにフォークし合っている場合に適したメカニズムです。
ですが、このケースでは代替URLのリストは空だったので、オブジェクトはpackfileの中にあるに違いありません。
サーバー上のアクセス可能なpackfileの一覧は、 objects/info/packs
ファイルに格納されているので、これを取得する必要があります(このファイルも update-server-info
で生成されます)。
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
サーバー上にはpackfileが1つしかないので、探しているオブジェクトは明らかにこの中にあります。しかし念の為にインデックスファイルをチェックしてみましょう。 これにより、サーバー上にpackfileが複数ある場合でも、必要なオブジェクトがどのpackfileに含まれているか調べられます。
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
packfileのインデックスが取得できたので、これで探しているオブジェクトがpackfileの中にあるか調べられます – なぜなら、インデックスにはpackfileの中にあるオブジェクトのSHA-1ハッシュと、それらのオブジェクトに対するオフセットの一覧が格納されているからです。 探しているオブジェクトは、どうやらそこにあるようです。さあ、そのpackfileをまるごと取得してみましょう。
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
探していたツリーオブジェクトが見つかりました。さらにコミットを辿ってみましょう。
コミットはいずれも、先ほどダウンロードしたpackfileの中にあります。そのため、もうサーバーに対するリクエストは不要です。
Gitは、最初にダウンロードしたHEADが指している master
ブランチの作業用コピーをチェックアウトします。
smartプロトコル
dumbプロトコルはシンプルですが、少し非効率ですし、クライアントからサーバーへのデータの書き込みも行えません。 データ転送においては、smartプロトコルの方がより一般的な手段です。ただし、リモート側にGitと対話できるプロセス – ローカルのデータを読んだり、クライアントが何を持っていて何が必要としているかを判別したり、それに応じたpackfileを生成したりできるプロセス – が必要です。 データの転送には、プロセスを2セット使用します。データをアップロードするペアと、ダウンロードするペアです。
データのアップロード
リモートプロセスにデータをアップロードする際、Gitは send-pack
プロセスと receive-pack
プロセスを使用します。send-pack
プロセスはクライアント上で実行されリモート側の receive-pack
プロセスに接続します。
SSH
例えば、あなたのプロジェクトで git push origin master
を実行するとします。そして origin
はSSHプロトコルを使用するURLとして定義されているとします。
この際、Gitは send-pack
プロセスを起動して、あなたのサーバーへのSSH接続を開始します。
このプロセスは、以下のようなSSHの呼び出しを介して、リモートサーバー上でコマンドを実行しようとします。
$ ssh -x git@server "git-receive-pack 'simplegit-progit.git'"
00a5ca82a6dff817ec66f4437202690a93763949 refs/heads/master□report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1+github-607-gfba4028 delete-refs
0000
git-receive-pack
コマンドは、今ある参照1つにつき1行の応答を、その都度返します。このケースでは、master
ブランチとそのSHA-1ハッシュのみを返しています。
最初の行には、サーバーの持っている機能(ここでは、report-status
や delete-refs
など。クライアント識別子も含む)のリストも含まれています。
各行は4文字の16進数で始まっており、その行の残りがどれくらいの長さなのかを示しています。 最初の行は00a5で始まっていますが、これは16進数で165を示し、その行はあと165バイトあることを意味します。 次の行は0000であり、サーバーが参照のリストの表示を終えたことを意味します。
サーバーの状態がわかったので、これで send-pack
プロセスは、自分の側にあってサーバー側にないコミットを判別できます。
これからこのプッシュで更新される各参照について、send-pack
プロセスは receive-pack
プロセスにその情報を伝えます。
例えば、 master
ブランチの更新と experiment
ブランチの追加をしようとしている場合、 send-pack
のレスポンスは次のようになるでしょう。
0076ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 \
refs/heads/master report-status
006c0000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d \
refs/heads/experiment
0000
Gitは更新しようとしている参照のそれぞれに対して、行の長さ、古いSHA-1、新しいSHA-1、更新される参照を含む行を送信します。 最初の行にはクライアントの持っている機能も含まれています。 すべてが 0 のSHA-1ハッシュ値は、以前そこには何もなかったことを意味します。それはあなたが experiment の参照を追加しているためです。 もしもあなたが参照を削除していたとすると、逆にすべてが 0 のSHA-1ハッシュ値が右側に表示されるはずです。
次に、クライアントは、まだサーバー側にないオブジェクトすべてを含むpackfileを送信します。 最後に、サーバーは成功(あるいは失敗)を示す内容を返します。
000eunpack ok
HTTP(S)
このプロセスは、ハンドシェイクが少し違うだけで、HTTP経由の場合とほとんど同じです。 接続は以下のリクエストで初期化されます。
=> GET http://server/simplegit-progit.git/info/refs?service=git-receive-pack
001f# service=git-receive-pack
00ab6c5f0e45abd7832bf23074a333f739977c9e8188 refs/heads/master□report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1~vmg-bitmaps-bugaloo-608-g116744e
0000
これで初回のクライアント・サーバー間の交信は終了です。
クライアントは次に別のリクエストを作成します。この場合は send-pack
が提供するデータをもとに POST
リクエストを作成します。
=> POST http://server/simplegit-progit.git/git-receive-pack
この POST
リクエストには send-pack
の出力とpackfileがペイロードとして含まれています。
サーバーはこれに対して成功か失敗かをHTTPレスポンスで示します。
データのダウンロード
データをダウンロードするときには、 fetch-pack
と upload-pack
の2つのプロセスが使用されます。
クライアントが fetch-pack
プロセスを起動すると、リモート側の upload-pack
プロセスに接続してネゴシエーションを行い、何のデータをダウンロードするか決定します。
SSH
SSHを介してフェッチを行っているなら、fetch-pack
は以下のようなコマンドを実行します。
$ ssh -x git@server "git-upload-pack 'simplegit-progit.git'"
fetch-pack
の接続のあと、upload-pack
は以下のような内容を返信します。
00dfca82a6dff817ec66f44342007202690a93763949 HEAD□multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag \
multi_ack_detailed symref=HEAD:refs/heads/master \
agent=git/2:2.1.1+github-607-gfba4028
003fe2409a098dc3e53539a9028a94b6224db9d6a6b6 refs/heads/master
0000
これは receive-pack
が返す内容にとても似ていますが、持っている機能は異なります。
加えて、HEADがどこを指しているか (symref=HEAD:refs/heads/master
) を返すので、クローン処理の場合、クライアントが何をチェックアウトするのかを知ることができます。
この時点で、 fetch-pack
プロセスは手元にあるオブジェクトを確認します。そして、必要なオブジェクトを返答するため、 “want” という文字列に続けて必要なオブジェクトのSHA-1ハッシュを送ります。
また、既に持っているオブジェクトについては、 “have” という文字列に続けてオブジェクトのSHA-1ハッシュを送ります。
さらに、このリストの最後には “done” を書き込んで、必要なデータのpackfileを送信する upload-pack
プロセスを開始します。
003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
HTTP(S)
フェッチ操作のためのハンドシェイクは2つのHTTPリクエストからなります。
1つめはdumbプロトコルで使用するのと同じエンドポイントへの GET
です。
=> GET $GIT_URL/info/refs?service=git-upload-pack
001e# service=git-upload-pack
00e7ca82a6dff817ec66f44342007202690a93763949 HEAD□multi_ack thin-pack \
side-band side-band-64k ofs-delta shallow no-progress include-tag \
multi_ack_detailed no-done symref=HEAD:refs/heads/master \
agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
0000
これはSSH接続経由で git-upload-pack
を呼び出す場合と非常によく似ていますが、2つ目の交信が個別のリクエストとして実行される点が異なります。
=> POST $GIT_URL/git-upload-pack HTTP/1.0
0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7
0032have 441b40d833fdfa93eb2908e52742248faf0ee993
0000
これもまた、上と同じフォーマットです。 このリクエストに対するレスポンスは、成功したか失敗したかを示しています。また、packfileも含まれています。
プロトコルのまとめ
このセクションでは転送プロトコルの最も基本的な概要を取り上げました。
プロトコルには他にも multi_ack
や side-band
など数多くの機能がありますが、それらは本書の範囲外です。
ここでは、一般的なクライアントとサーバーの間の行き来に関する感覚を捉えてもらえるよう努めました。これ以上の知識が必要な場合は、おそらくGitのソースコードを見てみる必要があるでしょう。