* Add support for using git instead of subversion as the RCS backend,
authorjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>
Fri, 2 Jun 2006 05:32:20 +0000 (05:32 +0000)
committerjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>
Fri, 2 Jun 2006 05:32:20 +0000 (05:32 +0000)
  tremendous thanks to Recai Oktaş for this.
* Doc updates for git.

12 files changed:
IkiWiki/Rcs/git.pm [new file with mode: 0644]
debian/changelog
debian/control
doc/about_rcs_backends.mdwn
doc/features.mdwn
doc/index.mdwn
doc/post-commit.mdwn
doc/recentchanges.mdwn
doc/setup.mdwn
doc/subversion.mdwn
doc/usage.mdwn
doc/whyikiwiki.mdwn

diff --git a/IkiWiki/Rcs/git.pm b/IkiWiki/Rcs/git.pm
new file mode 100644 (file)
index 0000000..374904f
--- /dev/null
@@ -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
index e60eb9a3e4eb7e55fcc5f1ed36ea2c944a74029b..1f1f2a83a17d3efaa15ffe5a1db42b3dcfe5bbbd 100644 (file)
@@ -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
 
index bd392154978dc9796c1f4aa56a26bc6e92750dd5..a475f69e350d6667b1d4ca5a8771a4cbc9890204 100644 (file)
@@ -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.
index 197f093946cee2284f9d3024989309cbf3ac4d36..1aafd4a8ee50ad412b9f0546c247c359f5c6a012 100644 (file)
@@ -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.
index 30fe5987bb8cb3e53f19685ab64886cc00289dbc..12e9d0528b952b72b54428e40ced848744137fa9 100644 (file)
@@ -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
index 3d10751c6cbdd15fbfcf245a0aec1a0804949cff..5cb4ddee36161103c2ecd8f4b4cef7b3f4a45ed6 100644 (file)
@@ -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]].
 
index 93d85195930589c55ec254eed97b215af827431f..1ce271487a909c24180596c1306e7bce953ae010 100644 (file)
@@ -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.
index 56b68273ab997e6c3397dc4006042e6ed92dfe8e..a40f396b69213dd24471d4d92e72653d2970a861 100644 (file)
@@ -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.
\ No newline at end of file
+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.
index 7e4aaecf9465cab258b8827365c04a17e52a7c21..f93f71fe1fa47a060ed940d19899d3fc95a809d0 100644 (file)
@@ -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,12 +84,20 @@ 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.
 
index 51fc5f485da692564eb02d28387b7b914784289f..47cdba450f96d6669112e83f8594f2cd87b740df 100644 (file)
@@ -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.
index 011cf53a2303537d7088ba00917be39953e9c4f9..eec1856a7dfd4c710beb63c709cdeb0ab1724ad6 100644 (file)
@@ -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
 
index a2355d344600277e637a22eee9edf4ec4a2ceb12..2bc1fe416e40ddbea9d56202d9a2983178a99716 100644 (file)
@@ -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.
\ No newline at end of file
+Hmm, the above paragraph is less true today than it was when I wrote it.