Merge branch 'master' of ssh://git.ikiwiki.info/srv/git/ikiwiki.info
authorJoey Hess <joey@kitenet.net>
Sun, 18 Jul 2010 21:39:28 +0000 (17:39 -0400)
committerJoey Hess <joey@kitenet.net>
Sun, 18 Jul 2010 21:39:28 +0000 (17:39 -0400)
IkiWiki.pm
debian/changelog
doc/bugs/conflicts.mdwn [new file with mode: 0644]
t/conflicts.t

index 701f7137dd78483215637e205bbde0b720ea0387..fa49b2c340fe9b40f46db39a42c0d084b1fcd4f1 100644 (file)
@@ -823,6 +823,17 @@ sub prep_writefile ($$) {
                if (-l "$destdir/$test") {
                        error("cannot write to a symlink ($test)");
                }
+               if (-f _ && $test ne $file) {
+                       # Remove conflicting file.
+                       foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) {
+                               foreach my $f (@{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) {
+                                       if ($f eq $test) {
+                                               unlink("$destdir/$test");
+                                               last;
+                                       }
+                               }
+                       }
+               }
                $test=dirname($test);
        }
 
@@ -876,10 +887,36 @@ sub will_render ($$;$) {
        my $dest=shift;
        my $clear=shift;
 
-       # Important security check.
+       # Important security check for independently created files.
        if (-e "$config{destdir}/$dest" && ! $config{rebuild} &&
            ! grep { $_ eq $dest } (@{$renderedfiles{$page}}, @{$oldrenderedfiles{$page}}, @{$wikistate{editpage}{previews}})) {
-               error("$config{destdir}/$dest independently created, not overwriting with version from $page");
+               my $from_other_page=0;
+               # Expensive, but rarely runs.
+               foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) {
+                       if (grep {
+                               $_ eq $dest ||
+                               dirname($_) eq $dest
+                           } @{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) {
+                               $from_other_page=1;
+                               last;
+                       }
+               }
+
+               error("$config{destdir}/$dest independently created, not overwriting with version from $page")
+                       unless $from_other_page;
+       }
+
+       # If $dest exists as a directory, remove conflicting files in it
+       # rendered from other pages.
+       if (-d _) {
+               foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) {
+                       foreach my $f (@{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) {
+                               if ($f eq dirname($dest) || dirname($f) eq $dest) {
+                                       unlink("$config{destdir}/$f");
+                                       rmdir(dirname("$config{destdir}/$f"));
+                               }
+                       }
+               }
        }
 
        if (! $clear || $cleared{$page}) {
index bb9a43692d459dac45b98fe8bfa2a207c6319399..21b5d01fa6277a97a6eb24bd8852427ade3e1272 100644 (file)
@@ -18,6 +18,8 @@ ikiwiki (3.20100705) UNRELEASED; urgency=low
   * po: needstranslation() pagespec can have a percent specified.
   * Drop Cache-Control must-revalidate (Firefox 3.5.10 does not seem to have
     the caching problem that was added to work around). Closes: #588623
+  * Made much more robust in cases where multiple source files produce
+    conflicting files/directories in the destdir.
 
  -- Joey Hess <joeyh@debian.org>  Mon, 05 Jul 2010 13:59:42 -0400
 
diff --git a/doc/bugs/conflicts.mdwn b/doc/bugs/conflicts.mdwn
new file mode 100644 (file)
index 0000000..bef0f54
--- /dev/null
@@ -0,0 +1,32 @@
+The `conflicts` testcase has 4 failing test cases. The underlaying problem 
+is that there are multiple possible source files that can create the same
+destination files.
+
+1. `foo.mdwn` is in srcdir, rendered to destdir. Then
+   it is removed, and `foo` is added, which will be rendered
+   raw to destdir. Since the `foo/` directory still exists,
+   it fails.
+1. `foo` is added to srcdir, rendered raw to destdir.
+   Then it is removed from srcdir, and `foo.mdwn` is added.
+   The `foo` file is still present in the destdir, and mkdir
+   of the directory `foo/` fails.
+1. `foo.mdwn` renders to `foo/index.html`. Then `foo/index.html`
+   is added to the srcdir, using rawhtml. It renders to the same 
+   thing.
+1. `foo/index.html` in srcdir is rendered to same thing in destdir
+   using rawhtml. Then `foo.mdwn` is added; renders same thing.
+
+Note that another case, that of page `foo.mdwn` and page `foo.txt`, that
+both render to `foo/index.html`, used to cause problems, but no longer
+crashes ikiwiki. It now only complains in this situation, and which
+file "wins" is undefined. The fix for this relied on both pages being
+named `foo`; but in the above cases, the source files have different
+pagenames.
+
+One approach: Beef up checking in `will_render` to detect when the same
+destination file is rendered by multiple pages. Or when one page renders
+a file that is a parent directory of the rendered file of another page.
+It could warn, rather than erroring. The last page rendered would "win";
+generating the destdir file.
+
+[[done]]
index 7748c105f6041bd73a75408e60b33f54fdf29a95..d7e04d3ae39a66b1bdcd711aa0aa0ee12f0584ec 100755 (executable)
@@ -2,7 +2,7 @@
 # Tests for bugs relating to conflicting files in the srcdir
 use warnings;
 use strict;
-use Test::More 'no_plan';
+use Test::More tests => 106;
 
 # setup
 my $srcdir="t/tmp/src";
@@ -48,6 +48,35 @@ ok(! system("touch $srcdir/foo.mdwn"));
 ok(! system("touch $srcdir/foo.txt"));
 setupiki("conflicting page sources in setup");
 
+# Page and non-page file with same pagename.
+newsrcdir();
+ok(! system("touch $srcdir/foo.mdwn"));
+ok(! system("touch $srcdir/foo"));
+setupiki("conflicting page and non-page in setup");
+newsrcdir();
+ok(! system("touch $srcdir/foo.mdwn"));
+setupiki("initial setup");
+ok(! system("touch $srcdir/foo"));
+refreshiki("conflicting page added (non-page already existing) in refresh");
+newsrcdir();
+ok(! system("touch $srcdir/foo"));
+setupiki("initial setup");
+ok(! system("touch $srcdir/foo.mdwn"));
+refreshiki("conflicting non-page added (page already existing) in refresh");
+
+# Page that renders to a file that is also a subdirectory holding another
+# file.
+newsrcdir();
+ok(! system("touch $srcdir/foo.mdwn"));
+ok(! system("mkdir -p $srcdir/foo/index.html"));
+ok(! system("touch $srcdir/foo/index.html/bar.mdwn"));
+setupiki("conflicting page file and subdirectory");
+newsrcdir();
+ok(! system("touch $srcdir/foo.mdwn"));
+ok(! system("mkdir -p $srcdir/foo/index.html"));
+ok(! system("touch $srcdir/foo/index.html/bar"));
+setupiki("conflicting page file and subdirectory 2");
+
 # Changing a page file into a non-page could also cause ikiwiki to fail.
 newsrcdir();
 ok(! system("touch $srcdir/foo.mdwn"));
@@ -70,6 +99,27 @@ setupiki("initial setup");
 ok(! system("mkdir -p $srcdir/foo"));
 ok(! system("touch $srcdir/foo/index.html"));
 refreshiki("rawhtml file rendered same as existing page in refresh");
+# Moved when refreshing
+newsrcdir();
+ok(! system("touch $srcdir/foo.mdwn"));
+setupiki("initial setup");
+ok(! system("mkdir -p $srcdir/foo"));
+ok(! system("mv $srcdir/foo.mdwn $srcdir/foo/index.html"));
+refreshiki("existing page moved to rawhtml file in refresh");
+# Inverse added when refreshing
+newsrcdir();
+ok(! system("mkdir -p $srcdir/foo"));
+ok(! system("touch $srcdir/foo/index.html"));
+setupiki("initial setup");
+ok(! system("touch $srcdir/foo.mdwn"));
+refreshiki("page rendered same as existing rawhtml file in refresh");
+# Inverse moved when refreshing
+newsrcdir();
+ok(! system("mkdir -p $srcdir/foo"));
+ok(! system("touch $srcdir/foo/index.html"));
+setupiki("initial setup");
+ok(! system("mv $srcdir/foo/index.html $srcdir/foo.mdwn"));
+refreshiki("rawhtml file moved to page in refresh");
 # Present during setup
 newsrcdir();
 ok(! system("touch $srcdir/foo.mdwn"));