2 # For subversion support.
9 my $svn_log_infoline=qr/^r(\d+)\s+\|\s+([^\s]+)\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/;
11 sub svn_info ($$) { #{{{
15 my $info=`LANG=C svn info $file`;
16 my ($ret)=$info=~/^$field: (.*)$/m;
20 sub rcs_update () { #{{{
21 if (-d "$config{srcdir}/.svn") {
22 if (system("svn", "update", "--quiet", $config{srcdir}) != 0) {
23 warn("svn update failed\n");
28 sub rcs_prepedit ($) { #{{{
29 # Prepares to edit a file under revision control. Returns a token
30 # that must be passed into rcs_commit when the file is ready
32 # The file is relative to the srcdir.
35 if (-d "$config{srcdir}/.svn") {
36 # For subversion, return the revision of the file when
38 my $rev=svn_info("Revision", "$config{srcdir}/$file");
39 return defined $rev ? $rev : "";
43 sub rcs_commit ($$$) { #{{{
44 # Tries to commit the page; returns undef on _success_ and
45 # a version of the page with the rcs's conflict markers on failure.
46 # The file is relative to the srcdir.
51 if (-d "$config{srcdir}/.svn") {
52 # Check to see if the page has been changed by someone
53 # else since rcs_prepedit was called.
54 my ($oldrev)=$rcstoken=~/^([0-9]+)$/; # untaint
55 my $rev=svn_info("Revision", "$config{srcdir}/$file");
56 if (defined $rev && defined $oldrev && $rev != $oldrev) {
57 # Merge their changes into the file that we've
59 chdir($config{srcdir}); # svn merge wants to be here
60 if (system("svn", "merge", "--quiet", "-r$oldrev:$rev",
61 "$config{srcdir}/$file") != 0) {
62 warn("svn merge -r$oldrev:$rev failed\n");
66 if (system("svn", "commit", "--quiet", "-m",
67 possibly_foolish_untaint($message),
68 "$config{srcdir}") != 0) {
69 my $conflict=readfile("$config{srcdir}/$file");
70 if (system("svn", "revert", "--quiet", "$config{srcdir}/$file") != 0) {
71 warn("svn revert failed\n");
76 return undef # success
79 sub rcs_add ($) { #{{{
80 # filename is relative to the root of the srcdir
83 if (-d "$config{srcdir}/.svn") {
84 my $parent=dirname($file);
85 while (! -d "$config{srcdir}/$parent/.svn") {
87 $parent=dirname($file);
90 if (system("svn", "add", "--quiet", "$config{srcdir}/$file") != 0) {
91 warn("svn add failed\n");
96 sub rcs_recentchanges ($) { #{{{
100 eval q{use CGI 'escapeHTML'};
101 eval q{use Date::Parse};
102 eval q{use Time::Duration};
104 if (-d "$config{srcdir}/.svn") {
105 my $svn_url=svn_info("URL", $config{srcdir});
107 my $div=qr/^--------------------+$/;
109 my ($rev, $user, $when, @pages, @message);
110 foreach (`LANG=C svn log -v '$svn_url'`) {
112 if ($state eq 'start' && /$div/) {
115 elsif ($state eq 'header' && /$svn_log_infoline/) {
118 $when=concise(ago(time - str2time($3)));
120 elsif ($state eq 'header' && /^\s+[A-Z]+\s+\/\Q$config{svnpath}\E\/([^ ]+)(?:$|\s)/) {
122 my $diffurl=$config{diffurl};
123 $diffurl=~s/\[\[file\]\]/$file/g;
124 $diffurl=~s/\[\[r1\]\]/$rev - 1/eg;
125 $diffurl=~s/\[\[r2\]\]/$rev/g;
127 link => htmllink("", pagename($file), 1),
131 elsif ($state eq 'header' && /^$/) {
134 elsif ($state eq 'body' && /$div/) {
135 my $committype="web";
136 if (defined $message[0] &&
137 $message[0]->{line}=~/^web commit by (\w+):?(.*)/) {
139 $message[0]->{line}=$2;
145 push @ret, { rev => $rev,
146 user => htmllink("", $user, 1),
147 committype => $committype,
148 when => $when, message => [@message],
151 return @ret if @ret >= $num;
154 $rev=$user=$when=undef;
157 elsif ($state eq 'body') {
158 push @message, {line => escapeHTML($_)},
166 sub rcs_notify () { #{{{
167 if (! exists $ENV{REV}) {
168 error("REV is not set, not running from svn post-commit hook, cannot send notifications");
172 foreach my $change (`svnlook changed $config{svnrepo} -r $ENV{REV}`) {
174 if (/^[A-Z]+\s+\Q$config{svnpath}\E\/(.*)/) {
175 push @changed_pages, $1;
179 require IkiWiki::UserInfo;
180 my @email_recipients=page_subscribers(@changed_pages);
181 if (@email_recipients) {
182 eval q{use Mail::Sendmail};
183 # TODO: if a commit spans multiple pages, this will send
184 # subscribers a diff that might contain pages they did not
185 # sign up for. Should separate the diff per page and
186 # reassemble into one mail with just the pages subscribed to.
187 my $body=`LANG=C svnlook diff $config{svnrepo} -r $ENV{REV} --no-diff-deleted`;
188 foreach my $email (@email_recipients) {
191 From => "$config{wikiname} <$config{adminemail}>",
192 Subject => "$config{wikiname} $ENV{REV} update notification",
194 ) or error("Failed to send update notification mail");
199 sub rcs_getctime () { #{{{
200 eval q{use Date::Parse};
201 foreach my $page (keys %pagectime) {
202 my $file="$config{srcdir}/$pagesources{$page}";
203 next unless -e $file;
204 my $child = open(SVNLOG, "-|");
206 exec("svn", "log", $file) || error("svn log $file failed to run");
211 if (/$svn_log_infoline/) {
215 close SVNLOG || warn "svn log $file exited $?";
217 if (! defined $date) {
218 warn "failed to parse svn log for $file\n";
222 $pagectime{$page}=$date=str2time($date);
223 debug("found ctime ".localtime($date)." for $page");