Git の Push コマンドの使い方


ローカルレボジトリの変更内容をリモートレポジトリにアップロードするコマンド Git Push の使い方を紹介します.

Git Push はローカルレポジトリのコミットをリモートレポジトリにアップロードします. なのでリモートからコミットの内容をダウンロードする Git Fetch や Git Pull コマンドの対にあたるコマンドになります.

ローカルのコミットヒストリが, git commit --amend によって書き換えられたり, 最後に git fetchgit pull した状態から他のコントリビュータによるコミットによって書き換えられたりしていなければ, そのまま git push でローカルのコミットをリモートにアップロードすることができますが, そうでない場合, git push は失敗します.

そのような場合でも無理矢理ローカルのコミットを git push --force でリモートにアップロードすることもできますが, 他のコントリビュータがいない場合はいいですが, そうでない場合限られた場合でのみ使うことが許されます.

Git Push の使い方

それぞれの状況に応じた git push の使い方を紹介します.

コントリビュータが自分一人だけ

コミットヒストリを書き換えていない

コントリビュータが自分一人だけで, git resetgit commit --amend などでコミットヒストリを書き換えていない場合, ローカルとリモートのコミットヒストリは一致するので, そのまま git push できます.

例えば次のようなコミットヒストリの場合:

A---B---C---D <- master <- HEAD
    ^
    |
    origin/master

そのまま次のコマンドを打つと:

git push origin master

次のようになり:

A---B---C---D <- (master <- HEAD, origin/master)

ファストフォワード Push となり, C, D のローカルのコミットをそのままリモートに送信することができます.

コミットヒストリを書き換えている

コントリビュータが自分一人だけで, git resetgit commit --amend などでコミットヒストリを書き換えた場合, --force オプションをも使うことによって無理矢理リモートのコミットヒストリをローカルのコミットヒストリに一致させることができます.

例えば次のようなローカルの master とリモートの master が一致しているコミットヒストリで:

A---B---C <- master <- HEAD
        ^
        |
        origin/master

C のコミットになんらかの不具合があったので, 不具合を修正し git commit --amendC のコミットを書き換えた場合, 次のようにコミットヒストリが変化します:

A---B---D <- master <- HEAD
     \
      \
       C
       ^
       |
       origin/master

ご覧のように B まではローカルとリモートのコミットヒストリは一致するのですが, git commit --amend したことによって CD とにコミットが B から分岐してしまいました.

なので新しく書き換えられた A, B, D というローカルのコミットヒストリに無理矢理リモートのコミットヒストリを一致させる場合, 次のコマンドでできます:

git push --force origin master

するとコミットヒストリは次のようになります:

A---B---D <- (master <- HEAD, origin/master)

このようにコントリビュータが自分一人だけでしたら, git commit --amend などで既存のコミットヒストリを書き換えたとしても無理矢理 git push --force でローカルのコミットヒストリにリモートのコミットヒストリを一致させることができます.

コントリビュータが自分以外にいる

コントリビュータが自分以外にいる場合, 自分が最後に git fetch した時のリモートのコミットヒストリから現在のリモートのコミットヒストリが変わっている可能性がありますので, まず最初に git fetch してリモートのコミットヒストリをダウンロードします:

git fetch origin master

そして次のようにローカルのコミットヒストリとリモートのコミットヒストリが異なっていた場合:

               A---B---C <- origin/master
              /
             /
    D---E---F---G---H <- master <- HEAD

このままでは git push しても失敗するのであらかじめ git merge したり git rebase する必要があります.

それぞれの場合のやり方を紹介します:

git merge の場合

そのままリモートのコミット C をローカルのコミット H にマージさせる場合, 次のコマンドでできます:

git merge origin/master

そしてマージコンフリクトが発生した場合, コンフリクトを解消し git add <file> でコンフリクトが解消されたというマークをし, git commit でマージを完了させます.

すると CH がマージした I というコミットが作られます:

               A---B---C <- origin/master
              /         \
             /           \
    D---E---F---G---H-----I <- master <- HEAD

そしたら次のコマンドで:

git push origin master

このようになり, git push を完了させることができます:

               A---B---C
              /         \
             /           \
    D---E---F---G---H-----I <- (master <- HEAD, origin/master)

git rebase の場合

そのまま git mergeCH をマージさせて I というコミットを作ってもいいのですが, コミットヒストリが一直線でなくなりやや複雑になってしまうので, コミットヒストリを一直線にさせたいという場合は git merge の代わりに git rebase を使うこともできます.

次のコマンドを入力すると:

git rebase origin/master

このように今度はコミットヒストリが変化します:

    D---E---F---A---B---C---G---H <- master <- HEAD
                        ^
                        |
                        origin/master

もし git merge の時のようにマージコンフリクトが発生した場合は, コンフリクトを解消し, git add <file> でコンフリクトが解消されたというマークをし, git rebase --continue でリベースを完了させます.

リベースが完了したら, 次のコマンドを入力し:

git push origin master

ローカルのコミットをリモートにアップロードすることができます:

    D---E---F---A---B---C---G---H <- (master <- HEAD, origin/master)

まとめ

このように git push はローカルのコミットをリモートにアップロードし, ローカルとリモートのレポジトリを同期させるためのコマンドです.

コントリビュータが自分一人だけの場合は, 他の人のコミットというものが存在しないので git fetchgit merge を使う必要はなく, そのまま git push したり, 場合によっては git push --force で無理矢理アップロードすることもできます.

ただ, コントリビュータが自分以外にもいる場合は, git fetchgit merge で他の人のコミットを自身のローカルのレポジトリに取り込んだ上で, git push する必要があります.

そのような共有のレポジトリで git push --force することは, 他の人のコミットをなかった事にしてしまう可能性があり, コミットヒストリも書き換えてしまって混乱を呼んでしまう事にもなりかねないので, 共有のレポジトリで git push --force することは本当に必要な場合を除いて避けるべきだと思います.

git push はそのような事態を引き起こすリスクがあるので, 使われるときは注意して使う必要があるコマンドです.

ただ git push は Git の操作において必要不可欠な役割を担っているコマンドですので, 上手に付き合っていきたいコマンドです.