sub run_or_cry ($@) { safe_git(sub { warn @_ }, undef, @_) }
sub run_or_non ($@) { safe_git(undef, undef, @_) }
+sub ensure_committer {
+ if (! length $ENV{GIT_AUTHOR_NAME} || ! length $ENV{GIT_COMMITTER_NAME}) {
+ my $name = join('', run_or_non("git", "config", "user.name"));
+ if (! length $name) {
+ run_or_die("git", "config", "user.name", "IkiWiki");
+ }
+ }
+
+ if (! length $ENV{GIT_AUTHOR_EMAIL} || ! length $ENV{GIT_COMMITTER_EMAIL}) {
+ my $email = join('', run_or_non("git", "config", "user.email"));
+ if (! length $email) {
+ run_or_die("git", "config", "user.email", "ikiwiki.info");
+ }
+ }
+}
sub merge_past ($$$) {
# Unlike with Subversion, Git cannot make a 'svn merge -rN:M file'.
my @undo; # undo stack for cleanup in case of an error
my $conflict; # file content with conflict markers
+ ensure_committer();
+
eval {
# Hide local changes from Git by renaming the modified file.
# Relative paths must be converted to absolute for renaming.
sub rcs_find_changes ($) {
my $oldrev=shift;
+ # Note that git log will sometimes show files being added that
+ # don't exist. Particularly, git merge -s ours can result in a
+ # merge commit where some files were not really added.
+ # This is why the code below verifies that the files really
+ # exist.
my @raw_lines = run_or_die('git', 'log',
'--pretty=raw', '--raw', '--abbrev=40', '--always', '-c',
'--no-renames', , '--reverse',
foreach my $i (@{$ci->{details}}) {
my $file=$i->{file};
if ($i->{sha1_to} eq $nullsha) {
- delete $changed{$file};
- $deleted{$file}=1;
+ if (! -e "$config{srcdir}/$file") {
+ delete $changed{$file};
+ $deleted{$file}=1;
+ }
}
else {
- delete $deleted{$file};
- $changed{$file}=1;
+ if (-e "$config{srcdir}/$file") {
+ delete $deleted{$file};
+ $changed{$file}=1;
+ }
}
}
}
sub rcs_update () {
# Update working directory.
+ ensure_committer();
+
if (length $config{gitorigin_branch}) {
run_or_cry('git', 'pull', '--prune', $config{gitorigin_branch});
}
$u=$params{session}->remote_addr();
}
if (defined $u) {
- $u=encode_utf8($u);
+ $u=encode_utf8(IkiWiki::cloak($u));
$ENV{GIT_AUTHOR_NAME}=$u;
}
if (defined $params{session}->param("nickname")) {
}
}
+ ensure_committer();
+
$params{message} = IkiWiki::possibly_foolish_untaint($params{message});
my @opts;
if ($params{message} !~ /\S/) {
# So we should ignore its exit status (hence run_or_non).
if (run_or_non('git', 'commit', '-m', $params{message}, '-q', @opts)) {
if (length $config{gitorigin_branch}) {
- run_or_cry('git', 'push', $config{gitorigin_branch});
+ run_or_cry('git', 'push', $config{gitorigin_branch}, $config{gitmaster_branch});
}
}
my ($file) = @_;
- run_or_cry('git', 'add', $file);
+ ensure_committer();
+
+ run_or_cry('git', 'add', '--', $file);
}
sub rcs_remove ($) {
my ($file) = @_;
- run_or_cry('git', 'rm', '-f', $file);
+ ensure_committer();
+
+ run_or_cry('git', 'rm', '-f', '--', $file);
}
sub rcs_rename ($$) {
my ($src, $dest) = @_;
- run_or_cry('git', 'mv', '-f', $src, $dest);
+ ensure_committer();
+
+ run_or_cry('git', 'mv', '-f', '--', $src, $dest);
}
sub rcs_recentchanges ($) {
my @pages;
foreach my $detail (@{ $ci->{'details'} }) {
my $file = $detail->{'file'};
- my $efile = uri_escape_utf8($file);
+ my $efile = join('/',
+ map { uri_escape_utf8($_) } split('/', $file)
+ );
my $diffurl = defined $config{'diffurl'} ? $config{'diffurl'} : "";
$diffurl =~ s/\[\[file\]\]/$efile/go;
my $rev = shift;
my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint
+ ensure_committer();
+
if (run_or_non('git', 'revert', '--no-commit', $sha1)) {
return undef;
}