🙏

「git push」じゃなくて「git push -f <remote> <branch>」って打ったほうがいいこともある

2021/11/21に公開

この物語はフィクションです。実在の人物や団体などとは関係ありません。

昔々あるところに git を勉強した駆け出しエンジニアがおりました。駆け出しエンジニアは Windows マシンに Git for Windows をインストールし、 git の勉強をしました。
ある程度 git の操作もできるようになったねと先輩にも認められつつあったとき、簡単な修正作業を頼まれました。 CentOS6 で構築された環境のファイルを書き換える作業です。そのサーバーは、駆け出しエンジニアから見ても、数年の経験のある先輩から見ても、ごく普通の RedHat 系サーバーです。ごく普通に git clone して、ごく普通に開発したらいいだろうと誰もが思いました。

駆け出しエンジニアは少々躓きつつも CentOS6 サーバーにて目的のファイルを書き換え、動作確認をしました。レビュー依頼を出すために、開発途中のコミットを git rebase で綺麗に整えるのも忘れませんでした。何度もセルフチェックを行い、最後に 作業ブランチをチェックアウトしていることを確認してから git push -f を打ち込み、エンターキーを押します。ちゃんと何度も確認したし、大丈夫!先輩にレビュー依頼をします。

数分後、先輩から Slack で連絡が来ます。「main ブランチが巻き戻ってるんだけど force push とかしてないよね?」駆け出しエンジニアは答えました「いえ、 main ブランチには force push していません!」


勘のいい方ならお気づきになるかと思いますが、この物語で main ブランチを巻き戻した犯人は駆け出しエンジニアです。

といっても、 git push -f というコマンドが悪いわけではありません。 git 2.x の初期設定では、ブッシュするブランチを指定しない場合は現在のブランチを同名の上流ブランチにプッシュする ようになっていますし、私も普段よく使うコマンドです。
ただ、上記の物語での CentOS6 環境では git のバージョンが 1.x だった のです。

なぜ git 1.x だと物語のような悲劇が起きるのか

git の push 時に引数を指定しなかったときの挙動は いくつかの設定が可能 ですが、 1.x だと matching がデフォルト設定でした。( 2.x だと simple がデフォルト)
matching は「すべてのローカルブランチを同名のブランチにプッシュする」というもので、物語は

  1. 駆け出しエンジニアが main ブランチをチェックアウトしたあと、作業ブランチを切る
  2. 先輩が main ブランチを更新する
  3. 駆け出しエンジニアが作業ブランチと main ブランチを更新する

というような順序をたどった場合起こります。

余談

一時期みんな大好きだった CentOS6 に初期で入っている git は 1.x でした。アップデート、大事。

考えられる対策

  • git の config で push.default の設定を matching 以外 にする
    • たぶん 1.x 以外でも設定はできるはず。たぶん。
  • git のバージョンを 2.x にする
    • config で指定忘れても事故らないので安全
  • main ブランチへの直 push はガードするような設定をする
    • GitHub の ブランチ保護ルール とか。 GitLab でも似たようなのあるっぽいです。
    • git のバージョンうんぬん以前に設定しておくと普通に無難。

最後に

このような悲劇があったので、筆者は git push -f <remote> <branch> って打つのが癖になってます

GitHubで編集を提案

Discussion