わかりづらいgitコマンドのメモ

たまに忘れるので、自分にもわかりづらい適当なメモを残します。

##基本
|用語|概念|
|---|---|
|GIT|警察、検察|
|リポジトリ|国|
|ファイル|人間|
|ファイルの変更|犯罪|
|アントラッキングファイル|不法入国者|
|アンモディファイドファイル|カタギ|
|モディファイドファイル|被疑者|
|ステージドファイル|被告人|
|コミット|服刑|
|ブランチ|前科|
|ステージング|裁判所|

![18333fig0201-tn.png](https://qiita-image-store.s3.amazonaws.com/0/94686/901c74dc-56ca-c770-69c7-d5699ce371ae.png)


##逮捕&起訴
```console
$ git add <Filename>
$ git add . # 全部
$ git add -p # 対話式で選択
```

##判決
```console
$ git commit -m 'commit message' # 判決理由が入る
```

##強制送還
```console
$ rm <filename> # 普通に削除
$ git clean -f # 先に--dry-run で確認すると酔い
```

##免罪、誤認逮捕、不起訴
```console
$ git rm <filename>
$ git mv <filename>
$ git reset <filename> # addしたindexにあるfileをindexから除外
$ git reset --hard HEAD^
$ git reset --hard <commit hash>　# 刑の免除
$ git revert <commit hash>　# 逆位相
$ git rebase -i # タイムリープ
$ git checkout <commit hash>
```

## 処分保留？
```console
$ git stash list
$ git stash save -u <message> # アントラッキングも含める
$ git stash apply <stash number>
$ git stash drop <stash number>
$ git stash clear # 全消し
```

## 比較系
```console
$ git diff 
$ git diff --cached
$ git diff HEAD HEAD^
```

##取り調べ
```console
$ git status
$ git status --short
```

##実刑の確認
```console
$ git show <commit name>
```

##前科の確認
```console
$ git log 
$ git log --graph --date=short --format='%C(yellow)%h%C(reset) %C(magenta)[%ad]%C(reset)%C(red)%d%C(reset) %s %C(cyan)@%an%C(reset)' # aliasのやつ
```

## マージ系
```console
$ git merge <branchname> # 現在のブランチに別のブランチをマージ
$ git push -f # プルリク歴史修正の時の強制プッシュ
$ git push origin <local branchname> # 現在のブランチをリモートにマージ
$ git pull origin <remote branchname> # リモートのブランチを現在のブランチにマージ
$ git fetch # originの今のブランチの変更履歴を取得 --allだと全てのブランチ
$ git mergetool
```

## ブランチ刑
```console
$ git reflog # ブランチの操作ログ
$ git checkout -b <branchname> # ブランチを作成してチェックアウト
$ git branch -a  # 全部ブランチ名の確認
$ git branch -r  # リモート全部ブランチ名の確認
$ git branch -v  # 詳細表示
$ git branch -d <branchname> # ローカルのを削除
$ git push origin :<branchname> # リモートに削除

$ git fetch
$ git checkout master
$ git merge origin/master
```


## タグとか
```console
$ git tag # tag一覧
$ git tag <tagname> # タグ付け
$ git -d <tagname> # ローカルのタグ削除
$ git push origin :<tagname> # リモートに削除
$ git push origin --tags # タグ全部送信
```

## 最初にやること
```console
$ git init # 一から作る
$ git remote add origin git@<ssh hostname>:<myname>/<reponame>.git
$ touch README.md
$ git add .
$ git commit -m 'init'
$ git push -u origin master
```
```
$ git clone # 他人のパクる
```

## 不法入国者へ戻す
```console
$ git rm --cached "filename"
$ echo "filename" >> .gitignore 
```

## submodule
```console
$ git submodule add https://github.com/twbs/bootstrap.git bootstrap
$ git submodule update # 内部submoduleを同期する
```

## 後からgitignoreに追加
```console
$ echo "hoge.tmp" >> .gitignore
$ git rm --cached hoge.tmp
```

# リベース
## マージとリベースの違い
※Gitは共通の祖先のコミットから比較してdiffを出している。

```console
$ # 下図だと、共通の親はCなのでCからFまでの変更点（EとDへ変更した内容）としてまとめられる
$ checkout master && git merge topic 
$ # 下図だと、共通の親はEなのでEからD'までの変更点（Dで変更した内容）としてまとめられる
$ checkout topic && git rebase master
```

マージ
![image](https://qiita-image-store.s3.amazonaws.com/0/94686/7b344b9f-e5db-21dc-63a4-9596c096bcb4.png)
リベース
![image](https://qiita-image-store.s3.amazonaws.com/0/94686/339b28ed-33ae-69d2-4076-ac1eeabb1ae6.png)


## プルリクが別のプルリクに依存する場合のプルリクの作り方
```console
$ git fetch --all # リモートのブランチ一覧取得
$ git checkout develop # developブランチは派生元にしたいブランチ(このブランチはmasterから派生していおり、変更が今後ありそうにないブランチ、プルリクではLGTM済み)
$ git checkout feature # developブランチから新たなfeatureを作成する
$ # featureブランチのプルリク作成
$ git commit -m "追加" # featureに追加する
$ # developのプルリクがmasterにマージされた
$ git fetch --all # リモートのブランチ一覧取得
$ git checkout feature 
$ git rebase develop # featureブランチの上で最新のdevelopにrebaseする
$ git push origin feature -f # フォースプッシュでプルリクの履歴を書き換える
```

## プルリクのコミットをキレイにする(rebase squash)
```console
$ git rebase -i <commit hash> # 修正したいコミットの一つ前のコミットのハッシュを指定する（0d4a808の前のコミットを指定する）

pick 9a54fd4 commitの説明を追加　  # <- この中で一番古いのコミット
squash 0d4a808 pullの説明を追加    # <- これをsオプションで上のコミットと混ぜられる

# Rebase 326fc9f..0d4a808 onto d286baa
#
# 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.
#
$ git push origin hoge -f
$ # もしcannot squash without a previous commitが出た場合
$ git rebase --abort

```

## プルリクの過去のコミットを変更する(rebase edit)
- 空のコミットメッセージと内容のcommitがある場合は下手に

```console
$ git rebase -i <commit hash> # 修正したいコミットの一つ前のコミットのハッシュを指定する（0d4a808の前のコミットを指定する）

pick 9a54fd4 commitの説明を追加　  # <- この中で一番古いのコミット
edit 0d4a808 pullの説明を追加      # <- このコミットでの変更内容を再変更する

# Rebase 326fc9f..0d4a808 onto d286baa
#
# 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.
#
$ vim hoge.js 
$ git add hoge.js # 変更を追加
$ git commit --amend # コミットに追加する
$ git rebase --continue # コミットを適用する
$ git push origin hoge -f
```

## リベースのマージ
```console
$ git rebase branch
// コンフリクトしたファイルを修正後
$ git add conflicted_file
$ git commit
$ git rebase --continue
$ git rebase --abort // ダメそうだったら
```

## スタッシュ
コマンドをよく忘れるが次を見ればいい。
https://qiita.com/akasakas/items/768c0b563b96f8a9be9d


## コンフリクト解消
```console
$ git pull origin master # コンフリ発生！！
$ git reset --hard ORIG_HEAD # マージを元にもどす
```

## リベース前のブランチからの派生した新ブランチを、リベース済みの元ブランチへのリベースする方法

- develop > feature > hotfix の順で派生しておりhotfixがfeatureの機能に依存している
  1. featureをdevelopでrebaseした(その結果featureのコミット番号が全部変わった)
  2. bugfixをfeatureでrebaseしようとするとconflictが発生した(しかも、diffがめちゃくちゃ)

```console
$ git push origin hotfix # rebase前のhotfixのバックアップ
$ git checkout hotfix
$ git rebase --onto feature ff34ed1 # featureを元にしたff34ed1に続くcommitを持つhotfixのブランチを作る
$ # つまり、
$ # - ff34ed1は古いfeature(リベースされる前)の最後のcommit番号。
$ #   - つまり、hotfixのPRのコミットログにあるfeatureの最後のCommit番号　
$ #   - 別の言い方をすればhotfixブランチの元のcommit番号
$ # - featureオプションはもちろん新しいrebase済みのブランチを指す
First, rewinding head to replay your work on top of it...
Applying: AAAAAAAA #31
Applying: XXXXXXX #31
Applying: VVVVVVV #31
$ git diff master --stat # hotfixのPRのdiffとくらべて同じなら多分大丈夫
6 files changed, 251 insertions(+), 3 deletions(-)
```
つまり、rebaseされた元のブランチfeaturに、
rebaseされる前のfeatureの最後のcommit(hotfixの元のcommit)を足したということ

## リベース前のブランチからの派生した新ブランチ(マージコミット済み)を、リベース済みの元ブランチへのリベースする方法

```
master > feature > hotfix の順でhotfixはfeatureからrebaseで切った。
ただし、masterが別のブランチ(feature2)でマージされ、歴史が進められたので、featureブランチとfeature2ブランチでconflictした
そこで、featureブランチをmasterでmergeし、マージコミットをfeatureブランチに作って、conflictを解消した
その後、featureブランチはmasterブランチにマージした(mergeコミットありで)
hotfixはfeatureから切られているので、featureに再rebaseしたが、hotfixの修正内容が反映されていない。
その時は、hotfixをmasterブランチでmergeして解消するのが良い。
```

## 同一ファイルを分割してCommit

```terminal
$ git add -p
y or n or q...
$ git diff --cached # the diff between staging and HEAD
```

## あるファイルをあるコミット/ブランチの前に戻す

```
$ git checkout c5f567~1 -- file_a.py file_b.py
```

https://stackoverflow.com/questions/215718/reset-or-revert-a-specific-file-to-a-specific-revision-using-git

## --no-ff --no-commit

https://qiita.com/nog/items/c79469afbf3e632f10a1

# 参考文献
脳
🍏さん
https://liginc.co.jp/web/tool/79390
http://www.backlog.jp/git-guide/stepup/stepup7_5.html
https://stackoverflow.com/questions/27445747/how-to-rebase-over-already-rebased-branch
https://stackoverflow.com/questions/1587846/how-do-i-show-the-changes-which-have-been-staged