#!/usr/bin/perl

use strict;

my $ret;
my $i;
my @wanted = ();
my $argc = scalar(@ARGV);
my $commit;
my $stop;
my $file_list = "";

sub print_usage() {
    print STDERR "usage: file-changes [-c commit] [-s stop commit] file_list\n";
    exit(1);
}

sub print_commit($) {
    my ($c) = @_;
    print "cat-file commit $c\n";
    open(COMMIT, "cat-file commit $c|") || die "cat-file $c failed";
    while(<COMMIT>) {
	print "    $_";
    }
    close(COMMIT);
    if ($? && ($ret = $? >> 8)) {
	die "cat-file failed with $ret";
    }
    print "\n";
}

sub test_diff($$) {
    my ($a, $b) = @_;
    my $matched = 0;
    open(DT, "diff-tree -r $a $b $file_list|") || die "diff-tree failed";
    while(<DT>) {
        chomp;
	my @words = split;
	my $file = $words[3];
	# if the filename has whitespace, suck it in
	if (scalar(@words) > 4) {
	    if (m/$file(.*)/) {
	        $file .= $1;
	    }
	}
	foreach my $m (@wanted) {
	    if ($file =~ m/^$m/) {
		if (!$matched) {
		    print "diff-tree -r $a $b\n";
		}
		print "$words[2] $file\n";
		$matched = 1;
	    }
	}
    }
    close(DT);
    if ($? && ($ret = $? >> 8)) {
	die "diff-tree failed with $ret";
    }
    return $matched;
}

if ($argc < 1) {
    print_usage();
}

for ($i = 0 ; $i < $argc ; $i++)  {
    if ($ARGV[$i] eq "-c") {
    	if ($i == $argc - 1) {
	    print_usage();
	}
	$commit = $ARGV[++$i];
    } elsif ($ARGV[$i] eq "-s") {
    	if ($i == $argc - 1) {
	    print_usage();
	}
	$stop = $ARGV[++$i];
    } else {
	push @wanted, $ARGV[$i];
	$file_list .= "$ARGV[$i] ";
    }
}

if (!defined($commit)) {
    $commit = `commit-id`;
    if ($?) {
    	print STDERR "commit-id failed, try using -c to specify a commit\n";
	exit(1);
    }
    chomp $commit;
}

open(RL, "rev-list $commit|") || die "rev-list failed";
while(<RL>) {
    chomp;
    my $cur = $_;
    my $matched = 0;
    open(PARENT, "cat-file commit $cur|") || die "cat-file failed";
    while(<PARENT>) {
        chomp;
	my @words = split;
	if ($words[0] eq "tree") {
	    next;
	} elsif ($words[0] ne "parent") {
	    last;
	}
	$matched += test_diff($words[1], $cur);
    }
    close(PARENT);
    if ($? && ($ret = $? >> 8)) {
        die "cat-file failed with $ret";
    }
    if ($matched) {
        print_commit($cur);
    }
    if ($cur eq $stop) {
        last;
    }
}
close(RL);

if ($? && ($ret = $? >> 8)) {
    die "rev-list failed with $ret";
}

