Git入門:バージョン管理のきほん
push と pull で同期する
この回でやること
手元とリモートを行き来させる push と pull を学びます。pull が fetch と merge の 2 段構えであること、push が弾かれたときの正しい直し方、force push の危険までを押さえましょう。
- 読む 約 8 分
コミットしていない編集は、いくら push しても届かない
push と pull は、手元とリモートの間でコミットをやりとりする操作です。運ばれるのがコミットだという点が大事で、ファイルの中身が同期されているのではありません。
ターミナル
$ git status
modified: src/app.js
$ git push
Everything up-to-date編集が残っているのに Everything up-to-date と言われて戸惑うのは誰もが通る道です。コミットしていないので、Git から見れば送るものが無いのです。
作ったばかりのブランチを push すると、今度は上流ブランチが無いと怒られます。上流ブランチとは「手元のこのブランチは、リモートのあれと対応する」という結びつきです。
ターミナル
$ git push -u origin feature-login
branch 'feature-login' set up to track 'origin/feature-login'.-u が要るのは最初の 1 回だけで、以後は引数なしの push と pull が使えます。結びつきができると git status に ahead by 2 のようにずれも出ます。
pull を打った瞬間に、相手の変更と合流させられる
git pull は単独のコマンドに見えますが、実際には 2 つの操作を続けて実行しています。リモートの最新を取ってくる fetch と、それを自分のブランチに合流させる merge です。下の 2 行は git pull とほぼ同じです。
ターミナル
$ git fetch origin
$ git merge origin/mainfetch は取ってくるだけ、pull は取ってきて合流までやる。 何が起きているか分からなくなったら、pull ではなく git fetch だけを打ってください。手元が変わらないので、確認してから次を決められます。
rejected を force で押し通すと、相手の半日が消える
チーム開発で必ず出会います。
ターミナル
$ git push
! [rejected] main -> main (fetch first)
hint: Updates were rejected because the remote contains work that you dorejected という強い言葉が出ますが、これは Git があなたを守っている状態です。作業している間に別の人が同じブランチへ push していて、リモートにはあなたが持っていないコミットがあります。ここで push を通せば、それが履歴から消えます。だから断っています。
正しい直し方は 3 手です。git pull で相手の変更を取り込み、競合が出たら解決してコミットし、push し直す。検索すると git push --force という答えが見つかりますが、これはエラーを消すと同時に相手のコミットも消します。リモートの履歴を手元のもので置き換える命令だからです。相手の半日分の作業が消えたという事故は、ほぼ例外なくここから起きます。
上書きが正しい場面もあります。個人ブランチで rebase や commit --amend をするとハッシュが変わり、普通の push は弾かれます。そのときも --force ではなく --force-with-lease を使ってください。
ターミナル
$ git push --force-with-leaseこれは上書きの前に「リモートの先端は、自分が最後に fetch した位置と同じか」を確かめ、違えば stale info と言って止まります。素の --force にこの確認はありません。main のような共有ブランチには、--force-with-lease であっても打たないのが原則です。
- push と pull が運ぶのはコミット。コミットしていない編集は送られない
git pullは fetch と merge の 2 段構え。fetch は取ってくるだけ- 弾かれたら
git pullで取り込んでから push し直す。上書きが要る個人ブランチでも--force-with-leaseを使う