🧬

Git branch を 1 revision 1 issue に直す方法

に公開
2

Git branch を 1 revision 1 issue に直すべき理由

  • Pull request での可読性を確保するため
  • Git branch の差分の量が多すぎる場合、
    差分を複数の pull request に分割して作成しやすくするため

Git の用語の復習

用語 説明
Revision git commit によって作成される特定の変更履歴を指します
SHA1 checksum Git が各 revision に対して生成する 40 文字のハッシュ値です
これにより、特定の revision を一意に識別できます

この手順内の用語

用語 説明
開発用 Git branch 開発中の機能や修正を行うための Git branch
手戻りの履歴があったり、複数の issue が混在していることがある状態です
この状態から 1つの issue につき 1つの revision になるように整理していきます

手順

  1. 開発用 Git branch のバックアップ
  2. 開発用 Git branch を origin/main まで reset
  3. 整理作業
  4. 整理が終わったら開発用 Git branch のバックアップを削除

1. 開発用 Git branch のバックアップ

まず、開発用 Git branch をバックアップしておくため、名前を変えて push しておきます:

git branch <開発用 Git branch 名>-backup
git push origin <開発用 Git branch 名>-backup

2. 開発用 Git branch を origin/main まで reset

git switch <開発用 Git branch 名>
git reset --hard origin/main

3. 整理作業

開発用 Git branch と開発用 Git branch のバックアップの差分がなくなるまで
1 revision 1 issue になるように次の作業を繰り返します:

  • 開発用 Git branch から revision を持ってくる
  • 複数の revision を 1 つの revision にまとめる
  • Revision の変更内容を足したり引いたりする

上記のそれぞれの作業手順は後述

差分がなくなっているかどうかを確認する方法

Git Graph の場合とコマンドラインの場合それぞれの方法を説明します

Git Graph の場合

開発用 Git branch をクリックした後に開発用 Git branch のバックアップを:

  • Mac の場合: Command + クリック
  • Windows の場合: Ctrl + クリック
コマンドラインの場合
git diff <開発用 Git branch 名>-backup --exit-code
echo $?

もし、差分がなければ 0 が表示されます
差分があれば 1 が表示されます

開発用 Git branch から revision を持ってくる

Git コマンドの cherry-pick を使います
まず、開発用 Git branch から持ってきたい revision の SHA1 checksum を確認します:

git log <開発用 Git branch 名>

持ってきたい revision の SHA1 checksum を確認したら、次のように cherry-pick します:

git cherry-pick <対象 revision の SHA1 checksum 1> <対象 revision の SHA1 checksum 2> ...

一度に範囲内の複数の revision を持ってくる場合は、次のようにします:

git cherry-pick <対象 revision の SHA1 checksum 1>^..<対象 revision の SHA1 checksum 2>

複数の revision を 1 つの revision にまとめる

複数の revision を 1 つの revision にまとめるには、
まとめる revision 全てが現在の Git branch にある必要があります

まとめたい revision が複数の Git branch に分かれている場合は、
まず上記の「開発用 Git branch から revision を持ってくる」の方法で、
それらの revision を整理用 Git branch に持ってきます

その後、1 つの revision にまとめる操作を行います

この時点の revision でブランチかタグを作成して
この時点の状態をバックアップしておくことをお奨めします

  • この後のまとめる操作の中で、まとめなかった revision は削除されるため
  • 操作を誤ると復旧が困難なため

ブランチの場合は次のようにします:

git branch backup

まず、まとめたい一連の revision の内、最初の revision の SHA1 checksum を確認します
VS Code 拡張機能 Git Graph などの GUI ツールの使用をお奨めします
コマンドラインだと次のようなコマンドが確認しやすそうです:

git log --oneline --graph --decorate --all

最初の revision の SHA1 checksum が確認できたら、
rebase --interactive
最初の revision の SHA1 checksum の直前の revision を ^ を使って指定します:

git rebase -i <最初の revision の SHA1 checksum>^

すると、エディタが開きます:

pick <最初の revision の SHA1 checksum> <最初の revision のメッセージ>
pick <2 番目の revision の SHA1 checksum> <2 番目の revision のメッセージ>
pick <3 番目の revision の SHA1 checksum> <3 番目の revision のメッセージ>
pick <4 番目の revision の SHA1 checksum> <4 番目の revision のメッセージ>
# Rebase <最初の revision の SHA1 checksum> onto <最初の revision の SHA1 checksum>^ (4 commands)
# Commands:
# p, pick <revision> = use commit
# r, reword <revision> = use commit, but edit the commit message
# e, edit <revision> = use commit, but stop for amending
# s, squash <revision> = use commit, but meld into previous commit
# f, fixup <revision> = like "squash", but discard this commit
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <revision> = remove commit

ここで、最初の revision 以外の行でまとめたい revision の先頭の pick
f または fixup に変更します
また、まとめたくない revision は先頭の pickd または drop に変更します

例えば、2 番目と 4 番目の revision をまとめたい場合は次のようにします:

pick <最初の revision の SHA1 checksum> <最初の revision のメッセージ>
- pick <2 番目の revision の SHA1 checksum> <2 番目の revision のメッセージ>
+ f <2 番目の revision の SHA1 checksum> <2 番目の revision のメッセージ>
- pick <3 番目の revision の SHA1 checksum> <3 番目の revision のメッセージ>
+ d <3 番目の revision の SHA1 checksum> <3 番目の revision のメッセージ>
- pick <4 番目の revision の SHA1 checksum> <4 番目の revision のメッセージ>
+ f <4 番目の revision の SHA1 checksum> <4 番目の revision のメッセージ>
# Rebase <最初の revision の SHA1 checksum> onto <最初の revision の SHA1 checksum>^ (4 commands)
# Commands:
# p, pick <revision> = use commit
# r, reword <revision> = use commit, but edit the commit message
# e, edit <revision> = use commit, but stop for amending
# s, squash <revision> = use commit, but meld into previous commit
# f, fixup <revision> = like "squash", but discard this commit
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <revision> = remove commit

その後、エディタを保存して閉じると、エディタで指定した通りに revision がまとめられます

Revision の変更内容を足したり引いたりする

まず、上記「開発用 Git branch から revision を持ってくる」の方法で、
変更内容を足したり引いたりしたい revision を持ってきます

その後、足したり引いたりしたい変更内容をプロジェクトに加えてステージングし、
commit --amend --no-edit で、持ってきた revision の内容を修正します

git add .
git commit --amend --no-edit

4. 整理が終わったら開発用 Git branch のバックアップを削除

整理が終わり、
前述の「差分がなくなっているかどうかを確認する方法」で
差分がなくなっていることを確認できたら、
開発用 Git branch のバックアップを削除します:

git branch -D <開発用 Git branch 名>-backup
git push origin --delete <開発用 Git branch 名>-backup

Discussion

rakiraki

PRの可読性を上げたいなら cherry-pick しないほうがいいのでは?
最後に squash マージすればいいし、そのマージコミットに issue の内容いれておけばいい気がする。。。

篠田 将彦篠田 将彦

反応ありがとうございます

まず、記事の内容が不完全だったので修正しました
開発用 Git branch を整理して、元の Git branch を削除するようにしました
cherry-pick するのは、元の Git branch を削除する前提だったためでした

その上で、
この記事では Git branch の差分の量が多すぎる場合 を想定する状況の 1 つに置いています
PR の可読性は変更の量が多いと下がると考えています
最近は AI で大量の更新が行えるようになりましたが、それらを単純に squash でまとめてしまうと
Git の graph は視認性が上がるかもしれませんが、
個別の revision の可読性は下がると考えています

具体的な例では、
バグ修正を目的とした 1 つの revision にリファクタリングが混ざっているなどの場合は
可読性が下がると考えています

そのため、この記事では 1 revision 1 issue にまとめ直し、
必要があれば複数の pull request に分割できるようにする方法を解説しました