git push -f でチームに迷惑をかけるときはどうゆう時か(pushの取り消し)


# 背景
よくgit push -f は「チーム開発ではやらないでください。」、「自己責任でお願いします。」等が書かれることがあるが、どうゆうときに”やばい”(他人に迷惑をかける)状況をなるのかについて知らなかったため検証した結果の備忘録。
(理解の及んでいない部分がある際には教えていただけると助かります。)

※「間違えてpushすること」を「誤push」として表記します。
※「間違えてpushしたのを取り消すためのgit push -f」を「修正push」として表記します。

# 前提
今回は間違えてpushしてしまった際の直前のpushの取り消しに対して話を進めていきます。

# 結果
自分が間違えてpushをしてから取り消すまでの間に誰かがgit pullをした際に他のメンバーに影響が出る。

# 詳細
今回、誤pushと修正push、他人のpullのタイミングについて場合分けして考えていきます。以下の2パターンが想定できるとしています。(他のタイミングがあれば教えてください。)
1. 「誤push」前に他人がpullした場合
2. 「誤psuh」後かつ「修正push」前に他人がpullした場合
どちらも「修正push」後に他人がgit pull,git pushを行いその時の挙動について見ていきます。

図で書くと以下のような感じです。

![IMG_0138.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478972/dd1a9a17-536f-11a7-8af3-864f85f0c09d.jpeg)

## パターン1 「誤push」前に他人がpullした場合
### 手順
(mainブランチにいるとします。)
1. 【自分】と【他人】でpullで状況を揃える
2. 誤pushする内容を書いてpushする
3. 間違えたことに気づいたので、resetで修正
4. 修正内容をリモートリポジトリに反映させる(git push -f )
5. 【他人】がgit pullもしくはgit pushでリモートリポジトリに干渉する

``` sh:パターン1
# 【】は実行する人を書いています。他人はチームメンバです。

# 1. 【自分】と【他人】でpullで状況を揃える
# 【自分】と【他人】
git pull origin main

# ２. 誤pushする内容を書いてpushする
# 【自分】
echo "間違えてpushする内容です。" > test.txt # ファイルの書き換え
git add .
git commit -m "誤pushするもの"
git push origin main

# 3. 間違えたことに気づいたので、resetで修正
# 【自分】
git reset --hard HEAD^

# 4. 修正内容をリモートリポジトリに反映させる(git push -f)
# 【自分】
git push -f origin main # -f がないとエラーとなる

# 5. 【他人】がgit pullもしくはgit pushでリモートリポジトリに干渉する
# 【他人】
git pull origin main
# or 
ファイルの書き換え
git add, commit
git push origin main
```
### 結果

#### 【他人】の最後のgit pullの場合
``` sh
$ git pull origin main
From github.com:**********
 * branch            main       -> FETCH_HEAD
Already up to date.
```
何も変わっていないよ(すでに最新)って言われている
最後に【他人】がpullした地点から【自分】が「誤push」、「修正push」して取り消しているから何も変化なくて当たり前か、、、

#### 【他人】の最後のgit pushの場合
``` sh
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 285 bytes | 285.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:**********
   83ba5f6..b2a9b4d  main -> main
```
問題なくpushできてる

### 結論
パターン1においては影響がない。
つまり、絶対に他人が「誤push」と「修正push」の間にpullしていない自信があるならgit push -fをしても他人に影響がない

## パターン2 「誤psuh」後かつ「修正push」前に他人がpullした場合
### 手順
(mainブランチにいるとしています。)
1. 【自分】がpullする
2. 誤pushする内容を書いてpushする
3. 【他人】がpullする　
4. 【自分】が間違えたことに気づいたので、resetで修正
5. 修正内容をリモートリポジトリに反映させる(git push -f)
6. 【他人】がgit pullもしくはgit pushでリモートリポジトリに干渉する

``` sh:パターン2
# 【】は実行する人を書いています。他人はチームメンバです。

# 1. 【自分】がpullする
# 【自分】がpullする
git pull origin main

# ２. 誤pushする内容を書いてpushする
# 【自分】
echo "間違えてpushする内容です。" > test.txt # ファイルの書き換え
git add .
git commit -m "間違えてpushした"
git push origin main

# 3. 【他人】がpullする　 <= 誤pushの後で修正push前にpullしている
# 【他人】
git pull origin main

# 4. 間違えたことに気づいたので、resetで修正
# 【自分】
git reset --hard HEAD^

# 5. 修正内容をリモートリポジトリに反映させる(git push -f )
# 【自分】
git push -f origin main # -f がないとエラーとなる

# 6. 【他人】がgit pullもしくはgit pushでリモートリポジトリに干渉する
# 【他人】
git pull origin main
# or 
ファイルの書き換え
git add, commit
git push origin main
```
### 結果

#### 【他人】の最後のgit pullの場合
``` sh
From github.com:*********
 * branch            main       -> FETCH_HEAD
 + 66b9f9e...8e3228e main       -> origin/main  (forced update)
Already up to date.
``` 
うまくいってる！
結構、以外だけど、localの方が最新になっていただけか

#### 【他人】の最後のgit pushの場合
``` sh
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 580 bytes | 580.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:*********
   8e3228e..74aa3b5  main -> main
```
なんだ、成功しているやん！
でも。。。コミット履歴を見たら
![スクリーンショット 2024-06-14 11.44.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478972/8d0d4d8f-80e8-b502-a1fb-2c8a4ee069f1.png)

なぜか残ってるーーー
じゃあこの場合がダメなんだ

## 理由：なぜパターン２の場合だけ他人に影響が出てしまっているのか
パターン2の場合には「誤push」の履歴が【他人】がgit pullしたときに【他人】のローカルリポジトリに残ってしまう。
このため、【自分】がいくらpushの取り消しをしてもその反映はリポートリポジトリだけであって【他人】のローカルリポジトリにまで反映されない。
その後、【他人】がgit pushをすると【他人】の中のローカルリポジトリの内容がpushされるのでリポートリポジトリに「誤push」の内容が反映されている。

## じゃあ取り消したいときはどうすればいいのか
- git revertを使う
- git resetで間違えたcommitの削除をgit pullした全員にお願いする

個人なら【他人】がいないのでgit push -f でいいと思いますが、チームならgit revertが無難かなとは思います。
間違えたのは自分ですので、それで他人に迷惑かけるのもアレなので。。。