インタラクティブなリベース
インタラクティブなリベースは、Gitの最も強力で、潜在的に複雑な機能の1つです。これにより、非常に柔軟な方法でコミット履歴を書き換えることができます。コミットの順序を入れ替えたり、コミットを結合(スクエッシュ)したり、コミットメッセージを編集したり、コミット自体を完全に削除したりすることができます。コミット履歴に対して微調整できるエディタのようなものです。
警告:インタラクティブなリベースは、特に共有ブランチでは、履歴を書き換えるため、リスクがあります。既に共有リポジトリにプッシュされたコミットをリベースすることは決してしないでください。ただし、その影響を完全に理解し、チームと調整した場合を除きます。 リベースは、共有する前にローカルのコミットを整理するために、一般的にはより安全で適切です。
実際にインタラクティブなリベースを見てみるために、いくつかのコミットを作成しましょう。
echo "First change" >> hello.txt
git commit -am "First change"
echo "Second change" >> hello.txt
git commit -am "Second change"
echo "Third change" >> hello.txt
git commit -am "Third change"
これらのコマンドは、「hello.txt」ファイルに1行ずつ追加しながら、3つの新しいコミットを連続して作成します。git commit -am
の-am
ショートハンドは、すべての変更されたファイルと削除されたファイルをステージング(-a
)し、コミット(-m
)することを組み合わせています。既にファイルを追跡しており、それらに対する変更をコミットしたい場合に便利なショートカットです。
今度は、これらの3つのコミットを整理したいとしましょう。たとえば、「First change」と「Second change」は実際には同じ論理的な変更の一部であり、それらを1つのコミットに結合したい場合があります。また、「Third change」のコミットメッセージを改善したい場合もあります。インタラクティブなリベースはこれに最適です。
次のコマンドを使用してインタラクティブなリベースセッションを開始します。
git rebase -i HEAD~3
このコマンドを解説しましょう。
git rebase -i
:git rebase
はリベースするためのコマンドです。-i
フラグは「インタラクティブ」を意味し、Gitに対してインタラクティブなリベースを実行したいことを伝えます。
HEAD~3
:これはリベースするコミットの範囲を指定します。HEAD
は現在のコミットを指します。~3
は「HEADから3つ前のコミットに戻る」という意味です。したがって、HEAD~3
は最後の3つのコミットを選択します。特定の履歴のポイントからリベースを開始するには、コミットハッシュを指定することもできます。
このコマンドを実行すると、Gitは最後の3つのコミットの一覧を持つデフォルトのテキストエディタを開きます。エディタのインターフェイスでは、Gitに対して履歴をどのように変更するかを指示します。次のようなものが表示されます(コミットハッシュは異なります)。
1 pick 63c95db First change
2 pick 68e7909 Second change
3 pick 5371424 Third change
4
5 ## Rebase 3bf348d..5371424 onto 3bf348d (3 commands)
6 #
7 ## Commands:
8 ## p, pick <commit> = use commit
9 ## r, reword <commit> = use commit, but edit the commit message
10 ## e, edit <commit> = use commit, but stop for amending
11 ## s, squash <commit> = use commit, but meld into previous commit
12 ## f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
13 ## commit's log message, unless -C is used, in which case
14 ## keep only this commit's message; -c is same as -C but
15 ## opens the editor
16 ## x, exec <command> = run command (the rest of the line) using shell
17 ## b, break = stop here (continue rebase later with 'git rebase --continue')
18 ## d, drop <commit> = remove commit
19 ## l, label <label> = label current HEAD with a name
20 ## t, reset <label> = reset HEAD to a label
21 ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
22 #. create a merge commit using the original merge commit's
23 #. message (or the oneline, if no original merge commit was
24 #. specified); use -c <commit> to reword the commit message
25 #
26 ## These lines can be re-ordered; they are executed from top to bottom.
27 #
28 ## If you remove a line here THAT COMMIT WILL BE LOST.
29 #
30 ## However, if you remove everything, the rebase will be aborted.
インタラクティブなリベースエディタの理解:
- コミット一覧:ファイルの上部には、リベース操作の対象となるコミットが一覧表示されます。デフォルトでは、逆順(一番上が古い、一番下が最新の)で表示されます。
- コマンド:コミット一覧の下には、各コミットを変更するために使用できる利用可能なコマンドの一覧がGitによって提供されます。最も一般的なものは以下の通りです。
pick
(またはp
):コミットをそのまま使用します。これがデフォルトです。
reword
(またはr
):コミットを使用しますが、コミットメッセージを変更できるようにします。
edit
(またはe
):コミットを使用しますが、このコミットでリベースプロセスを停止して、さらなる変更(たとえば、ファイルを修正したり、追加の変更を加えたり)を行うことができます。
squash
(またはs
):このコミットを一覧の前のコミットに結合します。このコミットのコミットメッセージは、前のコミットのメッセージに追加されます(後で結合されたメッセージを編集できます)。
fixup
(またはf
):squash
と同様ですが、このコミットのコミットメッセージを破棄し、前のコミットのメッセージのみを使用します。
drop
(またはd
):このコミットを履歴から完全に削除します。
インタラクティブなリベースの計画:
以下のことを行いたいとします。
- 「Second change」を「First change」に結合する。
- 「Third change」のコミットメッセージをより良いものに変更する。
これを達成するには、エディタファイルを以下のように変更します。
pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change
エディタファイル内のコミットハッシュを変更しないでください。
Vim(エディタがVimの場合)での編集の手順:
- 挿入モードに入る:
i
キーを押します。これにより、ファイルを入力および編集できるようになります。
- 変更を行う:矢印キーを使って行に移動し、上記のように「pick」を「squash」と「reword」に変更します。
- 挿入モードを終了する:
Esc
キーを押します。
- 保存して終了する:
:wq
と入力し、Enter
キーを押します。
エディタファイルを保存して閉じた後、Gitは指示に基づいてリベース操作を開始します。
まず、コミットを結合する:
Gitはまず「squash」コマンドを処理します。「Second change」の変更を「First change」に結合します。その後、再びテキストエディタを開き、今度は結合されたコミットのコミットメッセージを編集できるようにします。エディタには次のようなものが表示されます。
## This is a combination of 2 commits.
## This is the 1st commit message:
First change
## This is the 2nd commit message:
Second change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:30:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (2 commands done):
## pick abc1234 First change
## squash def5678 Second change
## Next command to do (1 remaining command):
## reword ghi9101 Third change
## You are currently rebasing branch'master' on '<base_commit_hash>'.
このエディタは、結合されるコミットの元のコミットメッセージを表示します。これで、結合されたコミットに対して1つの統一的なコミットメッセージを作成するために、このテキストを編集できます。たとえば、次のように変更できます。
Combined first and second changes: Initial setup of hello.txt
メッセージを編集した後、エディタを保存して閉じます。
次に、コミットメッセージを変更する:
その後、Gitは「Third change」の「reword」コマンドに移ります。再びエディタを開き、今度は「Third change」のコミットメッセージを編集できるようにします。元のメッセージが表示されます。
Third change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:35:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (3 commands done):
## pick abc1234 First change
## squash def5678 Second change
## reword ghi9101 Third change
## Next command to do (0 remaining commands):
## (finish)
## You are currently rebasing branch'master' on '<base_commit_hash>'.
メッセージをより詳細なものに変更します。たとえば:
Improved third change: Added a more descriptive line to hello.txt
エディタを保存して閉じます。
リベースの完了:
インタラクティブなリベースの指示に含まれるすべてのコマンドを処理した後、Gitはリベースプロセスを完了します。通常、「Successfully rebased and updated refs/heads/master.」のようなメッセージが表示されます。
リベースされた履歴を確認する:
今、コミットログを再度確認しましょう。
git log --oneline
次のような履歴が表示されるはずです(コミットハッシュは異なります)。
<commit_hash_third_revised> Improved third change: Added a more descriptive line to hello.txt
<commit_hash_combined> Combined first and second changes: Initial setup of hello.txt
<commit_hash_cherry_pick> Add new feature
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original_reverted> Add line to be reverted
<commit_hash_initial_amended> Initial commit with important note
次のことに注意してください。
- 今は以前よりもコミットが少なくなっています(2つのコミットを1つに結合しました)。
- 「First change」と「Second change」のコミットメッセージが結合され、より詳細になっています。
- 「Third change」のコミットメッセージが変更されています。
- リベースされたコミットのコミットハッシュが変更されています。履歴を書き換えたためです。
インタラクティブなリベースに関する重要な注意事項:
- ローカル履歴のツール:インタラクティブなリベースは主に、ローカルのコミット履歴を整理するためのツールです。
- 共有ブランチのリベースを避ける:既に共有リポジトリにプッシュされたブランチをリベースしないでください。ただし、何をしているかを絶対に確信し、チームと調整した場合を除きます。共有履歴を書き換えると、共同作業者にとって深刻な問題を引き起こす可能性があります。
git rebase --abort
:インタラクティブなリベース中に間違いを犯した場合、または混乱した場合や解決できないコンフリクトに遭遇した場合、常にgit rebase --abort
を使用してリベース操作をキャンセルし、リベースを開始する前の状態にブランチを戻すことができます。これは、何かが悪くなった場合にリベースを取り消す安全な方法です。