+ my $id=shift; # 0 = mtime ; 1 = ctime
+
+ if (! keys %time_cache) {
+ my $date;
+ foreach my $line (run_or_die('git', 'log',
+ '--pretty=format:%at',
+ '--name-only', '--relative')) {
+ if (! defined $date && $line =~ /^(\d+)$/) {
+ $date=$line;
+ }
+ elsif (! length $line) {
+ $date=undef;
+ }
+ else {
+ my $f=decode_git_file($line);
+
+ if (! $time_cache{$f}) {
+ $time_cache{$f}[0]=$date; # mtime
+ }
+ $time_cache{$f}[1]=$date; # ctime
+ }
+ }
+ }
+
+ return exists $time_cache{$file} ? $time_cache{$file}[$id] : 0;
+}
+
+}
+
+sub rcs_getctime ($) {
+ my $file=shift;
+
+ return findtimes($file, 1);
+}
+
+sub rcs_getmtime ($) {
+ my $file=shift;
+
+ return findtimes($file, 0);
+}
+
+{
+my $ret;
+sub git_find_root {
+ # The wiki may not be the only thing in the git repo.
+ # Determine if it is in a subdirectory by examining the srcdir,
+ # and its parents, looking for the .git directory.
+
+ return @$ret if defined $ret;
+
+ my $subdir="";
+ my $dir=$config{srcdir};
+ while (! -d "$dir/.git") {
+ $subdir=IkiWiki::basename($dir)."/".$subdir;
+ $dir=IkiWiki::dirname($dir);
+ if (! length $dir) {
+ error("cannot determine root of git repo");
+ }
+ }
+
+ $ret=[$subdir, $dir];
+ return @$ret;
+}
+
+}
+
+sub git_parse_changes {
+ my $reverted = shift;
+ my @changes = @_;
+
+ my ($subdir, $rootdir) = git_find_root();
+ my @rets;
+ foreach my $ci (@changes) {
+ foreach my $detail (@{ $ci->{'details'} }) {
+ my $file = $detail->{'file'};
+
+ # check that all changed files are in the subdir
+ if (length $subdir &&
+ ! ($file =~ s/^\Q$subdir\E//)) {
+ error sprintf(gettext("you are not allowed to change %s"), $file);
+ }
+
+ my ($action, $mode, $path);
+ if ($detail->{'status'} =~ /^[M]+\d*$/) {
+ $action="change";
+ $mode=$detail->{'mode_to'};
+ }
+ elsif ($detail->{'status'} =~ /^[AM]+\d*$/) {
+ $action= $reverted ? "remove" : "add";
+ $mode=$detail->{'mode_to'};
+ }
+ elsif ($detail->{'status'} =~ /^[DAM]+\d*/) {
+ $action= $reverted ? "add" : "remove";
+ $mode=$detail->{'mode_from'};
+ }
+ else {
+ error "unknown status ".$detail->{'status'};
+ }