前置き
git pull
とは fetch
して merge
することだ、というのは聞いたことがあるでしょうか?
実際 fetch
と merge
だけで 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 氏のこの挙動を詳しく調べた結果が上記の通りです。
おじさん結構複雑なことやってた。