【Git・GitHub操作ガイド】第6章 図解:Gitのコミット履歴を修正する3つの方法!git rebase, reflogで失敗も怖くない

はじめに:「やっちゃった!」そのコミット、無かったことにできます

「あ、デバッグ用のconsole.logを消し忘れたままコミットしちゃった…」
「レビューで『このコミット、1つにまとめてください』と指摘された…」
wip(作業中)コミットを積んだまま、うっかりプルリクエストを…」

開発現場でよく聞く、こんな「やっちゃった!」経験はありませんか?

綺麗なコミット履歴は、未来の自分やチームメンバーを助ける大切な道しるべです。しかし、日々の開発に追われると、つい履歴の整理は後回しになりがちです。

この記事では、そんな少し散らかったコミット履歴を、誰が見ても分かりやすい美しいものに作り変えるための、3つの強力なGitコマンドを徹底解説します。rebaseのコンフリクトも、reflogがあればもう怖くありません。

これらの操作は非常に強力ですが、安全に使うための「お作法」があります。「共有された履歴は書き換えない」という黄金律を胸に、まずは自分だけが見るローカルブランチを綺麗にする達人を目指しましょう。


目次


対象読者

この記事は、主に以下のような方を対象としています。

  • Gitの基本操作(add, commit, push)は覚えたが、コミット履歴を綺麗に整理する方法がわからない方
  • プルリクエストを送る前に、細かすぎるコミットを一つにまとめたい方
  • 間違えてコミットしてしまった内容を取り消したいが、安全な方法が知りたい方

この記事で紹介する主なコマンド

  • インタラクティブリベース (rebase -i): コミット履歴を編集するためのツールです。「あのコミットメッセージ、間違えちゃった」「この3つのコミット、一つにまとめたいな」「コミットの順番を入れ替えたい」といった、要望に応えます。
  • Reflog (reflog): Gitの「操作ログ」であり、あなたの命綱です。reset --hardでコミットを消してしまったり、リベースに失敗して途方に暮れたりした時でも、reflogを見れば過去の状態に復元できる可能性が残されています。
  • filter-repo: リポジトリの履歴全体にわたる大規模な修正を行うための専門ツールです。誤ってコミットしてしまったパスワードや巨大なファイルを、履歴の始まりから終わりまで、完全に消し去るような管理用途に使います。

コマンドごとの利用事例と実行手順

1. git rebase -i:コミットを「編集」して整える

概要

rebase -i(インタラクティブリベース)は、コミット履歴を対話形式で自由に編集できる、非常に強力な機能です。

rebasemergeの違いは?
mainブランチの最新の変更を取り込む際、git mergegit rebaseのどちらを使うか迷うかもしれません。

  • git merge: 変更の履歴を合流させ、マージしたという事実を「マージコミット」として残します。歴史が分岐・合流するため、複雑になることがあります。
  • git rebase: あなたのブランチの変更を一旦脇に置き、mainブランチの最新地点に移動してから、あなたの変更を一つずつ適用し直します。履歴が一直線になり、スッキリと見やすくなります。

この章で紹介するrebase -iは、後者の「履歴を繋ぎ直す」仕組みを応用して、自分のコミットを編集するテクニックです。

主な利用シーン

  • squash (まとめる): プルリクエスト前に、細かすぎるコミットを一つの意味のある単位にまとめる。
  • reword (メッセージ修正): コミットメッセージのタイポや内容を後から修正する。
  • pick (順序変更): コミットの順序を入れ替える。

実行手順:3つのコミットを1つにまとめる (squash)

ここでは、最もよく使うsquashを例に手順を解説します。

  1. feature/loginブランチに、以下のような3つのコミットがあるとします。
bash $ git log --oneline 
c3c3c3c (HEAD -> feature/login) fix: correct typo in title 
b2b2b2b style: adjust layout 
a1a1a1a feat: add user profile page
  1. 直近3つのコミットを編集対象にするため、以下のコマンドを実行します。
bash git rebase -i HEAD~3
  1. すると、テキストエディタが開き、以下のような編集画面が表示されます。
    このリストは上が古く、下が新しいコミット順になっています。
pick a1a1a1a feat: add user profile page 
pick b2b2b2b style: adjust layout 
pick c3c3c3c fix: correct typo in title 
# Commands: 
# p, pick <commit> = use commit 
# r, reword <commit> = use commit, but edit the commit message 
# s, squash <commit> = use commit, but meld into previous commit 
# ... (他にも多くのコマンドがあります) ... 
  1. 2番目と3番目のコミットを、最初のコミットにまとめるため、picksquash(またはs)に変更し、ファイルを保存してエディタを終了します。
pick a1a1a1a feat: add user profile page 
s b2b2b2b style: adjust layout 
s c3c3c3c fix: correct typo in title
  1. すると、再びテキストエディタが開き、3つのコミットメッセージを統合して、新しい一つのコミットメッセージを記述する画面になります。不要な行を削除し、最終的なメッセージを綺麗に整えて、再度保存・終了します。
# This is a combination of 3 commits. 
# The first line is the new commit message: 

feat: Add user login feature 

# The rest of the lines are the old commit messages: 
# 
# feat: add user profile page 
# style: adjust layout 
# fix: correct typo in title
  1. git log --onelineで履歴を確認すると、3つのコミットが1つにまとまっていることがわかります。
bash $ git log --oneline 
d4d4d4d (HEAD -> feature/login) feat: Add user login feature

【TIPS】図で理解するrebase
コミットの歴史が書き換わる様子は、図で見るとより直感的に理解できます。

Before: Rebase前のコミット履歴
feature/loginブランチに3つのコミットが積まれています。

Rebase前のコミット履歴

After: squashで1つにまとめた後のコミット履歴
3つのコミットが1つの新しいコミットDにまとめられました。

squashで1つにまとめた後のコミット履歴
  • 【超重要】リベースの黄金律
    mainブランチなど、すでに他の人と共有しているブランチでリベースを行うと、歴史の食い違いが発生し、チームに深刻な混乱を招きます。
    他の人が古い履歴を元に作業している場合、あなたがリベース後の履歴を強制的にプッシュ(git push --force)すると、他の人の作業内容が消えてしまう危険性すらあります。
    リベースは、まだプッシュしていない、あるいは自分専用のブランチでのみ行いましょう!

【困った時は】rebaseでコンフリクトが発生したら?

rebaseはコミットを一つずつ適用し直すため、途中で変更が衝突(コンフリクト)することがあります。でも、慌てないでください。解決方法はmergeの時と似ています。

  1. コンフリクトの発生: rebaseを実行すると、以下のようなメッセージが表示され、処理が一時停止します。
bash $ git rebase -i HEAD~3 
Auto-merging your_file.txt 
CONFLICT (content): Merge conflict in your_file.txt 
error: could not apply b2b2b2b... style: adjust layout 
Resolve all conflicts manually, mark them as resolved with 
"git add/rm <conflicted_files>", then run "git rebase --continue". 
You can instead skip this commit in question with "git rebase --skip". 
To abort and get back to the state before "git rebase", run "git rebase --abort".
  1. コンフリクトの解決:
    • メッセージに表示されたファイル(この例では your_file.txt)を開き、<<<<<<<, =======, >>>>>>> で示された箇所を修正します。
    • 修正後、git add your_file.txt を実行して、解決したことをGitに伝えます。
  2. リベースの続行:
    • git rebase --continue を実行すると、rebaseが再開されます。
  3. リベースの中止:
    • もし途中でパニックになったら、git rebase --abort を実行しましょう。すべての変更が取り消され、rebaseを開始する前の状態に安全に戻ることができます。

2. git reflog:操作履歴を遡る「タイムマシン」

概要

reflogは、あなたのGit操作の全てを記録している「タイムマシン」です。commitresetrebaseなど、ブランチの先端(HEAD)が移動するたびに、そのログが自動的に記録されます。

これは、まるでゲームの「セーブポイントリスト」のようなものです。git reset --hardで最新のセーブデータを消してしまっても、reflogを見れば、その前に通過したセーブポイントにいつでも戻ることができます。

利用シーン

  • git reset --hardで必要なコミットを誤って消してしまった!
  • リベース操作に失敗して、ブランチの状態がおかしくなってしまった!

実行手順

  1. 「直近3時間分の作業が消えた!」とパニックになったとします。まずは落ち着いて、タイムマシンを起動しましょう。
bash $ git reflog
  1. すると、あなたの操作ログが一覧で表示されます。HEAD@{n}が「n回前のHEADの状態」を示しています。
a1b2c3d HEAD@{0}: reset: moving to a1b2c3d (← 間違ったreset操作) 
f4a1b2c HEAD@{1}: rebase -i (finish): returning to refs/heads/feature/login 
e5d6c7b HEAD@{2}: rebase -i (squash): feat: Add user login feature 
f4a1b2c HEAD@{3}: commit: Add important feature (← 戻りたいのはココ!) ...
  1. ログを遡り、resetしてしまう直前のコミットf4a1b2cHEAD@{3}の状態)に戻りたいことが分かりました。
  2. git reset --hard <コミットID>を実行して、その状態に完全に復元します。
    これで、失われたコミットが魔法のように復活します。reflogは、ローカルでの失敗を恐れずに挑戦するための、最強のセーフティネットです。
bash git reset --hard f4a1b2c

【注意点】

reflogで追跡されるのは、あくまであなたのローカルリポジトリ上の操作履歴のみです。リモートリポジトリにpushした履歴は、他の開発者と共有されているためreflogで安易に戻すことはできません。reflogは、ローカルでのみ作業しているときの「お守り」だと考えましょう。


3. 【管理用】git filter-repo:リポジトリの履歴を大規模に書き換える

概要

リポジトリの全履歴の中から、特定のファイルや情報を完全に削除するための、強力な外部ツールです。(pip install git-filter-repoなどで別途インストールが必要です)
※以前はgit filter-branchというコマンドが使われていましたが、操作が複雑で危険性も高かったため、現在は非推奨となり、このgit-filter-repoの使用が公式に推奨されています。

利用シーン

  • 機密情報(パスワード、APIキー)を誤って過去のコミットに含めてしまったので、全履歴から完全に抹消したい。
  • 巨大な動画ファイルを誤ってコミットしてしまい、リポジトリが肥大化したので、そのファイルを履歴から消し去りたい。

実行手順(例:特定のファイルを履歴から削除)

git filter-repoはリポジトリ全体を書き換える非常に強力な「最終兵器」です。実行する際は、個人ではなくリポジトリ管理者が、以下の手順に沿って慎重に作業を進める必要があります。

  1. 事前準備:ミラークローンを作成する
    安全のため、元のリポジトリを直接操作するのではなく、「ミラークローン」という特殊なクローンを作成して作業します。

ミラークローンとは?
git clone --mirrorで作成されるクローンは、通常のgit cloneとは少し異なります。ワーキングツリー(実際にファイルが見えるディレクトリ)を持たず、リモートリポジトリの内部データベース(.gitディレクトリの中身)を完全にそのままコピーしたものです。「裸の(bare)リポジトリ」とも呼ばれます。

リモートリポジトリと全く同じ構造を持つため、履歴の書き換えのようなリポジトリ全体に影響を及ぼす操作を行うのに最適です。

# 元のリポジトリのミラークローンを作成
git clone --mirror https://github.com/your-org/your-repo.git

# 作成されたディレクトリに移動
cd your-repo.git

これにより、ブランチやタグを含むリポジトリの全データが、作業用のyour-repo.gitディレクトリに複製されます。

  1. 履歴の書き換えを実行する
    git filter-repoコマンドを実行して、不要なファイルを履歴から完全に削除します。
# 'path/to/secret.txt' というファイルを全歴史から削除
git filter-repo --path path/to/secret.txt --invert-paths

コマンドが完了すると、指定したファイルを含まない新しいコミット履歴が作成されます。

  1. リモートリポジトリへ変更を反映する
    【最重要警告】この操作は、リモートリポジトリの履歴を強制的に書き換えます。実行する前に、必ずチームメンバー全員に連絡を取り、作業を一時停止してもらってください。

準備ができたら、書き換えた履歴をリモートリポジトリにプッシュします。

# 書き換えた全てのブランチとタグを強制的にプッシュ
git push origin --force --all
git push origin --force --tags
  1. 後処理:チームメンバーのローカル環境を更新する
    歴史を書き換えた後は、チームメンバー全員が手元にある古いローカルリポジトリを一度削除し、新しくクローンし直す必要があります。
# (各メンバーのPCで実行)
# 古いリポジトリのディレクトリを削除
rm -rf your-repo

# 新しい履歴を持つリポジトリをクローンし直す
git clone https://github.com/your-org/your-repo.git

この後処理を全員が徹底しないと、誰かが古い履歴をプッシュしてしまい、書き換え作業が台無しになる危険性があります。


参考資料


まとめ:綺麗な履歴は、未来への最高のプレゼント

この記事では、Gitのコミット履歴を自在に操るための、3つの強力なコマンドを解説しました。

  • git rebase -i: コミットの統合・修正・順序変更を行い、プルリクエスト前の履歴を美しく整えるための基本ツール。
  • git reflog: resetrebaseの失敗からでも安全に復旧できる、ローカル作業の「タイムマシン」。
  • git filter-repo: リポジトリ全体の履歴から機密情報などを完全に削除する、管理者向けの「最終兵器」。

これであなたも、自信を持ってコミット履歴を整理し、同僚から「お、綺麗なコミット履歴だね!」と一目置かれるエンジニアに一歩近づきました。綺麗な履歴は、未来の自分やチームメンバーへの最高のプレゼントです。

さあ、今すぐ実践してみましょう!
まずは、この後すぐに自分専用の練習ブランチを作り、git rebase -iでいくつかのコミットをsquashしてみてください。失敗を恐れず、reflogで戻れる安心感を体験することが、上達への一番の近道です。


次のステップへ

この記事が役に立ったら、ぜひチームメンバーにもシェアしてください!

Gitの基本操作に不安がある方は、こちらの記事もおすすめです。


本記事をご利用いただくにあたって

この記事は、公開時点(2025年9月)の情報に基づき、正確な情報を提供するよう努めています。

しかし、本記事で解説するソフトウェアやサービスの仕様は日々更新されるため、記事内で紹介している画面や手順が、ご覧いただいている時点では変更されている可能性があります。

もし内容に相違がある場合は、各サービスの最新の公式ドキュメントも併せてご参照ください。本記事の情報を利用される際は、ご自身の判断と責任においてお願いいたします。


SNSでもご購読できます。

コメントを残す

*