* Add support for using git instead of subversion as the RCS backend,
tremendous thanks to Recai Oktaş for this. * Doc updates for git.master
parent
c0f8126143
commit
30afedcfe2
|
@ -0,0 +1,491 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
# Git backend for IkiWiki.
|
||||||
|
# Copyright 2006 Recai Oktaş <roktas@debian.org>
|
||||||
|
#
|
||||||
|
# Licensed under the same terms as IkiWiki.
|
||||||
|
|
||||||
|
use warnings;
|
||||||
|
use strict;
|
||||||
|
use IkiWiki;
|
||||||
|
|
||||||
|
package IkiWiki;
|
||||||
|
|
||||||
|
my $origin_branch = 'origin'; # Git ref for main repository
|
||||||
|
my $master_branch = 'master'; # Working branch
|
||||||
|
my $sha1_pattern = qr/[0-9a-fA-F]{40}/; # pattern to validate Git sha1sums
|
||||||
|
my $dummy_commit_msg = 'dummy commit'; # will be used in all dummy commits
|
||||||
|
# and skipped in recent changes list
|
||||||
|
my $web_commit_msg = qr/^web commit by (\w+):?(.*)/; # pattern for web commits
|
||||||
|
|
||||||
|
sub _safe_git (&@) { #{{{
|
||||||
|
# Start a child process safely without resorting /bin/sh.
|
||||||
|
# Return command output or success state (in scalar context).
|
||||||
|
|
||||||
|
my ($error_handler, @cmdline) = @_;
|
||||||
|
|
||||||
|
my $pid = open my $OUT, "-|";
|
||||||
|
|
||||||
|
error("Cannot fork: $!") if !defined $pid;
|
||||||
|
|
||||||
|
if (!$pid) {
|
||||||
|
# In child.
|
||||||
|
open STDERR, ">&STDOUT"
|
||||||
|
or error("Cannot dup STDOUT: $!");
|
||||||
|
# Git commands want to be in wc.
|
||||||
|
chdir $config{srcdir}
|
||||||
|
or error("Cannot chdir to $config{srcdir}: $!");
|
||||||
|
exec @cmdline or error("Cannot exec '@cmdline': $!");
|
||||||
|
}
|
||||||
|
# In parent.
|
||||||
|
|
||||||
|
my @lines;
|
||||||
|
while (<$OUT>) {
|
||||||
|
chomp;
|
||||||
|
push @lines, $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
close $OUT;
|
||||||
|
|
||||||
|
($error_handler || sub { })->("'@cmdline' failed: $!") if $?;
|
||||||
|
|
||||||
|
return wantarray ? @lines : ($? == 0);
|
||||||
|
}
|
||||||
|
# Convenient wrappers.
|
||||||
|
sub run_or_die ($@) { _safe_git(\&IkiWiki::error, @_) }
|
||||||
|
sub run_or_cry ($@) { _safe_git(sub { warn @_ }, @_) }
|
||||||
|
sub run_or_non ($@) { _safe_git(undef, @_) }
|
||||||
|
#}}}
|
||||||
|
|
||||||
|
sub _merge_past ($$$) { #{{{
|
||||||
|
# Unlike with Subversion, Git cannot make a 'svn merge -rN:M file'.
|
||||||
|
# Git merge commands work with the committed changes, except in the
|
||||||
|
# implicit case of '-m' of git-checkout(1). So we should invent a
|
||||||
|
# kludge here. In principle, we need to create a throw-away branch
|
||||||
|
# in preparing for the merge itself. Since branches are cheap (and
|
||||||
|
# branching is fast), this shouldn't cost high.
|
||||||
|
#
|
||||||
|
# The main problem is the presence of _uncommitted_ local changes. One
|
||||||
|
# possible approach to get rid of this situation could be that we first
|
||||||
|
# make a temporary commit in the master branch and later restore the
|
||||||
|
# initial state (this is possible since Git has the ability to undo a
|
||||||
|
# commit, i.e. 'git-reset --soft HEAD^'). The method can be summarized
|
||||||
|
# as follows:
|
||||||
|
#
|
||||||
|
# - create a diff of HEAD:current-sha1
|
||||||
|
# - dummy commit
|
||||||
|
# - create a dummy branch and switch to it
|
||||||
|
# - rewind to past (reset --hard to the current-sha1)
|
||||||
|
# - apply the diff and commit
|
||||||
|
# - switch to master and do the merge with the dummy branch
|
||||||
|
# - make a soft reset (undo the last commit of master)
|
||||||
|
#
|
||||||
|
# The above method has some drawbacks: (1) it needs a redundant commit
|
||||||
|
# just to get rid of local changes, (2) somewhat slow because of the
|
||||||
|
# required system forks. Until someone points a more straight method
|
||||||
|
# (which I would be grateful) I have implemented an alternative method.
|
||||||
|
# In this approach, we hide all the modified files from Git by renaming
|
||||||
|
# them (using the 'rename' builtin) and later restore those files in
|
||||||
|
# the throw-away branch (that is, we put the files themselves instead
|
||||||
|
# of applying a patch).
|
||||||
|
|
||||||
|
my ($sha1, $file, $message) = @_;
|
||||||
|
|
||||||
|
my @undo; # undo stack for cleanup in case of an error
|
||||||
|
my $conflict; # file content with conflict markers
|
||||||
|
|
||||||
|
eval {
|
||||||
|
# Hide local changes from Git by renaming the modified file.
|
||||||
|
# Relative paths must be converted to absolute for renaming.
|
||||||
|
my ($target, $hidden) = (
|
||||||
|
"$config{srcdir}/${file}", "$config{srcdir}/${file}.${sha1}"
|
||||||
|
);
|
||||||
|
rename($target, $hidden)
|
||||||
|
or error("rename '$target' to '$hidden' failed: $!");
|
||||||
|
# Ensure to restore the renamed file on error.
|
||||||
|
push @undo, sub {
|
||||||
|
return if ! -e "$hidden"; # already renamed
|
||||||
|
rename($hidden, $target)
|
||||||
|
or debug("rename '$hidden' to '$target' failed: $!");
|
||||||
|
};
|
||||||
|
|
||||||
|
my $branch = "throw_away_${sha1}"; # supposed to be unique
|
||||||
|
|
||||||
|
# Create a throw-away branch and rewind backward.
|
||||||
|
push @undo, sub { run_or_cry('git-branch', '-D', $branch) };
|
||||||
|
run_or_die('git-branch', $branch, $sha1);
|
||||||
|
|
||||||
|
# Switch to throw-away branch for the merge operation.
|
||||||
|
push @undo, sub {
|
||||||
|
if (!run_or_cry('git-checkout', $master_branch)) {
|
||||||
|
run_or_cry('git-checkout','-f',$master_branch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
run_or_die('git-checkout', $branch);
|
||||||
|
|
||||||
|
# Put the modified file in _this_ branch.
|
||||||
|
rename($hidden, $target)
|
||||||
|
or error("rename '$hidden' to '$target' failed: $!");
|
||||||
|
|
||||||
|
# _Silently_ commit all modifications in the current branch.
|
||||||
|
run_or_non('git-commit', '-m', $message, '-a');
|
||||||
|
# ... and re-switch to master.
|
||||||
|
run_or_die('git-checkout', $master_branch);
|
||||||
|
|
||||||
|
# Attempt to merge without complaining.
|
||||||
|
if (!run_or_non('git-pull', '--no-commit', '.', $branch)) {
|
||||||
|
$conflict = readfile($target);
|
||||||
|
run_or_die('git-reset', '--hard');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
my $failure = $@;
|
||||||
|
|
||||||
|
# Process undo stack (in reverse order). By policy cleanup
|
||||||
|
# actions should normally print a warning on failure.
|
||||||
|
while (my $handle = pop @undo) {
|
||||||
|
$handle->();
|
||||||
|
}
|
||||||
|
|
||||||
|
error("Git merge failed!\n$failure\n") if $failure;
|
||||||
|
|
||||||
|
return $conflict;
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub _parse_diff_tree (@) { #{{{
|
||||||
|
# Parse the raw diff tree chunk and return the info hash.
|
||||||
|
# See git-diff-tree(1) for the syntax.
|
||||||
|
|
||||||
|
my ($dt_ref) = @_;
|
||||||
|
|
||||||
|
# End of stream?
|
||||||
|
return if !defined @{ $dt_ref } || !length @{ $dt_ref }[0];
|
||||||
|
|
||||||
|
my %ci;
|
||||||
|
|
||||||
|
# Header line.
|
||||||
|
HEADER: while (my $line = shift @{ $dt_ref }) {
|
||||||
|
return if $line !~ m/^diff-tree (\S+)/;
|
||||||
|
|
||||||
|
my $sha1 = $1;
|
||||||
|
$ci{'sha1'} = $sha1;
|
||||||
|
last HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Identification lines for the commit.
|
||||||
|
IDENT: while (my $line = shift @{ $dt_ref }) {
|
||||||
|
# Regexps are semi-stolen from gitweb.cgi.
|
||||||
|
if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
|
||||||
|
$ci{'tree'} = $1;
|
||||||
|
} elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
|
||||||
|
# XXX: collecting in reverse order
|
||||||
|
push @{ $ci{'parents'} }, $1;
|
||||||
|
} elsif ($line =~ m/^(author|committer) (.*) ([0-9]+) (.*)$/) {
|
||||||
|
my ($who, $name, $epoch, $tz) =
|
||||||
|
($1, $2, $3, $4 );
|
||||||
|
|
||||||
|
$ci{ $who } = $name;
|
||||||
|
$ci{ "${who}_epoch" } = $epoch;
|
||||||
|
$ci{ "${who}_tz" } = $tz;
|
||||||
|
|
||||||
|
if ($name =~ m/^([^<]+) <([^@]+)/) {
|
||||||
|
my ($fullname, $username) = ($1, $2);
|
||||||
|
$ci{"${who}_fullname"} = $fullname;
|
||||||
|
$ci{"${who}_username"} = $username;
|
||||||
|
} else {
|
||||||
|
$ci{"${who}_fullname"} =
|
||||||
|
$ci{"${who}_username"} = $name;
|
||||||
|
}
|
||||||
|
} elsif ($line =~ m/^$/) {
|
||||||
|
# Trailing empty line signals next section.
|
||||||
|
last IDENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error("No 'tree' or 'parents' seen in diff-tree output")
|
||||||
|
if !defined $ci{'tree'} || !defined $ci{'parents'};
|
||||||
|
|
||||||
|
$ci{'parent'} = @{ $ci{'parents'} }[0];
|
||||||
|
|
||||||
|
# Commit message.
|
||||||
|
COMMENT: while (my $line = shift @{ $dt_ref }) {
|
||||||
|
if ($line =~ m/^$/) {
|
||||||
|
# Trailing empty line signals next section.
|
||||||
|
last COMMENT;
|
||||||
|
};
|
||||||
|
$line =~ s/^ //;
|
||||||
|
push @{ $ci{'comment'} }, $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Modified files.
|
||||||
|
FILE: while (my $line = shift @{ $dt_ref }) {
|
||||||
|
if ($line =~ m{^
|
||||||
|
:([0-7]{6})[ ] # from mode
|
||||||
|
([0-7]{6})[ ] # to mode
|
||||||
|
([0-9a-fA-F]{40})[ ] # from sha1
|
||||||
|
([0-9a-fA-F]{40})[ ] # to sha1
|
||||||
|
(.) # status
|
||||||
|
([0-9]{0,3})\t # similarity
|
||||||
|
(.*) # file
|
||||||
|
$}xo) {
|
||||||
|
my ($sha1_from, $sha1_to, $file) =
|
||||||
|
($3, $4, $7 );
|
||||||
|
|
||||||
|
if ($file =~ m/^"(.*)"$/) {
|
||||||
|
($file=$1) =~ s/\\([0-7]{1,3})/chr(oct($1))/eg;
|
||||||
|
}
|
||||||
|
if (length $file) {
|
||||||
|
push @{ $ci{'details'} }, {
|
||||||
|
'file' => $file,
|
||||||
|
'sha1_from' => $sha1_from,
|
||||||
|
'sha1_to' => $sha1_to,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
next FILE;
|
||||||
|
};
|
||||||
|
last FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
error("No detail in diff-tree output") if !defined $ci{'details'};
|
||||||
|
|
||||||
|
return \%ci;
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub git_commit_info (;$$) { #{{{
|
||||||
|
# Return an array of commit info hashes of num commits (default: 1)
|
||||||
|
# starting from the given sha1sum (default: HEAD).
|
||||||
|
|
||||||
|
my ($sha1, $num) = @_;
|
||||||
|
|
||||||
|
$num ||= 1;
|
||||||
|
|
||||||
|
my @raw_lines =
|
||||||
|
run_or_die(qq{git-rev-list --max-count=$num $sha1 |
|
||||||
|
git-diff-tree --stdin --pretty=raw -c -M -r});
|
||||||
|
|
||||||
|
my @ci;
|
||||||
|
while (my $parsed = _parse_diff_tree(\@raw_lines)) {
|
||||||
|
push @ci, $parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wantarray ? @ci : $ci[0];
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub git_sha1 (;$) { #{{{
|
||||||
|
# Return head sha1sum (of given file).
|
||||||
|
|
||||||
|
my $file = shift || q{--};
|
||||||
|
|
||||||
|
my ($sha1) = run_or_die('git-rev-list', '--max-count=1', 'HEAD', $file);
|
||||||
|
($sha1) = $sha1 =~ m/($sha1_pattern)/; # sha1sum is untainted now
|
||||||
|
debug("Empty sha1sum for '$file'.") if !length $sha1;
|
||||||
|
return $sha1;
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_update () { #{{{
|
||||||
|
# Update working directory.
|
||||||
|
|
||||||
|
run_or_cry('git-pull', $origin_branch);
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_prepedit ($) { #{{{
|
||||||
|
# Return the commit sha1sum of the file when editing begins.
|
||||||
|
# This will be later used in rcs_commit if a merge is required.
|
||||||
|
|
||||||
|
my ($file) = @_;
|
||||||
|
|
||||||
|
my $sha1 = git_sha1($file);
|
||||||
|
return defined $sha1 ? $sha1 : q{};
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_commit ($$$) { #{{{
|
||||||
|
# Try to commit the page; returns undef on _success_ and
|
||||||
|
# a version of the page with the rcs's conflict markers on
|
||||||
|
# failure.
|
||||||
|
|
||||||
|
my ($file, $message, $rcstoken) = @_;
|
||||||
|
|
||||||
|
# XXX: Wiki directory is in the unlocked state when starting this
|
||||||
|
# action. But it takes time for a Git process to finish its job
|
||||||
|
# (especially if a merge required), so we must re-lock to prevent
|
||||||
|
# race conditions. Only when the time of the real commit action
|
||||||
|
# (i.e. git-push(1)) comes, we'll unlock the directory.
|
||||||
|
lockwiki();
|
||||||
|
|
||||||
|
# Check to see if the page has been changed by someone else since
|
||||||
|
# rcs_prepedit was called.
|
||||||
|
my $cur = git_sha1($file);
|
||||||
|
my ($prev) = $rcstoken =~ m/^$sha1_pattern$/; # untaint
|
||||||
|
|
||||||
|
if (defined $cur && defined $prev && $cur ne $prev) {
|
||||||
|
my $conflict = _merge_past($prev, $file, $dummy_commit_msg);
|
||||||
|
return $conflict if defined $conflict;
|
||||||
|
}
|
||||||
|
|
||||||
|
# git-commit(1) returns non-zero if file has not been really changed.
|
||||||
|
# so we should ignore its exit status (hence run_or_non).
|
||||||
|
if (run_or_non('git-commit', '-m', $message, '-i', $file)) {
|
||||||
|
unlockwiki();
|
||||||
|
run_or_cry('git-push', $origin_branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef; # success
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_add ($) { # {{{
|
||||||
|
# Add file to archive.
|
||||||
|
|
||||||
|
my ($file) = @_;
|
||||||
|
|
||||||
|
run_or_cry('git-add', $file);
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_recentchanges ($) { #{{{
|
||||||
|
# List of recent changes.
|
||||||
|
|
||||||
|
my ($num) = @_;
|
||||||
|
|
||||||
|
eval q{use CGI 'escapeHTML'};
|
||||||
|
eval q{use Date::Parse};
|
||||||
|
eval q{use Time::Duration};
|
||||||
|
|
||||||
|
my ($sha1, $type, $when, $diffurl, $user, @pages, @message, @rets);
|
||||||
|
INFO: foreach my $ci (git_commit_info('HEAD', $num)) {
|
||||||
|
my $title = @{ $ci->{'comment'} }[0];
|
||||||
|
|
||||||
|
# Skip redundant commits.
|
||||||
|
next INFO if ($title eq $dummy_commit_msg);
|
||||||
|
|
||||||
|
$sha1 = $ci->{'sha1'};
|
||||||
|
$type = "web";
|
||||||
|
$when = concise(ago(time - $ci->{'author_epoch'}));
|
||||||
|
|
||||||
|
foreach my $bit (@{ $ci->{'details'} }) {
|
||||||
|
my $diffurl = $config{'diffurl'};
|
||||||
|
my $file = $bit->{'file'};
|
||||||
|
|
||||||
|
$diffurl =~ s/\[\[file\]\]/$file/go;
|
||||||
|
$diffurl =~ s/\[\[sha1_parent\]\]/$ci->{'parent'}/go;
|
||||||
|
$diffurl =~ s/\[\[sha1_from\]\]/$bit->{'sha1_from'}/go;
|
||||||
|
$diffurl =~ s/\[\[sha1_to\]\]/$bit->{'sha1_to'}/go;
|
||||||
|
|
||||||
|
push @pages, {
|
||||||
|
link => htmllink("", pagename($file), 1),
|
||||||
|
diffurl => $diffurl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
push @message, { line => escapeHTML($title) };
|
||||||
|
|
||||||
|
if (defined $message[0] &&
|
||||||
|
$message[0]->{line} =~ m/$web_commit_msg/) {
|
||||||
|
$user = "$1";
|
||||||
|
$message[0]->{line} = $2;
|
||||||
|
} else {
|
||||||
|
$type ="git";
|
||||||
|
$user = $ci->{'author_username'};
|
||||||
|
}
|
||||||
|
|
||||||
|
push @rets, {
|
||||||
|
rev => $sha1,
|
||||||
|
user => htmllink("", $user, 1),
|
||||||
|
committype => $type,
|
||||||
|
when => $when,
|
||||||
|
message => [@message],
|
||||||
|
pages => [@pages],
|
||||||
|
} if @pages;
|
||||||
|
|
||||||
|
$sha1 = $type = $when = $diffurl = $user = undef;
|
||||||
|
@pages = @message = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return @rets;
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_notify () { #{{{
|
||||||
|
# Send notification mail to subscribed users.
|
||||||
|
#
|
||||||
|
# In usual Git usage, hooks/update script is presumed to send
|
||||||
|
# notification mails (see git-receive-pack(1)). But we prefer
|
||||||
|
# hooks/post-update to support IkiWiki commits coming from a
|
||||||
|
# cloned repository (through command line) because post-update
|
||||||
|
# is called _after_ each ref in repository is updated (update
|
||||||
|
# hook is called _before_ the repository is updated). Since
|
||||||
|
# post-update hook does not accept command line arguments, we
|
||||||
|
# don't have an $ENV variable in this function.
|
||||||
|
#
|
||||||
|
# Here, we rely on a simple fact: we can extract all parts of the
|
||||||
|
# notification content by parsing the "HEAD" commit (which also
|
||||||
|
# triggers a refresh of IkiWiki pages) and we can obtain the diff
|
||||||
|
# by comparing HEAD and HEAD^ (the previous commit).
|
||||||
|
|
||||||
|
my $sha1 = 'HEAD'; # the commit which triggers this action
|
||||||
|
|
||||||
|
my $ci = git_commit_info($sha1);
|
||||||
|
if (!defined $ci) {
|
||||||
|
warn "Cannot parse info for '$sha1' commit";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @changed_pages = map { $_->{'file'} } @{ $ci->{'details'} };
|
||||||
|
|
||||||
|
my ($user, $message);
|
||||||
|
if (@{ $ci->{'comment'} }[0] =~ m/$web_commit_msg/) {
|
||||||
|
$user = "$1";
|
||||||
|
$message = $2;
|
||||||
|
} else {
|
||||||
|
$user = $ci->{'author_username'};
|
||||||
|
$message = join "\n", @{ $ci->{'comment'} };
|
||||||
|
}
|
||||||
|
|
||||||
|
require IkiWiki::UserInfo;
|
||||||
|
my @email_recipients = commit_notify_list($user, @changed_pages);
|
||||||
|
return if !@email_recipients;
|
||||||
|
|
||||||
|
# TODO: if a commit spans multiple pages, this will send
|
||||||
|
# subscribers a diff that might contain pages they did not
|
||||||
|
# sign up for. Should separate the diff per page and
|
||||||
|
# reassemble into one mail with just the pages subscribed to.
|
||||||
|
my $diff = join "\n", run_or_die('git-diff', "${sha1}^", $sha1);
|
||||||
|
|
||||||
|
my $subject = "$config{wikiname} update of ";
|
||||||
|
if (@changed_pages > 2) {
|
||||||
|
$subject .= "$changed_pages[0] $changed_pages[1] etc";
|
||||||
|
} else {
|
||||||
|
$subject .= join " ", @changed_pages;
|
||||||
|
}
|
||||||
|
$subject .= " by $user";
|
||||||
|
|
||||||
|
my $template = HTML::Template->new(
|
||||||
|
filename => "$config{templatedir}/notifymail.tmpl"
|
||||||
|
);
|
||||||
|
$template->param(
|
||||||
|
wikiname => $config{wikiname},
|
||||||
|
diff => $diff,
|
||||||
|
user => $user,
|
||||||
|
message => $message,
|
||||||
|
);
|
||||||
|
|
||||||
|
eval q{use Mail::Sendmail};
|
||||||
|
foreach my $email (@email_recipients) {
|
||||||
|
sendmail(
|
||||||
|
To => $email,
|
||||||
|
From => "$config{wikiname} <$config{adminemail}>",
|
||||||
|
Subject => $subject,
|
||||||
|
Message => $template->output,
|
||||||
|
) or error("Failed to send update notification mail: $!");
|
||||||
|
}
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
sub rcs_getctime ($) { #{{{
|
||||||
|
# Get the ctime of file.
|
||||||
|
|
||||||
|
my ($file) = @_;
|
||||||
|
|
||||||
|
my $sha1 = git_sha1($file);
|
||||||
|
my $ci = git_commit_info($sha1);
|
||||||
|
my $ctime = $ci->{'author_epoch'};
|
||||||
|
debug("ctime for '$file': ". localtime($ctime) . "\n");
|
||||||
|
|
||||||
|
return $ctime;
|
||||||
|
} #}}}
|
||||||
|
|
||||||
|
1
|
|
@ -23,8 +23,11 @@ ikiwiki (1.5) UNRELEASED; urgency=low
|
||||||
can be useful when using link-based globbing for page categorisation.
|
can be useful when using link-based globbing for page categorisation.
|
||||||
* Remove preprocessor directives from inlined pages.
|
* Remove preprocessor directives from inlined pages.
|
||||||
* Allow simple preprocessor directive values to be specified w/o quotes.
|
* Allow simple preprocessor directive values to be specified w/o quotes.
|
||||||
|
* Add support for using git instead of subversion as the RCS backend,
|
||||||
|
tremendous thanks to Recai Oktaş for this.
|
||||||
|
* Doc updates for git.
|
||||||
|
|
||||||
-- Joey Hess <joeyh@debian.org> Thu, 1 Jun 2006 23:47:43 -0400
|
-- Joey Hess <joeyh@debian.org> Fri, 2 Jun 2006 01:13:18 -0400
|
||||||
|
|
||||||
ikiwiki (1.4) unstable; urgency=low
|
ikiwiki (1.4) unstable; urgency=low
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,17 @@ Standards-Version: 3.7.2
|
||||||
Package: ikiwiki
|
Package: ikiwiki
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${perl:Depends}, markdown, libtimedate-perl, libhtml-template-perl, libhtml-scrubber-perl, libcgi-formbuilder-perl (>= 3.02.02), libtime-duration-perl, libcgi-session-perl, libmail-sendmail-perl, gcc | c-compiler, libc6-dev | libc-dev
|
Depends: ${perl:Depends}, markdown, libtimedate-perl, libhtml-template-perl, libhtml-scrubber-perl, libcgi-formbuilder-perl (>= 3.02.02), libtime-duration-perl, libcgi-session-perl, libmail-sendmail-perl, gcc | c-compiler, libc6-dev | libc-dev
|
||||||
Recommends: subversion, hyperestraier
|
Recommends: subversion | git-core, hyperestraier
|
||||||
Suggests: viewcvs
|
Suggests: viewcvs
|
||||||
Description: a wiki compiler
|
Description: a wiki compiler
|
||||||
ikiwiki converts a directory full of wiki pages into html pages suitable
|
ikiwiki converts a directory full of wiki pages into html pages suitable
|
||||||
for publishing on a website. Unlike many wikis, ikiwiki does not have its
|
for publishing on a website. Unlike many wikis, ikiwiki does not have its
|
||||||
own means of storing page history or its own markup language. Instead it
|
own means of storing page history or its own markup language. Instead it
|
||||||
uses subversion and markdown.
|
uses subversion (or git) and markdown.
|
||||||
.
|
.
|
||||||
ikiwiki implements all of the other standard features of a wiki, including
|
ikiwiki implements all of the other standard features of a wiki, including
|
||||||
web-based page editing, user registration and logins, a RecentChanges
|
web-based page editing, user registration and logins, a RecentChanges
|
||||||
page, BackLinks, search, Discussion pages, smart merging and conflict
|
page, BackLinks, search, Discussion pages, tags, smart merging and conflict
|
||||||
resolution, page locking, and commit emails.
|
resolution, page locking, and commit emails.
|
||||||
.
|
.
|
||||||
It also supports generating RSS feeds and blogging.
|
It also supports generating RSS feeds and blogging.
|
||||||
|
|
|
@ -76,9 +76,9 @@ off from R1.
|
||||||
(To be continued.)
|
(To be continued.)
|
||||||
|
|
||||||
|
|
||||||
## [[Git]] (not yet included)
|
## [[Git]]
|
||||||
|
|
||||||
A patch with full [Git](http://git.or.cz) support is at <http://people.debian.org/~roktas/patches/ikiwiki/git.patch>. Regarding the patch, Recai says:
|
Regarding the Git support, Recai says:
|
||||||
|
|
||||||
I have been testing it for the past few days and it seems satisfactory. I
|
I have been testing it for the past few days and it seems satisfactory. I
|
||||||
haven't observed any race condition regarding the concurrent blog commits
|
haven't observed any race condition regarding the concurrent blog commits
|
||||||
|
@ -90,35 +90,3 @@ bugs. It also has some drawbacks (especially wrt merge which was the hard
|
||||||
part). GIT doesn't have a similar functionality like 'svn merge -rOLD:NEW
|
part). GIT doesn't have a similar functionality like 'svn merge -rOLD:NEW
|
||||||
FILE' (please see the relevant comment in mergepast for more details), so I
|
FILE' (please see the relevant comment in mergepast for more details), so I
|
||||||
had to invent an ugly hack just for the purpose.
|
had to invent an ugly hack just for the purpose.
|
||||||
|
|
||||||
Some other notes:
|
|
||||||
|
|
||||||
- There are two separate helper packages in git.pm. To keep things self
|
|
||||||
confined, I haven't split it up.
|
|
||||||
|
|
||||||
- I've used a (mini) Debug.pm during the tests and made it a separate file
|
|
||||||
for the convenience of others. It relies on the "constant folding"
|
|
||||||
feature of Perl, so there shouldn't be a runtime penalty (at least this
|
|
||||||
is what the 'perl -MO=Deparse shows', haven't made a real benchmark).
|
|
||||||
|
|
||||||
- rcs_notify() has not been implemented yet (I have noticed it after I
|
|
||||||
finished the main work).
|
|
||||||
|
|
||||||
- GIT backend uses the gitweb for repository browsing (the counterpart of
|
|
||||||
ViewCVS).
|
|
||||||
|
|
||||||
- There might be some subs in GIT name space which you may prefer to move to
|
|
||||||
the main code.
|
|
||||||
|
|
||||||
- Due to the reasons explained in the code, I've written an rcs_invoke()
|
|
||||||
wrapper. May be there should be a better approach to reach the same
|
|
||||||
goal.
|
|
||||||
|
|
||||||
- There are some parts which I may change in future, like using a global
|
|
||||||
rcs_fatal_error and the ugly error reporting code in _rcs_commit.
|
|
||||||
|
|
||||||
- Documentation is missing.
|
|
||||||
|
|
||||||
It works for me, but of course in the end, the final decision is yours (due
|
|
||||||
to mostly GIT quirks, the implementation is not clean as SVN). Feel free
|
|
||||||
to fix/delete/add whatever you want. Hope it doesn't have any serious bug.
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ Some of ikiwiki's features:
|
||||||
* [[Subversion]]
|
* [[Subversion]]
|
||||||
|
|
||||||
Rather than implement its own system for storing page histories etc,
|
Rather than implement its own system for storing page histories etc,
|
||||||
ikiwiki simply uses subversion. (It's also possible to [[plugins/write]] support for other systems.)
|
ikiwiki simply uses subversion. (It's also possible to [[plugins/write]]
|
||||||
|
support for other systems, and ikiwiki also includes support for [[Git]]
|
||||||
|
now.)
|
||||||
|
|
||||||
Instead of editing pages in a stupid web form, you can use vim and commit
|
Instead of editing pages in a stupid web form, you can use vim and commit
|
||||||
changes via svn. Or work disconnected using svk and push your changes out
|
changes via svn. Or work disconnected using svk and push your changes out
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
ikiwiki is a **wiki compiler**. It converts a directory full of wiki pages
|
ikiwiki is a **wiki compiler**. It converts a directory full of wiki pages
|
||||||
into html pages suitable for publishing on a website. Unlike a traditional
|
into html pages suitable for publishing on a website. Unlike a traditional
|
||||||
wiki, ikiwiki does not have its own means of storing page history or its own
|
wiki, ikiwiki does not have its own means of storing page history or its own
|
||||||
markup language. Instead it uses [[Subversion]] and [[MarkDown]].
|
markup language. Instead it uses [[Subversion]] (or [[Git]]) and [[MarkDown]].
|
||||||
|
|
||||||
* [[News]] is a blog (built using ikiwiki) of news items about ikiwiki. It's the best way to find out when there's a new version to [[Download]].
|
* [[News]] is a blog (built using ikiwiki) of news items about ikiwiki. It's the best way to find out when there's a new version to [[Download]].
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,19 @@
|
||||||
A post-commit hook is run every time you commit a change to your subversion
|
A post-commit hook is run every time you commit a change to your
|
||||||
repository. To make the wiki be updated each time a commit is made, it can
|
[[subversion]] (or [[git]]) repository. To make the wiki be updated each
|
||||||
be run from (or as) a post-commit hook.
|
time a commit is made, it can be run from (or as) a post-commit hook.
|
||||||
|
|
||||||
The best way to run ikiwiki in a [[Subversion]] post-commit hook is using
|
The best way to run ikiwiki in a post-commit hook is using a wrapper, which
|
||||||
a wrapper, which can be generated using `ikiwiki --wrapper`.
|
ikiwiki is usually configured to generate using a setup file.
|
||||||
|
|
||||||
First, set up the subversion checkout that ikiwiki will update and compile
|
|
||||||
into your wiki at each subversion commit. Run ikiwiki a few times by hand
|
|
||||||
to get a feel for it. Now, generate the wrapper by adding "--wrapper"
|
|
||||||
to whatever command line you've been using to run ikiwiki. For example:
|
|
||||||
|
|
||||||
~/wiki-checkout> ikiwiki . ~/public_html/wiki
|
|
||||||
~/wiki-checkout> ikiwiki . ~/public_html/wiki --wrapper
|
|
||||||
successfully generated ikiwiki-wrap
|
|
||||||
|
|
||||||
The generated wrapper is a C program that is designed to safely be made
|
The generated wrapper is a C program that is designed to safely be made
|
||||||
suid if necessary. It's hardcoded to run ikiwiki with the settings
|
suid if necessary. It's hardcoded to run ikiwiki with the settings
|
||||||
specified when you ran --wrapper, and can only be used to update and
|
specified when you ran --wrapper, and can only be used to update and
|
||||||
compile that one checkout into the specified html directory.
|
compile that one checkout into the specified html directory.
|
||||||
|
|
||||||
Now, put the wrapper somewhere convenient, and create a post-commit hook
|
Depending on your setup, the post-commit hook might end up
|
||||||
script in your subversion repository for the wiki. All the post-commit
|
getting called by users who have write access to the repository, but not to
|
||||||
hook has to do is run the wrapper (with no parameters).
|
|
||||||
|
|
||||||
Depending on your Subversion setup, the post-commit hook might end up
|
|
||||||
getting called by users who have write access to subversion, but not to
|
|
||||||
your wiki checkout and html directory. If so, you can safely make
|
your wiki checkout and html directory. If so, you can safely make
|
||||||
ikiwiki-wrap suid to a user who can write there (*not* to root!). You might
|
the wrapper suid to a user who can write there (*not* to root!). You might
|
||||||
want to read [[Security]] first.
|
want to read [[Security]] first.
|
||||||
|
|
||||||
[[setup]] explains setting this up in more detail.
|
[[setup]] explains setting this up in more detail.
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
ikiwiki generates the list of recent changes by examining the [[Subversion]] commit log. You have to have [[CGI]] set up for this feature to be enabled.
|
ikiwiki generates the list of recent changes by examining the
|
||||||
|
[[Subversion]] or [[Git]] commit log. You have to have [[CGI]] set up for
|
||||||
|
this feature to be enabled.
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
So you want to set up your own wiki using ikiwiki? This tutorial will walk
|
So you want to set up your own wiki using ikiwiki? This tutorial will walk
|
||||||
you through setting up a wiki that is stored in [[Subversion]] and that has
|
you through setting up a wiki that is stored in [[Subversion]] or [[Git]],
|
||||||
optional support for commits from the web.
|
and that has optional support for commits from the web.
|
||||||
|
|
||||||
1. [[Install]] ikiwiki. See [[download]] for where to get it.
|
1. [[Install]] ikiwiki. See [[download]] for where to get it.
|
||||||
|
|
||||||
2. Create the subversion repository for your wiki.
|
2. Create the master rcs repository for your wiki.
|
||||||
|
|
||||||
|
# Subversion
|
||||||
svnadmin create /svn/wikirepo
|
svnadmin create /svn/wikirepo
|
||||||
svn mkdir file:///svn/wikirepo/trunk -m create
|
svn mkdir file:///svn/wikirepo/trunk -m create
|
||||||
|
|
||||||
|
# Git
|
||||||
|
mkdir /git/wikirepo
|
||||||
|
cd /git/wikirepo
|
||||||
|
git init-db
|
||||||
|
|
||||||
3. Check out the repository to make the working copy that ikiwiki will use.
|
3. Check out the repository to make the working copy that ikiwiki will use.
|
||||||
|
|
||||||
|
# Subversion
|
||||||
svn co file:///svn/wikirepo/trunk ~/wikiwc
|
svn co file:///svn/wikirepo/trunk ~/wikiwc
|
||||||
|
|
||||||
|
# Git
|
||||||
|
git clone /git/wikirepo ~/wikiwc
|
||||||
|
|
||||||
4. Build your wiki for the first time.
|
4. Build your wiki for the first time.
|
||||||
|
|
||||||
ikiwiki --verbose ~/wikiwc/ ~/public_html/wiki/ \
|
ikiwiki --verbose ~/wikiwc/ ~/public_html/wiki/ \
|
||||||
|
@ -25,10 +35,18 @@ optional support for commits from the web.
|
||||||
used if you don't have a custom version, so let's start by making a
|
used if you don't have a custom version, so let's start by making a
|
||||||
custom version of the wiki's index page:
|
custom version of the wiki's index page:
|
||||||
|
|
||||||
cp /usr/share/ikiwiki/basewiki/index.mdwn ~/wikiwc
|
cd ~/wikiwc
|
||||||
svn add ~/wikiwc/index.mdwn
|
cp /usr/share/ikiwiki/basewiki/index.mdwn .
|
||||||
$EDITOR ~/wikiwc/index.mdwn
|
$EDITOR index.mdwn
|
||||||
svn commit ~/wikiwc/index.mdwn -m customised
|
|
||||||
|
# Subversion
|
||||||
|
svn add index.mdwn
|
||||||
|
svn commit -m customised index.mdwn
|
||||||
|
|
||||||
|
# Git
|
||||||
|
git add index.mdwn
|
||||||
|
git commit -m customised index.mdwn
|
||||||
|
git push origin
|
||||||
|
|
||||||
You can also add any files you like from scratch of course.
|
You can also add any files you like from scratch of course.
|
||||||
|
|
||||||
|
@ -46,15 +64,15 @@ optional support for commits from the web.
|
||||||
`doc/ikiwiki.setup` in the ikiwiki sources), and edit it.
|
`doc/ikiwiki.setup` in the ikiwiki sources), and edit it.
|
||||||
|
|
||||||
Most of the options, like `wikiname` in the setup file are the same as
|
Most of the options, like `wikiname` in the setup file are the same as
|
||||||
ikiwiki's command line options (documented in [[usage]]. `srcdir`
|
ikiwiki's command line options (documented in [[usage]]. `srcdir` and
|
||||||
and `destdir` are the two directories you specify when
|
`destdir` are the two directories you specify when running ikiwiki by
|
||||||
running ikiwiki by hand. `svnrepo` is the path to your subversion
|
hand. `rcsrepo` is the path to your master rcs repository. Make sure
|
||||||
repository. Make sure that all of these are pointing to the right
|
that all of these are pointing to the right directories, and read
|
||||||
directories, and read through and configure the rest of the file to your
|
through and configure the rest of the file to your liking.
|
||||||
liking.
|
|
||||||
|
|
||||||
Note that the default file has a block to configure a svn wrapper. This
|
Note that the default file has a block to configure an Rcs wrapper to
|
||||||
sets up a [[post-commit]] hook to update the wiki.
|
update the wiki. You need to uncomment the related block for whatever
|
||||||
|
rcs you use and comment out the other rcs blocks.
|
||||||
|
|
||||||
When you're satisfied, run `ikiwiki --setup ikiwiki.setup`, and it
|
When you're satisfied, run `ikiwiki --setup ikiwiki.setup`, and it
|
||||||
will set everything up and update your wiki.
|
will set everything up and update your wiki.
|
||||||
|
@ -66,13 +84,21 @@ optional support for commits from the web.
|
||||||
`ikiwiki --setup ikiwiki.setup`, and you're done!
|
`ikiwiki --setup ikiwiki.setup`, and you're done!
|
||||||
|
|
||||||
9. Add [[PageHistory]] links to the top of pages. This requires you to have
|
9. Add [[PageHistory]] links to the top of pages. This requires you to have
|
||||||
setup [[ViewCVS]] or something similar to access your [[Subversion]]
|
setup a repository browser. For Subversion, you may use [[ViewCVS]] or
|
||||||
repository. The `historyurl` setting makes ikiwiki add the links, and
|
something similar to access your [[Subversion]] repository. For Git,
|
||||||
in that url, "\[[file]]" is replaced with the name of the file to view. So
|
[[Gitweb]] can be used.
|
||||||
edit ikiwiki.setup and set `historyurl` to something like this:
|
|
||||||
|
The `historyurl` setting makes ikiwiki add the links, and in that url,
|
||||||
|
"\[[file]]" is replaced with the name of the file to view. So edit
|
||||||
|
ikiwiki.setup and set `historyurl` to something like this for
|
||||||
|
Subversion:
|
||||||
|
|
||||||
`http://svn.host/trunk/\[[file]]?root=wiki`
|
`http://svn.host/trunk/\[[file]]?root=wiki`
|
||||||
|
|
||||||
|
Or this for Git:
|
||||||
|
|
||||||
|
`http://git.host/gitweb.cgi?p=wiki.git;a=history;f=[[file]]`
|
||||||
|
|
||||||
Then run `ikiwiki --setup ikiwiki.setup` again.
|
Then run `ikiwiki --setup ikiwiki.setup` again.
|
||||||
|
|
||||||
10. Enjoy your new wiki! Add yourself to [[IkiWikiUsers]]
|
10. Enjoy your new wiki! Add yourself to [[IkiWikiUsers]]
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
Subversion is a revision control system. While ikiwiki is relatively
|
Subversion is a revision control system. While ikiwiki is relatively
|
||||||
independant of the underlying revision control system, and can easily be used without one, using it with Subversion is recommended.
|
independant of the underlying revision control system, and can easily be
|
||||||
|
used without one, using it with Subversion is recommended since it's how
|
||||||
|
the author uses it.
|
||||||
|
|
||||||
Ikiwiki can run as a [[post-commit]] hook to update a wiki whenever commits come in. When running as a [[cgi]] with Subversion, ikiwiki automatically commits edited pages to the subversion repostory, and uses the Subversion log to generate the [[RecentChanges]] page.
|
Ikiwiki can run as a [[post-commit]] hook to update a wiki whenever commits
|
||||||
|
come in. When running as a [[cgi]] with Subversion, ikiwiki automatically
|
||||||
|
commits edited pages to the subversion repostory, and uses the Subversion
|
||||||
|
log to generate the [[RecentChanges]] page.
|
||||||
|
|
|
@ -46,7 +46,7 @@ These options control the mode that ikiwiki is operating in.
|
||||||
directory. The filename to use for the wrapper is optional.
|
directory. The filename to use for the wrapper is optional.
|
||||||
|
|
||||||
The wrapper is designed to be safely made suid and be run by untrusted
|
The wrapper is designed to be safely made suid and be run by untrusted
|
||||||
users, as a [[Subversion]] [[post-commit]] hook, or as a [[CGI]].
|
users, as a [[post-commit]] hook, or as a [[CGI]].
|
||||||
|
|
||||||
Note that the generated wrapper will ignore all command line parameters.
|
Note that the generated wrapper will ignore all command line parameters.
|
||||||
|
|
||||||
|
@ -88,14 +88,17 @@ These options configure the wiki.
|
||||||
* --notify
|
* --notify
|
||||||
|
|
||||||
Enable email notification of commits. This should be used when running
|
Enable email notification of commits. This should be used when running
|
||||||
ikiwiki as a [[Subversion]] [[post-commit]] hook.
|
ikiwiki as a [[post-commit]] hook.
|
||||||
|
|
||||||
* --rcs=svn, --no-rcs
|
* --rcs=svn, --no-rcs
|
||||||
|
|
||||||
Enable or disable use of a revision control system.
|
Enable or disable use of a revision control system.
|
||||||
|
|
||||||
If you use svn ([[Subversion]]), the `source` directory is assumed to be
|
If you use svn, the `source` directory is assumed to be
|
||||||
a working copy, and is automatically updated before building the wiki.
|
a [[Subversion]] working copy.
|
||||||
|
|
||||||
|
If you use git, the `source` directory is assumed to be a clone of the
|
||||||
|
[[git]] repository.
|
||||||
|
|
||||||
In [[CGI]] mode, with a revision control system enabled pages edited via
|
In [[CGI]] mode, with a revision control system enabled pages edited via
|
||||||
the web will be committed. Also, the [[RecentChanges]] link will be placed
|
the web will be committed. Also, the [[RecentChanges]] link will be placed
|
||||||
|
@ -164,8 +167,8 @@ These options configure the wiki.
|
||||||
|
|
||||||
* --plugin name
|
* --plugin name
|
||||||
|
|
||||||
Enables the use of the specified plugin in the wiki. See [[plugins]] for
|
Enables the use of the specified [[plugin|plugins]] in the wiki.
|
||||||
details. Note that plugin names are case sensative.
|
Note that plugin names are case sensative.
|
||||||
|
|
||||||
* --disable-plugin name
|
* --disable-plugin name
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,13 @@ this a pretty Iky Wiki, since it's so different from other Wikis. Partly
|
||||||
because "ikiwiki" is a nice palindrome. Partly because its design turns
|
because "ikiwiki" is a nice palindrome. Partly because its design turns
|
||||||
the usual design for a Wiki inside-out and backwards.
|
the usual design for a Wiki inside-out and backwards.
|
||||||
|
|
||||||
(BTW, I'm told that "iki" is Finnish for "forever" so ikiwiki is "forever wiki".)
|
(BTW, I'm told that "iki" is Finnish for "forever" so ikiwiki is "forever
|
||||||
|
wiki".)
|
||||||
|
|
||||||
Oh, maybe you wanted to know why you'd want to choose ikiwiki instead of all the other wikis out there? Unless your personal strangeness significantly aligns with [[Joey]]'s, so that keeping everything in subversion, compiling websites to static html, and like design [[features]] appeal to you, you probably won't.
|
Oh, maybe you wanted to know why you'd want to choose ikiwiki instead of
|
||||||
|
all the other wikis out there? Unless your personal strangeness
|
||||||
|
significantly aligns with [[Joey]]'s, so that keeping everything in
|
||||||
|
subversion, compiling websites to static html, and like design [[features]]
|
||||||
|
appeal to you, you probably won't.
|
||||||
|
|
||||||
Hmm, the above paragraph is less true today than it was when I wrote it.
|
Hmm, the above paragraph is less true today than it was when I wrote it.
|
Loading…
Reference in New Issue