git push: リモートリポジトリに変更を送る方法
git push は、現在のブランチのうちリモートにないローカルのコミットを送り、リモートのブランチの参照を最新のコミットへ進めます。初回は git push -u origin ブランチ名 で公開し、その後は git push だけで送れます。リモートに他の人のコミットがある場合は、まず git pull で取り込み、強制 push は使わないでください。
git push の働き
git push は、ローカルのブランチにあって、まだサーバーにないコミットを取り出し、リモートリポジトリ(通常は origin と呼ばれます)へ送り、そのブランチの参照を最新のコミットへ進めます。作業ディレクトリのファイルや、コミットされていない変更は送られません。送られるのは履歴、つまりコミットだけです。
push は「同期」ではないことを覚えておきましょう。方向は一方向で、ローカルからリモートへです。他の人の変更を取り込むには git fetch か git pull を使います。この方向を取り違えると、初心者が先輩に相談する問題のほとんどが起きます。
Git にはもう1つのルールがあります。既定の push は、早送り(fast-forward)でなければ許可されません。サーバーの履歴の続きとしてコミットが積まれている場合にのみ、サーバーはそれを受け入れます。他の人が先にコミットを送っていると、あなたの push は拒否されます。これはエラーではなく、保護の仕組みです。
構文
git push [<remote>] [<ローカルブランチ>[:<リモートブランチ>]]
引数なしで git push を実行すると、Git は現在のブランチを、upstream(追跡先)として設定されたブランチへ送ります。upstream がまだ設定されていない場合や、別の名前で送りたい場合は、明示的な形式が役立ちます。
git push origin main # ローカルの main を origin/main へ送る
git push origin feature/login # feature/login を送る
git push origin fix:hotfix-123 # ローカルの fix をサーバー上では hotfix-123 として送る
git push origin HEAD # 現在のブランチを同じ名前で送る
初めて変更を送る
新しいプロジェクトを GitHub や GitLab に載せるまでの典型的な手順です。まずサーバーに空のリポジトリを作成し、ローカルと関連付けて、ブランチを送ります。
git init
git add .
git commit -m "最初のコミット"
git branch -M main
git remote add origin git@github.com:user/project.git
git push -u origin main
-u(正式には --set-upstream)は、ローカルの main をリモートの origin/main に関連付けます。その後は、そのブランチで git push と git pull と書くだけで済み、git status はサーバーより何コミット進んでいるか、遅れているかを表示するようになります。
リモートがどこを指しているかは git remote -v で確認できます。アドレスが間違っている場合は、リポジトリを作り直すのではなく、git remote set-url origin <新しいアドレス> で修正してください。
新しいブランチを送る
タスク用のブランチを作り、コミットを行ってから git push を実行すると、Git は次のように表示します。
fatal: The current branch feature/login has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin feature/login
これはエラーではなくヒントです。そのブランチにはサーバー上の対応物がまだないのです。表示されたコマンドか、短い形式の git push -u origin feature/login を実行してください。新しいブランチで毎回これを書きたくない場合は、設定で自動化できます(Git 2.37 以降)。
git config --global push.autoSetupRemote true
push が拒否される: rejected、non-fast-forward
チームでの作業で最も多い状況です。
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:user/project.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.
同僚があなたより先にコミットを送っています。あなたの履歴とサーバーの履歴が分かれてしまっています。もし Git があなたの push を受け入れていたら、同僚のコミットが失われていたでしょう。正しい対応は、まず変更を取り込み、そのうえで自分のコミットを重ねることです。
git pull --rebase # 他の人のコミットを取り込み、自分のコミットを上に積み直す
# コンフリクトがあれば: ファイルを直し、git add、git rebase --continue
git push
--rebase の代わりに通常の git pull を使うこともできます。その場合はマージコミットができます。どちらも正しい方法です。多くのチームでは、自分のローカルのコミットには rebase を使い、履歴を一直線に保ちます。大切なのは、拒否された push を --force で「解決」しないことです。
git push --force と --force-with-lease
強制 push が必要になるのは、自分のブランチの履歴を意図的に書き換えた場合です。たとえば git commit --amend、git rebase、レビュー前のコミットの統合などです。古いコミットはサーバーの中で新しいコミットの祖先ではなくなるため、通常の push は受け付けられません。
git push --force-with-lease
--force-with-lease は、サーバー上のブランチが、ローカルの Git が最後に見た位置にある場合にのみ上書きします。その位置は origin/<ブランチ> の参照で判断されます。同僚がその間に何かを push していれば、コマンドは拒否され、その作業を消さずに済みます。一方 --force(-f)にはそのような確認がなく、黙って全部上書きします。
注意点があります。強制 push の前に git fetch を実行すると、origin/<ブランチ> の参照が更新され、この保護が効かなくなります。Git は、あなたが同僚のコミットを「見た」と判断してしまうからです。fetch したら、何が届いたかを確認してから強制 push してください。
| コマンド | 動作 | 使いどころ |
|---|---|---|
git push | 早送りのみ | 常に既定で使う |
git push --force-with-lease | 確認付きの上書き | 自分のブランチを rebase または amend した後 |
git push --force | 確認なしの上書き | ほぼ使わない。共有ブランチには絶対に使わない |
main や develop のような共有ブランチへの強制 push は禁止です。古い履歴を取得した人の履歴と、サーバーの履歴が食い違ってしまうからです。こうしたブランチは通常リポジトリの設定で保護されており、サーバーは protected branch hook declined を返します。
タグを送る
通常の git push はタグを送りません。明示的に指定する必要があります。
git tag -a v1.2.0 -m "リリース 1.2.0"
git push origin v1.2.0 # 1つのタグ
git push --tags # ローカルのすべてのタグ
git push --follow-tags # コミットと、それを指す注釈付きタグ
--follow-tags は --tags より便利です。ローカルの軽量タグを誤って送らず、送るコミットから到達できる注釈付きタグだけを送ります。
リモートのブランチを削除する
削除も push で行います。
git push origin --delete feature/login
# 古い書き方(同じ動作):
git push origin :feature/login
ローカルのブランチは残るので、git branch -d feature/login で削除します。また、同僚の環境には origin/feature/login の参照が残っているため、git fetch --prune を実行してもらう必要があります。
git push の便利なオプション
| オプション | 用途 |
|---|---|
-u、--set-upstream | リモートのブランチを upstream として記憶する |
-n、--dry-run | 送られる内容を表示するだけで、実際には送らない |
--all | すべてのローカルブランチを送る |
--tags / --follow-tags | タグを送る |
-d、--delete | サーバー上のブランチまたはタグを削除する |
--force-with-lease | 安全に履歴を書き換える |
-v、--verbose | 詳細な出力を表示する |
--no-verify | pre-push フックを飛ばす(理由を理解したうえで使う) |
git push のよくあるエラーと解決法
Permission denied (publickey)
サーバーが SSH 鍵を受け入れていません。鍵が存在すること(ls ~/.ssh)、公開鍵がアカウントに登録されていること、接続が通ることを確認します: ssh -T git@github.com。リモートが HTTPS で指定されている場合、Git は鍵ではなくログイン名とトークンを求めます。git remote -v で確認してください。
HTTPS での Authentication failed
GitHub などの多くのサービスは、HTTPS の操作でアカウントのパスワードを受け付けなくなっています。パーソナルアクセストークンを使うか、SSH に切り替える必要があります。認証情報を保存するには、credential helper を使います: git config --global credential.helper store(平文で保存されます)、またはOSの認証情報マネージャーを使います。
src refspec main does not match any
そのブランチが存在しません。空のリポジトリでまだコミットが1つもないか、ブランチ名が master のままであることが多いです。git branch で確認し、コミットを作るか、git branch -M main で名前を変えてください。
Everything up-to-date なのに変更がある
コミットを忘れています。git push が送るのはコミットであり、ファイルではありません。git status を実行して、git add と git commit を行ってください。また、想定しているブランチと違うブランチにいる可能性もあります。
ファイルが大きすぎる
サービスには1ファイルのサイズ制限があります(GitHub では 100 MB)。そのようなファイルがすでに履歴に入っている場合、新しいコミットで削除するだけでは不十分です。履歴を書き換えるか、まだ送っていないコミットから取り除きます(git reset --soft の後に作り直す)。大きなバイナリファイルには Git LFS を使います。
作業の流れ
git switch -c feature/x: 作業用のブランチを作る。- 小さな論理的なステップごとにコミットする。
git fetchとgit rebase origin/main: 最新の主要ブランチを取り込む。git push -u origin feature/x: ブランチを公開する。- pull request を作る。レビューの後は、追加のコミットを作り、通常の
git pushを行う。 - 履歴を書き換えた場合は
git push --force-with-lease。
送る前に、サーバーへ何が行くかを確認すると安心です。git log origin/main..HEAD --oneline で、サーバーにまだないコミットを確認できます。
一度だけ設定しておくこと
いくつかのグローバル設定で、push の驚きの多くを防げます。push.default は、引数なしの git push が何を送るかを決めます。新しいバージョンの既定値は simple で、現在のブランチだけを、同名の upstream ブランチへ送ります。通常は変更不要ですが、古い設定に、同名のブランチをすべて一度に送る matching が残っていないか確認しておくとよいでしょう。
git config --global push.default simple
git config --global push.autoSetupRemote true
git config --global push.followTags true
git config --global pull.rebase true
push.followTags は --follow-tags を既定の動作にします。pull.rebase は、push が拒否されて他の人の変更を先に取り込む必要があるときに、余計なマージコミットを避けます。
pre-push フック
.git/hooks/pre-push は、push の前に実行されます。ゼロ以外の終了コードで終わると、push は行われません。チームはこのフックでテストや linter を実行し、壊れたコードがサーバーに届かないようにすることがよくあります。--no-verify でフックを回避するのは、なぜ失敗しているのかを正確に理解しているときだけにしてください。
まとめ
git push はあなたのコミットをリモートリポジトリに公開し、一方向にだけ働きます。ブランチの初回送信には -u を付け、拒否されたら先に他の人の変更を pull で取り込みます。書き換えた自分のブランチの履歴は --force-with-lease でだけ送ります。リモートリポジトリ、push、pull、SSH 鍵の扱いは、Gitコースで実践的に解説しています。
よくある質問
git push は未コミットの変更も送りますか?
いいえ。push が送るのはコミットだけです。作業ディレクトリとインデックスの変更は、コミットするまでローカルに残ります。
git push を取り消すにはどうすればよいですか?
自分のブランチなら、ローカルでコミットを取り消し(git reset)、git push --force-with-lease で送ります。共有ブランチでは、git revert で逆向きのコミットを作り、通常の push をするほうが安全です。
1つのコミットだけを送ることはできますか?
はい。ハッシュを指定します: git push origin abc1234:main。これはそのコミットまでの履歴を送ります。それより後のコミットは送られません。親のコミットを残してコミットだけを送ることはできません。
git push と git push origin main の違いは何ですか?
引数なしの場合、Git は現在のブランチに設定された upstream を使います。明示的な形式は、リモートとブランチを直接指定するので、upstream が設定されていなくても動きます。
2つのリポジトリに同時に push するにはどうすればよいですか?
2つ目のリモートを追加し、名前を指定してそれぞれに送ります。または、origin に送信先を2つ設定します: git remote set-url --add --push origin アドレス。この場合、元のアドレスも含めて両方を設定しないと、片方にしか送られません。