履歴の変更

Gitはコミットによって、履歴が積みあがっていきます。

しかし、積み上げた履歴に、間違ったものだったり履歴に入れてはいけないような秘密の情報が入ってしまっていたりということがあるかもしれません。

Gitではそのようなときに、履歴を変更することが可能になっています。

注意点

履歴を変更する機能があるのだから、自由に変更しても大丈夫だろうと思うかもしれませんが、そんなことはありません。履歴を変えるには大いなる責任が付きまといます。

Gitは、複数人で開発することが可能なツールです。つまり、一人が過去の履歴を変更すると、その変更によって、他人のリポジトリの内容も変更しなくてはならなくなります。

そのため、これから説明するような履歴の変更を禁止するようなルールを定めてリポジトリを管理することが多々あります。

もしたくさんの人が参加しているようなリポジトリの場合、今回のような履歴の変更は慎重にする必要があります。

直前の履歴の変更

直前にコミットした内容について変更するのは非常に簡単です。

次の--amendオプションをつけて実行するだけです。

git commit --amend

私は、コミットメッセージを間違えたときなどによく使います。

過去の履歴の変更

直前のものではない、履歴の変更は少々面倒です。

いくつか方法はあるのですが、ここではリベース(rebase)を使った方法を紹介します。

リベースは、特定の範囲における履歴の変更を行う機能です。コミットの分離や結合、削除などができます。またこの際にコミットメッセージについても変更可能です。

リベースの際の範囲の指定にはコミットのハッシュ値を使用します。ハッシュ値は、git log等で調べます。この際、リベースは現在のブランチの最新のコミットから、変更したいコミットからひとつ古いコミットを範囲として指定します。

変更したいコミットの範囲よりひとつ古いコミットのハッシュ値をAとした場合は、次のようにします。

git rebase -i A

-iは対話的に実行することを示すオプションです。これをつけてリベースをした方がやりやすいためつけてあります。

このコマンドを実行すると、それぞれのコミットについてどのような処理を実行するか記述する画面が開きます。

記述の仕方については、一緒に説明が書かれているためここでは詳しく説明しません。簡潔に言えばコミットを変更したいところについて、pickと書かれているところを適当なものに変更します。

また、リベースにおける操作は実行途中に表示されるため、それに従って操作をします。

他のコミットの範囲の指定方法としてHEADを使ったものがあります。HEADを使うと、最新のコミットからいくつ前なのかで指定できます。

git rebase -i HEAD~3

この例では、最新のコミットを含めて3つの履歴を変更しています。3という数字を他のもので書き換えると、その数字の数のコミットを変更できます。

プッシュとプル

開発方法によっては履歴を変更した場合、それをリモート上に反映させる必要があるかもしれません。

まだプッシュしていないブランチで履歴を変更した場合は、今まで通りの方法でプッシュすることができますが、、すでにリモート上にプッシュされているブランチで履歴を変更した場合は、プッシュの際に--forceというオプションを付ける必要があります。

git push origin ブランチ名 --force

上のような方法で、リモート上のブランチの過去の履歴が変更されている場合は、プルも少々面倒です。

面倒な理由として、ローカルのブランチとリモートのブランチの変更のマージがうまくいかないというものがあります。そこで、ローカルのブランチに強制的にリモートのブランチの情報を上書きすることで問題を解決する方法を紹介します。

git checkout ブランチ名
git fetch origin ブランチ名
git reset --hard origin/ブランチ名

fetchはリモートのブランチの情報を取得するコマンドでpullとは違いマージをしません。取得した後は、resetを使って強制的に上書きしています。

まとめ

履歴の変更は、ローカルだけで行う分には影響は小さいのですが、リモート上へも反映させてしまうと、他の方への影響が大きく出てしまいます。

私個人の考えとしては、履歴の変更は最終手段だと思っています。つまり、他人に知られるとまずいような情報を含めたコミットがあった場合など、どうしてもこの手段しかないというときに限り使うべきです。

しかし、積極的に使用を推奨する場面もあります。これは、ローカルでいくつかのコミットをした後に、リモートにプッシュするというようなときです。どういうことかというと、ローカルでコミットした後に、リモートでいままでのコミットを整理するというようなときに、リベースを使うということです。

Gitの機能の中でも、かなり危険な部類に入るコマンドのため、用法容量を守って使いましょう。