Gitのコミットをfilter-branchコマンドで編集する

森裕介(プログラマー)
無念

前回の Brewfileについての記事 を書くにあたり、 とりあえずどこかにコミットしておけばいいやくらいの軽い気持ちで ウカツにも 自分のdotfiles管理レポジトリ にコミットして作業を続けてきたわけですが、 Brewfileの管理はやっぱ別レポジトリのほうがいいよなと思いレポジトリを移行することにしました。

その際にファイルをただコピーペーストするのでなく、 コミットログを残しておきたかったので filter-branch コマンドで各コミットを編集しました。 自分用の備忘録を兼ねて公開する次第です。

git filter-branch

filter-branchコマンドは複数のコミットに対して特定の操作を一括して行うコマンドです。 Gitの公式ドキュメント いわく 最強のオプション です。 記されているとおり 破壊的な作業を行うことができるので取り扱いには要注意 です。

作業概要

dotfiles レポジトリにコミットされている home/Brewfilebrewfile レポジトリを新規作成して Brewfile としてコミットします。

brewfile レポジトリを作成する

まずは brewfile レポジトリを作成します。 新規作成するのではなく dotfiles レポジトリをコピーします。

1
2
$ cp -r path/to/dotfiles path/to/brewfile
$ cd path/to/brewfile

brewfileのレポジトリの大元ができましたので以降はそこで作業します。

Brewfileがコミットされている場所を移動する

現在Brewfileが home/Brewfile へコミットされているので、 ディレクトリ直下に移動させたいです。

1
2
3
4
5
6
7
8
$ ls -l
total 0
drwxr-xr-x  16 jiska  staff  544 May  7 19:59 home

$ ls -l home
total 8
-rw-r--r--  1 jiska  staff  1402 May  7 19:59 Brewfile
drwxr-xr-x  3 jiska  staff   102 May  7 19:58 bin

そこで git filter-branch—subdirectory-filter オプションを指定します。 subdirectory-filterに特定のディレクトリを指定することでそのディレクトリ以下の コミットのみを抽出することができます。

1
2
3
$ git filter-branch --subdirectory-filter home
Rewrite 1205dbf587d9956b364cfcee275eb2c03e120e6d (38/38)
Ref 'refs/heads/master' was rewritten

これで移動ができました。ですがまだ不要なコミット、ファイルが含まれています。

1
2
3
4
5
6
7
8
9
$ git log --oneline
602bc5b Replace tap phinze/cask to caskroom/cask
f8b5be0 Replace tap josengonzales/php to homebrew/php
cbe1a55 Add git alias
a38d210 Track .gitignore.global
25c4460 Custom zsh settings
44bd033 Add dropbox
d95514b Add .zshrc from oh-my-zsh
...
1
2
3
4
$ ls -l
total 8
-rw-r--r--  1 jiska  staff  1402 May  7 20:00 Brewfile
drwxr-xr-x  3 jiska  staff   102 May  7 20:00 bin

Brewfileの編集が含まれるコミットだけ抽出する

Brewfileに関するコミットのみ抽出するためにファイル名を指定します。 この際、すでに filter-branch を実行しているためバックアップが存在しているため —force オプションを指定する必要があります。

1
2
3
$ git filter-branch --force HEAD -- Brewfile
Rewrite 602bc5ba2e24a6206a4d38875f5b7aeb70dc286c (10/10)
Ref 'refs/heads/master' was rewritten

Brewfileが含まれるコミットだけが抽出されました。

1
2
3
4
5
6
7
8
9
10
11
$ git log --oneline
3d53ea7 Replace tap phinze/cask to caskroom/cask
03ab07e Replace tap josengonzales/php to homebrew/php
6d37848 Add dropbox
cfea0ac Add autojump, compser and docker
dc53df8 Add zsh, airdisplay
3b231f4 Add Firealpaca
2a01d1e Tap alphanumeric order
82c5709 Install cask alphanumeric order
da41fbc Remove unnecessary operators
a881022 Add Brewfile

Brewfile以外のファイルを削除する

最後に —tree-filter オプションを指定します。 tree-filterにコマンドを指定することで各コミットにそのコマンドを適用します。 Brewfile以外を削除していきましょう。

1
2
3
$ git filter-branch --force --tree-filter 'rm -fr ./bin' HEAD
Rewrite 3d53ea70240eb1d178e00b638787ffbb1b553e76 (10/10)
Ref 'refs/heads/master' was rewritten

この調子で不要ファイルを削除していきます。

1
2
3
4
$ git filter-branch --force --tree-filter 'rm -fr ./.bash.d' HEAD
$ git filter-branch --force --tree-filter 'rm -f ./.**rc' HEAD
$ git filter-branch --force --tree-filter 'rm -f ./.**.conf' HEAD
...

これで不要コミット、不要ファイルが削除されてBrewfileだけのコミットログが完成しました!

完成したレポジトリ

おわり

今回のまとめ

  • filter-branch を正しく使うと便利
  • そもそもコミットは適切な場所に適切な粒度でしましょうねという話