GitHubから受け取るコマンドは3つあり、名前が似ているので混ざります。git clone、git fetch、git pull です。
この記事では、サンプル料理教室の代表の手元と外注エンジニアBの手元を実際に作り、Bが送った変更を代表が受け取るところまでを動かします。読み終わると、どの場面でどれを打てばよいかが分かり、pullが止まったときの英語も読めるようになります。
この記事の内容
先に答えると
cloneは最初の1回だけ打つ丸ごとの複製です。fetchはGitHubの新しい変更を確認するだけで、手元のファイルは変わりません。pullはfetchをしたうえで手元に反映します。ふだん使うのはpullで、cloneは最初だけ、fetchは反映する前に中身を見たいときに打ちます。
- 手元にまだ何もないなら
git clone。 - 手元にあって、最新にしたいなら
git pull。 - 反映せずに、何が来ているか見たいだけなら
git fetch。 - forkはGitHubの中での複製で、cloneとは別の話です。自社のリポジトリに招待されているなら、forkは要りません。
- Mac1台で一人で使っているなら、pullの出番は多くありません。
3つの違い
3つの違いは、手元の何が変わるかで見ると分かります。
| コマンド | いつ打つか | 手元のファイル | 手元の履歴 |
|---|---|---|---|
git clone | 最初の1回 | 新しいフォルダに全部できる | 全部の履歴が入る |
git fetch | 中身を見てから反映したいとき | 変わらない | GitHub側の控えだけ更新される |
git pull | 作業を始める前など、ふだん | 最新になる | 手元の枝(ブランチ)まで更新される |
レシピの束に例えると、cloneはレシピ帳を丸ごと1冊コピーすること、fetchは新しいレシピの封筒を受け取って机に置くこと、pullは封筒を開けてレシピ帳に綴じることです。Git公式の説明でも、git fetch はデータを取ってくるだけでマージはしないとされ、git pull はfetchと合流を続けて行うものとされています。

forkはcloneと何が違うか
fork(フォーク)は、GitHubの中で他の人のリポジトリを自分のアカウントかOrganizationへ複製する機能です。cloneは手元のパソコンへ複製します。
| fork | clone | |
|---|---|---|
| 複製先 | GitHub上の自分のアカウントかOrganization | 手元のパソコン |
| 使う場面 | 書き込み権限のないリポジトリに手を加えて提案したい | 手元でファイルを編集したい |
| 操作する場所 | GitHubの画面 | コマンドや手元の道具 |
サンプル料理教室の website のように、Organizationの中で招待されて権限があるリポジトリなら、forkは使いません。cloneして、枝(ブランチ。履歴の流れの1本)を作って送れば済みます。forkの出番は、誰かが公開しているツールを自分用に直したいときくらいです。
代表とBの手元を作って動かす
ここからの表示は、すべて実際に動かしたものです。GitHubの代わりに、手元のフォルダに練習用のリモート(共有の置き場となるリポジトリ)を作り、代表用とB用のフォルダにそれぞれ複製しました。そのため送り先の場所がフォルダの形で出ます。長いフォルダ名は … に縮めました。本物のGitHubなら https://github.com/sample-cooking/website.git のようなURLになります。
cloneで複製する
$ git clone file://…/remote/website.git
Cloning into 'website'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (5/5), done.
website というフォルダができます。Gitで動かすときは、そのフォルダの中へ移動します。中を見ると、ファイルと一緒に履歴の入った .git フォルダがあります。
$ ls -a
. .git menu.html
.. index.html style.css
$ git remote -v
origin file://…/remote/website.git (fetch)
origin file://…/remote/website.git (push)
$ git log --oneline
58768ea (HEAD -> main, origin/main, origin/HEAD) ホームページの最初の版を登録
HEAD -> main は今いる枝が main だという印です。origin は、複製元を指す名前です。cloneすると自動で付くので、自分で登録する必要はありません。origin/main は、GitHub側の main が最後に確かめたときどこにいたかの控えです。手元の main とは別に持っています。
Bが送る
Bが自分の手元で料金表を直して、pushします。
$ git commit -m "料金表を修正"
[main d3a2f25] 料金表を修正
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 11 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 386 bytes | 386.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
To file://…/remote/website.git
58768ea..d3a2f25 main -> main
代表が確かめる。statusだけでは分からない
この直後に、代表が自分の手元で git status を打ちます。
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
Bが送った直後なのに「up to date(最新です)」と出ました。ここがいちばんの落とし穴です。git status はGitHubに確かめに行きません。手元にある控えと比べているだけです。
fetchで確かめる
$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 366 bytes | 183.00 KiB/s, done.
From file://…/remote/website
58768ea..d3a2f25 main -> origin/main
$ git status
On branch main
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
nothing to commit, working tree clean
「behind ‘origin/main’ by 1 commit」は、手元の main がGitHubより1つ遅れているという意味です。「can be fast-forwarded」は、手元には新しいコミットがないので、先頭を進めるだけで追いつけるという意味です。
fetchではファイルは変わっていません。料金表はまだ3,000円のままです。
$ grep 体験 menu.html
<p>体験レッスン 3,000円</p>
反映する前に、何が来たかを見られます。
$ git log --oneline main..origin/main
d3a2f25 (origin/main, origin/HEAD) 料金表を修正
$ git diff main origin/main
diff --git a/menu.html b/menu.html
index 27bcd2a..d7374cb 100644
--- a/menu.html
+++ b/menu.html
@@ -1,5 +1,5 @@
<h1>料金表</h1>
-<p>体験レッスン 3,000円</p>
+<p>体験レッスン 3,500円</p>
<p>月謝 12,000円</p>
<p>道具代 500円</p>
<p>入会金 5,000円</p>
main..origin/main は、「手元の main になくて、origin/main にあるもの」の指定です。- が今の手元、+ が受け取る内容です。

pullで反映する
$ git pull
Updating 58768ea..d3a2f25
Fast-forward
menu.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ grep 体験 menu.html
<p>体験レッスン 3,500円</p>
$ git log --oneline
d3a2f25 (HEAD -> main, origin/main, origin/HEAD) 料金表を修正
58768ea ホームページの最初の版を登録
$ git pull
Already up to date.
fetch済みなので、今回は受信の行が出ずに反映だけが走りました。ふつうは先にfetchせず、git pull だけを打ちます。その場合は受信の行のあとに、同じ表示が続きます。「Already up to date.」は、もう受け取るものがないという表示です。
作業を始める前にpullする
実際に運用していて、事故がいちばん減るのは、ファイルを開く前に git pull を打つ習慣です。Bが直した古い料金表の上から代表が編集を始めると、後で合流させる手間が増えます。先に受け取っておけば、ほとんどの場合、次の章で出てくるエラーに会いません。
一方で、Mac1台で一人だけが触っているリポジトリなら、受け取るものが発生しないのでpullの出番は多くありません。それでも要るのは次のようなときです。
- GitHubの画面でファイルを直接編集したとき
- 2台目のパソコンでも作業しているとき
- 外注エンジニアBが同じリポジトリにpushするとき
pullで起きる4つのこと
pullの結果は、手元の状態で4通りに分かれます。
| 手元の状態 | 起きること | 表示 |
|---|---|---|
| 手元に新しいコミットがない | 先頭を進めるだけ | Fast-forward |
| 未コミットの編集がぶつかる | 止まる。手元のファイルは変わらない | Your local changes ... would be overwritten |
| お互いにコミットがある | 指定がないと止まる | divergent branches |
| お互いのコミットを合流する指定あり | マージする | Merge made by the 'ort' strategy. |
fast-forwardは、先ほど見たとおりです。残りの3つを順に再現します。
未コミットの編集があると止まる
代表がキャンセル料を編集している途中で、Bが月謝を直してpushしました。同じ menu.html です。
$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 364 bytes | 182.00 KiB/s, done.
From file://…/remote/website
d3a2f25..bfd8b28 main -> origin/main
Updating d3a2f25..bfd8b28
error: Your local changes to the following files would be overwritten by merge:
menu.html
Please commit your changes or stash them before you merge.
Aborting
「このまま反映すると、手元の編集が上書きされるので止めた」という意味です。編集は消えていません。直し方は2つあります。編集が区切りのよいところまで進んでいるなら、先にコミットします。途中なら、git stash で一時的によけて、pullしてから戻します。
$ git stash
Saved working directory and index state WIP on main: d3a2f25 料金表を修正
$ git pull
Updating d3a2f25..bfd8b28
Fast-forward
menu.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git stash pop
Auto-merging menu.html
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: menu.html
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (0dbf1b4d6e02b0b67ebce5a8d17b29d4e6551b62)
Bの月謝の修正と、代表のキャンセル料の編集が、両方入った状態に戻りました。
お互いがコミットしていると止まる
代表が「夏の教室を追加」をコミットしたところへ、Bが先に「見出しの色を修正」をpushしていました。fetchして見ると、枝分かれしています。
$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean
$ git log --oneline --graph --all
* 5cf712d (HEAD -> main) 夏の教室を追加
| * cab72ff (origin/main, origin/HEAD) 見出しの色を修正
|/
* e9e7eeb キャンセル料を修正
* bfd8b28 月謝を修正
* d3a2f25 料金表を修正
* 58768ea ホームページの最初の版を登録
この状態で git pull を打つと、Gitは止まります。
$ git pull
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
長いですが、言っているのは「枝分かれしているので、合流のやり方を指定してください」だけです。
マージで合流する
最初のうちは、マージ(2つの流れを1つに合わせること)を選ぶのが分かりやすいです。--no-rebase がその指定です。合流のメッセージを書くエディタが開いたら、既定の文言のまま保存して閉じます。
$ git pull --no-rebase
Merge made by the 'ort' strategy.
style.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph
* 4e3586a (HEAD -> main) Merge branch 'main' of file://…/remote/website
|\
| * cab72ff (origin/main, origin/HEAD) 見出しの色を修正
* | 5cf712d 夏の教室を追加
|/
* e9e7eeb キャンセル料を修正
* bfd8b28 月謝を修正
* d3a2f25 料金表を修正
* 58768ea ホームページの最初の版を登録
このあと git push を打てば、合流した結果がGitHubへ届きます。毎回指定するのが面倒なら、リポジトリごとに git config --local pull.rebase false と設定しておくと、次からは git pull だけでマージまで進みます。
git pull –rebase は最初は使わなくていい
エラーに出てくるもう1つの選択肢がrebase(リベース)です。git pull --rebase は、自分のコミットをいったん外して、受け取ったコミットの後ろに付け直します。履歴が枝分かれせずに1本で並ぶので、好む人もいます。
ただ、コミットを作り直す操作なので、途中で止まったときの扱いが少し難しくなります。小さな会社で最初から使う必要はありません。外注エンジニアBのリポジトリで「rebaseで揃えてください」と言われたときに、調べれば足ります。

できないことと、その代わり
| できないこと | 代わりにどうするか |
|---|---|
| fetchだけで手元のファイルを更新する | git pull を打つ |
git status だけでGitHubの最新を知る | 先に git fetch を打つ |
| 「Download ZIP」で履歴ごと受け取る | ZIPは履歴を含まない。履歴も要るならcloneする |
| 招待されていない非公開リポジトリをcloneする | 管理者に招待してもらう |
| GitHubのアカウントがあるだけで手元からcloneする | 非公開リポジトリは認証の設定が要る。「Gitのインストールと初期設定:MacとWindows、名前とメールと認証」を先に |
よく詰まるところ
destination path already exists と出る
$ git clone file://…/remote/website.git
fatal: destination path 'website' already exists and is not an empty directory.
同じ名前のフォルダがすでにあります。もう一度cloneする必要はほとんどなく、そのフォルダに入って git pull を打てば最新になります。
not a git repository と出る
$ git status
fatal: not a git repository (or any of the parent directories): .git
原因は、リポジトリの外にいることです。cloneで website ができたあと、その中へ移動していないときによく出ます。cd website で入ってから打ち直します。
Repository not found と出る
GitHubの公式ページによると、リポジトリが存在しないか、見る権限がないときに出る表示です。URLの打ち間違いを避けるため、リポジトリの画面からコピーします。非公開リポジトリなら、招待を受けているかを担当者に確かめます。
There is no tracking information と出る
作ったばかりの枝で git pull を打つと出ます。
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> add-summer-class
この枝にはまだ追いかける先がないので、どこから受け取ればよいか分からないという意味です。その枝をまだ誰も送っていないなら、受け取るものはありません。git push -u origin add-summer-class で一度送れば、次からは git pull が通ります。
Bが送ったと言うのに Already up to date と出る
多くの場合、Bが送ったのが別の枝です。git fetch のあとに git branch -a を打つと、GitHub側の枝も一覧で見られます。remotes/origin/ で始まる行が、GitHub側の枝の控えです。その枝をBが何という名前で送ったかを聞いて、git switch 枝の名前 で移ります。
pullしたら CONFLICT と出た
お互いが同じ行を変えていると、Gitは自動で合流できません。
$ git pull --no-rebase
Auto-merging menu.html
CONFLICT (content): Merge conflict in menu.html
Automatic merge failed; fix conflicts and then commit the result.
実際の表示では、この前に受信の行が並びます。壊れたわけではありません。やめて元に戻したいなら git merge --abort を打つと、pullの前の状態に戻ります。直し方は「コンフリクトの直し方:起きる理由と、解消の手順」で扱います。

次に読む
送る側の話は「git add・commit・pushの違い:保存して送るまでの流れ」にあります。枝分かれと合流をきちんと理解するなら「git branchとmergeの使い方:枝を分けて合流する、GitHub Flowまで」へ。
同じ行がぶつかったときは「コンフリクトの直し方:起きる理由と、解消の手順」、cloneの前の準備は「Gitのインストールと初期設定:MacとWindows、名前とメールと認証」に書いています。