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