Gitレポジトリのスペースや改行を統一するには

みなさん、こんにちは
kaoruです。

今回は、Gitレポジトリで管理しているソースコードのスペースや改行など体裁を統一する方法です。文字コードの一括変更やリファクタリングなど色々応用ができると思います。

フィルタースクリプトの準備

まず、ソースコードをきれいに書き換えるフィルタースクリプトを用意します。フィルタースクリプトは標準入力からテキストを入力して、加工した後、標準出力から出力するようなものなら何でもOKです。

以下は例です。この例では、TABを半角スペース4つに置換し、行末のスペースを取り除きます。また、改行コードをLFにします。ファイルの末尾が必ず改行コードLFで終わるようにしています。

/usr/local/bin/normalize-text.pl
#!/usr/bin/perl
use Text::Tabs;
$tabstop = 4;
while (<>) {
$_ = expand($_);
s/[ \r\n]+$//;
print "$_\n";
}

コミット時に自動的にフィルターを適用

改行コードや不要なスペースは気をつけていても、ついうっかりコミットしてしまうものです。EclipseやVisual StudioなどIDEを利用していると、暗黙的に不要なスペースが挿入されてしまうこともあります。そこで、コミット時に自動的にフィルターを適用して体裁を整える方法をご紹介します。

まず、フィルターをGitに登録します。/etc/gitconfigや$HOME/.gitconfigなどに以下の設定を追加します。

/etc/gitconfigの例
[filter "normalize-text"]
clean = /usr/local/bin/normalize-text.pl
smudge = cat

次にファイルとフィルターを関連づけます。ワーキングツリーに.gitattributesというファイルを作成し、コミットしてください。

.gitattributes
*.php   eol=lf filter=normalize-text

このような設定を行うことで、以降コミットするファイルにはすべてフィルターが適用されるようになります。

過去の履歴の書き換え

フィルターを指定するだけでは、今後コミットする変更に対しては体裁を統一できますが、すでにコミットしてしまった変更に対しては効果がありません。さらに、ある時点で急に体裁を変更してしまうと前後でdiffによる比較やマージが出来ないといった問題が発生します。そこで、過去の履歴に対してもフィルターを適用する方法をご紹介します。

次にワーキングディレクトリーの中のすべてのファイルを書き換えるスクリプトを用意します。

以下は例です。拡張子が.phpであるファイルを抽出してフィルターを適用しています。[1]

/home/kaoru/rewrite_history.sh
#!/bin/bash
for f in $(find -name \*.php)
do
mv $f $f.bak
cat $f.bak | /usr/local/bin/normalize-text.pl > $f
rm $f.bak
done

それから、作業を行うために、Gitレポジトリーをクローンします。

git clone EXAMPLE.COM:/repos/my_project.git

チェックアウトされたワーキングツリーにカレントディレクトリーを移動します。

cd my_project

ワーキングツリーの中でgit filter-branchを使って履歴を書き換えます。

git filter-branch --tree-filter '/home/kaoru/rewrite_history.sh' HEAD

おわりに

GitはSubversionと違って履歴の編集ができるため、最初はあまりコーディング規則などを設けずに開発を始めて、ある程度出来上がってきてから、過去に遡って、ソースコードを掃除するというようなことができます。

最初から完璧を目指すと敷居が高くなってしまいますが、Gitなら気軽にコミットが出来ます。

今回のような履歴を書き換えるテクニックは公開レポジトリでは厳しいものがありますので、公開レポジトリにpushする前に、一度履歴を見直してみることをお勧めします。

[1] 2010/09/30 フィルターのソースコードを少し修正しました。



Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 16271409 bytes) in /home/yumeco/www/prod/wp-includes/wp-db.php on line 1171