叫ぶうさぎの悪ふざけ

うさぎが目印のWebエンジニアが、得たことや思ったことを言の葉に乗せて叫ぶ場所です。

学習記録:12月3日(月):【git stash】作業ブランチを作らず作業してしまった時の対処法

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の3日目のエントリーです。

今日は MyNA(日本MySQLユーザ会) 望年LT大会2018@赤坂 に参加して多少酔っています。いやー楽しかった!次は僕もLTできるネタを持って行こう。

ところでみなさん、gitでうっかり 作業ブランチを作らず作業してしまった なんてこと1度はありませんか?僕はあります。

とはいえ他に同じようなエントリーは溢れていると思うので、備忘録とこれからの人にすんなり解説できるためにも、自分のブログに書いてみます。

前提条件

  • gitはコマンドから実行します
  • 仮に以下のプロジェクトがあったとします
$ pwd;find . | sort | sed '1d;s/^\.//;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/|  /g'
/Users/mamy1326/dev/mamy1326/git_stash_test
|--app
|  |--controller
|  |  |--Fuga.php
|  |  |--Hoge.php
  • このプロジェクト、うっかりmasterに変更しちゃったよ
  • それをstashを使って作業ブランチへ移動し、merge
  • 無事に作業ブランチからadd, commit, pushして、PullRequestを出す

というところまで実施します。

正しい(と僕は思っている)方法

僕が実際にやってみて、他でも調べて、これが正解と思う内容を紹介します。

やっちまった

さあ修正が終わったぞ、と思って、 add, commit, push してさっさとPullRequestしちまおう!と思うじゃないですか。

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

OKOK、さてブランチは…っと。

$ git branch
* master

( ゚д゚)ハッ! 作業ブランチ切ってないやないかーい!

そしてこの時、僕はまだstashを知らないわけです。

まず深呼吸して落ち着く

困った時に最初にやること。いや大事です。まず深呼吸しましょう。そのあと、できれば何に困っててどうしたいか紙に書きましょう。 絶対に解決できる方法がある。先人の知恵ってすごいんです。

まずは落ち着いて未来を信じましょう。

変更を保存する: git stashgit stash list

落ち着いたら以下のコマンドを実行します

$ git stash
Saved working directory and index state WIP on master: 2b38798 first commit.
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.

これにより、masterで実行した変更が一時的な保存リスト stash@{0} に保存されたことがわかります。

変更を確認する: git stash show

一応、保存された内容を git stash show で確認してみましょう。

$ git stash show stash@{0}
 app/controller/Fuga.php | 2 +-
 app/controller/Hoge.php | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

どうでしょう。変更した内容をちゃんと確認できるかと思います。 こうなれば、別のブランチに移動できます。

つまり、作業ブランチを作成することができるということです。

作業ブランチを作って移動: git checkout -b

Issueに基づいて作業することを前提としているので、Issue番号で作業ブランチを作成します。

$ git checkout -b feature/1 origin/master
Branch 'feature/1' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'feature/1'
$ git branch
* feature/1
  master

無事に作業ブランチを作成し、移動できたことがわかります。

一時保存した変更内容をmergeする: git stash apply

まずは一応、一時保存リストを確認します。 先程はmasterで確認しましたが、新しい作業ブランチでも見れることを確認しないと不安で胸が張ちきれそうです。

特に僕はブログなどの「わかる人にはわかる行間」がわからなくてハマることが多いので、1つずつ確認していきます。

$ git branch
* feature/1
  master
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.

先ほど保存した内容が、作業ブランチでも確認できます。

一応の一応、変更内容そのものも確認しておきましょう。

$ git diff stash@{0}
diff --git a/app/controller/Fuga.php b/app/controller/Fuga.php
index 173f17f..4f6b682 100644
--- a/app/controller/Fuga.php
+++ b/app/controller/Fuga.php
@@ -1,2 +1,2 @@
 <?php
-echo("fugafuga!!");
+echo("fuga!!");
diff --git a/app/controller/Hoge.php b/app/controller/Hoge.php
index 079d8f7..4a1f61a 100644
--- a/app/controller/Hoge.php
+++ b/app/controller/Hoge.php
@@ -1,3 +1,2 @@
 <?php
-echo("hogehoge!!");
-
+echo("hoge!!");

いやーつまんない修正例ですみませんね!でもちゃんと修正の差分も見れることが確認できました。

では落ち着いて、mergeしちゃいましょう。

$ git stash apply stash@{0}
On branch feature/1
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

git stash で一時保存された内容がmergeされ、そのファイルのリストが表示されます。 これは git status を実行した時と同じです。

念の為 git status も実行します。

$ git status
On branch feature/1
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   app/controller/Fuga.php
    modified:   app/controller/Hoge.php

no changes added to commit (use "git add" and/or "git commit -a")

おんなじですね。

やっといつもの: git add, git commit, git push

ここまでくればいつものやつ。 落ち着いて git add, git commit, git push すればOKです。

まずは git add で追加します。

$ git add .

次に git commit します。コメントはご愛嬌。

余談ですが、コメントは英文にこだわっていた時期がありました。が、英語圏の人と仕事をしないなら、頑張って英文にするより、一目見てわかる日本語のコメントの方が良いと個人的に思っています。

ちなみに、commitコメントに Issue番号 を入れる ( #1 ) と、Issueに記録されていくので便利です。

$ git commit -m "#1 メッセージが寂しいので単語を多くする(ひどい)"
[feature/1 c094ec8] #1 メッセージが寂しいので単語を多くする(ひどい)
 Committer: まみー <mamy1326@gmail.com>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:

    git config --global --edit

After doing this, you may fix the identity used for this commit with:

    git commit --amend --reset-author

 2 files changed, 3 insertions(+), 2 deletions(-)

そしてリモートブランチに git push します。

$ git push origin feature/1
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 502 bytes | 251.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote: 
remote: Create a pull request for 'feature/1' on GitHub by visiting:
remote:      https://github.com/mamy1326/git_stash_test/pull/new/feature/1
remote: 
To github.com:mamy1326/git_stash_test.git
 * [new branch]      feature/1 -> feature/1

gitを日常で使っている方にはおなじみのコマンドを実行してリモートブランチにpushし、PullRequestを出すだけです。

本当にお疲れさまでした。焦るとロクなことないですよね!

一時保存を消す: git stash drop

push、PullRequestしてmergeされたら stash で一時保存した内容は不要なので消しておきましょう。

ローカルもリモートも作業ブランチは削除する(mergeされたら不要だし残しておくべきではない)ので、masterブランチへ移動してから実施します。

# masterブランチへ移動
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
# 現在のブランチを確認
$ git branch
  feature/1
* master
# 一時保存リストを見る
$ git stash list
stash@{0}: WIP on master: 2b38798 first commit.
# 一時保存を消す
$ git stash drop stash@{0}
Dropped stash@{0} (ed610ef4a14ee816875b883ff558afac02303ddb)
# dropしたので消えている
$ git stash list

ついでにmasterも最新にしておけば、次の作業もスムーズですね。

$ git pull origin master
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From github.com:mamy1326/git_stash_test
 * branch            master     -> FETCH_HEAD
   2b38798..9ad9e31  master     -> origin/master
Updating 2b38798..9ad9e31
Fast-forward
 app/controller/Fuga.php | 2 +-
 app/controller/Hoge.php | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

エントリーにしようと思った動機

そもそもなぜ記録を残して解説しようと思ったのか。

うっかりmasterブランチのまま、修正作業をしてました。で、stashコマンドを使い慣れていればいいんですけど、僕の場合は今回(数週間前)が初めてだったんですよね。

しかも20ファイル以上に修正入れてた。

で、深夜に作業しててひとりでめっちゃ焦って「どうすればええんや…」という脱力感と絶望感に襲われたので、同じような思いをこれからする人もいるかなーと思ったのと、一緒に働くメンバーにgitコマンドを教える機会がありそうなので、出来るだけ丁寧な形で残そうと思ったのでした。

人は誰しも焦る(と思いたい)

うわーこれどうすべぇ…と焦って、調べたらstashってコマンドあるじゃないですか。

で実行して作業ブランチ作って移動してmerge…しようと思ったらなんか怒られる。ブランチの移動もできない。

ニッチもサッチも行かなくなってしまってどう解決したかを記録しておこう、って思ったのです。

きっと1週間後、1ヶ月後、1年後の自分のためになるはず。そう思って どんな小さなことでも、困ったら記録に残す ことを徹底していたりします。

やってみて

こうしてエントリーにまとめてみて思うのですが、 git stash って毎日叩くコマンドでもないので、良い復習になりましたし、アウトプットするためにリポジトリ作って同じことを何度かやったので、もう2度と忘れないですね。

これは普段から発言しているんですが、 インプットとアウトプットの輪廻を作る ことで学習が継続されますし、質も上がりますし、自分の中に刻まれていきます。

かつ、もしかしたら誰かのためになるかもしれないし、内容次第では登壇テーマにもなりうる。

小さなことからコツコツと。 インプットしたら、どんな形でも良いのでアウトプットしよう って思っています。

というわけで、今回は超初心にかえるような内容となりましたが、1つのコマンドについて丁寧に解説するとこんなボリュームになるんだな、と改めて思うとともに、まずは自分にとって不明点がないように書くのって大事だな、ここまでやって初めて 他の人に解説できるようになる な、って思います。

誰かのお役に立てたら良いなと思いつつ、インプット・アウトプットやっていきましょう!