実務でよく使うgitコマンドライン

# バージョン管理の個人的な考え方
~~基本的に歴史は改変したくないタイプ。タイポの修正もわざわざ`rebase`しないで普通に`commit`します。
初回の`push`までは色々試行錯誤もあるので、ローカルのコミットはしばらく放置していますが、目処が立ったらステージングしコミットを付けていきます。
[Gitでやらかした時に使える19個の奥義](https://qiita.com/muran001/items/dea2bbbaea1260098051)がよく検索ヒットしますが、個人的にはあまり実務で使ってないんです。
そこで、ビギナー〜中級の人向けに役に立つコマンドを以下にまとめます。~~

という書き出しで当初はまとめていましたが、実は考えが変わってきました。

rebaseからのforced pushはやっても良い派になりました。ただし、forkからのPull Requestもしくはレビュー前のコミットの場合に限ります。自分のリポジトリのブランチなんだからキレイにした後にマージしてもらう方が親切だったり、レビューワーを意識したまとまりにしておく方が親切かなっと。

# 脱Git初心者のための参考書
- [Gitのリポジトリの中身をなるべく正確に理解する](https://www.kaitoy.xyz/2015/12/27/git-repository/)
  - これはマジで一回読んだほうがいい
- [A successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model/)

# 実務でよく使うGitコマンド
## 変更点を部分的にステージングしたいとき
最初は試行錯誤し、目処が付いたらコミットを開始するんですが、同じファイルの一部だけをコミットしたい場合が多発します。そんなときはこれ。

```terminal
$ git add -p [ファイル]
```

### 概要
変更点の塊＝ハンク（Hunk）を指定してステージングすることができます。
ハンクごとにどうするのかを聞かれ、回答に以下のオプションを入力してステージングするかどうか決めていく。

ちなみに、`p`は`patch`のことです。
>       -p, --patch
           Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to
           review the difference before adding modified contents to the index.
           This effectively runs add --interactive, but bypasses the initial command menu and directly jumps to the patch subcommand. See
           "Interactive mode" for details.

### オプション

|オプション|意味（原文）|意味|
|-------|---------|---|
|y|stage this hunk|このハンクをステージングする（インデックスに移す）|
|n|do not stage this hunk|このハンクは無視する|
|q|quit; do not stage this hunk or any of the remaining ones|このハンク以降は無視して終了する|
|a|stage this hunk and all later hunks in the file|このハンクとそれ以降のハンク全部ステージングする|
|d|do not stage this hunk or any of the later hunks in the file|このハンク以降はステージングしない|
|g|select a hunk to go to|ステージングするハンクを選択する|
|/|search for a hunk matching the given regex|正規表現に一致するハンクを探す|
|j|leave this hunk undecided, see next undecided hunk|このハンクは保留して次の未操作ハンクに移る|
|J|leave this hunk undecided, see next hunk|このハンクは保留して次のハンクに移る|
|k|leave this hunk undecided, see previous undecided hunk|このハンクは保留して前の未操作ハンクに移る|
|K|leave this hunk undecided, see previous hunk|このハンクは保留して前のハンクに移る|
|s|split the current hunk into smaller hunks|このハンクを自動で分割する（分割されたハンクごとに同じように操作していく）|
|e|manually edit the current hunk|このハンクを手動で分割する（別の画面へ）|
|?|print help|ヘルプを表示|

## 勢いでステージングしたハンクをワークツリーに戻したい
さっきの`git add -p`で追加間違いしたらこれで戻します。

```terminal
$ git reset -p [ファイル名]
```

### 概要
基本的には`git add -p`と同じ使い方ですが、オプション`e`で手動で各行の操作を実行する場合は固有の操作が必要です。
`e`を選択した場合は以下のような表示でvi等のエディタが起動します。

追加部分`+`をワークツリーに戻す場合は、行頭の`+`をスペースに置き換えてあげます。
削除部分`-`をワークツリーに戻す場合は、その行を削除。
編集が完了したら上書き保存してエディタを終了します。

```sh
# Manual hunk edit mode -- see bottom for a quick guide.
@@ -21,6 +25,6 @@ class UsersController < ApplicationController
   private

   def method
-    params.require(:user).permit(:name)
+    params.require(:user).permit(:name, :avator)
   end
 end
# ---
# 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 unstaging.
# 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.
```


## プルリクエストをレビューしたいからローカルに落としたい
レビューを依頼されたら、そのプルリクの変更が反映された環境をローカルで動作確認したい場合があります。
そんなときはこれ。

```terminal
$ git fetch [remoteリポジトリ名（originなど）] pull/[id]/head:[ブランチ名]
$ git branch
* master
 [ブランチ名]
$ git checkout [ブランチ名]
```

### 概要
ローカルに該当プルリクエストのブランチをfetchし、そのブランチにcheckoutします。
例えば、以下のようなプルリクがあったとすれば、`git fetch origin pull/4893/head:check-confirmation-token-present`となります。

<img width="806" alt="スクリーンショット 2018-07-05 23.43.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/249168/f61ce915-6e74-2db0-42bd-dc5bcc580511.png">

### 参考
https://qiita.com/tarr1124/items/d807887418671adbc46f

## インデックスの内容の差分を確認
コミットする直前で、今から何のコミットだっけ？ってど忘れをすることが多い方にはこれ。

```terminal
$ git diff --cached
```

### 概要
ステージングエリア（インデックス）の内容と最新コミット時点の差分を表示します。

## ワークツリーの内容の差分を確認
これからどの変更をステージングしようかな？と悩んだときはこれ。

```terminal
$ git diff
```

### 概要
ワークツリーの内容と最新コミット時点の差分を表示します。

## ローカルとリモートのmasterブランチに差異があるので、リモートの内容に強制同期
なぜかリモートと差異があった場合にはコレでリモートの内容に合わせます。

```terminal
$ git fetch origin
$ git reset --hard origin/master
```

## 過去のコミットメッセージを変更したい
以下の方法は、今更気づいた場合かつ、***pushしていない場合***のみ有効な方法です。
ちなみに、`n`はHEADを1としたときに何番目のコミットかを表す数字。つまり、`git log --oneline`で上から数えた値です。

```terminal
$ git log --oneline
0237ff3 (HEAD -> hoge) 
98071c1 途中のコミット
8d99250 大切なコミットっ
$
$ git rebase -i HEAD~n
## コミットがコミット順の昇順でならんだviが開くので、変更したいコミット（今は8d99250とします）の行でpickをeditに変更して上書き保存します。
## すると...
Stopped at 8d99250...  大切なコミットっ
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue
$
$ git commit --amend -m '大切なコミット'
[detached HEAD 2b6a3d2] 大切なコミット
 Date: Thu Feb 14 14:52:37 2019 +0900
 1 file changed, 84 insertions(+), 4 deletions(-)
$
$ git rebase --continue
Successfully rebased and updated refs/heads/feature/AL-809.
$
```

### 概要
つまりはただの`rebase`です。`rebase`なので`push`していないときに使うようにします。

# まだまだ追加していきます。

# 参考
- [gitでリモートのブランチにローカルを強制一致させたい時](https://qiita.com/ms2sato/items/72b48c1b1923beb1e186)