push はローカル→リモート、fetch はリモートの取得だけ、pull は fetch +作業ブランチへの合流です。non-fast-forward で push が拒否される意味と、認証エラーの見分け方も入れます。
参考: git-push · git-pull · git-fetch · Working with Remotes
目次
- 3 つのコマンドの役割
- fetch — 取得だけする
- pull — 取得して合流する
- push — 送る
- upstream(追跡ブランチ)を軽く知る
- rejected(non-fast-forward)とは何か
- 認証エラーの症状
- よくある失敗
3 つのコマンドの役割
3 つのコマンドは、いずれもローカルとリモートの履歴をやり取りしますが、方向とどこまで自動でやるかが異なります。
| コマンド | 方向 | やること |
|---|---|---|
git fetch |
リモート → ローカル | リモートの最新履歴を 取得するだけ。作業ブランチには反映しない |
git pull |
リモート → ローカル | fetch した上で、現在のブランチに自動で合流(merge) する |
git push |
ローカル → リモート | ローカルのコミットを リモートに送る |
リモート (origin) │ fetch(取得だけ) ▼origin/main(ローカルに保存されたリモートの写し) │ merge(pull はここまで自動で行う) ▼main(自分の作業ブランチ)「pull は fetch + merge のショートカット」と覚えておくと、pull で予期しないコンフリクトが起きた理由(=内部で merge している)が理解しやすくなります。
fetch — 取得だけする
git fetch は、リモートに新しいコミットがあるかどうかを確認し、あれば取得するだけ です。自分の作業中のファイルやブランチには一切影響しません。
git fetch originremote: 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/mainpull — 取得して合流する
git pull は fetch と merge(既定では 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 pullremote: 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 pushEnumerating 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 -> mainpush が成功するのは、リモートの 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 statusOn 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 pullpull によってリモートの D を取り込み、必要なら自動 fast-forward、コンフリクトがあれば解消してから、改めて push します。
git pushforce 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 になった(再掲・落ち着いて対応)
- まず
git fetchして状況を確認する git log --oneline main..origin/mainでリモートにしかないコミットを見るgit pullで取り込む(コンフリクトがあれば解消する)- 改めて
git pushする
force push(-f / --force)で強引に上書きするのは、この段階では避けてください。 特に共有リポジトリでは、他の人のコミットを消してしまう可能性があります。
Everything up-to-date と言われるが変更したはず
Everything up-to-dateコミットせずに push しようとしていないか確認します。git status で Changes not staged や Untracked 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 で疎通確認します。