公開:2022年11月8日
7分で読めます
fixupからautosquashまで、Git rebaseを実際の開発現場で活用する具体的な方法をご紹介します。

同僚のChrisが先日、Git rebaseを活用する方法について解説しました。この記事では、そのテクニックを実際の日常的な開発作業にどう応用するかを紹介します。
マージリクエストを作成した後、パイプラインの失敗やレビュアーからのコメントが重なり、コミット履歴が次のような状態になってしまうことがあります。
$ git log --oneline
8f8ef5af (HEAD -> my-change) More CI fixes
e4fb7935 Apply suggestion from reviewer
c1a1bec6 Apply suggestion from reviewer
673222be Make linter happy
a0c30577 Fix CI failure for X
5ff160db Implement feature Y
f68080e3 Implement feature X
3cdbc201 (origin/main, origin/HEAD, main) Merge branch 'other-change' into 'main'
...
この例では、フィーチャーXとYを実装する2つのコミットの後に、それ単体では意味をなさないコミットがいくつか続いています。Git rebaseのfixup機能を使って、これらを整理しましょう。
このテクニックの考え方は、後続のコミットの変更内容を、各フィーチャーを導入したコミットに統合することです。そのためには、後続の各コミットがどのコミットに属するかを特定する必要があります。
ファイル名から関連するコミットが分かる場合もありますが、分からない場合はgit-blameを使って調べられます。
git blame <revision> -L<start>,<end> <filename>
-Lオプションで対象の行番号の範囲を指定します。<end>は省略できませんが、<start>と同じ値でも構いません。<revision>は省略できますが、リベースで除外したいコミットをスキップするために指定することをお勧めします。実際のコマンドは次のようになります。
$ git blame 5ff160db -L22,22 app/model/user.rb
f68080e3 22) scope :admins, -> { where(admin: true) }
これにより、22行目がf68080e3 Implement feature Xによって変更されたことが分かります。
リベースで除外したい各コミットについて、対応するコミットが特定できるまでこの手順を繰り返します。
次のステップは、インタラクティブリベースを開始することです。
$ git rebase -i main
$EDITORに命令のリストが表示されます。
pick 8f8ef5af More CI fixes
pick e4fb7935 Apply suggestion from reviewer
pick c1a1bec6 Apply suggestion from reviewer
pick 673222be Make linter happy
pick a0c30577 Fix CI failure for X
pick 5ff160db Implement feature Y
pick f68080e3 Implement feature X
これを次のように書き換えます。
fixup 8f8ef5af More CI fixes
fixup e4fb7935 Apply suggestion from reviewer
fixup 673222be Make linter happy
pick 5ff160db Implement feature Y
fixup c1a1bec6 Apply suggestion from reviewer
fixup a0c30577 Fix CI failure for X
pick f68080e3 Implement feature X
コミットを並べ替え、一部のpickをfixupに変更しています。
Git rebaseはこのリストを下から上に処理します。pickの行はそのコミットメッセージをそのまま使用し、fixupの行はその変更内容を直下のコミットに統合します。ファイルを保存してエディターを閉じると、Gitの履歴は次のようになります。
$ git log --oneline
e880c726 (HEAD -> my-change) Implement feature Y
e088ea06 Implement feature X
3cdbc201 (origin/main, origin/HEAD, main) Merge branch 'other-change' into 'main'
...
autosquashは、上記の方法の代替として使えるテクニックです。まず、除外したいコミットの変更をすべて取り消します。
git checkout f68080e3
これで、変更内容はすべてワーキングツリーにのみ存在し、コミット履歴からは消えた状態になります。git addまたはgit add -pを使ってe088ea06 Implement feature Xに関連するすべての変更をステージングします。git commitやgit commit -mの代わりに、--fixupオプションを使います。
$ git commit --fixup e088ea06
これで履歴は次のようになります。
$ git log --oneline
e744646b (HEAD -> my-change) fixup! Implement feature X
5ff160db Implement feature Y
f68080e3 Implement feature X
3cdbc201 (origin/main, origin/HEAD, main) Merge branch 'other-change' into 'main'
...
残りの変更はすべて5ff160db Implement feature Yに属するはずなので、次を実行します。
$ git add .
$ git commit --fixup 5ff160db
$ git log --oneline
18c0fff9 (HEAD -> my-change) fixup! Implement feature Y
e744646b fixup! Implement feature X
5ff160db Implement feature Y
f68080e3 Implement feature X
3cdbc201 (origin/main, origin/HEAD, main) Merge branch 'other-change' into 'main'
...
fixup!コミットの内容を確認して問題なければ、次を実行します。
$ git rebase -i --autosquash main
--autosquashオプションを追加しています。このオプションはfixup!コミットを自動的に並べ替え、命令をfixupに設定します。通常はこの状態で何もする必要はなく、エディターで命令リストをそのまま閉じるだけです。git logを実行すると、fixup!コミットが消えていることが確認できます。
最後に、コミットの統合をより簡単に行える代替ツールもいくつかご紹介します。
このブログ記事を楽しんでいただけましたか?ご質問やフィードバックがあればお知らせください。GitLabコミュニティフォーラムで新しいトピックを作成してあなたの声を届けましょう。
フィードバックを共有する