
git rebaseは既存の「歴史」を書き換えるコマンドです。
慣れないとややとっつきにくいコマンド。。。
とっつきにくいものは実際に手を動かしてみるのが一番効果的ですよね。
そこで今回は、代表的な以下のユースケースでgit rebaseを試してみましょう。
- 他のブランチの内容を作業中のブランチに適用する。(更新されたmainの内容を取り込みたいとき)(コンフリクトの解決)
- 複数のcommitをまとめる(pushする前に整理したいとき)
開発中にこのような処理を行って、「歴史」を整えたい時がありますね。
実際にgit rebaseを使ってこれらを実現しましょう。
今回はHTMLを編集するようなストーリーで試してみましょう。
このハンズオンを通じて
- rebaseはどんなコマンドなの?
- rebaseで発生したコンフリクト解決するには?
- どんなときに便利なの?
- 使う上で注意するポイントは?
を知ることができます。
その過程で、以下のコマンドを使います。
- git rebase
- git rebase --abort
- git rebase --continue
- git rebase -i
※予めブランチなどの概念を知っておく必要があります。
- 他のブランチの内容を作業中のブランチに適用する。(更新されたmainの内容を取り込みたいとき)
- 複数のコミットを1つのコミットにまとめる
- rebaseの注意点
- まとめ
- 参考リンク
- Icon Licence
他のブランチの内容を作業中のブランチに適用する。(更新されたmainの内容を取り込みたいとき)
今回は企業情報のサイトを作成するイメージでやってみます。
index.htmlを作成し、初期化してcommittopic_companyブランチを作成(checkoutはしない)mainブランチでindex.htmlに何か変更してcommittopic_companyブランチでindex.htmlを編集&commitmainブランチで行った変更をtopic_companyブランチにrebase(コンフリクトを解決します。)mainブランチにもどり、topic_companyの変更内容をmerge(Fast-forward merge)
ではやっていきましょう!
まずは、適当に作業用のディレクトリを作成し、そこでGitを初期化します。
$ mkdir rebase-hanson $ cd rebase-hanson $ git init Initialized empty Git repository in /*****/rebase-hanson/.git/
続いて、index.htmlを作成しましょう。
$ touch index.html
そして、index.htmlに以下の内容を書き込んで保存します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>企業情報</title> </head> <body> <article> <section id="message"></section> <section id="company"></section> </article> </body> </html>
最初のコミットをしましょう。
$ git add index.html $ git commit -m "Initial commit"
ここで、topic_companyブランチを作成しておきましょう。
まだチェックアウトしないので、branchコマンドで作成します。
$ git branch topic_company
index.htmlを以下のように、修正します。messageセクションを編集します。
... <body> <article> <section id="message"> とても良い会社です! </section> <section id="company"></section> </article> </body> ...
commitします。
$ git add index.html $ git commit -m "add message"
さて、続いてtopic_companyブランチで作業します。
$ git checkout topic_company
では、ここで以下の変更を行ってそれぞれ commitしましょう。(2つのコミットを作成してください。)
#companyのセクションに資本金情報を追加する。
... <body> <article> <section id="message"></section> <section id="company"> <dl> <dt>資本金</dt> <dd>1000万円</dd> </dl> </section> </article> </body> ...
commitします。
$ git add index.html $ git commit -m "add capital"
つづいて、先ほどのセクションに従業員数を追加します。
...
<section id="company">
<dl>
<dt>資本金</dt>
<dd>1000万円</dd>
<dt>従業員数</dt>
<dd>100人</dd>
</dl>
</section>
...
commitします。
$ git add index.html $ git commit -m "add employees"
さて、ここからいよいよmainブランチの変更をtopic_companyブランチにrebaseします。
その前に、現在の状態をおさらいしておきましょう。

では、git rebaseを使っていきます。
$ git rebase main
残念ながら、コンフリクトが発生してしまいます。。。
すぐにコンフリクトの解決が難しい(他人と相談が必要な場合など)場合は、--abortでrebaseをキャンセルし実行前の状態に戻せます。
$ git rebase --abort
今回はコンフリクトを解決しましょう!
index.htmlを開いて以下の内容になるようにマーカーを削除しましょう。
※add capitalのコミット内容とのコンフリクトである点を覚えておきましょう。
<<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>企業情報</title> </head> <body> <article> <section id="message"> とても良い会社です! </section> <section id="company"> <dl> <dt>資本金</dt> <dd>1000万円</dd> </dl> </section> </article> </body> </html>
コンフリクトを解決したので、addします。
$ git add index.html
以下のコマンドを実行すると、rebaseを継続します。
$ git rebase --continue
※エディタが起動しますので、コミットメッセージを修正(orそのまま)して保存しましょう。
Vimの場合は保存した段階でrebaseが完了します。
リポジトリの状態は以下のようになります。

rebaseでは、差分となるコミットを1つずつ適用していきます。
今回はmainの先に対して以下の順で適用されました。
add capitalのコミットadd employeesのコミット
コンフリクトが発生した時を思い出してください。
今回のコンフリクトは「1」の段階で発生しました。
※実際に解決するとき、add employeesの内容は含まれてませんでしたよね。
コンフリクトが解決されたあと、つまり、--continueを実行したあとに「2」のadd employeesのコミットが適用されたということになります。

rebaseはコミットを適用し直すため、コミットのハッシュが変わります。

ではmainブランチにもどってtopic_companyをマージしてみましょう。
$ git check main $ git merge topic_company Updating 0f36ab7..7450d56 Fast-forward index.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
以下のようになります。

また、ログにFast-forwardと出ていますね。
Fast-forwardのマージとは、今回のように、「merge commitを作る必要がなく、単純なポインタの差し替えだけでmergeが完了した」ことを意味しています。
うまく使えば、コミットログをシンプルに保つことにつながります。
最後に、不要になったtopic_companyを削除しましょう。
$ git branch -d topic_company
コミットログが一直線になったのできれいになりました。

複数のコミットを1つのコミットにまとめる
つづいて、複数のコミットを1つのコミットにまとめて見ましょう。
今回はadd capitalとadd employeesを1つのコミットadd company infoにまとめましょう。
コミットをまとめるには-iオプションを使います。
$ git rebase -i HEAD~~
※今回はHEADから2つ分まとめるのでHEAD~~を使っています。もっとたくさんある場合はコミットのハッシュで指定することもできます。
すると、エディタが開きます。
pick cad164a add capital pick 7450d56 add employees
ここで、add employeesのコミットをpick -> squashに変更しましょう。(省略してsでもOK)
pick cad164a add capital squash 7450d56 add employees
保存すると、もう一度エディタが開きます。
2つのコミットをまとめるので、新しいコミットメッセージを入力する必要があります。
ここではadd company infoというメッセージにしましょう。
あとは保存するとrebase完了です。
1つのコミットにまとまっているはずです。

もちろん、この操作を行うとコミットのハッシュが変わります。
また、3つなど、2つ以上のコミットもまとめることができます。
さらに、もっとたくさんの「まとめ」コミットを1度に作成できます。
例えば、以下のように1度に複数の「まとめ」コミットを作成します。
pick *** A squash *** B pick **** C squash *** D
この場合は、2つのまとめコミットができます。

rebaseの注意点
rebaseはとても有用な機能ですが、1つ重大な注意点があります。
それは、「すでにremoteにpushしたコミットには使わない。」ということです。
Gitの中ではブランチなどはハッシュの参照として実装されています。
そのため、rebaseしてハッシュが変わってしまうと、参照先が消失してしまいます。
他の開発者がrebase前の状態をcheckoutしている場合、非常に面倒なことになります。
これは複数人で作業しているときに大きな影響があります。
「よく分からないよ」という場合は、公式サイトにもあるように、「rebaseはまだpushしていないローカルの変更だけに対して使う」ことを心がけましょう。
Gitの公式サイトには以下のように厳しいことが書いてあります。
絶対に気をつけましょう。
公開リポジトリにプッシュしたコミットをリベースしてはいけない
この指針に従っている限り、すべてはうまく進みます。 もしこれを守らなければ、あなたは嫌われ者となり、友人や家族からも軽蔑されることになるでしょう。
まとめ
git rebaseを使えば
- 履歴をキレイにしつつ、他のブランチのコミットを適用できる
- 複数のコミットを1つにまとめる
というメリットがあります。
しかし、「すでにremoteにpushしたコミットには使わない。」というルールを守って使いましょう。
参考リンク
Icon Licence

Git Icon by Icon Mafia on Iconscout