git clone を手作業でやってみた

Git-Logo-2Color

前置き

git pull とは fetch して merge することだ、というのは聞いたことがあるでしょうか?
実際 fetchmerge だけで pull は実現できます。

git pull origin master
git fetch origin master
git merge FETCH_HEAD

しかし git clone も他のコマンドで実現できるのをご存知でしょうか?

本記事では git clone を手作業で行う手順を記します。

筆者環境

git version 2.10.1 (Apple Git-78)
(Xcode 8 command-line tools についてきたもの)

テーマ

本記事を読むと以下のようなことが起きるかも知れません。

  • git clone がいかに煩わしい作業を手軽にしてくれているかを知る。
  • git clone の具体的な挙動を理解する手助けになる。
  • git clone では手が届かない深い挙動をカスタマイズできるようになる。

git clone の中身

git clone の粒度を1段階細かくすると、以下のようになります。

git clone ほげほげ.git mylocal
git init mylocal
git -C mylocal remote add origin ほげほげ.git
git -C mylocal fetch origin
git -C mylocal remote set-head origin --auto
git -C mylocal checkout -B `git -C mylocal symbolic-ref refs/remotes/origin/HEAD | sed -E 's|refs/remotes/origin/||'` origin/HEAD

以下1行ずつ解説してまいります。

git init mylocal

何もないリポジトリを新規作成します。 → git init
この状態では、まだコミットは1つもなければ master ブランチすらありません。

git -C mylocal remote add origin ほげほげ.git

-C は、サブディレクトリ内のリポを cd せずに操作したいときに使うオプションです。以下同様。

remote add ではクローン元のリポを origin という名前でローカルリポに登録しています。

git -C mylocal fetch origin

origin にコミットされてる差分を取得します。

git -C mylocal remote set-head origin --auto

origin の HEAD がどこなのかを取得します。 → git remote set-head

これはクローン直後の既定ブランチに利用します。
普通は master になっていることが多いですが、サーバーの設定によって変化します。
参考 (外部サイト) → git clone したときに出来る origin/HEAD というリモートブランチ – ngの日記

git -C mylocal checkout -B (中略) origin/HEAD

origin の既定ブランチをオッカケる同名ブランチをローカルに作成します。

中央の git -C mylocal symbolic-ref refs/remotes/origin/HEAD | sed -E 's|refs/remotes/origin/||' はローカルに作成するブランチ名を作ってます。
(ここだけ git 以外のコマンドに頼ってる…) → sed でググる

以上!

出来上がり〜検証

試しに普通に clone したのと手動 clone したのでコンフィグを比べてみます。

diff <(git -C auto-clone config --local --list) <(git -C manual-clone config --local --list)

何も出ませんでした。コンフィグの真似は完璧。

続いてブランチです。

diff <(git -C auto-clone branch -avv) <(git -C manual-clone branch -avv)

何も出なければ成功です!

diff -r auto-clone manual-clone

ただしここまで厳密に比較すると細かい違いがあるので、
上記の手作業 clone は通常の clone と全く同一ではないようです。
(タイムスタンプの差分もありますが他にもある模様)

本当はもう少し追求したいところですが…今回はこの辺りでお開きにします。

調査のきっかけ

余談ですが、なぜこんなことを調べたのかというと Jenkins 氏がきっかけでした。

某 Jenkins ジョブの git clone がとても遅いので改善できないものかと思い Jenkins のログを読んでいたら、確かに clone しているはずなのに git clone を実行したログが出力されていないことに気づきました。
というか、 clone のタイムアウトとして設定した値がなぜか fetch に適用されてる…
というわけで Jenkins 氏のこの挙動を詳しく調べた結果が上記の通りです。
おじさん結構複雑なことやってた。