hogehoge foobar Blog Style Beta

Web,Mac,Linux,JavaScript,Perl,PHP,RegExp,Git,Vim,Redmineなど技術的なことのメモや、ちょっと便利そうなものの紹介をしています。

git diffで比較した差分のファイルだけを抽出するスクリプト

HTML等をgitで管理していると、サーバーへのアップロード用のファイルとしてコミットした差分のファイルだけを抽出したい事があります。
「git diff」で差分の表示は出来るのですが、ディレクトリ構造を保持した状態でのファイル抽出みたいな事は出来ないようだったので、Perlでスクリプトを書いてみました。
bashとかでも書けると思いますが、なんとなくPerlで書いてみました。

「git diff --name-only」で差分のファイル名を取得

普通にgit diffを使うと・・・

gitでの差分の表示には「git diff」を使用しますが、そのまま使うとファイルの内容まで表示されてしまいます。

$ git diff HEAD^
diff --git a/bar.txt b/bar.txt
new file mode 100755
index 0000000..9e74262
--- /dev/null
+++ b/bar.txt
@@ -0,0 +1,5 @@
+1
+2
+3
+4
+5
「git diff --name-only」でファイル名だけ表示

今回の場合、差分のファイル名だけが取得出来れば良いので、「--name-only」オプションを使ってファイル名だけを表示させるようにします。

$ git diff --name-only HEAD^
bar.txt
hoge.txt

使い方

「git_diff_export.pl」を「.git」のあるディレクトリに格納します。
あとは「perl ./git_diff_export.pl」のようにスクリプトを実行することで、「export/YYYYMMDDhhmmss」以下に差分ファイルがコピーされます。
「export」ディレクトリはgitのワークディレクトリ内に作成されるので、「.gitignore」で管理対象から除外しておくのが良いと思います。

.gitignoreの例
.gitignore
git_diff_export.pl
export/

それと、「git diff」と同じようにバージョン指定での差分ファイル抽出も出来るようにしています。
例えば、「perl ./git_diff_export.pl HEAD^ HEAD^^」のような形で特定のバージョンを指定しての差分ファイル抽出が出来ます。

「git_diff_export.pl」(コード全部)

#!/usr/bin/perl
use strict;
use warnings;

use File::Basename;
use File::Path;
use File::Copy;

my $export = 'export'; # 該当するファイルのコピー先ディレクトリ名
my $datetime = &get_datetime_string;
my $ver_l = '';   # 比較対象のバージョン1
my $ver_r = '';   # 比較対象のバージョン2

# 比較バージョンの取得
if( @ARGV == 1 ){
    $ver_l = shift @ARGV;
}
elsif( @ARGV == 2 ){
    $ver_l = shift @ARGV;
    $ver_r = shift @ARGV;
}
elsif( @ARGV >= 3 ){
    die "3 arguments or more cannot be specified.\n"
}

# 指定した条件で検索
# 検索結果を配列に格納
my @files = split(/\n/, `git diff --name-only $ver_l $ver_r`);

foreach (@files)
{
    # ファイルコピー用のディレクトリを作成
    # (File::Basename,File::Path)
    my $dirname = $export . '/' . $datetime . '/' . dirname($_);
    &make_directory($dirname);

    # ファイルをコピー(File::Copy)
    copy $_, $dirname;
}

##################################################
# [sub] Get DateTime(YYYYMMDDhhmmss).
##################################################
sub get_datetime_string {
    my $time = time();
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
    my $result = sprintf("%04d%02d%02d%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
    return $result;
}

##################################################
# [sub] Create File Copy Directory.
##################################################
sub make_directory
{
    (my $dirname) = @_;

    # 既にディレクトリが存在しているか?
    if (! -d $dirname ){

        # 「mkpath」が失敗した場合、例外が発生するので「eval」で囲む
        eval{
            # ディレクトリの作成(File::Path)
            mkpath($dirname);
        };
        # 「mkpath」の例外の内容は「$@」にセットされる
        if( $@ ){
            die "$dirname creace err -> $@";
        }
    }
}

今回参考にしたページ

作業ツリーと任意のコミットの差分を確認する(Git入門) - サンプルコードによるPerl入門
http://d.hatena.ne.jp/perlcodesample/20090708/1246679588

.gitignoreで不要なファイル ディレクトリを無視する - 苔の一念岩をも通す ~目指せいつかはプログラマ~
http://d.hatena.ne.jp/stfuawsc/20090321/1237611478

[ヅラド] Perl で現在日時のYYYYMMDD文字列を生成する
http://www.nilab.info/zurazure2/000314.html