Gitを使った分散開発管理10 – rebaseで履歴管理

2011.08.23

ブランチの変更を再生する

あなたがAというブランチからBというブランチを作成して作業をしているとします。
そして、Bブランチの作業中も、派生元のAブランチにも数々の変更がされているとしましょう。
Bブランチでの変更を派生元であるAブランチのHEADに対して適用させたい場合、
Gitではrebaseコマンドを使用します。
rebaseはブランチの変更を別のブランチで再生するような動きをします。
では実際にrebaseコマンドを試してみましょう。

masterブランチのhello.htmlは以下のようになっています。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello Git!</title>
</head>
<body>
  <h2 id="toc-hello-git">Hello Git!</h2>
  <a href="http://git-scm.com/">Git Home.</a>
  </br>
 <a href="http://subversion.apache.org/">Subversion Home.</a> 
  </br>
  <a href="http://mercurial.selenic.com/">Mercurial Home.</a>
  </br>
  <a href="http://bazaar.canonical.com/en/">Bazaar Home.</a>
</body>
</html>

ある程度機能が完成したため、今の状態をベースにリリースブランチを作成することにしました。
このリリースブランチは、リリースするまで最低限の変更、修正しかしません。
こうすることで、リリース準備と次のバージョンの開発を同時に行うことが出来ます。
ではリリースブランチを作成しましょう。

$ git branch RB_1.0
$ git branch
  RB_1.0
* master

masterブランチでは引き続き機能追加を行います。headタグ内にscriptタグを追加しました。

・・・・
<head>
  <meta charset="UTF-8">
  <title>Hello Git!</title>
  <script></script>
</head>
・・・・

この変更をaddしてコミットしましょう。
さらにもう1つ変更を加えます。metaタグを1つ追加します。

・・・・
<head>
  <meta charset="UTF-8">
  <meta http-equiv="cache-control" content="no-cache">
  <title>Hello Git!</title>
・・・・

これもaddしてコミットしましょう。
合計2回コミットしたことになります。

コミットしたら今度はリリースブランチへ移動します。

$ git checkout RB_1.0
Switched to branch 'RB_1.0'
$ git branch
* RB_1.0
  master

当然、masterブランチの変更は反映されてません。
RB_1.0ブランチではリリース前の修正が必要になりました。
subersionのリンクを削除します。

・・・・
<body>
  <h2 id="toc-hello-git1">Hello Git!</h2>
  <a href="http://git-scm.com/">Git Home.</a>
  </br>
  <a href="http://mercurial.selenic.com/">Mercurial Home.</a>
  </br>
  <a href="http://bazaar.canonical.com/en/">Bazaar Home.</a>
</body>
・・・・

addしてコミットしましょう。
この時点でのリポジトリの状態は下記のようになっています。

RB_1.0で修正した文言は、masterブランチにも適用することになりました。
そのために使用するのが、git rebaseコマンドです。 git rebase <ブランチ名>として指定したブランチのコミットを取得します。

$ git checkout master
Switched to branch 'master'
$ git rebase RB_1.0
First, rewinding head to replay your work on top of it...
Applying: add script
Applying: add meta

git logでログを確認してみてください。
RB_1.0でのリンク削除のコミットが追加され、その後にmetaタグ追加とscriptタグ追加のコミットがあると思います。
あたかもリンク削除のコミットが、元からmasterリポジトリにあったかのようになっています。
現在のmasterブランチを図に表すとこのようになります。

今回はすんなりrebaseできましたが、当然競合が起こるケースもあります。
その場合はmergeのときと同じく手動でファイルを編集し、競合を解決します。
ファイルを保存したら、addしたあとにgit rebase --confinueとして処理を継続します。

$ git add <ファイル名>
$ git rebase --continue

もしrebase中になんらかの理由でrebaseを行う前の状態に戻したい場合、git rebase --abortとすれば、元の状態に戻すことができます。
また、--continueのかわりにgit rebase --skipとすると、そのコミットを飛ばして次へいきます。

今回は非常にシンプルなケースでrebaseを使用してみました。
rebaseはいろいろな使い方ができ、例えばgit rebase -iとすると、インタラクティブに履歴編集ができたり、
--ontoオプションを使用して派生元ブランチをさらにその親ブランチに切り替えたり、といった変更が可能です。
rebaseは非常によく使用するコマンドなので、しっかり覚えましょう。