* 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.
|
||||
* Remove preprocessor directives from inlined pages.
|
||||
* 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
|
||||
|
||||
|
|
|
@ -9,17 +9,17 @@ Standards-Version: 3.7.2
|
|||
Package: ikiwiki
|
||||
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
|
||||
Recommends: subversion, hyperestraier
|
||||
Recommends: subversion | git-core, hyperestraier
|
||||
Suggests: viewcvs
|
||||
Description: a wiki compiler
|
||||
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
|
||||
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
|
||||
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.
|
||||
.
|
||||
It also supports generating RSS feeds and blogging.
|
||||
|
|
|
@ -76,9 +76,9 @@ off from R1.
|
|||
(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
|
||||
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
|
||||
FILE' (please see the relevant comment in mergepast for more details), so I
|
||||
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]]
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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]].
|
||||
|
||||
|
|
|
@ -1,32 +1,19 @@
|
|||
A post-commit hook is run every time you commit a change to your subversion
|
||||
repository. To make the wiki be updated each time a commit is made, it can
|
||||
be run from (or as) a post-commit hook.
|
||||
A post-commit hook is run every time you commit a change to your
|
||||
[[subversion]] (or [[git]]) repository. To make the wiki be updated each
|
||||
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
|
||||
a wrapper, which can be generated using `ikiwiki --wrapper`.
|
||||
|
||||
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 best way to run ikiwiki in a post-commit hook is using a wrapper, which
|
||||
ikiwiki is usually configured to generate using a setup file.
|
||||
|
||||
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
|
||||
specified when you ran --wrapper, and can only be used to update and
|
||||
compile that one checkout into the specified html directory.
|
||||
|
||||
Now, put the wrapper somewhere convenient, and create a post-commit hook
|
||||
script in your subversion repository for the wiki. All the post-commit
|
||||
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
|
||||
Depending on your setup, the post-commit hook might end up
|
||||
getting called by users who have write access to the repository, but not to
|
||||
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.
|
||||
|
||||
[[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
|
||||
you through setting up a wiki that is stored in [[Subversion]] and that has
|
||||
optional support for commits from the web.
|
||||
you through setting up a wiki that is stored in [[Subversion]] or [[Git]],
|
||||
and that has optional support for commits from the web.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
# Subversion
|
||||
svn co file:///svn/wikirepo/trunk ~/wikiwc
|
||||
|
||||
# Git
|
||||
git clone /git/wikirepo ~/wikiwc
|
||||
|
||||
4. Build your wiki for the first time.
|
||||
|
||||
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
|
||||
custom version of the wiki's index page:
|
||||
|
||||
cp /usr/share/ikiwiki/basewiki/index.mdwn ~/wikiwc
|
||||
svn add ~/wikiwc/index.mdwn
|
||||
$EDITOR ~/wikiwc/index.mdwn
|
||||
svn commit ~/wikiwc/index.mdwn -m customised
|
||||
cd ~/wikiwc
|
||||
cp /usr/share/ikiwiki/basewiki/index.mdwn .
|
||||
$EDITOR index.mdwn
|
||||
|
||||
# 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.
|
||||
|
||||
|
@ -46,15 +64,15 @@ optional support for commits from the web.
|
|||
`doc/ikiwiki.setup` in the ikiwiki sources), and edit it.
|
||||
|
||||
Most of the options, like `wikiname` in the setup file are the same as
|
||||
ikiwiki's command line options (documented in [[usage]]. `srcdir`
|
||||
and `destdir` are the two directories you specify when
|
||||
running ikiwiki by hand. `svnrepo` is the path to your subversion
|
||||
repository. Make sure that all of these are pointing to the right
|
||||
directories, and read through and configure the rest of the file to your
|
||||
liking.
|
||||
ikiwiki's command line options (documented in [[usage]]. `srcdir` and
|
||||
`destdir` are the two directories you specify when running ikiwiki by
|
||||
hand. `rcsrepo` is the path to your master rcs repository. Make sure
|
||||
that all of these are pointing to the right directories, and read
|
||||
through and configure the rest of the file to your liking.
|
||||
|
||||
Note that the default file has a block to configure a svn wrapper. This
|
||||
sets up a [[post-commit]] hook to update the wiki.
|
||||
Note that the default file has a block to configure an Rcs wrapper to
|
||||
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
|
||||
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!
|
||||
|
||||
9. Add [[PageHistory]] links to the top of pages. This requires you to have
|
||||
setup [[ViewCVS]] or something similar to access your [[Subversion]]
|
||||
repository. 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:
|
||||
setup a repository browser. For Subversion, you may use [[ViewCVS]] or
|
||||
something similar to access your [[Subversion]] repository. For Git,
|
||||
[[Gitweb]] can be used.
|
||||
|
||||
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`
|
||||
|
||||
Or this for Git:
|
||||
|
||||
`http://git.host/gitweb.cgi?p=wiki.git;a=history;f=[[file]]`
|
||||
|
||||
Then run `ikiwiki --setup ikiwiki.setup` again.
|
||||
|
||||
10. Enjoy your new wiki! Add yourself to [[IkiWikiUsers]]
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -88,14 +88,17 @@ These options configure the wiki.
|
|||
* --notify
|
||||
|
||||
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
|
||||
|
||||
Enable or disable use of a revision control system.
|
||||
|
||||
If you use svn ([[Subversion]]), the `source` directory is assumed to be
|
||||
a working copy, and is automatically updated before building the wiki.
|
||||
If you use svn, the `source` directory is assumed to be
|
||||
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
|
||||
the web will be committed. Also, the [[RecentChanges]] link will be placed
|
||||
|
@ -164,8 +167,8 @@ These options configure the wiki.
|
|||
|
||||
* --plugin name
|
||||
|
||||
Enables the use of the specified plugin in the wiki. See [[plugins]] for
|
||||
details. Note that plugin names are case sensative.
|
||||
Enables the use of the specified [[plugin|plugins]] in the wiki.
|
||||
Note that plugin names are case sensative.
|
||||
|
||||
* --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
|
||||
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.
|
Loading…
Reference in New Issue