X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/013b99aeb266383b8d889d2add69f8685bdcfc57..a4e4cacc2d1e2c314f8b6791c3a212fc3ff62c3f:/IkiWiki/Plugin/git.pm diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm index 0140768ff..021ee726c 100644 --- a/IkiWiki/Plugin/git.pm +++ b/IkiWiki/Plugin/git.pm @@ -425,6 +425,16 @@ sub parse_diff_tree ($) { } shift @{ $dt_ref } if $dt_ref->[0] =~ /^$/; + $ci{details} = [parse_changed_files($dt_ref)]; + + return \%ci; +} + +sub parse_changed_files { + my $dt_ref = shift; + + my @files; + # Modified files. while (my $line = shift @{ $dt_ref }) { if ($line =~ m{^ @@ -442,7 +452,7 @@ sub parse_diff_tree ($) { my $status = shift(@tmp); if (length $file) { - push @{ $ci{'details'} }, { + push @files, { 'file' => decode_git_file($file), 'sha1_from' => $sha1_from[0], 'sha1_to' => $sha1_to, @@ -456,7 +466,7 @@ sub parse_diff_tree ($) { last; } - return \%ci; + return @files; } sub git_commit_info ($;$) { @@ -469,7 +479,7 @@ sub git_commit_info ($;$) { my @raw_lines = run_or_die('git', 'log', @opts, '--pretty=raw', '--raw', '--abbrev=40', '--always', '-c', - '-r', $sha1, '--', '.'); + '-r', $sha1, '--no-renames', '--', '.'); my @ci; while (my $parsed = parse_diff_tree(\@raw_lines)) { @@ -567,7 +577,10 @@ sub rcs_commit (@) { # Check to see if the page has been changed by someone else since # rcs_prepedit was called. my $cur = git_sha1_file($params{file}); - my ($prev) = $params{token} =~ /^($sha1_pattern)$/; # untaint + my $prev; + if (defined $params{token}) { + ($prev) = $params{token} =~ /^($sha1_pattern)$/; # untaint + } if (defined $cur && defined $prev && $cur ne $prev) { my $conflict = merge_past($prev, $params{file}, $dummy_commit_msg); @@ -597,18 +610,24 @@ sub rcs_commit_helper (@) { elsif (defined $params{session}->remote_addr()) { $u=$params{session}->remote_addr(); } - if (defined $u) { + if (length $u) { $u=encode_utf8($u); $ENV{GIT_AUTHOR_NAME}=$u; } + else { + $u = 'anonymous'; + } if (defined $params{session}->param("nickname")) { $u=encode_utf8($params{session}->param("nickname")); $u=~s/\s+/_/g; $u=~s/[^-_0-9[:alnum:]]+//g; } - if (defined $u) { + if (length $u) { $ENV{GIT_AUTHOR_EMAIL}="$u\@web"; } + else { + $ENV{GIT_AUTHOR_EMAIL}='anonymous@web'; + } } ensure_committer(); @@ -654,7 +673,7 @@ sub rcs_add ($) { ensure_committer(); - run_or_cry('git', 'add', $file); + run_or_cry('git', 'add', '--', $file); } sub rcs_remove ($) { @@ -664,7 +683,7 @@ sub rcs_remove ($) { ensure_committer(); - run_or_cry('git', 'rm', '-f', $file); + run_or_cry('git', 'rm', '-f', '--', $file); } sub rcs_rename ($$) { @@ -672,7 +691,7 @@ sub rcs_rename ($$) { ensure_committer(); - run_or_cry('git', 'mv', '-f', $src, $dest); + run_or_cry('git', 'mv', '-f', '--', $src, $dest); } sub rcs_recentchanges ($) { @@ -946,10 +965,14 @@ sub rcs_preprevert ($) { my $rev=shift; my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint + my @undo; # undo stack for cleanup in case of an error + + ensure_committer(); + # Examine changes from root of git repo, not from any subdir, # in order to see all changes. my ($subdir, $rootdir) = git_find_root(); - in_git_dir($rootdir, sub { + return in_git_dir($rootdir, sub { my @commits=git_commit_info($sha1, 1); if (! @commits) { @@ -962,7 +985,68 @@ sub rcs_preprevert ($) { error gettext("you are not allowed to revert a merge"); } + # Due to the presence of rename-detection, we cannot actually + # see what will happen in a revert without trying it. + # But we can guess, which is enough to rule out most changes + # that we won't allow reverting. git_parse_changes(1, @commits); + + my $failure; + my @ret; + # If it looks OK, do it for real, on a branch. + eval { + IkiWiki::disable_commit_hook(); + push @undo, sub { + IkiWiki::enable_commit_hook(); + }; + my $branch = "ikiwiki_revert_${sha1}"; # supposed to be unique + + push @undo, sub { + run_or_cry('git', 'branch', '-D', $branch) if $failure; + }; + if (run_or_non('git', 'rev-parse', '--quiet', '--verify', $branch)) { + run_or_non('git', 'branch', '-D', $branch); + } + run_or_die('git', 'branch', $branch, $config{gitmaster_branch}); + + push @undo, sub { + if (!run_or_cry('git', 'checkout', '--quiet', $config{gitmaster_branch})) { + run_or_cry('git', 'checkout','-f', '--quiet', $config{gitmaster_branch}); + } + }; + run_or_die('git', 'checkout', '--quiet', $branch); + + run_or_die('git', 'revert', '--no-commit', $sha1); + run_or_non('git', 'commit', '-m', "revert $sha1", '-a'); + + # Re-switch to master. + run_or_die('git', 'checkout', '--quiet', $config{gitmaster_branch}); + + my @raw_lines; + @raw_lines = run_or_die('git', 'diff', '--pretty=raw', + '--raw', '--abbrev=40', '--always', '--no-renames', + "ikiwiki_revert_${sha1}.."); + + my $ci = { + details => [parse_changed_files(\@raw_lines)], + }; + + @ret = git_parse_changes(0, $ci); + }; + $failure = $@; + + # Process undo stack (in reverse order). By policy cleanup + # actions should normally print a warning on failure. + while (my $handle = pop @undo) { + $handle->(); + } + + if ($failure) { + my $message = sprintf(gettext("Failed to revert commit %s"), $sha1); + error("$message\n$failure\n"); + } + + return @ret; }); } @@ -973,11 +1057,11 @@ sub rcs_revert ($) { ensure_committer(); - if (run_or_non('git', 'revert', '--no-commit', $sha1)) { + if (run_or_non('git', 'merge', '--ff-only', "ikiwiki_revert_$sha1")) { return undef; } else { - run_or_die('git', 'reset', '--hard'); + run_or_non('git', 'branch', '-D', "ikiwiki_revert_$sha1"); return sprintf(gettext("Failed to revert commit %s"), $sha1); } }