[Git入門]コマンドから学ぶGit[解説]

# コマンドから学ぶGit

## 対象読者

- gitを勉強中の人
- 機械的にfetchしてaddしてcommitしてpushみたいなのが出来るようになったけど業務に活かしたい人
- リモート追跡ブランチって何？って人
- HEADとかHEAD^って何？って人
- addとかcommitを取り消すコマンドを毎回ググっちゃう人
- コマンドで色々参照したい人
- エイリアスを設定したい人

（正確な説明ではないところがあります）

## git branch

### 概念

ブランチはいくつかに分類して考えるとわかりやすいです。

1. リモートブランチ　　（GitHubとかにあるやつ）
1. リモート追跡ブランチ（ローカルにあるremotes/origin/なんとかとかいうやつ）
1. 追跡ブランチ　　　　（ローカルブランチのうち、remotes/origin/なんとかを参照してるやつ）
1. ローカルブランチ　　（ローカルにしかないブランチ）

2,3のあたりの理解がGitを理解する上で大事かなと個人的には思います。

例えば、git fetchすると、2.が1.を元に最新化されます。

（ちなみにgit pullをすると、3.の追跡ブランチにマージまでされます。）



```
$ git branch [branch_name]
```

で、ブランチを新しく作ることができますよね。

そのとき、勝手に、同じ名前の「リモート追跡ブランチ」を追跡する「追跡ブランチ」が自動で作られます。
闇雲に git checkout -b とかでブランチを切っちゃダメ。
必ず、リモート追跡ブランチを意識すること。

新規作成してリモート追跡ブランチがないの時は、たとえば以下のpush時に -u オプションによって設定される。

```
$ git push -u origin [branch_name]
```



#### どのリモート追跡ブランチを追跡するのかを自分で指定したい場合

```
$ git branch [branch_name] [origin/branch_name]
```

とします。




だから、例えば、
「ローカルのブランチの状態が意味わからん。リモートの状態と一致させたい。」
「リモートブランチが強制pushされた。」
となったら以下のようにしてください。

#### リモート追跡ブランチを最新化して、追跡ブランチを作成する

```
$ git branch -D [branch_name]

$ git fetch

$ git branch [branch_name] [origin/branch_name]

or

$ git checkout [branch_name]
```

上のようにすると、.git/configに自動で設定を追記してくれる。
逆に .git/config に設定を追記すると、 fetch するブランチを指定できる。




## git stash

いろんな作業を並行していると他のブランチにチェックアウトできない！などが発生しますよね。

git stashしてください。


### git stash初めての人

2個だけ覚えていってください。

#### 一時的に退避

```
$ git stash
```

#### 退避した内容を元に戻す

```
$ git stash pop
```

上記を行えば、「一旦退避しといて、ブランチをcheckout 後、元に戻す」とかできます。




### ちょっと詳しい説明

#### 退避するときにコメントを追加

```
$ git stash save [comment]
```

#### stashされてるリストを表示

```
$ git stash list
```

#### stashの概要を表示

```
$ git stash show [index(listで出てくるstash@{0}とか)]
```

#### stashのindexを指定して元に戻す

```
$ git stash apply [index]
```
#### 上でapplyした場合、listからは消えないので、削除する。（popなら削除もされる）

```
$ git stash drop [index]
```



## git reset

間違えてaddしてしまった！間違えてcommitしてしまった！などで役立ちます。

毎回ググってよくわからずコマンドをコピペしてる人は、以下の2つを理解してください。

#### 3種類のRESETオプション
1. オプションなし（または --mixed）（commitとaddに影響）
1. --soft　　　　（commitのみに影響）
1. --hard　　　（commitとaddとワーキングツリーに影響）

#### HEAD（ブランチの場所を指し示すもの）
1. HEAD（現在のコミットの場所と思っておいたらいい）
1. HEAD^（ひとつ前のコミットの場所）
1. （何個前とか絶対参照もできるけど、あんまり使わない気がするので必要ならググってください）


### addを取り消したい場合

```
$ git reset
```
これは、以下の略です。

```
$ git reset --mixed HEAD
```
つまり、「オプションなし(--mixed)だから、add,commitに影響。commitをHEADの位置にし、addもHEADの位置にする。」

＝addのみしている場合、commitしていないため、HEADの位置は（コミットは）動いていない。

だから、「addのみを取り消す」という意味になる。




### commitを取り消したい場合

#### commitもaddも、ひとつ前に戻したい

```
$ git reset HEAD^
```

addもcommitもしている場合、HEADはひとつ動いているため、HEAD^を指定する。
→ オプションなしなので、addもcommitもHEAD^に移動する。



#### commitのみ取り消して、addはそのままにしたい

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

つまり、「--softを指定しているので、commitのみに影響。commitをHEAD^(ひとつ前のコミット)の位置にする」
（--softの場合、addの状態は変わらない）




### ローカルでの変更内容を破棄したい場合

#### すべて、ひとつ前のコミットの状態に戻したい（変更はローカルファイルからも消え去ります）

```
$ git reset --hard HEAD^
```
「--hardを指定しているので、add,commit,working_treeに影響。すべてをHEAD^（ひとつ前のコミット）の位置にする。」



#### まだコミットする前だけど、やっぱりローカルの変更を全部消し去りたい

```
$ git reset --hard HEAD
```
addもcommitもしていないなら、working_tree(ローカルの変更)のみがHEAD(現在のコミット)の位置に移動する。



#### git reflog
git reset --hard するなら覚えておきたい

git reset --hardしたら、git logからも見えなくなります。

```
$ git reflog
```

なら、HEADの移動の履歴が追えるので、もしかしたら、以下のようにしたら戻せるかも？（保証はしない）

```
$ git reset --hard HEAD@{No}
```



## その他少し便利なコマンド

### grep

```
git grep [pattern] [(branch_name等指定可)]
（例）
$ git grep test_pattern master
```
gitのファイルをgrepする場合、通常のgrepで検索するより速い。


### log -L

```
git log -L [start],[end]:[file_name]
（例）
$ git log -L 20,100:app/models/user.rb
```
file_nameのファイルの、start行からend行までの変更履歴を表示する。

この変更どういう経緯やねん、ってときに使える。


### show

```
git show [commit_id(git logとかで見る)]
git show [branch_name(or commit_id)]:[file_name]
（例）
$ git show asdfghjkl1234567890
$ git show master:app/model/group.rb
```
指定したコミットの変更を見る（GitHub見てもいいけど）
特に、別のブランチ(や、別のコミット)のファイルを参照したいとき、わざわざcheckoutするのが面倒なとき


### cherry-pick

```
git cherry-pick [commit_id]
(例)
$ git cherry-pick asdfghjkl1234567890
```
指定したcommitを取り込む（現在のHEADにマージする）
例えば、ブランチごとマージすると色々混じってしまって困る場合、欲しい箇所だけマージすることが可能。


### merge --squash

```
git merge --squash
（例）
$ git merge --squash origin/feature
```
マージするときに、マージ元のコミットを一つにまとめることができる。
origin/feature と commit1, commit2, commit3の差分がある場合、それをまとめて、差分を取り込む。それを commit4 としてコミットできる。
例えば、自分の作業用ブランチで荒れたコミットを一つにまとめたり、整えるときに便利。
（注意）あくまで、別のコミットとなるので、diffで参照すると、コミットの差分は発生します。




## そんなのもあるの？的なコマンド（おまけ）

### bisect

```
git bisect start [なんかおかしくなったコミットID] [正常だった頃のコミットID]
(例)
$ git bisect start asdfghjkl1234567890 zxcvbnm0987654321
```
どのコミットで、なんかおかしくなったのかを高速に探す。

（自動で二分探索を使ってコミットにチェックアウトして、テストを繰り返して原因のコミットを特定する）



```
$ git bisect run [test_file]
```
で、テストファイルを指定するか、手動テスト後、どちらかを実行します。

```
$ git bisect good
```

```
$ git bisect bad
```



今のコミットを見る

```
$ git bisect view
```
git bisectを終わりたかったら、

```
$ git bisect reset
```



## Tips

以上を全部理解しちゃったら、以下の設定をして、さらに快適にGitを使ってください。

### エイリアス登録

Gitのエイリアスを登録していない人は、登録すると少し幸せになれます。

checkout を co としたところでなんなん？と思っていましたが、結構快適です。



```
$ git config --global --edit
```

viが開くので、以下を追記

```
[alias]
  st = status
  br = branch
  ci = commit
  co = checkout
```


これで、例えば、$ git st で、git statusが見れます。