Gitで一度pushしたブランチでgit commit –amend/git rebase後再度pushする方法
概要
Gitでブランチを作成して,別のブランチの内容を取り込む場合,git mergeとgit rebaseの2種類の基本的なコマンドがある。
git mergeはマージ用のコミットが発生して,コミット履歴が枝分かれして汚くなる。
git rebaseは派生元を付け替えるので,マージ用のコミットが発生せず,コミット履歴の枝分かれもせず,履歴がきれいになる。ただし,一度git pushしてしまうと,他の人がリモートリポジトリーの内容を参照できるため,git rebaseして再度git pushするとコンフリクトする。git rebaseだけでなく,git commit --amendのように過去のコミットを変更する場合も同じパターンとなる。
自分一人のプロジェクトであれば,git push -fで強制上書きすればいいが,多人数での共有リポジトリーでは,gitサーバーの設定でgit push -fはほぼ禁止されている。
そのため,git pushしていなければ,git rebase,git pushしてしまったらgit mergeで取り込むのがブランチのマージの基本だった。
ただ,git mergeは余計なマージ用コミットが入るのが気に入らなかったり,コミット履歴が汚くなるのが,以前から気に入らなかった。
方法
ぼんやり考えていたら,一度git pushしたブランチをgit rebaseしてコンフリクトを起こさずに再度git pushする方法を思いついた。
その方法とは,git pushしたリモートブランチを削除することだ。
git pushした後にgit rebaseしてgit pushすると,当然ながらリモートブランチにはgit rebaseした情報が含まれていないため,ローカルブランチとリモートブランチのコミットの内容に齟齬が生じるため,コンフリクトする。しかし,そもそもリモートブランチを削除してしまえば,ローカルとリモートとの差異を気にする必要がなく,問題なくgit pushできる。
リモートブランチの削除方法は「Gitのリモートブランチとローカルブランチの削除方法 – senooken.jp」に記した通り,以下のコマンドとなる。
git push -d <remote-repository> <branch-name>
git push -d origin topicリモートブランチさえ削除してしまえば,問題なくgit pushできる。
例
試しにありがちな事例でコマンド例を示す。具体的には,README.txtを追加するつもりが,誤ってREDME.txtと入力してgit pushしてしまうケースを想定する。
まずはdevelopブランチを作成して,間違えた状態でコミットgit pushする。
git checkout -b develop
touch REDME.txt
git add REDME.txt # type miss
git commit -m "Add README.txt"
git pushEnumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 4 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 281 bytes | 281.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'develop' on GitHub by visiting: remote: https://github.com/senooken/git-example/pull/new/develop remote: To https://github.com/senooken/git-example.git * [new branch] develop -> develop
ここでタイプミスに気付いて直し,再度pushする。
git mv REDME.txt README.txt
git add README.txt
git commit --amend --no-edit
git push
To https://github.com/senooken/git-example.git ! [rejected] develop -> develop (non-fast-forward) error: failed to push some refs to 'https://github.com/senooken/git-example.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
当然ながら,コンフリクトする。以前まではこれでお手上げだった。こうなると,git pullでリモートブランチの内容でローカルにマージしてからgit pushする必要があった。
ここでリモートブランチを削除して再度git pushする。
git push -d origin develop
git pushEnumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 4 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 284 bytes | 284.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'develop' on GitHub by visiting: remote: https://github.com/senooken/git-example/pull/new/develop remote: To https://github.com/senooken/git-example.git * [new branch] develop -> develop
問題なくgit pushが成功した。git pushしてしまっても,リモートブランチを削除してしまえば,コミットのやり直しが自由にできる。
結論
git push後にgit rebaseやgit commit --amendで過去のコミットを変更して再度git pushする方法を記した。
Gitはブランチを気軽に作ったり削除することが念頭に置かれている。間違えてgit pushしてしまっても,慌てずにリモートブランチを削除して対応すればいい。
ただし,複数人の作業でgit pushした過去の内容を他の誰かが参照していて,その人がgit pushするとコンフリクトしてしまう。安全策を取るならば,削除後にgit pushする際のブランチ名は違う名前にしたほうが無難だろう。

“Gitで一度pushしたブランチでgit commit –amend/git rebase後再度pushする方法” に対して1件のコメントがあります。