Gitで一度pushしたブランチでgit commit –amend/git rebase後再度pushする方法


概要

Gitでブランチを作成して,別のブランチの内容を取り込む場合,git mergegit 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 rebasegit 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 push
Enumerating 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 push
Enumerating 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 rebasegit commit --amendで過去のコミットを変更して再度git pushする方法を記した。

Gitはブランチを気軽に作ったり削除することが念頭に置かれている。間違えてgit pushしてしまっても,慌てずにリモートブランチを削除して対応すればいい。

ただし,複数人の作業でgit pushした過去の内容を他の誰かが参照していて,その人がgit pushするとコンフリクトしてしまう。安全策を取るならば,削除後にgit pushする際のブランチ名は違う名前にしたほうが無難だろう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です