読者です 読者をやめる 読者になる 読者になる

カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

続・Git中級者に送る便利なコマンド群

プログラミング プログラミング-Git

前回の記事「Git中級者に送る便利なコマンド群」でははてブ経由で多くのコメントを頂きました。今回の記事では、頂いたコメントのうち、いくつか取り上げて、可能な限り補足をしたいと思います。

git push origin master -f

前回の記事で最も多くご指摘頂いたのがmasterに対するforce pushに関してでした。結論から言うと、これはやっちゃいけませんね。

forceが必要になるときでパッと思いつくのはgit commit --amend後であったり、git rebase後なのですが、これらは過去の歴史を書き換えているので、共同レポジトリとかでやってしまうと、他の人がpushできなくなってしまうのですね。

AさんのローカルレポジトリAと、BさんのローカルレポジトリBをもとに再現させてみましょう。ディレクトリAもディレクトリBもともに、リモートからcloneしてきたものです。

# Aさんのローカルレポジトリのつもりとして、Aディレクトリを用意
$ cd A
$ touch sample2.txt

$ git add sample2.txt

# Aさんが破壊的な変更を加える
$ git commit --amend
[master 1f44e09] amend
 Author: masudak <masudak@hogehoge.com>
 Date: Tue Jun 30 22:55:19 2015 +0900
 2 files changed, 1 insertion(+)
 create mode 100644 sample2.txt


# 当然ながら自分自身でもpushできない
$ git push origin master                  
To git@github.com:masudaK/git_sample.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:masudaK/git_sample.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

# なので、force pushする
$ git push origin master -f
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 341 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 1 (delta 0)
To git@github.com:masudaK/git_sample.git
 + e730598...1f44e09 master -> master (forced update)

次にBさんがどういう影響を受けてしまうのか見てみます。

$ cd B

# とりあえずpullしてみる
$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 1
Unpacking objects: 100% (3/3), done.
From github.com:masudaK/git_sample
 + e730598...1f44e09 master     -> origin/master  (forced update)
Merge made by the 'recursive' strategy.
 sample2.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 sample2.txt

$ git log -1
commit 685f6fa16a5ee82090d5cb25710e32857922cf74
Merge: e730598 1f44e09
Author: masudak <masudak@hogehoge.co.jp>
Date:   Mon Aug 3 19:11:08 2015 +0900

    Merge branch 'master' of github.com:masudaK/git_sample

マージログは出ますが、pullはできますね。

では、reflogで過去に戻って、今度は編集してpushしてみましょう。

$ git reflog
685f6fa HEAD@{0}: pull: Merge made by the 'recursive' strategy.
e730598 HEAD@{1}: clone: from git@github.com:masudaK/git_sample.git

$ git reset --hard e730598

# コンフリクトしない適当なファイル作成
$ touch sample3.txt
$ git add sample3.txt
$ git commit -m "add sample3.txt"

# pushしてみる
$ git push origin master
To git@github.com:masudaK/git_sample.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:masudaK/git_sample.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

このようにpushできなくなってしまいます。それではまずいわけですね。なので、remote masterにたいしてはforce pushしないほうがいいわけです。force pushしていいのは、トピックブランチとかですね。自分しかpush, pullしないようなものだったら、大丈夫かと思います。

masterで修正したい場合はrevertですね。

revertはこんな感じです。 こんなlogになっていたとして。

$ git log
commit 6f00bc3941089096849d4acb94034f95fce54752
Author: masudaK <masudak@hogehoge.com>
Date:   Wed Aug 5 12:17:48 2015

    second commit

commit a986acb800e5d52dcdc4e775e61b9aba267c0fca
Author: masudaK <masudak@hogehoge.com>
Date:   Wed Aug 5 12:17:00 2015

    initial commit

この二番目の「second commit」を取り消したいと思います。 その場合はrevertの引数にリバートしたいコミットのハッシュ値を指定します。

$ git revert 6f00bc3941089096849d4acb94034f95fce54752

実行するとこういう画面がでます。

Revert "second commit"

This reverts commit 6f00bc3941089096849d4acb94034f95fce54752.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#       deleted:    sample2.txt

保存すると、コミットログはこうなります。

$ git log
commit bb527160c5cda26a8ffffbd68f104b1054ad2b60
Author: masudaK <masuken@hogehoge.com>
Date:   Wed Aug 5 12:22:12 2015

    Revert "second commit"

    This reverts commit 6f00bc3941089096849d4acb94034f95fce54752.

commit 6f00bc3941089096849d4acb94034f95fce54752
Author: masudaK <masuken@hogehoge.com>
Date:   Wed Aug 5 12:17:48 2015

    second commit

commit a986acb800e5d52dcdc4e775e61b9aba267c0fca
Author: masudaK <masuken@hogehoge.com>
Date:   Wed Aug 5 12:17:00 2015

    initial commit
    

これで「second commit」で行ったことを取り消すことができました。あとはpushすればよいですね。

git stashとgit branch

前回の記事でstashの説明の際に以下のように書いてしまったのがそもそもの間違いですね。。。

ここまであなたがいくつか修正をしてきたと過程しましょう。そしたら、バグが見つかり、さらに修正をしなければならなくなったとします。そういうときはどうすればよいでしょう。

このような場合は確かに、新しくブランチ切るほうがよいですよね。stashはもう少しカジュアルなイメージです。自分の作業している最中に、割り込みがあって、誰かにコード説明しなきゃいけないから、一時的に自分のを退避させて、ライブコーディングで示すとか(あくまで僕の場合です)、一瞬退避したい人向けですね。このタイミングならstashだなというときにぜひぜひ使ってください><

終わりに

色々コメント頂き、勉強になりました。また、何かあればご意見いただければと思います。ではでは!

前編はこちら:
Git中級者に送る便利なコマンド群

この本はgitを深く理解するために、オススメです!