git rebase
コマンドは、初心者が離れているべき魔法のGit voodooであると評判がありますが、注意して使用すると、開発チームの生活をはるかに容易にすることができます。 この記事では、git rebase
と関連するgit merge
コマンドを比較し、一般的なGitワークフローにリベースを組み込む可能性のあるすべての機,
概念の概要
git rebase
について最初に理解すべきことは、git merge
と同じ問題を解決することです。 これらのコマンドはどちらも、あるブランチから別のブランチに変更を統合するように設計されています。
専用ブランチで新しい機能の作業を開始すると、別のチームメンバーがmaster
ブランチを新しいコミットで更新します。 これにより、gitをコラボレーションツールとして使用したことのある人には馴染み深い履歴が得られます。,
ここで、master
の新しいコミットは、作業している機能に関連しているとしましょう。 新しいコミットをfeature
ブランチに組み込むには、マージまたはリベースの二つのオプションがあります。,
マージオプション
最も簡単なオプションは、master
ブランチをフィーチャブランチにマージすることです。
git checkout feature git merge master
または、これをワンライナーに凝縮することができます。
git merge feature master
これにより、新しい”マージコミット”が作成されます。feature
両方のブランチの履歴を結びつけるブランチでは、次のような分岐構造が得られます。
マージは非破壊操作なので便利です。 既存のブランチは決して変更されません。, これにより、リベースの潜在的な落とし穴をすべて回避できます(以下で説明します)。
一方、これはまた、feature
ブランチは、上流の変更を組み込む必要があるたびに無関係なマージコミットを持つことを意味します。 master
が非常にアクティブな場合、これはあなたの機能ブランチの履歴をかなり汚染する可能性があります。 高度なgit log
オプションでこの問題を軽減することは可能ですが、他の開発者がプロジェクトの履歴を理解するのが難しくなる可能性が,
Rebaseオプション
マージの代わりに、feature
ブランチをmaster
ブランチにリベースすることができます。
git checkout feature git rebase master
これにより、feature
ブランチ全体がザmaster
ブランチ、効果的にすべての新しいコミットを組み込みますmaster
。 ものではなく統合を基準改定の再書き込みのプロジェクトの歴史創造によるブランドの新しいコミットコミット毎のです。,
リベースの主な利点は、よりクリーンなプロジェクト履歴を取得できることです。 まず、git merge
必要な不要なマージコミットを排除します。 次に、上の図でわかるように、リベースは完全に線形のプロジェクト履歴をもたらします—feature
のヒントに従って、フォークなしでプロジェクトの初めまで続くことができます。 これにより、git log
、git bisect
、gitk
のようなコマンドでプロジェクトを簡単にナビゲートできます。,
しかし、この自然のままのコミット履歴には、安全性とトレーサビリティの二つのトレードオフがあります。 リベースの黄金律に従わないと、プロジェクト履歴を書き直すことは、コラボレーションワークフローにとって壊滅的な可能性があります。 そして、それほど重要ではありませんが、リベーシングはマージコミットによって提供されるコンテキストを失います。
Interactive Rebasing
Interactive rebasingを使用すると、コミットが新しいブランチに移動されるときにコミットを変更することができます。, これは、ブランチのコミット履歴を完全に制御できるため、自動化されたリベースよりもさらに強力です。 通常、これはフィーチャブランチをmaster
にマージする前に、乱雑な履歴をクリーンアップするために使用されます。,
インタラクティブなリベースセッションを開始するには、i
オプションをgit rebase
コマンドに渡します。
git checkout feature git rebase -i master
これにより、移動しようとしているすべてのコミットをリストするテキストエディタが開きます。
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
このリストは、rebaseの実行後にブランチがどのように見えるかを正確に定義しています。 pick
コマンドを変更したり、エントリを並べ替えたりすることで、ブランチの履歴を好きなように見せることができます。, たとえば、2番目のコミットで1番目のコミットで小さな問題が修正された場合、fixup
コマンドでそれらを単一のコミットに凝縮することができます。
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
ファイルを保存して閉じると、Gitは指示に従ってリベースを実行し、次のようなプロジェクト履歴が生成されます。
このような重要でないコミットは、機能の履歴を理解しやすくします。 これは、git merge
ではできないことです。,
リベーシングの黄金律
リベーシングが何であるかを理解したら、学ぶべき最も重要なことは、それをしないときです。 git rebase
の黄金ルールは、パブリックブランチでは決して使用しないことです。
たとえば、master
をfeature
ブランチにリベースした場合にどうなるかを考えてください。
リベースにより、master
内のすべてのコミットがfeature
。 問題は、これがリポジトリでのみ起こったことです。, 他のすべての開発者はまだ元のmaster
で作業しています。 リベースすると新しいコミットが発生するため、Gitはあなたのmaster
ブランチの履歴が他のすべてのブランチから分岐したと考えます。
二つのmaster
ブランチを同期させる唯一の方法は、それらをマージし直すことです。その結果、余分なマージコミットと同じ変更を含む二つのコミットセット(元のもの、およびリベースされたブランチからのもの)が追加されます。 言うまでもなく、これは非常に混乱する状況です。,だから、あなたが実行する前にgit rebase
、常に自分自身に尋ねる、”他の誰かがこのブランチを見ていますか?”答えが”はい”の場合は、キーボードから手を離して、変更を加える非破壊的な方法について考え始めます(例:git revert
コマンド)。 それ以外の場合は、好きなだけ履歴を書き直しても安全です。
強制プッシュ
リベースされたmaster
ブランチをリモートリポジトリにプッシュしようとすると、Gitはリモートmaster
ブランチと競合するため、, しかし、次のように--force
フラグを渡すことで、プッシュを強制することができます。
# Be very careful with this command! git push --force
これにより、リモートのmaster
ブランチがリポジトリからリベースされたものと一致するように上書きされ、残りのチームにとって非常に混乱させます。 いうよう十分注意してくださいこのコマンドを使用する場合にのみがどうなのかを知りだしています。
強制プッシュする必要があるのは、プライベート機能ブランチをリモートリポジトリにプッシュした後にローカルクリーンアップを実行したときだけです(バックアップのためなど)。, これは、”おっと、私は本当に機能ブランチの元のバージョンをプッシュしたくありませんでした。 代わりに現在のものを取る。”繰り返しになりますが、featureブランチの元のバージョンからのコミットを誰も作業していないことが重要です。
ワークフローチュートリアル
リベースは、チームが慣れ親しんでいる限り、既存のGitワークフローに組み込むことができます。 このセクションでは、機能の開発のさまざまな段階でリベーシングが提供できる利点について見ていきます。,
git rebase
を活用するワークフローの最初のステップは、各機能の専用ブランチを作成することです。 これにより、リベースを安全に利用するために必要なブランチ構造が得られます。
ローカルクリーンアップ
ワークフローにリベースを組み込む最も良い方法の一つは、ローカルの進行中の機能をクリーンアップすることです。 定期的に対話型リベースを実行することにより、機能内の各コミットに焦点が当てられ、意味があることを確認できます。, これにより、分離されたコミットに分割することを心配することなくコードを記述できます—事実の後に修正することができます。
呼び出すときgit rebase
、あなたは新しいベースのための二つのオプションがあります:機能の親ブランチ(例えば、master
)、またはあなたの インタラクティブリベースセクションの最初のオプションの例を見ました。 後者のオプションは、最後のいくつかのコミットを修正するだけでよい場合に便利です。 たとえば、次のコマンドは、最後の3つのコミットのみの対話型リベースを開始します。,
git checkout feature git rebase -i HEAD~3
指定することにより、HEAD~3
新しいベースとして、あなたは実際にブランチを移動していない—あなたはちょうど対話的にそれに続く3つのコミットを書き直しています。 これは上流の変更をfeature
ブランチに組み込まないことに注意してください。
このメソッドを使用してフィーチャ全体を書き直したい場合は、git merge-base
コマンドは、feature
ブランチの元のベースを見つけるのに役立ちます。, 次に、元のベースのコミットIDを返します。git rebase
に渡すことができます。
git merge-base feature master
この対話型リベーシングの使用は、git rebase
をワークフローに導入するのに最適な方法です。 他の開発者が表示される唯一のものは、きれいで簡単にフォローできる機能のブランチ履歴でなければならない完成品です。
しかし、これはプライベート機能ブランチでのみ機能します。, 同じ機能ブランチを介して他の開発者とコラボレーションしている場合、そのブランチは公開されており、その履歴を書き直すことはできません。
対話型リベースでローカルコミットをクリーンアップするためのgit merge
代替はありません。
アップストリームの変更をフィーチャに組み込む
概念の概要セクションでは、master
からのアップストリームの変更をgit merge
またはgit rebase
のいずれかを使用してフィーチャブランチがどのように組み込むことができるかを説明しました。, マージは、リポジトリの履歴全体を保持する安全なオプションですが、リベースは、フィーチャブランチをmaster
の先端に移動することによ
このgit rebase
の使用は、ローカルクリーンアップに似ています(同時に実行できます)が、プロセスでは、master
からのアップストリームコミットが組み込まれています。master
の代わりにリモートブランチにリベースすることは完全に合法であることに注意してください。, これは、同じ機能を別の開発者とコラボレーションし、変更をリポジトリに組み込む必要がある場合に発生する可能性があります。,
たとえば、あなたとJohnという別の開発者がfeature
ブランチにコミットを追加した場合、Johnのリポジトリからリモートfeature
ブランチをフェッチした後、リポジトリは次のようになります。
このフォークを解決するには、:ローカルのfeature
をjohn/feature
とマージするか、ローカルのfeature
をjohn/feature
の先端にリベースします。,
このリベースは、ローカルのfeature
コミットのみが移動されるため、リベースの黄金ルールに違反しないことに注意してください。 これは言うことのよう、”ジョンが既にしたものに私の変更を加えなさい。”ほとんどの状況では、これはマージコミットを介してリモートブランチと同期するよりも直感的です。
デフォルトでは、git pull
コマンドはマージを実行しますが、--rebase
オプションを渡すことによって、リモートブランチをリベースと統合するように強制することができます。,
プルリクエストを使用した機能のレビュー
コードレビュープロセスの一部としてプルリクエストを使用する場合は、プルリクエストを作成した後にgit rebase
を使用しないようにする必要があります。 プルリクエストを行うとすぐに、他の開発者はあなたのコミットを見ています。 その履歴を書き直すと、Gitとあなたのチームメイトが機能に追加されたフォローアップコミットを追跡することができなくなります。
他の開発者からの変更は、git merge
の代わりにgit rebase
組み込む必要があります。,
このため、通常、プルリクエストを送信する前に、対話型のリベースでコードをクリーンアップすることをお勧めします。
承認された機能の統合
機能がチームによって承認された後、master
ブランチの先端に機能を再ベース化することができます。git merge
を使用して機能をメインコードベースに統合します。,
これは、上流の変更をfeatureブランチに組み込むのと同様の状況ですが、master
ブランチでコミットを書き直すことはできないため、最終的にgit merge
を使用して機能を統合する必要があります。 ただし、マージの前にリベースを実行することにより、マージが早送りされ、完全に線形の履歴が得られることが保証されます。 これにより、プル要求中に追加されたフォローアップコミットをスカッシュすることもできます。,
git rebase
に完全に慣れていない場合は、いつでも一時的なブランチでリベースを実行できます。 そうすれば、誤って機能の履歴を台無しにした場合は、元のブランチをチェックアウトして再試行することができます。 たとえば、
git checkout feature git checkout -b temporary-branch git rebase -i master # git checkout master git merge temporary-branch
Summary
ブランチのリベースを開始するために本当に知っておく必要があるのはそれだけです。, 不要なマージコミットのないクリーンで線形な履歴を希望する場合は、git rebase
代わりにgit merge
別のブランチからの変更を統合
一方、プロジェクトの完全な履歴を保持し、パブリックコミットを書き直すリスクを避けたい場合は、git merge
固執することができます。 どちらのオプションも完全に有効ですが、少なくとも今はgit rebase
の利点を活用するオプションがあります。
コメントを残す