【git】あとから修正を差し込む(fixupとautosquash)

2022-01-16

gitでコミットをいくつか進めているうちに「以前のコミットにこの修正もまとめたい」ということが結構ある。 そういう場合に fixupautosquash が使える。

squash

加えたい修正が直前のコミットに対してだったら commit --amend によるコミットの訂正で対応できる。 いくつか前のコミットだった場合には rebase -i でのインタラクティブ編集画面で過去のコミットを edit にして状態を戻してから編集するか、 修正をコミットしておき rebase -i でコミット順を並べ替えて picksquash に変更してまとめてやる必要がある。

例えば以下のような状態の時:

$ git log --oneline
c7f44a1 (HEAD -> main) 1つ目の修正コミット
107caa8 2つ目のコミット
904b22f 1つ目のコミット
9f0df60 git init

「1つ目の修正コミット」を「1つ目のコミット」にまとめたい場合、 git rebase -i HEAD~3 として表示されるコミット履歴を編集する:

pick 904b22f 1つ目のコミット
squash c7f44a1 1つ目の修正コミット # 順番を入れ替えて、pickからsquashに変更する
pick 107caa8 2つ目のコミット

するとまとめるコミットのメッセージ編集画面が開いて、それを保存して閉じることでまとめられる。

fixup

上記の手順で、 squash の代わりに fixup とすることで、メッセージの編集をスキップすることができる。 fixup に指定したコミットメッセージは破棄され、その上のメッセージが維持されるので、簡単にまとめることができる。

fixup に関しては rebsae -i の編集画面に説明がされている:

# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor

commit –fixup

squashfixup でまとめる時にコミット順を自分で入れ替えるのが面倒。 そこで修正コミットを commit するときにオプションで --fixup <対象コミット> とすることで簡単に行うことができる。

例えば上記の「1つ目の修正コミット」をコミットする際に、 git commit --fixup 904b22f と 「1つ目のコミット」のハッシュ値を指定することで、そのコミットに対する修正コミットだということを指定できる:

$ git commit --fixup 904b22f
[main 3ca7918] fixup! 1つ目のコミット
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 3.txt
$ git log --oneline
20ba3ff (HEAD -> main) fixup! 1つ目のコミット
107caa8 2つ目のコミット
904b22f 1つ目のコミット
9f0df60 git init

コミットメッセージは自動的に「fixup! (対象コミットのタイトル)」となる。

rebase –autosquash

commit --fixup は単にコミットが追加されるだけなので、 rebase -i してやる必要がある。 その際に --autosquash というオプションを指定すると自動的に並び替えられた状態で表示してくれる:

$ git rebase -i --autosquash HEAD~3
(エディタが開く)
pick 904b22f 1つ目のコミット
fixup 20ba3ff fixup! 1つ目のコミット
pick 107caa8 2つ目のコミット

そのまま保存して閉じれば目的が達成される。

--autosquash オプションを毎度指定するのも面倒なので、 gitconfig に指定することで自動的にできる:

$ git config --global rebase.autosquash true

エイリアス登録

fixupとautosquashの手順が分かれているが、気持ちとしては1動作で済ませたい。 そこでエイリアスを登録してやる。 ここではエイリアス名を fix とする:

# .gitconfig
[alias]
...
fix = "!sh -c 'git commit --fixup $1 && git rebase -i --autosquash $1~1 --autostash' -"

直接.gitconfigファイルを編集してもよいし、 git config --global --edit で編集してもよい (コマンドラインから直接 git config --global alias.fix ... と登録するのは、エスケープが大変なのでおすすめしない)。

これによって修正コミットを git fix <対象コミット> とすることで、自動的に変更を対象コミットに適用できる:

$ git fix 904b22f
(エディタが開く)
pick 904b22f 1つ目のコミット
fixup ed580bc fixup! 1つ目のコミット
pick 107caa8 2つ目のコミット

エディタも開かずに勝手に適用して欲しいが、そこまではわからなかった。 以下に追記:

エディタを開かずに修正する

Git interactive rebase without opening the editor - Stack Overflow にエディタを開かない方法が書かれていた。 rebase のオプションに -c sequence.editor=: を指定すればよい:

# .gitconfig
[alias]
...
fix = "!sh -c 'git commit --fixup $1 && git -c sequence.editor=: rebase -i --autosquash $1~1 --autostash' -"

参照