シチュエーション別Gitコマンド逆引き辞典[Memo]

#はじめに
この記事は個人的なメモとして残しておく想定とします。
なので追記や修正はこれからしていく予定です。

#ブランチ切って一人で開発
##developが更新されてそれを取り込みたい時
「xxxクラス作ってdevelopにマージしときました！皆さん使ってください。」
「ありがとうございます！使います！」
って言って使わせてもらいたい時がありました。
developブランチから自分のブランチに変更を取り込みます。
###pull

```git
git pull origin develop
```
コンフリクトが起これば、解消して、

```sh
git add <files>
git commit -m "コンフリクト解消"
```
コンフリクト解消後に解消コミットする必要があります。
それによってコミット履歴が汚れてしまいます。。。
###pull --rebase
```sh
git pull --rebase origin develop
```
developのコミットを取り込んで、その上に今のブランチのコミットを重ねるイメージ。
コンフリクトしたら解消しつつ、

```sh
git rebase --continue
```

またコンフリクトしたら解消と`git rebase --continue`を繰り返す。。
コンフリクトを解消してもコミット履歴が汚れることなく全部自分のコミットにまとめることができます。
あたかも最新のdevelopブランチから派生したような見た目になります。

###marge/rebase中にコンフリクトしているファイルが見つからず詰まったら
`git status`してもコンフリクトしているファイルが出てこないのに`git rebase --continue`できず

```
Applying: コミットメッセージ
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
```
このようなメッセージが出てきた時は今マージしようとしてるコミット間で差分がないので

```sh
git rebase --skip
```
をすることで次へ行けます。

##コミット単位小さすぎた時
「これプルリク通ったとしても、コミット粒度小さすぎてdevelopのコミット履歴を汚してしまうな。。。」
「全部１コミットにまとめて、やったことはプルリクのコメントで書けば良いやん！」
となる時がありました。
というわけで、複数コミットをまとめます。

### rebase -i
例えばこのようになっていたとします。
紆余曲折あって一部初期化処理を追加*3としてしまった。。。
これら3つのコミットはセットなので1つにまとめます。

```sh
$git log
commit 67c6c855922449b9b9b8a06f1f50b1b2b6baa65 (HEAD -> feature/hoge1)  

    <file3>一部初期化処理を追加

commit 2fdc0529b18102fa4fd3dfbc4ae2141c79fd404

    <file2>一部初期化処理を追加

commit 282a1380c153a8fe5cc29e24baca840197de3fc

    <file1>一部初期化処理を追加

commit 2dfb8cd50d099e0c30ba0bd35a858b084abccdc2 (master, develop)

    first commit
```
HEADから3コミット分をまとめる例です。

```sh
git rebase -i HEAD~3
```
editorが開き、上3行を変更します。

```
#pick 282a132 <file1>一部初期化処理を追加 -> このコミットメッセージは"初期化処理を追加"に変更
#pick 2fdc052 <file2>一部初期化処理を追加 -> このコミットメッセージいらね
#pick 67c6c85 <file3>一部初期化処理を追加 -> このコミットメッセージもいらね

r 282a132 <file1>一部初期化処理を追加
s 2fdc052 <file2>一部初期化処理を追加
s 67c6c85 <file3>一部初期化処理を追加

# Rebase 2dfb8cd..67c6c85 onto 2dfb8cd (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
`r: reword`はコミットメッセージ変更
`s: squash`はいらないので前のコミットに結合
`p: pick`はコミットメッセージを残す時に使う
保存＆閉じるとまたエディタが開き、今度は一番上のコミットメッセージを編集します。

```
#<file1>一部初期化処理を追加
初期化処理を追加

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Fri May 15 23:04:15 2020 +0900
#
# interactive rebase in progress; onto 2dfb8cd
# Last command done (1 command done):
#    reword 282a132 初期化処理を追加
# Next commands to do (2 remaining commands):
#    squash 2fdc052 <file2>一部初期化処理を追加
#    squash 67c6c85 <file3>一部初期化処理を追加
# You are currently editing a commit while rebasing branch 'feature/hoge1' on '2dfb8cd'.
#
# Changes to be committed:
#	modified:   file1.kt
#
```
保存＆閉じるとまたエディタが開き、いらないメッセージは消します。

```
 This is a combination of 3 commits.
# This is the 1st commit message:

初期化処理を追加

# This is the commit message #2:

#<file2>一部初期化処理を追加

# This is the commit message #3:

#<file3>一部初期化処理を追加
...
```
やっと編集終わって、

```sh
$git log
commit 6ab47738e0989664d3f7ade37c6df0c7b2b (HEAD -> feature/hoge1)  

    初期化処理を追加

commit 2dfb8cd50d099e0c30ba0bd35a858b084abccdc2 (master, develop)

    first init
```
あとは

```sh
git push -f origin feature/hoge1
```

後プルリクしました。
##別ブランチで作業するとき
A「やっぱり基底クラス用意して、各クラスで継承させた方がいいですね。。。」
B「じゃあ、基底クラスの実装は私がやります。今日のお昼過ぎにはプルリク投げて、夕方にはマージできるようにします。」
自分「わかりました。その間別のところやっておきますね。」
というわけで今のブランチでやっていることは中断して、別ブランチで作業したいと思います。
### stash -u
```sh
git stash -u　
git checkout （別ブランチ）
```
これでuntracked fileも含めて、一旦変更を退避できます。
その後checkoutで別ブランチへ移動します。
##別ブランチから戻ってくるとき
###stash pop, apply, list
その後、
B「developにマージしました！」
自分「了解です。元の作業再開します。」

```sh
git stash -u #基底クラスできるまでやっていた作業ブランチの変更待避
git checkout (元ブランチ)
git pull --rebase origin develop
git stash pop
```
stashで待避したものを戻してくる前に一旦更新されたdevelopを`git pull --rebase origin develop`で持ってきます。
その後`git stash pop`で元のブランチの変更が全て戻ってきます。
`stash pop`は最新のstashを適用し、そのstashを削除します。

```sh
git stash list #stash一覧を表示
git stash pop stash@{xxx} #適用後削除
git stash apply stash@{xxx} #適用後削除しない
```
stashを削除したくない場合は、`stash apply`を使用します。
また`pop`, `apply`で複数ある中から適用するstashを選択したい場合には、`list`で調べてから、適用するstashを選択し、後ろにくっつけます。

##コミットにその箇所とは関係ないコードを入れてしまったので後から整理したい時
「"`funtionB`の実装"コミットに`functionA`の軽微な修正も一緒に混ぜちゃったけど、気持ち悪いな。。。」
「このコミットを2つに分割しよう!」
"functionAの軽微な修正"　と　"`funtionB`の実装"　の2つのコミットに分けます。
要は行単位でaddしてcommitし直します。

###git add -p
```kotlin:sample
fun functionA(): String{
    val str = "Hello World"
    return str
}

```
 を、1コミットでいきなり以下のようにしたとします。

```kotlin:sample
fun functionA(): String{
    return "Hello World"
}

fun functionB(number1: Int, number2: Int): String{
    return number1+number2
}

```
これにはfunctionAの修正とfunctionBの実装が含まれていますが、前者と後者でコミットを分けたいと思います。
まずはファイルの変更はそのままにコミットログだけ変更前に戻します。

```sh
git reset HEAD~　#ここは何コミット分を修正するか各自によって異なる
```
すると`git status`でsampleが`modified`に戻っていることを確認できます。
次に、

```sh
git add -p sample
```
その後このように聞かれるので`e`を押下

```
...
(1/1) Stage this hunk [y,n,q,a,d,e,?]? e
```
エディタが開きます

```diff
# Manual hunk edit mode -- see bottom for a quick guide.
@@ -1,4 +1,7 @@
 fun functionA(): String{
-    val str = "Hello World"
-    return str
+    return "Hello World"
+}
+
+fun functionB(number1: Int, number2: Int): String{
+    return number1+number2
 }
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
# 
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

```
消さない行は`-`を半角スペースに変えます。
追加しない行は`+`の行を消します。

```diff
# Manual hunk edit mode -- see bottom for a quick guide.
@@ -1,4 +1,3 @@
 fun functionA(): String{
-    val str = "Hello World"
-    return str
+    return "Hello World"
 }
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
# 
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.
```
これでエディタを閉じます。

```diff
$ git diff
diff --git a/sample b/sample
index 467fb8e..4f200cc 100644
--- a/sample
+++ b/sample
@@ -1,3 +1,7 @@
 fun functionA(): String{
     return "Hello World"
 }
+
+fun functionB(number1: Int, number2: Int): String{
+    return number1+number2
+}
```
functionAの変更分だけが反映されており、functionBはステージングされていません。
この状態でいったんコミットしましょう。
その後もう一回sampleをaddしてcommitすれば、無事作業は完了です。

```sh
git commit -m "functionAの修正"
git add sample
git commit -m "functionBの実装"
```
##gitで行なった操作自体を取り消したい時
### reflog

```sh
git reflog 
HEAD@{0} 操作0
HEAD@{1} 操作1
HEAD@{2} 操作2
HEAD@{3} 操作3
....
```
「操作２をやった後に戻りたい。。。つまり操作１を無かったことにしたい」

```sh

git reset --soft HEAD@{2}
```