【gitをソフト開発で使いこなそう!:第11回】git pushで引数を省略すると何が起こる?

次回の記事

terapotan.hatenablog.jp

前回の記事

terapotan.hatenablog.jp

連載記事一覧

  terapotan.hatenablog.jp

今回は、「git push」コマンドについて解説していきます。このコマンドの動作自体はシンプルなのですが、ブランチ名などを省略したときの動作が複雑です。
その動作を詳しく解説していきます。

変更履歴

2020年4月18日 コラム:push.defaultを追加。
参考文献に、git-configのマニュアルを追加。
注意!:git push -fの使用は慎重に、慎重にを追加。

2020年4月22日 git push --deleteの動作説明を「リモートリポジトリを削除する」から「リモートブランチを削除する」に修正。

リモートリポジトリとローカルリポジトリ

リモートリポジトリというと、ローカルリポジトリとは何か別の構造をしたもののように感じます。
ですが、実際にはリモートリポジトリの構造もローカルリポジトリと同じです。

同じ構造だよ

上の四角がリモートリポジトリを表し、下の四角がローカルリポジトリを表しています。

ローカル/リモートブランチ・上流ブランチ・リモート追跡ブランチ

リモート追跡ブランチ

先ほどの図には、いくつかブランチがあります。
これらのブランチには、種類ごとに名前が付いています。

まずローカルリポジトリにあるブランチはローカルブランチ、リモートリポジトリにあるブランチはリモートブランチと呼ばれます。
ローカルリポジトリには、origin/masterという名前のブランチがあります。
このブランチは、リモートリポジトリoriginのリモートブランチmasterを追跡しています。

(originというのは、リモートリポジトリに付けられた名前です。単なる名前であるため、別にtestでもmyrepoでも構いません。今回の例では、originと名付けています。)

このようなブランチをリモート追跡ブランチと言います。

「追跡している」とは、どういうことでしょうか。

例えば下のような場面を考えてみましょう。リモートリポジトリが1つ、ローカルリポジトリが2つあるような場面です。

想定している場面

ここで、ローカルリポジトリBに変更を加えます。(コミットを行う。)

変更を加える

続いて、ローカルリポジトリBからリモートリポジトリoriginに変更を反映(プッシュ)します。

すると、リモートリポジトリにあるmasterブランチが移動します。
ローカルリポジトリBにあるorigin/masterは、リモートリポジトリにあるmasterブランチを追跡しているため、masterブランチの動きを追いかけるように、origin/masterも動きます。

動く

またローカルリポジトリAも、「リモートリポジトリで起こった変更を取り込むコマンド」(次回解説します。)を使うと、ローカルリポジトリAにあるorigin/masterが、リモートリポジトリにあるmasterの位置まで動きます。

ローカルリポジトリAの動作

今回の記事では、あまりリモート追跡ブランチを使っている意味がよくわからないかもしれません。
ですが、次回やる「リモートリポジトリの変更を、ローカルリポジトリに取り込む」ときに重要な役割を果たします。

Column:リモート追跡ブランチにチェックアウトすることは出来ない!

git checkout origin/masterのようにして、リモート追跡ブランチに移動することは出来ません。
そのため、リモート追跡ブランチを普通のブランチのように使うことは出来ません。
ここからも、リモート追跡ブランチは特殊なブランチであることがわかります。

上流ブランチ

……また謎な用語が出てきました。

一番最初に挙げた例を使いましょう。

一番最初に挙げた例

gitでは、あるリモート追跡ブランチをあるローカルブランチの上流ブランチと呼ばれるものに設定することが出来ます。

わかった。で、上流ブランチって何?――と思われるかもしれませんが、あまり上流ブランチの意味は考えず「gitには、上流ブランチというよくわからんものをローカルブランチに設定出来る機能がある」と割り切った方がいいです。

一応、参考文献に載せてあるgitのマニュアルには、上流ブランチについて解説されていますが、あまりピンと来ないと思います。

何でこんなの解説するの?

リモート追跡ブランチ、上流ブランチといういまいちピンとこない単語が二つも出てきました。
よくわからないものは、出来るだけ避けて通りたいものですが、この二つは次の解説するgit pushコマンドや次回出てくるコマンドの動作を説明する時に出てくるのです。

これを理解しておかないと、何を言っているのかさっぱり分からなくなってしまいます。

git push

ローカルブランチのmasterの変更を、リモートブランチoriginmasterに反映する時、gitでは次のコマンドを入力することで、変更を反映出来ます。(リモート追跡ブランチの図で出てきた【1:Push】の操作にあたる。)

git push origin master:master

上のコマンドの意味は、下の通りです。

git push <リモートブランチにつけた別名> <ローカルブランチの名前>:<リモートブランチの名前>

ですから、もしリモートリポジトリendにあるリモートブランチdevelopに、ローカルブランチmouseの変更を反映させたいとすると、

git push end mouse:develop

と入力すればいいことになります。

リモート追跡ブランチの移動

git pushコマンドを実行すると、リモートブランチが移動することになります。
リモート追跡ブランチは、リモートブランチと同期しているため、リモート追跡ブランチも移動することになります。

ブランチ名を省略する

最初に取り挙げた例では、次のようなコマンドを入力しました。

git push origin master:master

ローカルブランチの名前とリモートブランチの名前が同じです。
このような時、gitではmaster:mastermasterに省略することが出来ます。

git push origin master

複数のブランチを同時にプッシュする

先ほどの例では、masterブランチ一つだけをプッシュしましたが、下のように空白区切りでブランチ名を書くことで、複数のブランチを一気にプッシュすることが出来ます。

git push origin master develop sample

リモートブランチを削除する

--deleteオプションを付けることで、リモートブランチを削除することが出来ます。

git push --delete origin master
注意!:git push -fの使用は慎重に、慎重に

プッシュしようとしている内容によっては、リモートリポジトリの履歴と競合(コンフリクト)してしまい、プッシュが上手くいかないことがあります。
このような時「-f」というオプション(git push -fのように入力する。)を付けるとプッシュすることが出来ます……が、絶対にやってはいけません。

競合した状態で、強制的にプッシュしてしまうとリモートリポジトリの履歴が上書きされてしまいます。
強制的にプッシュした本人は、何の問題もなく使えるのですが、このリモートリポジトリを使っている別の人が困ってしまいます。
リモートリポジトリの履歴が上書きされてしまうと、今度はその履歴と別の人のローカルリポジトリの履歴が競合(コンフリクト)してしまうからです。
変更を取り込もうにも、プッシュしようにもエラーが多発して、使用不能になってしまいます。

インターネット上で、いくつか「-fを使ってエラーを解決出来る」としている記事がありますが、それは上で言ったことを全て分かった上で、問題が起こらないと確信しているから解決できるとしているのです。

ここまでの説明を聞いて「なんかよくわからんなぁ」と思っている人は特に使うべきではありません。

ソフトが出す警告には、意味があります。無視しないようにしましょう。
でないと……とんでもないことが起こるかもしれません。

これで終わり?

git push自体の動作の説明はこれで終わりです。

ですが、プッシュという操作はよく行われます。そのたびに長いコマンドを打つのは面倒です。

そこで、git pushコマンドには「リモートリポジトリの別名」や「ブランチ名」を省略出来る機能が付いています。

下のようにコマンドを入力しても、プッシュが行われるということです。

git push

……確かに便利なのですが、名前が全て省略された時の動作はかなり複雑です。
使い方によっては、プッシュしてほしくないブランチにプッシュしてしまうかもしれません。

以下で省略された時の動作を解説しますが、「読むのが面倒くさい!」と思うのであれば、とりあえず飛ばしておくのもいいでしょう。

「git push」と入力したときの動作

git push

と入力された時、省略されているのは

  1. リモートリポジトリ名
  2. ブランチの名前

の二つです。
この二つの値が、省略された場合どのように値が決まるのか見ていくことにしましょう。

リモートリポジトリ名――上流ブランチが設定されている場合

下の図のような例で、git pushを実行すると、どのような動きをするのか見ていきましょう。

例1

myrepo/masterは、リモートブランチmasterを追跡している、いわゆるリモート追跡ブランチです。
また、myrepo/masterはローカルブランチmasterの上流ブランチ(と呼ばれるもの)に設定しておいてあります。

カレントブランチ(HEADが指し示しているブランチ)はmasterにしておきます。

この状況で、

git push

と入力すると、まず初めにgitは、カレントブランチの上流ブランチを見に行きます。
今回カレントブランチの上流ブランチはmyrepo/masterに設定されています。

次に、その上流ブランチがどのリポジトリにあるリモートブランチを追跡しているのか見ます。このリポジトリ名が、リモートリポジトリ名を省略したときにセットされる名前です。

今回、上流ブランチはリモートリポジトリmyrepoにあるブランチを追跡しています。
ですから、今回の例で指定される名前はmyrepoということになります。

上流ブランチが指定されていなかった場合は?

先ほどの例は、上流ブランチが設定されていた場合でした。
では、上流ブランチが設定されていなかった場合はどうなるのでしょう?

その場合、originという名前が指定されることになっています。

Column:repoオプション

上流ブランチが設定されていなかった場合に指定される名前は、repoオプションを使って変更することが出来ます。
例えば、指定される名前をteraに変更したい場合次のようにします。

git push --repo=tera

リモートリポジトリ名――まとめ

リモートリポジトリ名が省略された場合、指定される名前は次のように決まります。

  1. カレントブランチに上流ブランチが設定されているか?
    1. (Yesの時):リモート追跡ブランチが、追跡しているリモートリポジトリ名が指定される
    2. (Noの時):repoオプションが指定されているか?
      1. (Yesの時):指定された名前が、設定される
      2. (Noの時):originが指定される

ブランチ名

続いてブランチ名ですが、プッシュするブランチを決める方法はいくつかあり、ユーザーが選ぶことが出来ます。
今回解説するのは、ユーザーが何も設定していない場合、デフォルトで設定される動作です。

Column:push.default

gitでは、ユーザ名やメールアドレスなどの値やコマンドの挙動について設定出来る機能があります。
例えば、ユーザ名の場合user.nameという設定項目がありこれに対して、user.name=myusernameという値をセットすることで、ユーザ名がmyusernameに設定されます。
同じように、ブランチ名を省略した場合の挙動を決めるpush.defaultという名前の設定項目が存在します。
push.defaultには、simple・matching・nothing・current・upstreamという5つの値(文字列)を設定することが出来ます。(それぞれの設定値の挙動については、参考文献にあるpush.defaultをご覧ください。)
本文にあった「ユーザーが何も設定していない場合」というのは、push.defaultに何の値も設定していない場合、という意味になります。

push.defaultに値が設定されているか見るには、「git config -l」というコマンドを入力します。
もし設定されていれば、「push.default=simple」のように表示されるはずです。設定されていなければ、「push.default」という設定項目自体が表示されません。

Column:引数無しのpushは危険?

「git push 省略」と検索してみると「git push」と打ってプッシュするのは危険だ!という記述がたまに見られます。
これは、Git2.0(2.0はバージョンを示します。)以前のGitでは、プッシュするブランチを決める方法をユーザーが決めていない場合、リモートブランチと同じ名前のローカルブランチをプッシュするという設定になっていたからです。

この設定だと、masterブランチだけプッシュしたくて「git push」と入力したのに、それ以外のブランチも、リモートリポジトリに同じ名前のブランチがあれば、プッシュされてしまう可能性があります。
Git2.0以降のGitでは、下で説明するような動作に変更されました。

今使っているGitが、Git2.0以降か確認したい場合は「git --version」とGitBashに入力してください。
「git version 2.xx.x.windows.x」のように表示されたら、Git2.0以降のGitです。

先ほどと同じ例を使って、ローカルブランチmasterをカレントブランチにしているとき、git pushと入力すると何が起こるのか見ていきましょう。

先ほどと同じ図

まず、gitはカレントブランチ(今回の場合だとmaster)の上流ブランチを見に行きます。

上流ブランチが設定されていない場合、プッシュは行われません。

加えて、この上流ブランチと同期しているリモートブランチの名前とカレントブランチの名前を比較します。

名前の比較

もし同じであれば、カレントブランチの内容を比較したリモートブランチにプッシュします。
異なる場合は、プッシュは行われません。

ローカルブランチmasterをカレントブランチにしているとき、git pushと入力すると、下のコマンドが入力されるのと同じ動作を行います。

git push myrepo master

ブランチ名――まとめ

ブランチ名が省略された場合、プッシュの動作は次のように決まります。
なお、プッシュするリモートリポジトリ名は「リモートリポジトリ名――まとめ」で、取り挙げた手順で決定されます。

  1. カレントブランチに上流ブランチが設定されているか?
    1. (Noの時):プッシュは行われない。(エラーが出る。)
    2. (Yesの時):上流ブランチが追跡しているリモートブランチの名前と、カレントブランチの名前は同じか?
      1. (Yesの時):カレントブランチから、先ほど名前を比較したリモートブランチへプッシュする
      2. (Noの時):プッシュは行われない。(エラーが出る。)

上流ブランチを設定する

上で見たように、git pushと入力するだけで、プッシュが行われるようにするには、リモート追跡ブランチをあるローカルブランチの上流ブランチに設定しなければなりません。

origin/masterというリモート追跡ブランチを、masterというローカルブランチの上流ブランチに設定するには、次のコマンドを入力します。

git branch --set-upstream-to=origin/master

エラーが出る?

git pushとコマンドを入力したとき、次のようなエラーが出ることがあります。

fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin master

ローカルブランチに上流ブランチが設定されていないことが原因です。

git push -uって何だ?

前回の最後で、下のようなコマンドを入力しました。

git push -u origin master

-uというオプションがついています。
これは、「下のコマンド2つを同時に実行せよ」というオプションになります。

  1. git push origin master
  2. git branch --set-upstream-to=origin/master

masterブランチをリモートリポジトリoriginにプッシュして、origin/mastermasterの上流ブランチにしなさいという意味になります。

初めてローカルブランチをプッシュするとき、-uオプションを付けておけば、いちいち上流ブランチを設定するコマンドを入力する必要がなくなります。

次回予告

次回は、リモートリポジトリから変更を取り込むコマンドであるgit fetchgit pullについて解説します。

う-ん、よく分からん!

この記事を読んで、疑問に思うことがあったときは、気軽にコメント欄や私のTwitterから質問してください。

参考文献

Pro Git

git,GitHubを使うにあたって必要なコマンドの使い方が詳しく解説されています。この連載を読んで分からないことや詳しく知りたいことがあったときはまずProgitを読んでみるといいでしょう。 git-scm.com

git用語解説マニュアル

git公式の用語解説マニュアルです。
上流ブランチ(upstream branch)や、コミット(commit)等の用語の意味について英語で書かれています。
git-scm.com

push.default

git-configで見ることが出来る設定値の意味について書かれた、git公式のマニュアルです。
ブラウザでCtrl+Fを押して、push.defaultを検索すると、コラムで出てきたpush.defaultの設定値の意味の説明に飛ぶことが出来ます。

git-scm.com

次回の記事

terapotan.hatenablog.jp

前回の記事

terapotan.hatenablog.jp

連載記事一覧

terapotan.hatenablog.jp