HOWTO · Git
Git チェックアウトと Git クローンの違い
git checkout コマンドと git clone コマンドを区別します。
このページの内容
git clone はリモートリポジトリを新しいローカルリポジトリにコピーします。git checkout はすでにあるリポジトリ内で使うコマンドで、従来はブランチの切り替えやファイルの復元を行っていました。SVN の利用者は、作業コピーを取得する操作として svn checkout を知っているかもしれませんが、Git で同じ目的に使うのは git clone です。
この 2 つのコマンドは作業の異なる段階に属するため、互いに置き換えられる代替手段ではありません。クローンは「このプロジェクトを自分のマシンにどうやって持ってくるか」という問いに答え、チェックアウトは「既存の作業ツリーは今どのブランチ、コミット、ファイルの状態を表示すべきか」という問いに答えます。リポジトリがまだなければチェックアウトするものは何もなく、すでにあるなら再度クローンしても別の独立したコピーが増えるだけです。
一目で見る違い
| 観点 | git clone |
git checkout |
|---|---|---|
| 出発点 | リモートリポジトリの URL またはパス | ローカルにすでに存在するリポジトリ |
| 主な結果 | 独自の .git ディレクトリを持つ新しいローカルリポジトリ |
同じリポジトリ内での作業ツリーの変更と HEAD の移動 |
| ネットワーク利用 | リモートからオブジェクトと履歴をダウンロードする | ローカルのブランチ、コミット、ファイルについては使わない |
| 典型的な頻度 | 必要なコピーごとに 1 回 | 作業中は 1 日に何度も |
| リポジトリを作成できるか | できる。それが目的である | できない。リポジトリの外では失敗する |
| 関連する現代的なコマンド | なし。クローンには分割された後継はない | ブランチには git switch、ファイルには git restore |
git clone でローカルリポジトリを作成する
リポジトリの URL を指定して git clone を実行します。Git はローカルリポジトリを作成し、そのデフォルトブランチをチェックアウトします。プロジェクトに初めて参加するときだけでなく、ローカルに別のコピーが必要なときにもクローンを作成できます。
次の単独で実行できる例では、ローカルにソースリポジトリを作成してクローンし、機能開発用のブランチを作成します。このコマンド列は、新しいブランチへの切り替え後に確認メッセージを表示します。
set -eu
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
git init -q "$workdir/source"
git -C "$workdir/source" config user.name "Example User"
git -C "$workdir/source" config user.email "example@example.com"
printf 'Project notes\n' > "$workdir/source/README.md"
git -C "$workdir/source" add README.md
git -C "$workdir/source" commit -qm "Add project notes"
git clone -q "$workdir/source" "$workdir/local"
git -C "$workdir/local" switch -q -c feature
printf 'Created a local clone and switched to feature.\n'
Created a local clone and switched to feature.
ホスティングサービス上のリポジトリを使う場合は、コピーした HTTPS または SSH の URL を git clone <repository-url> に指定します。保存先ディレクトリ名は既定でリポジトリ URL に基づいて決まります。別の場所に保存するには、URL の後にディレクトリ名を追加してください。重要な違いは URL を使う点です。このコマンドはリモートのソースからリポジトリを作成します。
クローンは現在のファイルをダウンロードするだけではありません。Git はコミット履歴をコピーし、リモートのブランチに対するリモート追跡ブランチを作成し、取得元を origin という名前のリモートとして登録します。そのため、後の git fetch や git pull が更新の取得元を把握できます。最後に Git は内部でデフォルトブランチのチェックアウトを実行し、作業ツリーを埋めます。この最後の手順が 2 つのコマンドの唯一の接点です。クローンはブランチのチェックアウトで終わりますが、チェックアウトがクローンを生み出すことは決してありません。
取得内容を変えるクローンのオプション
実際の選択肢のほとんどは 3 つのオプションでカバーできます。最新のスナップショットだけが必要な場合、たとえば継続的インテグレーションでは、--depth 1 を使って浅いクローンを作成します。履歴全体ではなく指定した数のコミットだけをダウンロードするため、時間とディスク容量を節約できますが、古いコミットはローカルで利用できなくなります。クローン後にデフォルト以外のブランチをチェックアウトするには --branch <name> を使い、他のブランチがまったく不要な場合は --single-branch と組み合わせます。ただし、素のローカルパスからのクローンでは --depth が無視される点に注意してください。ローカルソースで浅いクローンを試す場合は、下の 2 番目の例のように file:// URL を渡してください。
git checkout の役割
従来の git checkout は、ブランチ操作とファイルの復元の両方を担っていました。これらは別の作業なので、新しい Git では目的が分かりやすい git switch と git restore が用意されています。これらのコマンドを利用できるかはインストールされている Git のバージョンによります。使えない場合は、それぞれに対応する git checkout の形式を使ってください。
ブランチを作成して切り替えるには git switch -c <branch-name> を使います。従来の形式は git checkout -b <branch-name> です。既存のブランチに切り替えるには git switch <branch-name> または git checkout <branch-name> を使います。プレースホルダーはリポジトリ内に存在するブランチ名に置き換えてください。デフォルトブランチの名前はプロジェクトによって異なります。
追跡対象ファイルのステージしていない編集を破棄するには、git restore <file> を使います。ファイルの内容はインデックスから復元されます。ステージ済みの変更はそのまま残りますが、ステージしていない編集は破棄されます。以前から使われている同等の形式は git checkout -- <file> で、動作は同じです。復元する前に git status を確認し、必要な作業は保存してください。
ファイル形式のダブルダッシュは装飾ではありません。ブランチやコミットの引数とファイルパスを区切るため、Git は名前がブランチを指すのかファイルを指すのかを推測する必要がなくなります。ブランチとファイルが同じ名前を持つ場合、git checkout <name> は曖昧で、ファイルを復元するつもりがブランチを切り替えてしまうことがあります。一方、git checkout -- <name> は常にファイルを意味します。この曖昧さは、分割されたコマンドが日常利用でより安全な理由のひとつです。
コミットのチェックアウト:デタッチした HEAD
ブランチをチェックアウトすると HEAD はそのブランチに移動し、新しいコミットはそのブランチを伸ばしていきます。代わりに特定のコミットハッシュやタグをチェックアウトすると、デタッチした HEAD の状態になります。作業ツリーはその古いスナップショットを表示し、git branch --show-current は何も出力しません。古いリリースを調べたりバグ報告を再現したりする使い方は安全です。見るだけで何も変わらないからです。一方、デタッチした状態でコミットするのは危険です。新しいコミットはどのブランチにも属さず、切り替えた後に見つけるのが難しくなります。作業を残したい場合は、先に git switch -c <branch-name> でブランチを作成してください。この状態はクローンとは無関係です。あなたは依然として同じリポジトリの中にいて、そのリポジトリがすでに保持している履歴を見ているだけです。
1 つのリポジトリで違いを見る
次の単独で実行できる例では、コミットが 3 つあるソースリポジトリを構築し、2 つのコマンドを直接対比します。浅いクローンと完全なクローンを比較し、チェックアウトのファイル形式で編集済みファイルを復元し、最後に古いコミットをチェックアウトしてデタッチ状態を示します。出力されるコミット数とファイル内容が検証になります。浅いクローンはコミットを 1 つ、完全なクローンは 3 つすべて保持し、復元は追加された下書き行を取り除き、デタッチしたチェックアウトは 2 番目のコミットに着地します。変更していない例の出力メッセージは英語のままです。
set -eu
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
git init -q -b main "$workdir/source"
git -C "$workdir/source" config user.name "Example User"
git -C "$workdir/source" config user.email "example@example.com"
printf 'one\n' > "$workdir/source/notes.txt"
git -C "$workdir/source" add notes.txt
git -C "$workdir/source" commit -qm "First"
printf 'two\n' >> "$workdir/source/notes.txt"
git -C "$workdir/source" commit -qam "Second"
printf 'three\n' >> "$workdir/source/notes.txt"
git -C "$workdir/source" commit -qam "Third"
git clone -q --depth 1 "file://$workdir/source" "$workdir/shallow"
printf 'Shallow commits: %s\n' "$(git -C "$workdir/shallow" rev-list --count HEAD)"
git clone -q "$workdir/source" "$workdir/full"
printf 'Full commits: %s\n' "$(git -C "$workdir/full" rev-list --count HEAD)"
printf 'draft\n' >> "$workdir/full/notes.txt"
git -C "$workdir/full" checkout -- notes.txt
printf 'After restore: %s\n' "$(tail -n 1 "$workdir/full/notes.txt")"
git -C "$workdir/full" checkout -q HEAD~1
printf 'Detached commit subject: %s\n' "$(git -C "$workdir/full" log -1 --format=%s)"
if [ -z "$(git -C "$workdir/full" branch --show-current)" ]; then
printf 'HEAD state: detached\n'
fi
Shallow commits: 1
Full commits: 3
After restore: three
Detached commit subject: Second
HEAD state: detached
この例の操作順序に注目してください。実際の作業をそのまま映しているからです。すべての checkout は、clone(または git init)が先に作成したリポジトリの中で実行されます。この順序を逆にすることはできません。空のディレクトリで git checkout を実行しても、選択できるブランチ、コミット、追跡対象ファイルが存在しないため失敗します。
目的に合ったコマンドを選ぶ
- リモートリポジトリの URL があり、ローカルリポジトリが必要ですか?
git clone <repository-url>を使います。 - 既存のリポジトリでブランチを作成または切り替えますか?
git switch、または対応するgit checkoutの形式を使います。 - 追跡対象ファイルの未コミットの編集を破棄しますか?
git restore <file>、または従来のgit checkout -- <file>を使います。 - 大きなリポジトリの最新スナップショットだけが必要ですか。たとえばビルド用ですか? すべてをクローンして最新のコミットをチェックアウトするのではなく、
git clone --depth 1 <repository-url>を使います。 - 古いタグやコミットの時点のコードを調べる必要がありますか? 既存のクローンの中で
git checkout <tag-or-commit>を使い、git switch <branch-name>で戻ります。リポジトリを再度クローンしないでください。 - リモートにしか存在しないチームメンバーのブランチで作業を始めますか? 既存のクローンの中でフェッチし、そのブランチをチェックアウトします。新しいクローンが必要なのは、完全に独立したコピーが欲しい場合だけです。
混乱のほとんどは 2 つの間違いから生じます。1 つ目は、リポジトリの URL を渡して git checkout を実行し、ダウンロードを期待することです。チェックアウトが受け取るのはブランチ、コミット、パスの引数であり、URL は決して受け取りません。そのため、何も取得せずに失敗します。2 つ目は、別のブランチが必要になるたびに再度クローンすることです。時間とディスク容量を無駄にし、ずれていく複数のコピーが残ります。通常、1 つのクローンでプロジェクトのすべてのブランチを扱えます。関連する誤解として、チェックアウトが選択したブランチをダウンロードするという考えがあります。チェックアウトはネットワークに一切アクセスしません。選択できるのはリポジトリがすでに知っているブランチとコミットだけであり、そのため完全に新しいリモートブランチには先に git fetch が必要になる場合があります。
要するに、git clone はリモート URL からリポジトリを取得します。git checkout は既存のリポジトリ内で選択中のブランチを切り替えるか、従来のファイル指定形式でファイルを復元します。リモート URL からローカルリポジトリを作成するコマンドではありません。