[git] 複数の commit を1つの commit にまとめる方法

git

     

TAGS:

git の過去の複数 commit を 1つにまとめる方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterey 12.4 beta
  • Xcode 13.3
  • git 2.32.0

背景

git を使って、変更管理を行なっていると、(あとから見直すと)commit が細かすぎる気がする ということはよくあります。

非常に多くの変更が 1つの commit で行われているのは、まさしく 地獄ですが、小さな変更の commit が数多くあるという状況も、わかりやすくはないです。

git での commit は「適切な範囲/量についての変更を 1commit にする」というのが理想です。
ですが、その時点で最適と考えた commit が、作業が終わってからあらためて見てみると「もう少し 大きく commit してもよかった」とか「もう少し細かく commit しておいた方がよかった」となることもあります。

そういう観点で考えると、
・commit をできるだけ減らす(できるだけ多くの変更を1commit に入れる)
・commit をできるだけ増やす(1commit には、できるだけシンプルな変更を入れる)
の2択で考えるならば、後者の方向性で commit しておく方が良いです。
# 多くの変更が含まれる1commit をあとから分割するのは難しいです。

多すぎる commit は、今回説明する方法で まとめてしまうということができます。

注意
一度 リモートに push した commit を削除するのは危険です。
リモートへ強制的に反映することも技術的には可能ですが、お勧めしません。
リモートに push する前に メンテナンスすることをお勧めします。

過去の複数の commit を1つの commit にする

前提

作りたかったアプリは、押すごとに大きくなっていくボタンです。
何回かの commit を経て完成しました。結果として以下のような履歴を持つ git になりました。

0) initial Commit
1) テンプレートの Text を Button に置き換えた
2) 拡大倍率を保持する変数 scale を @State で追加した
3) Button の action で scale を定数倍するようにした
4) scale 計算を withAnimation で行うようにした
5) アプリの名前を "ボタン" に変更した

Xcode が、プロジェクト作成時に自動で行なった 最初の commit を入れると 6つの commit があります。

commit の粒度ですが、"押すたびにアニメーションしながら大きくなるボタンを作成した" という1つの commit と "アプリの名称を変更した" という commit で良いような気がします。

ということで、そのようにまとめてみます。

MEMO
通常は、開発作業は、feature/XXX 等の branch で行うと思いますが、例題ということで main branch で行なってます。
以降の操作自体に、main branch でないと行えないものはありません。

git コマンドを CUI で使う

残念ながら、git の詳細コマンドを Xcode の GUI 上から実行することはできません。

ですので、ターミナルから git のコマンドを使って操作していく必要があります。

MEMO
自信がないのであれば、git リポジトリを一度バックアップしてから作業すると慌てないですみます。
理論上は、git はその操作履歴を覚えていますので、戻せるはずですが、焦って作業するとより悪い方向に進むことがよくあります(汗

プロジェクトのフォルダに移動する

ターミナルを開き、プロジェクトのフォルダに移動します。


cd <path to project>

履歴を確認する

まずは、履歴を確認しましょう。 "git log" というコマンドを使います。"--oneline" というオプションをつけると commit が1行で表示されるので、全体を俯瞰しやすいです。
古いcommit が下、新しい commit は上に表示されています。


% git log --oneline
d9e8473 (HEAD -> main) change displayName to ボタン
02e7fff add withAnimation for scale calc
c8e558b multiply scale value on ButtonTap
6d7cbd0 add @State variable for scale value
00d8140 place Button
928100d Initial Commit
% 

先頭の数字列は、commit の ID です。(厳密には先頭7文字です)

この ID を使って、どの commit からどの commit までをまとめるかを指定します。

ここでは、00d8140 〜 02e7fff の commit を 1つの commit にまとめることにします。

commit をまとめる

git rebase というコマンドを使っていきます。

git では、ある変更を別の変更の上に適用する(できるようにする)ことを rebase すると言います。この概念を使用して、複数の commit をまとめることもできるようになっています。

git rebase を実行する

git rebase は、-i オプションをつけると、interactive ということで、手順を追って指定していくことができます。-i オプションに続いて、 rebase の操作対象とする commit を指定します。

2つ指定すると 指定 commit 間の commit が対象となり、1つ指定された時は、指定コミットから HEAD までが rebase 対象になります。

気をつける点は、開始 commit については、マージしたい commit の 1つ古い commit までを指定する必要があります。
今回の例では、00d8140 からが、マージしたい commit なので、その1つ前の 928100d(Initial Commit) を開始 commit として指定する必要があります。


git rebase -i 928100d

上記コマンドを実行すると、以下のような内容のファイルが開かれた状態になります。


pick 00d8140 place Button
pick 6d7cbd0 add @State variable for scale value
pick c8e558b multiply scale value on ButtonTap
pick 02e7fff add withAnimation for scale calc
pick d9e8473 change displayName to ボタン

# Rebase 928100d..d9e8473 onto 928100d (5 commands)
#
# Commands:
# p, pick  = use commit
# r, reword  = use commit, but edit the commit message
# e, edit  = use commit, but stop for amending
# s, squash  = use commit, but meld into previous commit
# f, fixup [-C | -c]  = 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
# x, exec  = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop  = remove commit
# l, label 

まとめ方を指示する

開かれたテキストファイルを変更して、どの commit をどうまとめるか指示します。
具体的には、1commit = 1行になっているいるので、どの commit を残して、どれを マージして・・・ということを指示します。(コメントに英語で書かれているとおりです)
commit を表している各行の先頭に指示を書くことで、処理されます。
残す commit は、"pick" のままにしておきます。"pick" を "squach" へ変更するとその直前の commit とまとめることを意味します。

今回は、4つの commit を最初の commit (00d8140) にまとめることにしますので、以下のように変更しました。


pick 00d8140 place Button
squash 6d7cbd0 add @State variable for scale value
squash c8e558b multiply scale value on ButtonTap
squash 02e7fff add withAnimation for scale calc
pick d9e8473 change displayName to ボタン

変更を反映してエディタを終了します。

commit のコメントを更新する

エディタを閉じると、新しいエディタが開かれます。新しく開かれたエディタを使って、 新しい commit についてのコメントを設定することになります。

以下のような、これまでの commit のコメントが列挙されたファイルが開かれます。


# This is a combination of 4 commits.
# This is the 1st commit message:

place Button

# This is the commit message #2:

add @State variable for scale value

# This is the commit message #3:

multiply scale value on ButtonTap

# This is the commit message #4:

add withAnimation for scale calc

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Apr 11 17:18:45 2022 +0900
#
# interactive rebase in progress; onto 928100d
# Last commands done (4 commands done):
#    squash c8e558b multiply scale value on ButtonTap
#    squash 02e7fff add withAnimation for scale calc
# Next command to do (1 remaining command):
#    pick d9e8473 change displayName to ボタン
# You are currently rebasing branch 'main' on '928100d'.
#
# Changes to be committed:
#	modified:   GitRebase/ContentView.swift
#

これまでのコメントをまとめつつ、以下のようにしました。
# で開始している行は無視されます。


# This is a combination of 4 commits.
# This is the 1st commit message:
押すと拡大するボタンを配置
- place Button
- add @State variable for scale value
- multiply scale value on ButtonTap
- add withAnimation for scale calc

修正を確認する

コマンドラインに、以下のように表示されれば完了です。


Successfully rebased and updated refs/heads/main.

# updated のあとは、commit 等の状況により変わります。基本的に Successfully rebased と出ていればOKです。

最初と同じように、"git log --oneline" を使って、履歴を確認してみます。


7ebcdca (HEAD -> main) change displayName to ボタン
3b8bc64 押すと拡大するボタンを配置 - place Button - add @State variable for scale value - multiply scale value on ButtonTap - add withAnimation for scale calc
928100d Initial Commit

以下の 3つの commit にまとめられていることが確認できます。
0) Initial Commit
1) 押すと拡大するボタンを配置
2) change displayName to ボタン

まとめ

git の 複数の commit を 1つの commit にまとめる

git の 複数の commit を 1つの commit にまとめる
  • git rebase -i を使う
  • まとめたい commit を pick/squash 指定する
  • コメントもまとめたものに更新することができる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。