Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Dec 7, 2025 - 日記

Gitの黒歴史を隠蔽する:履歴書き換えツールまとめ

git-history-rewrite-tools cover image

昨日紹介したgit-absorb は、変更を適切なコミットに自動的に吸収する便利なツールでした。しかし、開発の現場では、それ以外にもさまざまな「黒歴史」を隠蔽したい場面があります。

恥ずかしいコミットメッセージ、デバッグ用のprint文、うっかりコミットしてしまったパスワード、意味のない細かいコミットの乱立…。こうした「見せたくない履歴」を綺麗に整理するためのツールと手法をまとめてみました。

この記事では、Gitの履歴書き換え系ツールの使い分けと、それぞれのユースケースについて解説します。

黒歴史隠蔽の基本原則

まず、重要な大前提を確認しておきましょう。

できること・できないこと

✅ 安全にできること

  • プッシュ前のローカルコミットの修正・削除・統合
  • 自分だけのフィーチャーブランチでの履歴書き換え
  • まだ誰も参照していないブランチの整理

❌ 危険なこと・やってはいけないこと

  • すでに他の人がpullしたコミットの書き換え
  • mainブランチなど共有ブランチでの履歴改変
  • プッシュ済みの公開コミットの無断削除

基本ルール

  1. プッシュ前に綺麗にする:ローカルで履歴を整理してからプッシュ
  2. チーム共有ブランチは触らない:mainやdevelopは絶対に書き換えない
  3. force pushは慎重に--force-with-leaseを使い、必ず事前確認

これらを守れば、安全に黒歴史を隠蔽できます。

ツール別の使い分け

1. git commit –amend:最新コミットの修正

用途:直前のコミットを修正したい

最も基本的な黒歴史隠蔽ツールです。最新のコミットを直接修正できます。

# ファイルを修正
vim src/api.py

# 直前のコミットに追加
git add src/api.py
git commit --amend

# コミットメッセージだけ修正
git commit --amend -m "新しいコミットメッセージ"

使える場面

  • typoを見つけて即座に修正
  • 「あ、このファイル入れ忘れた」という追加
  • 恥ずかしいコミットメッセージを書き直し

制限

  • 最新のコミットのみ修正可能
  • すでにpushしている場合は--force-with-leaseが必要

2. git rebase -i:複数コミットの総合編集

用途:履歴全体を自由に編集したい

最も強力で柔軟な履歴書き換えツールです。インタラクティブモードで、コミットの順序変更・統合・削除・メッセージ編集などすべてを行えます。

# 過去5コミットを編集対象に
git rebase -i HEAD~5

# または特定のコミット以降を編集
git rebase -i abc123d

実行すると、以下のようなエディタが開きます:

pick abc123d Add API endpoint
pick def456e Fix typo
pick ghi789f oops forgot this
pick jkl012m Add tests
pick mno345p Update README

# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash" but discard this commit's log message
# d, drop = remove commit

よく使うコマンド

pick(p):そのまま使う

pick abc123d Add API endpoint  # そのまま

reword(r):メッセージだけ変更

reword abc123d Add API endpoint  # メッセージを変更

squash(s):前のコミットに統合(メッセージは両方残す)

pick abc123d Add API endpoint
squash def456e Fix typo  # abc123dに統合、両方のメッセージ保持

fixup(f):前のコミットに統合(メッセージは捨てる)

pick abc123d Add API endpoint
fixup def456e Fix typo  # abc123dに統合、このメッセージは捨てる
fixup ghi789f oops      # これも統合して捨てる

drop(d):コミットを削除

drop abc123d Temporary debug code  # このコミットは削除

edit(e):コミットを修正するために停止

edit abc123d Add API endpoint  # ここで停止して手動修正

実践例:恥ずかしいコミット履歴の整理

Before(恥ずかしい履歴):

* mno345p Update README
* jkl012m Add tests
* ghi789f oops forgot this
* def456e Fix typo
* abc123d Add API endpoint

rebaseで編集:

git rebase -i HEAD~5

エディタで以下のように編集:

pick abc123d Add API endpoint
fixup def456e Fix typo           # abc123dに統合
fixup ghi789f oops forgot this   # abc123dに統合
pick jkl012m Add tests
pick mno345p Update README

After(綺麗な履歴):

* mno345p Update README
* jkl012m Add tests
* abc123d Add API endpoint  # typo修正やoopsが統合された

3. git-absorb:変更の自動振り分け

用途:複数コミットへの修正を自動的に適切なコミットに反映

昨日の記事で詳しく解説した通り、変更内容を自動的に適切なコミットに吸収してくれるツールです。

# 修正をステージング
git add .

# 自動的に適切なコミットに吸収
git absorb --and-rebase

使える場面

  • プルリクエストのレビュー対応
  • 複数ファイルのtypo修正
  • 既存コミットへの小さな修正追加

詳細は昨日の記事 をご覧ください。

4. git commit –fixup + git rebase –autosquash:手動指定での統合

用途:特定のコミットに修正を統合したい(git-absorbの手動版)

git-absorbが自動判定してくれない場合や、明示的にコミットを指定したい場合に使います。

# 修正したいコミットのハッシュを確認
git log --oneline

# fixupコミットを作成
git add .
git commit --fixup=abc123d

# 履歴を確認(fixup!が付いている)
git log --oneline
# → def456e fixup! Add API endpoint
# → abc123d Add API endpoint

# autosquashで自動統合
git rebase -i --autosquash HEAD~5

--autosquashを使うと、fixup!で始まるコミットが自動的に適切な位置に移動し、fixupコマンドが設定されます。

git-absorbとの使い分け

  • git-absorb: 変更内容から自動判定(手間がかからない)
  • git commit –fixup: コミットを明示的に指定(正確にコントロール)

5. git filter-branch / git filter-repo:機密情報の完全削除

用途:パスワードやAPIキーなど、履歴から完全に抹消したい

最も強力(そして最も危険)な履歴書き換えツールです。履歴全体から特定のファイルや情報を完全に削除できます。

git filter-repo(推奨)

git filter-branchの後継で、より高速で安全です。

# インストール(macOS)
brew install git-filter-repo

# 特定のファイルを履歴から完全削除
git filter-repo --path secrets.txt --invert-paths

# 特定のディレクトリを削除
git filter-repo --path config/passwords/ --invert-paths

# パスワード文字列を置換
git filter-repo --replace-text <(echo "password123==>***REMOVED***")

使える場面

  • うっかりコミットしたパスワード・APIキーの削除
  • 大きなバイナリファイルの履歴からの削除
  • 機密情報を含むファイルの完全抹消

注意点

  • 非常に危険な操作:履歴全体が書き換わる
  • 全員の再クローンが必要:チーム全員に影響
  • 最後の手段:他の方法で対処できないか必ず検討

6. git reset:コミット自体をなかったことに

用途:コミットを取り消したい

コミットを「なかったこと」にして、作業ディレクトリの状態を戻します。

# 直前のコミットを取り消し(変更は保持)
git reset --soft HEAD^

# 直前のコミットを取り消し(ステージングも解除、変更は保持)
git reset --mixed HEAD^  # または git reset HEAD^

# 直前のコミットを完全に取り消し(変更も破棄)
git reset --hard HEAD^

オプションの使い分け

–soft:コミットだけ取り消し、変更はステージングに残る

git reset --soft HEAD^
# → コミットメッセージを書き直したい時に便利
git commit -m "新しいメッセージ"

–mixed(デフォルト):コミット取り消し、ステージング解除、変更は残る

git reset HEAD^
# → コミットをやり直したい時
git add -p  # 必要なものだけ選択
git commit

–hard:すべて完全に破棄

git reset --hard HEAD^
# → このコミットは完全に不要な時(注意!)

使える場面

  • 「やっぱりこのコミット不要だった」
  • 「間違えてコミットした、やり直したい」
  • コミットメッセージの全面書き直し

実践的な黒歴史隠蔽ワークフロー

ケース1:恥ずかしいデバッグコードの削除

# 状況:print文だらけのコミットをプッシュしてしまった
git log --oneline
# → def456e Add feature (←これにprint文がある)
# → abc123d Previous commit

# 1. デバッグコードを削除
vim src/api.py  # print文を削除

# 2. amendで直前のコミットに反映
git add src/api.py
git commit --amend --no-edit

# 3. force pushで更新(自分のブランチのみ!)
git push --force-with-lease

ケース2:複数の恥ずかしいコミットを統合

# 状況:細かいコミットがたくさん
git log --oneline
# → ghi789f fix typo again
# → def456e fix typo
# → abc123d Add feature

# 1. interactive rebaseで編集
git rebase -i HEAD~3

# 2. エディタで統合
# pick abc123d Add feature
# fixup def456e fix typo
# fixup ghi789f fix typo again

# 3. 保存して完了
# → abc123dに全部統合される

ケース3:プルリクレビュー後の整理

# 状況:レビュー指摘に対応した複数のコミット
git log --oneline
# → jkl012m Fix review comment 3
# → ghi789f Fix review comment 2
# → def456e Fix review comment 1
# → abc123d Original implementation

# 方法1:git-absorbで自動統合
git add .
git absorb --and-rebase

# 方法2:手動で整理(git-absorbがうまくいかない場合)
git rebase -i HEAD~4
# → レビュー修正をoriginal implementationにfixup

# 結果:綺麗な1コミットに
# → abc123d Original implementation (全修正が統合済み)

ケース4:うっかりパスワードをコミット

# 状況:.envファイルにパスワードが!
# → まだプッシュしていない場合

# 1. .gitignoreに追加
echo ".env" >> .gitignore

# 2. 履歴から完全削除
git filter-repo --path .env --invert-paths

# 3. 正しいファイルでコミット
cp .env.example .env  # サンプルファイルをコミット
git add .env.example .gitignore
git commit -m "Add .env.example and ignore .env"

# → すでにプッシュしている場合は、直ちに以下を実施
# 1. パスワードを無効化(最優先!)
# 2. filter-repoで削除
# 3. force pushで上書き
# 4. チームに通知して再クローンを依頼

ツール選択のフローチャート

何をしたい?
│
├─ 最新のコミットだけ修正
│  → git commit --amend
│
├─ 複数コミットにまたがる修正を自動振り分け
│  → git-absorb
│
├─ 特定のコミットに修正を統合
│  → git commit --fixup + git rebase --autosquash
│
├─ コミット履歴全体を編集(統合・削除・順序変更)
│  → git rebase -i
│
├─ コミットを取り消したい
│  → git reset (--soft/--mixed/--hard)
│
└─ 機密情報を履歴から完全削除
   → git filter-repo(最終手段)

注意点とベストプラクティス

安全な履歴書き換えのために

1. 必ずバックアップを作成

# 作業前にバックアップブランチを作成
git branch backup-before-rebase

# 問題があれば戻せる
git reset --hard backup-before-rebase

2. force pushは–force-with-leaseを使う

# ❌ 危険
git push --force

# ✅ 安全(他の人の変更があれば失敗する)
git push --force-with-lease

3. 共有ブランチでは絶対にやらない

# main/developブランチでの履歴書き換えは禁止
git branch  # 今いるブランチを確認
# → main なら絶対に履歴書き換えしない!

トラブルシューティング

rebase中にコンフリクトが発生

# 1. コンフリクトを解決
vim conflicted-file.txt

# 2. ステージングして続行
git add conflicted-file.txt
git rebase --continue

# または中止
git rebase --abort

間違えて履歴を書き換えてしまった

# reflogで過去の状態を確認
git reflog

# 特定の状態に戻る
git reset --hard HEAD@{2}

force pushしたけど元に戻したい

# reflogで書き換え前のコミットを探す
git reflog

# 元の状態に戻す
git reset --hard <元のコミットハッシュ>

# 再度force push
git push --force-with-lease

まとめ

Git の黒歴史隠蔽ツールは、用途によって使い分けることが重要です。

ツール用途難易度危険度
git commit --amend最新コミット修正★☆☆☆☆★☆☆☆☆
git resetコミット取り消し★★☆☆☆★★★☆☆
git-absorb自動振り分け統合★★☆☆☆★★☆☆☆
git commit --fixup手動指定統合★★★☆☆★★☆☆☆
git rebase -i履歴全体編集★★★★☆★★★☆☆
git filter-repo機密情報削除★★★★★★★★★★

基本方針

  1. プッシュ前に綺麗にする:ローカルで履歴を整理してから公開
  2. 簡単なものから使う:amendやresetで解決できないか検討
  3. 自動化を活用:git-absorbで作業効率化
  4. 危険な操作は慎重に:filter-repoは最後の手段

プルリクエスト前の整理チェックリスト

  • デバッグコードは削除したか
  • コミットメッセージは適切か
  • 細かいコミットは統合したか
  • typo修正は元のコミットに吸収したか
  • 機密情報は含まれていないか

これらのツールを適切に使いこなせば、常に綺麗なコミット履歴を維持できます。ぜひ試してみてください。

さらに詳しい情報は、以下の公式ドキュメントをご覧ください: