Git/GitHub

push・pull・fetch の違い

8

push はローカル→リモート、fetch はリモートの取得だけ、pullfetch +作業ブランチへの合流です。non-fast-forward で push が拒否される意味と、認証エラーの見分け方も入れます。

参考: git-push · git-pull · git-fetch · Working with Remotes


目次

  1. 3 つのコマンドの役割
  2. fetch — 取得だけする
  3. pull — 取得して合流する
  4. push — 送る
  5. upstream(追跡ブランチ)を軽く知る
  6. rejected(non-fast-forward)とは何か
  7. 認証エラーの症状
  8. よくある失敗

3 つのコマンドの役割

3 つのコマンドは、いずれもローカルとリモートの履歴をやり取りしますが、方向どこまで自動でやるかが異なります。

コマンド 方向 やること
git fetch リモート → ローカル リモートの最新履歴を 取得するだけ。作業ブランチには反映しない
git pull リモート → ローカル fetch した上で、現在のブランチに自動で合流(merge) する
git push ローカル → リモート ローカルのコミットを リモートに送る
リモート (origin)   │  fetch(取得だけ)origin/main(ローカルに保存されたリモートの写し)   │  merge(pull はここまで自動で行う)main(自分の作業ブランチ)

pullfetch + merge のショートカット」と覚えておくと、pull で予期しないコンフリクトが起きた理由(=内部で merge している)が理解しやすくなります。


fetch — 取得だけする

git fetch は、リモートに新しいコミットがあるかどうかを確認し、あれば取得するだけ です。自分の作業中のファイルやブランチには一切影響しません。

git fetch origin
remote: Enumerating objects: 5, done.remote: Counting objects: 100% (5/5), done.Unpacking objects: 100% (3/3), 300 bytes | 100 KiB/s, done.From https://github.com/<user>/<repo>   a1b2c3d..e4f5g6h  main       -> origin/main

取得した内容は origin/main という リモート追跡ブランチ に反映されます。実際に自分の main に取り込むかどうかは、この時点ではまだ決めていません。

git log --oneline main..origin/main

上のコマンドで「リモートにはあるが自分の main にはまだ無いコミット」を確認できます。まず fetch で差分を眺めてから、必要なら手動で merge するという慎重な進め方に向いています。

git merge origin/main

pull — 取得して合流する

git pullfetchmerge(既定では fast-forward が可能ならそれ、そうでなければ merge commit)を 1 コマンドで 行います。Git 2.27 以降、pull.rebase / pull.ff が未設定で履歴が分岐していると、divergent branches のような hint 警告が出ることがあります。動作自体は記述どおり merge されますが、警告を避けたい場合は git config --global pull.rebase false(merge 固定)や pull.ff only などを明示します。

git pull origin main

追跡ブランチが設定済みなら、リモート名・ブランチ名を省略できます。

git pull
remote: Enumerating objects: 5, done.Unpacking objects: 100% (3/3), done.From https://github.com/<user>/<repo>   a1b2c3d..e4f5g6h  main       -> origin/mainUpdating a1b2c3d..e4f5g6hFast-forward README.md | 2 ++ 1 file changed, 2 insertions(+)

自分もリモートも分岐して進んでいた場合は、pull の中で merge commit が作られたり、内容が競合すればコンフリクトになったりします。挙動そのものは merge とコンフリクト解消 で扱った merge と同じです。pull の途中でコンフリクトが起きたときの解消手順は、通常の merge と変わりません。


push — 送る

git push は、ローカルで進んだコミットをリモートに送ります。

git push origin main

追跡ブランチが設定済みなら、こちらも省略できます。

git push
Enumerating objects: 5, done.Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done.To https://github.com/<user>/<repo>.git   a1b2c3d..e4f5g6h  main -> main

push が成功するのは、リモートの main の先端が、自分がベースにしているコミットと同じ(かそれより前) の場合だけです。リモートが自分の知らないところで進んでいると、後述の「rejected」で拒否されます。


upstream(追跡ブランチ)を軽く知る

git push -u origin main-u--set-upstream)は、ローカルブランチとリモートブランチの 対応関係を記憶させる 設定です。

git push -u origin main

一度設定すると、以降は次のように省略形が使えます。

設定前 設定後
git push origin main git push
git pull origin main git pull
git fetch origin git fetch

現在どのリモートブランチを追跡しているかは、git status の先頭や次のコマンドで確認できます。

git status
On branch mainYour branch is up to date with 'origin/main'.
git branch -vv
* main e4f5g6h [origin/main] 最新のコミットメッセージ

「ahead」「behind」という表示が出たときは、ローカルとリモートの間にまだ同期していないコミットがあることを意味します。

* main e4f5g6h [origin/main: ahead 2] ...

これは「リモートより自分が 2 コミット進んでいる(push すれば追いつく)」状態です。


rejected(non-fast-forward)とは何か

push しようとして、次のように拒否されることがあります。

! [rejected]        main -> main (fetch first)error: failed to push some refs to 'https://github.com/<user>/<repo>.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. Integrate the remote changes (e.g.hint: 'git pull ...') before pushing again.

これは 「リモート側に、自分のローカルにはまだ無いコミットが存在する」 ことを Git が検知して、履歴を失わないように push を止めている状態です。典型的には、他の PC や別の共同作業者が先に push した場合に起きます。

自分のローカル:  A---B---C(ここまでしか知らない)リモート:        A---B---C---D(Dが追加されている)このまま push しようとすると、Dの存在を無視してしまうため拒否される

対処は force push ではなく、先に取り込むことです。

git pull

pull によってリモートの D を取り込み、必要なら自動 fast-forward、コンフリクトがあれば解消してから、改めて push します。

git push

force push(--force)はここでは扱いません。 履歴を上書きするため、共有ブランチでは他の人の作業を消す危険があります。まずは fetch / pull で状況を把握します。


認証エラーの症状

push / pull / fetch はいずれもリモートへのアクセスが必要なため、認証設定が正しくないと失敗します。代表的な症状だけ挙げます(設定手順は setup ラボを参照)。

症状 典型的な原因
Authentication failed HTTPS のパスワード欄に GitHub のログインパスワードを入れている(PAT が必要)
Permission denied (publickey) SSH 鍵が GitHub に登録されていない、または ssh-agent に鍵が登録されていない
remote: Support for password authentication was removed HTTPS で旧来のパスワード認証を試みている
fatal: could not read Username for 'https://github.com' 非対話環境(CI など)でトークンが渡されていない
ブラウザでの認証画面が急に出る/出ない Git の Credential Manager の状態が変わった(キャッシュ切れ・PAT の期限切れなど)

症状を見分けるコツは、HTTPS 系のメッセージか SSH 系のメッセージかをまず区別することです。publickey という単語が出ていれば SSH、Username / password という単語が出ていれば HTTPS の認証問題です。


よくある失敗

fetch しても何も表示されない

(何も出力されない)

これはエラーではなく、リモートに新しい変更が無い ことを意味します。git log --oneline origin/main で最新コミットを確認し、想定と合っているか確かめます。

pull したらコンフリクトになった

pull の内部は fetch + merge なので、コンフリクトが起きたら通常の merge コンフリクトと同じ手順で解消します。マーカー(<<<<<<< など)を編集し、git add してから git commit します。解消したくない場合は git merge --abort で pull 前の状態に戻せます。

push が rejected になった(再掲・落ち着いて対応)

  1. まず git fetch して状況を確認する
  2. git log --oneline main..origin/main でリモートにしかないコミットを見る
  3. git pull で取り込む(コンフリクトがあれば解消する)
  4. 改めて git push する

force push(-f / --force)で強引に上書きするのは、この段階では避けてください。 特に共有リポジトリでは、他の人のコミットを消してしまう可能性があります。

Everything up-to-date と言われるが変更したはず

Everything up-to-date

コミットせずに push しようとしていないか確認します。git statusChanges not stagedUntracked files が出ている場合、まだ add / commit していない変更は push の対象になりません。

git statusgit add .git commit -m "変更内容"git push

認証を何度も聞かれる

HTTPS の場合、Git の Credential Manager(Windows なら Git Credential Manager)がトークンをキャッシュしていないことがあります。1 度だけ手動で認証を通し、キャッシュされるか確認します。SSH の場合は ssh-agent に鍵が登録されているか、ssh -T git@github.com で疎通確認します。

シェア