自分、何のGitコマンドやったか分かっとんのか...？

※この記事は [今年イチ！お勧めしたいテクニック by ゆめみ feat.やめ太郎 Advent Calendar](https://qiita.com/advent-calendar/2019/yumemi_no1) 2019年12月10日（10日目)の記事です。

初めましての方は、はじめまして
ご存知の方は、そうだよ。ヒズミさんだよ。

縁とノリと勢いで、10日目を担当します。
~~僕は、俗物なので、ハマーンミュラーの椅子はきっと無理です（typoじゃないです）~~


## epic1:悲劇は突然に

自分が何やったかわかっとんのか・・・( 'ω')👉
わﾞかﾞっﾞとﾞんﾞのﾞかﾞあﾞあﾞあﾞ！？ (  ‘ᾥ’ )👉👉👉

上キー押せよ・・・( 'ω')👉
う゛え゛キ゛ー゛お゛せ゛よ゛お゛お゛お゛お゛お゛お゛お゛お゛お゛お゛お゛！？ (  ‘ᾥ’ )👉👉👉👉

　＿人人人人人人人人人人人人＿
＞　 $ git push -f origin master 　＜
　￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

![explosions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83578/15bf0094-515b-4866-7395-436031ec8a24.png)

## epic2: git push -f origin masterって？なんで危ないの？

Linuxの禁忌コマンド `rm -rf /`と同じように扱われる`git push -f origin master` なんかやっちゃダメという雰囲気はわかるけど、なんでダメなんだろう？ネットや先輩から聞いてそう思ったことないですか？

一言で言うなら、このコマンドでリポジトリを破壊することも可能です。
今までのリポジトリへの積み重ねを無に返すことが可能です。

その理由を、説明していきます。

### そのGitコマンド、意味分かってます？

いつも叩いているGitコマンド。
その意味を理解して、入れているっけ？
僕はまだ分からないコマンドがいっぱいです。

変更ファイルをステージングに追加する。
`git add`
ステージングしたファイルをインデックスに登録する。
`git commit`

別の人の変更を取り込むために、
`git pull`または`git fetch`/`git merge`を、使ったりしますね。

それで、`git push`は、リモートリポジトリにコミットを送るコマンドですね。

じゃあ、その先の"origin"って、何でしょうね。（-fについては後述）

### originとは何者か

結論から先にいうと、Gitのpush.defaultの設定によっては入力したことない人もいると思いますが"origin"は、リポジトリのURLに対するエイリアス(ショートカット)です。

gitのpush.defaultについては、こちらを参照ください↓
[gitのpush.defaultに関するノウハウ](https://qiita.com/awakia/items/6aaea1ffecba725be601#%E6%80%A7%E6%A0%BC%E5%88%A5%E3%81%8A%E3%81%99%E3%81%99%E3%82%81pushdefault%E8%A8%AD%E5%AE%9A)

自分のリポジトリのconfigを開いてみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83578/365ad0a4-26b4-c5d9-5552-5288da2dc0b3.png)
こんな記述があると思います。
これが、originの正体です。

## epic3:"F"の意味は
"-f"についてです。(--forceでも、同じ動作になります)
pushに、"-f"または”--force”のオプションをつけるとリモートのコミットを無視して、要求されたオブジェクトでリポジトリを上書きします。（歴史は勝者が作ります。この場合の勝者は、force pushをした人を指します。）

SFもので言うところの”歴史改変”です。~~食らえライダー！歴史改変ビーム！！~~
あったことをなかったことにしてしまいます。
毎年どこで、force pushが危険だと言われるのはここにあります。

## epic4:失われた過去を求めて

ここまで、危ない危ないといってきました。
でも実は、force pushしたリポジトリを戻すことも可能です。

方法はいくつかあって。

- 他の人がforce pushする。
一番単純で、楽な解決方法。
素直にごめんなさいして、ボコられた後にforce pushしてもらいましょう。
- reflogでハッシュ値を調べて、git resetで戻す。  
改変された歴史は繋がりがなくなっただけで、実際は残っています。  
reflogは、リポジトリに行われた操作を記録しています。  
無論、戻すべきコミットへのハッシュ値も記録されているので、これを元に改変された歴史をもとに戻すことができます。  
ここでは、説明は省きますが、reset --hardで消してしまったコミットも歴史に戻することが可能です。
具体的な方法は、こちらが参考になります。  
[git reflog についてまとめてみる](https://gist.github.com/kymmt90/9c997726b638b316f9be07aa4e3eea5e)
- ~~ジ○ウがアナザーライダーを倒す~~

ここで、「なんだ、うっかりやっても大丈夫か。こっそり元に戻せる。」と、お思いでしょう。  
**ところがドッコイ**

GitHubでは、これをユーザーからやることができません。
GitHub社に頼んで、reflogの内容を教えてもらうしか方法がないのです。
[Jenkinsの開発者、間違えて一ヶ月前のローカルリポジトリをgit push --forceしてしまう ](https://cpplover.blogspot.com/2013/11/jenkinsgit-push-force.html
)
（しかも、ガーベジコレクションで掃除されてしまうと、もう手の打ちようがなくなる）

## epic4:参考URL
- https://qiita.com/yaotti/items/e37c707938847aee671b
- https://qiita.com/muran001/items/dea2bbbaea1260098051

## epic5:まとめ

なんで、git push -f origin masterが、危険なのか
それは、origin(リモートリポジトリ)の歴史がローカルの歴史で上書きされてしまうから。
それにより他の人の成果物がなかったことにされてしまうからです。

これらを防止する方法がローカルのGit・リモートのGitHubの両方に用意されています。
origin/masterへのPush禁止やGit Hookでローカルの段階からmasterへのcommitを制限するなどなど。方法については、ご自身でググってみてください。

では、良きGitライフを送れますように。

## epicEx:おまけ

ここまで、散々悪者扱いしてきましたが、force pushを使うユースケースは存在します。

- Push済みのコミットメッセージの修正
- AuthorやCommitrerの修正

あとは、epic4でも記述しましたが、誤った歴史の修正をするときにも、force pushを使います。

force push = 悪ではなく、その挙動を理解して、適切なタイミングで使えば、あなたの力になってくれます。