Gitコマンドメモ

# Gitコマンドメモ
自分が使ったGitコマンドをまとめる。
前にもまとめたことを含めて綺麗に書きたくなった。

# ■基本コマンド
## +変更の状態を確認する
### ①全体の変更を確認する
```
$ git status 
```
変更点の状態を見る。
addしたファイル、commitしたファイル、conflictしたファイル…などが確認できる。


### ②特定のディレクトリ・ファイルの変更を確認する
```
$ git status [ディレクトリパス or ファイルパス]
```
ディレクトリを指定すると、その箇所の変更点のみ確認できる。
指定しないと全管理対象の変更点が確認できる。

---
## +変更の履歴を見る
```
$ git log [オプション]
```
作業ブランチの履歴を見ることができる。

オプション| 説明
------------ | -------------
--oneline| ハッシュ値＋commitメッセージの1行目を見ることができる
--graph| ブランチの分岐を確認できる。GitLabのNetworkみたいなもの
-p | コミットの変更点を見ることができる
-p [ファイルパス] | そのファイルの変更点を見ることができる
--author=[ユーザー名] | 指定したユーザーのcommitメッセージを見ることができる
--grep [検索文字列] | 検索文字列に合致するコミットのみ表示できる
--name-only | 変更したファイル名のみ表示(-pと併用した場合、こちらが優先)
---

### トピックブランチに対する変更内容を見る

`master` ブランチに対して、トピックブランチにどんなcommitがあったかを確認する

```
 git log master..[トピックブランチ名]
```


## +変更点をaddする
```
$ git add [ファイルパス]
```

設定方法| 説明
------------ | -------------
-u [削除済ファイルのパス]|ファイルの削除履歴をaddする

インデックスにファイルを登録する。
ディレクトリ指定可。

---
## +変更点をcommitする
```
$ git commit [オプション] 
```

オプション| 説明
------------ | -------------
-m "commitメッセージ" |1行のコミットメッセージを付けてcommitができる
--allow-empty|空commitの作成(-mと併用できる)


---
## +変更点をpushする
```
$ git push origin [ローカルブランチ名]
```

このコマンドの本来の構文[^1] は下記の通り。

originの省略については後述する。

```
$ git push origin [ローカルブランチ名]:[リモートブランチ名]
```


## +差分を抽出する
```
$ git diff [オプション] ([設定値])
```

設定方法| 説明
------------ | -------------
オプションなし|全体の差分を抽出する
--name-only|差分があるファイル名のみ抽出する
[ディレクトリパス]|特定のディレクトリの差分を抽出する
[ファイルパス] |特定のファイルの差分を抽出する
[old_commit] [new_commit]|commit同士の差分を抽出する

## +誰が修正したのかを見る
```
git blame [ファイル名]
```

## +指定したcommitの変更点を見る
```
$ git show [commitのハッシュ値]
```

## +変更点を一旦退避させる
使いどころとしては変更してる最中に別ブランチをMergeしたくなったとか、そういうとき。

```
$ git stash [オプション] ([設定値])
```

本来は`git stash save`だが、`save`は省略できる。

設定方法| 説明
------------ | -------------
save "コメント"|コメント付きで一旦退避できる
list|現在退避させている変更点の一覧を表示
pop|退避させた変更点を再度適用する
clear|退避させている変更点を全て削除する

`drop`で1つずつ削除できるらしい。(何も指定しなければ最新のもの、`drop`の後に引数を指定すれば特定のものを削除可能)
使ってないので使って便利だったら表に追記します。
(今のところ大体stashを消すときは「これいつのstash…？」ってときだけなので、clearしか使ってません。)

# ■Git設定編
## +リポジトリの作成をする
### ①Gitリポジトリをカレントディレクトリに作成する
```
$ git init
```

### ②Gitリポジトリを特定のディレクトリに作成する
```
$ git init [ディレクトリパス]
```

### ③ベアリポジトリ [^2] を作成する
```
$ git init --bare [ディレクトリパス]
```

## +ユーザー情報を設定する
```
$ git config --global user.name "あなたの名前"
$ git config --global user.email "あなたのメールアドレス"
```

最低限上記の内容は設定しないとエラーになる。
他にも下記のようなものがある。

## +Gitの出力をカラーリングする
```
$ git config --global color.ui auto
```

## +Gitのエイリアスを設定する
```
$ git config --global alias.co checkout
```

## +日本語のファイル名を表示できるようにする
```
$ git config --local core.quotepath false
```

## +エディタをvimにする

```
git config --global core.editor 'vim -c "set fenc=utf-8"'
```

## +Gitの設定を確認する
```
$ git config --global --list
```
※ここまで`--global`を設定していない場合は`--global`は不要です。

## +gitの設定ファイルについて
* グローバルは`~/.gitconfig` 、リポジトリ毎は`.git/config`に保存されている。
* コマンドを打つと保存される。

### 【便利な設定】git logを見やすくする
[参考サイト](http://blog.toshimaru.net/git-log-graph/)


--allの方は設定しなかった。
必要な場合は`git lg --all`をつければいいので…。

```
[alias]
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --date=format:'%Y/%m/%d %H:%M:%S'
```

参考サイトとの違いは、
* `--abbrev-commit`を外した。
* `%cr`を`%cd`にした。
* `dete=format`をかけた。
ぐらい。

※気づいたこと
* (format先に書いていたからかもしれないけど)formatの設定が優先される。(`%cr`してると`--date=short`がかからない)
* `%cD`だと`data=format`がかからない。

[参考サイト](https://git-scm.com/docs/pretty-formats)より、今回使ったプレースホルダの説明。
※英語読めないマンが無理矢理読んでるのでちょっと違うところあるかも。

プレースホルダ| 説明
------------ | -------------
%Cred|カラーを赤にする
%h|短いコミットIDで表示
%Creset|カラーリセット
%C(色名)|次に色指定があるまでこの色
%d|現在のブランチ?(英語のref nameをどう訳せばいいのかわかんなかった)
%s|コミットの一行目(多分)
%cr|相対的な日付
%cd|YYYY-MM-DD形式
%cD|↑で時間も表示したいとき
%an|コミットした人の名前


## +ブランチの設定をする
**.git/config**を設定する。
これにより、originを省略することも可能。
[参考サイト](https://git-scm.com/book/ja/v1/Git%E3%81%AE%E5%86%85%E5%81%B4-%E5%8F%82%E7%85%A7%E4%BB%95%E6%A7%98%EF%BC%88Refspec%EF%BC%89)

### 【設定コマンド】push/pull時の　`origin master`　を省略する
下記コマンドを打つと`.git/config`に設定が追記される。
設定後は`git pull` `git push`でOKとなる。

`git config branch.master.remote origin`
`git config branch.master.merge refs/heads/master`

## +リモートリポジトリの参照を確認
```
$ git remote -v
```
設定されている全てのリモートリポジトリが確認できる。

## +リモートリポジトリの参照を追加
```
$ git remote add [任意の名前] [Gitリポジトリ]
```

## +リモートリポジトリの参照を削除
```
$ git remote rm [任意の名前]
```

## +.gitignore
* Gitの管理外ファイルを設定する。
* リポジトリ下に配置。


### [ネタ].gitignoreのグローバル化
環境依存のファイル等を変更する場合、`.gitignore`をグローバル化すると、便利。

下記のコマンドで設定する。

```
$ git config --global core.excludesfile ~/.gitignore_global
```

この後、`~/.gitignore_global`を変更すればOK。


# ■ブランチ操作編
## +ブランチの作成・削除
```
$ git branch [オプション] [ブランチ名1] ([設定値])
```
設定方法| 説明
------------ | -------------
オプションなし|新規ブランチの作成
-d [ブランチ名1]|ブランチの削除
-D [ブランチ名1]|ブランチの強制削除
-m [ブランチ名1] [ブランチ名2]|ブランチ名1からブランチ名2へ名前を変更
-l [ブランチ名1] [commitのハッシュ値]|特定commitでブランチを切る
-vv|全ブランチのHEADを確認する

## +作業ブランチの変更(チェックアウト)
```
$ git checkout [オプション] [ブランチ名]
```
オプション| 説明
------------ | -------------
-b|指定したブランチ名でブランチを作成し、チェックアウトする

## +[おまけ]ブランチを切るとどうなるか
あるワークツリーからブランチを切ると、新しいブランチのHEADは切ったワークツリーのHEADと同じ状態になる。

```変更前
branchA：HEAD 1
branchB：HEAD 2
branchC：HEAD 3
```

```lang:branchAで新しくブランチを切ったとき
branchA：HEAD 1
branchB：HEAD 2
branchC：HEAD 3
branchD：HEAD 1
```

# ■履歴操作編
## +「HEAD^」について
HEAD^はワークツリーの最初のcommit。
「^」を増やすと次のcommitになる。
以降、「HEAD^」と記載した箇所は「^」を増やせば次のcommitを指定できるということとして記載する。

## +addを取り消す
```
$ git reset HEAD [ファイル名]
```
## +commitを取り消す
```
$ git reset [オプション] HEAD^
```
オプション| 説明
------------ | -------------
--soft|commitのみ取り消し、ファイルの変更は残す
--hard|commitもファイルの変更も全て取り消す

## +commitメッセージを修正する
直前のcommitメッセージを直す場合は下記のようになる。

```
$ git commit --amend
```
直前じゃないときは、git rebase -i を参考のこと。

## +commitを打ち消す
あるcommitを打ち消す。
commit履歴は残したままその変更をなかったことにできる。

```
$ git revert [commitCのhash値]
```
commitメッセージは**Revert [指定したcommitのcommitメッセージ]**になる。

## +commitした中の1ファイルだけ取り消す
複数ファイルでcommitしたが、1ファイルだけ戻したいときに使う。

```
$ git checkout [commitのハッシュ値] ファイルパス
```

## +pushしたcommitを取り消す

```
$ git push -f origin HEAD^:[ブランチ名]
```

## +commitを別ブランチのHEADに移動する
commitを移動先ブランチへ移動し、下記コマンドを使う。
conflictしやすいので注意する。

```
$ git cherry-pick [移動元のブランチでのハッシュ値]
```

```
【ex】

branchA commitA
を
branchB のHEADに移したい


$ git cherry-pick [commitAのハッシュ値]
```




## +git rebase -i でcommitメッセージの修正やcommitの統合を行う
```
$ git rebase -i [ハッシュ値 or HEAD^]
```
こうすると、画面が次のように表示される。

```
pick [ハッシュ値] commitメッセージ
pick [ハッシュ値] commitメッセージ

# Rebase XXXXX..XXXXX onto XXXXXX
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
```

※使ってみたところ

```
pick ……
s ……
```

みたいな、省略版と普通に書く版を混同するのはだめみたいでした。
一番上をpにしたら通りました。なるほど。

### ①commitメッセージの修正
* 変更したいcommitメッセージの左横にあるコマンドを「reword」にして保存
* 新しいcommitメッセージを書いて保存

### ②commitをまとめる
* まとめたいcommitメッセージの左横にあるコマンドを「squash」にして保存

```
pick [ハッシュ値] commitA
squash [ハッシュ値] commitB

⇒commitAとcommitBをまとめてくれる。
```
* 新しいcommitメッセージを書いて保存
* 何個でもsquashを書きこめば、そのコマンドを書き込んだcommitの前のcommitとまとめてくれる
 
```
pick [ハッシュ値] commitA
squash [ハッシュ値] commitB
squash [ハッシュ値] commitC
squash [ハッシュ値] commitD
squash [ハッシュ値] commitE
squash [ハッシュ値] commitF

⇒commitA～commitFをまとめてくれる。
```

## +git reflogを使って過去の履歴に戻る
```
$ git reflog
```

上記コマンドを打つと、下記のようなログが出る。

```
[ハッシュ値] HEAD@{0}: 処理:  処理の詳細
[ハッシュ値] HEAD@{1}: 処理:  処理の詳細
[ハッシュ値] HEAD@{2}: 処理:  処理の詳細
…
…
```

git reset --hard を使用し、HEADの位置をずらす。

```
git reset --hard HEAD@{X}
```
ちなみに ブランチに関わらずHEADが指定したものになるっぽいので注意。

### 注意点
`git reset --hard HEAD@{X}`したとき、**編集中ファイルの変更分**もぶっ飛びます。
さっきそれやって死にたい気持ちでいっぱいになりました。
気を付けよう…。
特に複数人で扱ってるブランチでは注意……。
みんなこまめにcommitしようよぉ、ふえぇ……。

# ■Git対象ファイル操作編
## +Git管理対象ファイルのファイル名を変更
```
git mv [変更前ファイル名] [変更後ファイル名]
```
新規ファイルではなく、変更ファイルとして扱われる。

# ■リポジトリ取得編
## +リポジトリをcloneする
```
git clone [リポジトリのURL] ([cloneしたいディレクトリ名])
```
ディレクトリ指定でcloneすることも可能。

# ■rebaseとmerge編

## +rebase
```
git rebase [取り込みたいブランチ名]
```

AブランチにBブランチを取り込みたい場合はAブランチに移動してから。

```
git checkout A
git rebase B
```


### rebaseの特徴
* 履歴を付け替える
* rebaseすると取り込み側のcommitIDが変わる(なのでリモートにpushする側のブランチではrebaseしない方がよい)

## +merge
```
git merge [取り込みたいブランチ名]
```

AブランチにBブランチを取り込みたい場合はAブランチに移動してから。

```
git checkout A
git merge B
```


### mergeの特徴
* 履歴を合流させる
* commitIDはそのまま
* Fast-Forword：MergeCommitを作らない(履歴が分岐していない)
* Non Fast-Forword：MergeCommitを作る(履歴が分岐している)


## merge or rebaseを間違った直後に取り消したいとき
```
git reset --hard ORIG_HEAD
```

## [おまけ] pull
* pull = fetch + merge
* pull --rebase = fetch + rebase


# ■シーン編
大体Railsの話。
## schemaファイルがリモートで更新されたとき
ローカル環境でテーブルを作成しながら開発をしていた。
だが、開発中に別の開発者がテーブルを作成し、リモートにpushした。
しかも作成した時間は自分がローカルに作成した時間よりも遅い。
このままでは、自分のブランチに持ってきたときにschemaファイルが競合するし、何よりリモートにpushしたとき自分の作りたいテーブルが作れない。
新しくマイグレートファイルを作り直しすればいいけど、他に方法はないかなって。
こんなときどうする？

### 前提：こうしておくとやりやすい
* branchは切ってある
* ローカルで作ったテーブルをdropしておく
* テーブル作成系のファイルのみでcommitしておく

### ①テーブル作成のcommitをrevertする

```
$ git revert [テーブル作成commitのhash値]
```

### ②revertしたcommitも含めて、commitをまとめる
```
$ git rebase -i [まとめたいcommitより前にあるcommitのハッシュ値]
```

### ③リモートと同期したブランチとmergeする
```
$ git merge [マージしたいcommitのハッシュ値]
```
※rebaseでも可。

---
[^1]: ローカルブランチ名とリモートブランチ名が同じであるため、省略することが可能。

[^2]: 作業ディレクトリを持たない空のGitリポジトリ。共有リポジトリとして作成する。→参照：[https://www.atlassian.com/ja/git/tutorial/git-basics#!init](https://www.atlassian.com/ja/git/tutorial/git-basics#!init)