Git コミット ワーキングディレクトリ管理コマンド

**こちらは個人の勉強メモです**

# ステージング

タイトルからはズレるが、コミットする時にはステージングされたものを全てコミットするしかないので
ステージングの時点で意味のあるコミットを心がけて整理しておくことが必要となる

## git status, 今コミットしたらどうなるか

インデックスのステータスの確認

```
git status
```

ワーキングディレクトリ (commit に反映されていない作業差分) の状態を表示する
一番大事。とにかく commitする前に現在のワーキングディレクトリの状態を常にこれで確認するべき。
全て commit してあったら何もないのでクリーンですと出る

## git add -p, 変更を分割しながらステージング

参考
[git add -p 使ってますか？]
(https://qiita.com/cotton_desu/items/bf08ac57d59b37dd5188)

インデックスにステージングするワーキングディレクトリの新しい作業を分割することができる。
これを使うと、内容を確認しながら ステージング してくれる

```
git add -p 
```

で stage this? と7 lines くらいずつ聞いてくれる。

返答で約半分にできる。かなり便利。

なお、すでにこれをするをの忘れて、
ワーキングディレクトリの新規作業を全てステージングしてしまった場合は

### git reset HEAD, ステージングの取り消し

```
git reset HEAD
```

でステージングしたのを取り消せる。

```
git reset HEAD
Unstaged changes after reset:
M       lib/posts.js
M       pages/index.js
M       posts/pre-rendering.md
```

コミットは後から取り消すことを考えると 1 ファイルずつ、 1 関数ずつしたほうがいいので、
作業した後にコミットし忘れたのを気づいたら、上記の `git add -p` で少しづつステージングして
それからコミットすればコミットを細かく意味がコミットメッセージの明確なものができる

```
git reset --soft HEAD^
git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   components/date.js
```

間違えて 

```
git add .
```

でワーキングディレクトリの内容を追加してコミットもしてしまったら

```
git reset --soft HEAD^
```

した方が確実にコミットを取り消せる。ワーキングディレクトリは巻き戻さずに。

## git log

コミットログの表示

### git log -p

詳細コミで表示

* commit の16進数の id
* した人
* 日付時間
* コミットメッセージ
* ファイル名
* 差分のテキスト

古い順で表示されていく

```
commit aaaaaaaaaaa161616161616hexhexhex
Author: yourusername <yourmail@exmaple.com>
Date:   Wed Jul 8 20:21:18 2020 +0900

    add worked hours

diff --git a/2020/07/08.md b/2020/07/08.md
index ccf9e0d..44cce18 100644
--- a/2020/07/08.md
+++ b/2020/07/08.md
@@ -37,11 +37,15 @@
 + 15:00 ~ 17:00
 + 19:00 ~ 20:10
 
+3+0.5+2+1
+
+6.5時間
+
 
 ## 次回稼働予定
 明日2020/07/09 木
 09:30 ~ 18:30
-8h
+8時間

commit aaaaaaaaaaa161616161616hexhexhexhex
Author: yourusername <your@mail.com>
Date:   Wed Jul 8 20:11:49 2020 +0900

    Filled whole 2020/08.md report
```

このように詳細コミでブランチのコミットログが出てくる。

### git log --oneline

1行で表示

```
0cf4e81 (HEAD -> 20200708-daily-report, origin/20200708-daily-report) add worked hours
b5375be Filled whole 2020/08.md report
9ecbd48 applied report template
39ce8fc made 07/08.md
133627b (origin/master, origin/HEAD, master) Merge pull request #2 from kodansha/20200702-daily-report
9d49266 replace hatena to Hatena
d06148b  、。に統一
488fc83 add lines to before and after headers and fence blocks
cb6dbba changed docker to Docker
683f641 integrated things name to `GitHub`, `Qiita`, `Qiita Teams`, `Docker`,
0e2f469 mod view
54d0ad1 Update issue templates
44e086c Update issue templates
043aab3 (origin/hoge, hoge, 20200701-report) wrote 0702
a4a14f4 (origin/20200702-daily-reoprt) add 2020/ 07/ dir, and ad http://01.md
e31b897 first commit
91eb15c Initial commit
```

1行ずつ コミット id の16進数, コミットメッセージが見れる。

この中から`git rebase -i`の引数を選べる

### git log --graph --oneline --decorate --all

グラフ で1行でデコって表示

```
git log --graph --oneine --decorate --all
```

ブランチが全て枝分かれして色がついて出る。

### push される コミットの確認

[git でpush前にcommitを確認する方法]
(https://hacknote.jp/archives/12028/)

```
git log origin/master..master
```

これで今何が push されるのか確認できる

```
git log origin/master..master
commit yourhexyourhexyourhex (HEAD -> master)
Author: yourusername <yourmail@exmaple.com>
Date:   Thu Sep 17 17:51:59 2020 +0900

    add 404 js
```

このように現在のローカルリポジトリの最新と、リモートリポジトリの最新の差分が取れる。

-----------------------

## reflog, 過去のすべての行動の履歴

コミット の管理コマンドという趣旨からは外れるが、関連コマンドとしてあげておく

参考
http://www-creators.com/archives/1116#6


> デフォルトではHEADの移動をすべて表示します。
> HEAD@{xx}というのは、過去何番目のHEADの状態かということですね。
> 元に戻したいポイントがわかれば、あとは、git reset --hard で強制的に巻き戻します。

> 指定のコミットに、ブランチ状態を強制上書き。全部取り消し。 `git reset --hard HEAD@{14}`　のように。

危険なので推奨されていないチームもある。
現在所属しているチームでは reset hard も rebase も, feature ブランチで行うならば非推奨ではない。
コミット を綺麗に保つ方が優先されている。

実際に実行すると

```
git reflog                                                                             
153a2e9 (HEAD -> 20200820-daily-report, origin/20200820-daily-report) HEAD@{0}: commit: 2020年08月20日の日報                           
2f3704d (origin/master, origin/HEAD, master) HEAD@{1}: checkout: moving from master to 20200820-daily-report
2f3704d (origin/master, origin/HEAD, master) HEAD@{2}: pull origin master: Fast-forward
15e4f2b HEAD@{3}: checkout: moving from 20200818-daily-report to master
e3c21fb (origin/20200818-daily-report, 20200818-daily-report) HEAD@{4}: commit: rm inside secret data
688d299 HEAD@{5}: checkout: moving from 20200819-daily-report to 20200818-daily-report
703b65c (origin/20200819-daily-report, 20200819-daily-report) HEAD@{6}: commit: 2020年08月19日の日報
15e4f2b HEAD@{7}: checkout: moving from master to 20200819-daily-report
15e4f2b HEAD@{8}: checkout: moving from 20200818-daily-report to master
688d299 HEAD@{9}: commit: 2020年08月18日の日報
15e4f2b HEAD@{10}: checkout: moving from master to 20200818-daily-report
15e4f2b HEAD@{11}: checkout: moving from 202008180daily-report to master
```

このように 

* id 
* 動作の HEAD(現在地)からの位置
* 動作の内容。commit や checkout など

が 1行ずつ出る
コミット だけの履歴が出る　git log と違って checkout などの「行動」も出る。


## merge と rebase 

> mergeとrebaseは共に履歴を統合するコマンドだが、特徴が異なる

### merge

> 変更内容の履歴はそのまま残るが、履歴が複雑になる。

### rebase

>履歴は単純になるが、元のコミットから変更内容が変更される。そのため、元のコミットを動かない状態にしてしまうことがある。

上司によると

>　
merge と rebase は、チームの運用方針に応じて使い分けます。
例えば、履歴を一本化するように運用をするのであれば
トピックブランチに統合ブランチの最新のコードを取り込む場合は rebase を使う
> 
統合ブランチにトピックブランチを取り込む場合は、まず rebase してから merge
というように使い分けます。

とのことで
トピック(リモートの最新の)ブランチをとってくる場合、コンフリクトが起きたら 
rebase でこちら側の歴史を改変して
統合ブランチに push する。

# git reset

コミットを「ひとます戻る」コマンド

reset は、ブランチの最新コミットから順番に記録を消していくもの

ソフトとハードの二種類がある。

ソフトは単にステージングしたものの取り消しで、実際のファイルの変更は戻らないが

--hard をつけるとワーキングツリーがリセットされるので、実際のファイルのコミットする前の変更が消える。

```
git status                                                                                                  
On branch master                                                                                                                       
                                                                                                                                       
No commits yet                                                                                                                         
                                                                                                                                       
Changes to be committed:                                                                                                               
  (use "git rm --cached <file>..." to unstage)                                                                                         
        new file:   .browserslistrc                                                                                                    
        new file:   .gitignore                                                                                                         
        new file:   .ruby-version                                                                                                      
        new file:   Gemfile                                                                                                            
        new file:   Gemfile.lock 
```

この例だと
git status で見るとコミットはされていないが、ステージングに
Gemfile など大量のファイルが上がっている。
これを

```
git reset
git status                                                                                                  
On branch master                                                                                                                       
                                                                                                                                       
No commits yet                                                                                                                         
                                                                                                                                       
Untracked files:                                                                                                                       
  (use "git add <file>..." to include in what will be committed)                                                                       
        .browserslistrc                                                                                                                
        .gitignore                                                                                                                     
        .ruby-version                                                                                                                  
        Gemfile                                                                                                                        
        Gemfile.lock 

nothing added to commit but untracked files present (use "git add" to track)
```

git reset して git status で確認してみると
git 管理下にステージングされていたファイルが `Untracked files` としてステージングをリセットされている。

ちなみに `@^` が HEAD のエイリアス。短く打てる。 

## git reset --soft HEAD^

```
git reset --soft HEAD^
```

これををすればつまりコミットが一つ無かったことになる。
つまりやるたびに1つ git log の表示が消えていく

なお、branch を切る前の master から生成する前までも戻れてしまう。注意。

### git reset --hard (HEAD^)

コミット単位でワーキングディレクトリの作業ごと消し去る。
デフォルトだと最後のコミットまで巻き戻す。

「いろいろ作業して動かなくなってわけわからなくなったからとりあえず前回のコミットしてある動く状態まで戻すぞ！」
といった使い方をしているが、結構危険なコマンドなので、
対策としてわかりやすい削除すると書いた名前のリモートブランチにバックアップをあげておいてからこれをした方がいい。

参考
[[Git]コミットの取り消し、打ち消し、上書き]
(https://qiita.com/shuntaro_tamura/items/06281261d893acf049ed)

> --hardオプション：コミット取り消した上でワークディレクトリの内容も書き換えたい場合に使用。
> --softオプション：ワークディレクトリの内容はそのままでコミットだけを取り消したい場合に使用。

## git stash -u / git stash apply で変更を一時的に逃して移動する

https://qiita.com/keisuke0508/items/4ad7caf544b1ad631fd7

ブランチをまちがえて作業してしまった場合に便利。

とりあえず

```
git stash -u
```

で最後のコミットまでの変更内容を stash という一時的な場所において

git checkout で適切なブランチに戻って

```
git stash apply
```

で変更内容を退避させる前の状態に戻してステージングを始めるといった使い方をよくしている。

## git rebase -i, ピンポイント history 修正

コミットログが汚くなってしまった時には必要な歴史修正コマンド
interactive の i、 双方向。会話型。

参考
[コミット履歴の改善に役立つ git rebase -i 5つの使い方]
(https://qiita.com/kmt/items/42f7f5678d0fc1126d79)

```
git rebase -i <hash>
```

これで git log で出てきたコミットの id を指定すると

```
git rebase -i 043a...

pick 89bc90e Update issue templates
pick 3b933ef 2020/07/03 日報を追加します
pick d335193 rebased del 07/02, 07/03
pick 7b956fd add 2020/07/03 daily report
pick 523ce31 Revert "wrote 0702"
pick 2b6286f Revert "Update issue templates"

Rebase 043aab3..2b6286f onto 7b956fd (6 commands)
```

指定したhashの一つ前までのコミットが vim で開かれる。

ここで pick を　変更する。詳細は

```
# 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
```

となっている。

非常に難しく、しくじると非常にスパゲティーなコミットログになってしまう、
**git で一番難しいコマンドである** (私の知る限りでは)

なお、この操作の後は `push -f origin <branchName>`が必要

参考
[ git commit を取り消して元に戻す方法、徹底まとめ　]
(http://www-creators.com/archives/1116)

>過去のコミットをhistoryから削除する
git rebase は、ブランチの歴史を修正するためのコマンドで、ブランチ履歴の整理のために、よく使うのgit コマンドの１つです。「i」は「interactive（インタラクティブ）」の略です。

>例えば、下記のように取り消したいコミットの直前を指定します。あくまで「リベース」を実行していますので、「どのコミットを取り消したいか？」ではなく、「どのコミットに対して、リベースをかけるか」を引数で指定する点を意識して下さい。

>現在のブランチを、<commit> までリベース。
`git rebase -i <commit>`

> 一般的に、git rebase -i は、「s（コミットを直前のコミットに統合）」「f（sと一緒。ログも破棄）」などを利用して、ログをキレイに整える目的で利用することが多いでしょう。



```
git rebase --abort
```

で今のrebaseをなかったことにできる

```
git rebase -i e31b897
CONFLICT (add/add): Merge conflict in 2020/07/03.md
Auto-merging 2020/07/03.md
error: could not apply a18f98b... add 2020/07/03 daily report
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".
Could not apply a18f98b... add 2020/07/03 daily report
```

参考
[git rebase --skip を知らなくて沼から出れなくなった]
(https://qiita.com/oke-py/items/3ed27931986d4fe09818)

`git rebase -i` の最中では

```
git rebase --skip
```

をしないと抜け出せない場合があるので注意。


## git revert

コミットの作業の取り消しコミットを作成する

```
git revert コミットのハッシュ値
```

>作業ツリーを指定したコミット時点の状態にまで戻し、コミットを行う（コミットをなかったことにはせず、逆向きのコミットをすることで履歴を残す）

例えば `hoge.md` を削除したコミットを revert に指定すると、
逆方向のコミットとして、`hoge.md` を作成したコミットが作成された。

本番環境で巻き戻しをする時にはこのコマンドを使うらしい。
それをスムースに行うために 提出する feature ブランチではわかりやすいコミット履歴にしておくことが重要。