リベースで履歴を書き換える
今回はリベースで履歴を書き換える方法について解説します。
コミットを綺麗に整えてからプッシュしたい時には、今回使うリベースを使って履歴を整えてからプッシュしていくようにしましょう。
例えば、コミットメッセージを書き換えたり、コミットの内容を書き換えてコミットを整えてからプッシュしたりする時のやり方をみていきます。
1つ注意点がありますが、GitHubにプッシュしていないコミットに関して今回のやり方を適用するようにしましょう。
GitHubにプッシュしたコミットに対してリベースをしてしまうと、ローカルとGitHubの履歴の内容が食い違ってしまって、プッシュできなくなってしまいます。
そのため、あくまでプッシュしていないコミットを整える時の方法についてみていきます。
リベースに具体的に入る前に、直前のコミットをやり直すコマンドについて確認します。
$ git commit --amend
コマンドです。
このコマンドを使うことで、直前のコミットを今のステージの内容をベースに、もう一度やり直すことができるというコマンドです。
もし直前のコミットをやり直したい場合は、「git commit --amend」コマンドを使いましょう。
ただ、直前のコミットだけでは無くて複数のコミットをやり直したい場合は、今から見て行くリベースコマンドを使っていきます。
リベースで複数のコミットをやり直すコマンドは
$ git rebase -i <コミットID>
です。
具体的には
$ git rebase -i HEAD~3
のように入力します。
これを入力すると何が起きるかというと、直前3つのコミットのやり直しができます。
まず、コミットエディタが立ち上がります。
コミットエディタが立ち上がって、直前3つ分のコミットがそこに
pick gh21f6d ヘッダー修正 pick 193054e ファイル追加 pick 84gha0d README修正
と言うように「pick コミットID コミットメッセージ」というように、3つ分が表示されます。
この「-i」オプションというのは、「--interactive」の略になります。
これは対話的リベースと言って、やり取りしながら履歴を変更していきます。
「git rebase -i HEAD~3」と入力して、コミットエディタが立ち上がったら、次にやり直したいコミットの「pick」と言う文字を「edit」に修正していきます。
例えば、「ヘッダーを修正」のコミットをやり直したい場合は、
edit gh21f6d ヘッダー修正 pick 193054e ファイル追加 pick 84gha0d README修正
と書き直します。
すると、このコミットのところをリベースして、修正することができるようになります。
「edit」と書き直したら、次にコミットエディタ上で保存をして、コミットエディタを終了します。
終了すると「edit」のところをリベースできるようになっているので、ローカルでファイルの内容を修正して、ステージに追加して、その後「git commit --amend」コマンドを実行します。
これを実行することで、コミットの履歴をやり直すことができます。
やり直したら、次にリベースを完了して、次のコミットに進んでいきます。
そのコマンドは「git rebase --continue」コマンドになります。
どういうことか、かなりわかりにくかったと思うので、次は図で見ていきます。
まず、「HEAD~3」とういのが何かと言うことから確認していきましょう。
「HEAD」というのが一番最新のコミットのことでした。
「HEAD~」と入力すると、その親コミットを指し示します。
「HEAD~1」ですと、HEADの1個上の親コミット、「HEAD~2」ですと、さらにその上の親コミット、「HEAD~3」ですと、そこからさらに1個上の親コミットを指定します。
コミットの指定方法には「~」だけでは無く、「^」で指定することもできます。
「^」を指定すると、マージした場合の2番目の親コミットを指定することができます。
「HEAD^1」ですと、マージした場合の親の1番目のコミット、1番本流の親コミット、「HEAD^2」と指定すると、マージした場合の2番目の親コミットを指定します。
なので、「HEAD~1」というのと「HEAD^1」というのは同じ親コミットを指定することになります。
これがコミットの指定方法になります。
今回の例ですと、「HEAD~3」でしたので、HEADから3つ遡った親コミットを指定していると理解しておきましょう。
では次に、「rebase -i」コマンドの一連の流れについて押さえておきましょう。
今状況として、「HEAD」が「README修正」、「HEAD~1」が「ファイル追加」、「HEAD~2」が「ヘッダー修正」と言うコミットでした。
この状況で、「git rebase -i HEAD~3」と入力すると、「HEAD~3」を起点として、それ以降のコミットをリベースしていくという意味になります。
ですので、「HEAD~2」以降の内容をリベースしていくということです。
ではここから手順を見ていきましょう。
まず初めは「git rebase -i」コマンドで対話的リベースモードに入ります。
このコマンドを入力すると、コミットエディタが立ち上がります。
そこで、修正したいコミットを「pick」から「edit」に修正してコミットエディタを終了します。
コミットエディタを終了すると、「edit」のところでコミットの適用が止まります。
コミットエディタを終了すると、リベース対象のコミット1つ1つに対してリベースを行っていきます。
「pick」のところではそのまま今までの内容を適用していけるのですが、「edit」としているとコミットの適用が止まります。
コミットの適用が止まるので、そこでファイルを修正して、ステージに追加して、「git commit --amend」コマンドで今のコミットの内容を修正していきます。
今回の場合ですと、「HEAD~2」のところを「edit」にしているので、「edit」のコミット内容を「git commit --amend」コマンドで修正していきます。
修正が完了したら、次のコミットをリベースして行きたいので、そのために「git rebase --continue」コマンドを入力します。
これで次のコミットにいって、またそちらの内容のコミットをリベースしていきます。
ここで「pick」となっていると、そのままのコミットの内容を適用して次に進んでいきます。
まず「HEAD~1」の内容を適用して、自動的にその次へいきます。
次は「HEAD」のコミットの内容をそのまま適用して次へいきます。
「HEAD」まで来たらその次はないので、ここで「git rebase -i」コマンドの対話的リベースモードが自動的に終了します。
これが「rebase -i」コマンドの一連の流れになります。
実際にやってみよう
ではターミナルを立ち上げて、実際にやっていきましょう。
いつものように「git_tutorial」ディレクトリに移動します。
今回、3つのコミットをまず作成して、その3つのコミットに対してリベースコマンドで内容を修正していくという方法を行ってみます。
まず3つのコミットをつくっていきます。
はじめは「touch」コマンドを使って新規ファイルを作成して、それをコミットしていきます。
$ touch first.html
確認してみます。
$ ls feature.html feature2.html first.html home.html index.html main.html main2.html secret.txt
「first.html」というファイルが確かに作成されていることが確認できます。
この「touch」コマンドは何かというと、空ファイルを作成するコマンドです。
本当はファイルの中身も作成した方が良いのですが、今回はリベースコマンドの挙動を確認したいだけなので、空ファイルのみを作っていきます。
ではこの内容をステージ追加して、コミットしていきましょう。
$ git add first.html $ git commit -m 'first.htmlを追加' [main 8d41a2e] first.htmlを追加 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 first.html
続けて、後2つコミットしていきましょう。
また同じように「touch」で作って行きます。
$ touch second.html
コミットします。
$ git add second.html $ git commit -m 'second.htmlを追加' [main 5e155d7] second.htmlを追加 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 second.html
コミットできました。
続けて最後のコミットです。
$ touch third.html
コミットします。
$ git add third.html $ git commit -m 'third.htmlを追加' [main 4f18da6] third.htmlを追加 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 third.html
これでコミットができました。
コミットができたか、確認しておきます。
$ git log --oneline 4f18da6 (HEAD -> main) third.htmlを追加 5e155d7 second.htmlを追加 8d41a2e first.htmlを追加 966020f (origin/main) feature2を新規作成 b638345 main2を新規作成 : :
確かに3つコミットができています。
これで3つのコミットの内容を修正していきましょう。
そのために今回は
$ git rebase -i HEAD~3
と入力します。
これでコミットエディタが立ち上がりました。
pick 8d41a2e first.htmlを追加 pick 5e155d7 second.htmlを追加 pick 4f18da6 third.htmlを追加
今回は一番最後の「third.html」のコミットメッセージを修正してみます。
今回のコミットメッセージは「third.htmlを追加」となっていて、「third.html」というようにコミットメッセージだけ変更するやり方をみていきます。
まず、「pick」を「edit」に修正します。
修正したら、保存してコミットエディタを終了します。
終了したらターミナルに戻ります。
$ git rebase -i HEAD~3 Stopped at 4f18da6... third.htmlを追加 You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue
終了すると、実はGitは親切なことに、次は何のコマンドをしたら良いかと言うことを教えてくれています。
コミットの内容をやり直したい場合は「git commit --amend」コマンドを使えと、amendした後、もうこれ以上変更しなくて良いよという場合は「git rebase --continue」コマンドを使えと書いてあります。
今回はコミットメッセージの内容を書き換えたいだけなので、「git commit --amend」コマンドを使っていきます。
もしここでファイルの内容を修正したい場合は、ファイルの内容を修正して、ステージに追加した後に「git commit --amend」コマンドを使ってみてください。
$ git commit --amend
すると、editとした「third.html」のところのコミットメッセージが立ち上がります。
このコミットメッセージを修正していきます。
「third.html」と修正します。
保存してテキストエディタを終了します。
ターミナルに戻ります。
$ git commit --amend [detached HEAD afe8acc] third.html Date: Wed Sep 1 17:05:06 2021 +0900 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 third.html
ターミナルに戻るとamendがうまくできているようです。
これでコミットメッセージが修正できたので、次のコミットへ進んでいきます。
$ git rebase --continue Successfully rebased and updated refs/heads/main.
すると、他のコミットは全部「pick」だったので、そのまま今までの内容が適用されて、これでリベースが完了しました。
内容が修正されたか、確認してみましょう。
$ git log --oneline afe8acc (HEAD -> main) third.html 5e155d7 second.htmlを追加 8d41a2e first.htmlを追加 966020f (origin/main) feature2を新規作成 b638345 main2を新規作成 : :
すると確かに「third.html」とコミットメッセージが変わっています。
今の状況も確認しておきます。
$ git status On branch main nothing to commit, working tree clean
特に何の問題もありません。
このように、「git rebase -i」コマンドでは修正したいコミットのところを「edit」にして、そのあと「git commit --amend」コマンドで修正していきます。
修正が完了したら「git rebase --continue」コマンドで次のコミットへ進んでいきます。
このようにすることで、リベースをすることができます。
今回はコミットをやり直すやり方について見てきたのですが、この「git rebase -i」コマンドを他にもコミットをまとめたいとか、逆にコミットを分割したいだとかそういうこともできるので、そういった他のやり方について次回みていきます。
参考図書
独学で挫折しそうになったら、オンラインプログラミングスクール