diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java index d0680cab7..f6f6753c7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.util; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -52,6 +53,7 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; +import org.eclipse.jgit.junit.JGitTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -321,4 +323,71 @@ public class FileUtilTest { assertTrue(f.exists()); assertFalse(e.exists()); } + + @Test + public void testRenameOverNonExistingFile() throws IOException { + File d = new File(trash, "d"); + FileUtils.mkdirs(d); + File f1 = new File(trash, "d/f"); + File f2 = new File(trash, "d/g"); + JGitTestUtil.write(f1, "f1"); + // test + FileUtils.rename(f1, f2); + assertFalse(f1.exists()); + assertTrue(f2.exists()); + assertEquals("f1", JGitTestUtil.read(f2)); + } + + @Test + public void testRenameOverExistingFile() throws IOException { + File d = new File(trash, "d"); + FileUtils.mkdirs(d); + File f1 = new File(trash, "d/f"); + File f2 = new File(trash, "d/g"); + JGitTestUtil.write(f1, "f1"); + JGitTestUtil.write(f2, "f2"); + // test + FileUtils.rename(f1, f2); + assertFalse(f1.exists()); + assertTrue(f2.exists()); + assertEquals("f1", JGitTestUtil.read(f2)); + } + + @Test + public void testRenameOverExistingNonEmptyDirectory() throws IOException { + File d = new File(trash, "d"); + FileUtils.mkdirs(d); + File f1 = new File(trash, "d/f"); + File f2 = new File(trash, "d/g"); + File d1 = new File(trash, "d/g/h/i"); + File f3 = new File(trash, "d/g/h/f"); + FileUtils.mkdirs(d1); + JGitTestUtil.write(f1, "f1"); + JGitTestUtil.write(f3, "f3"); + // test + try { + FileUtils.rename(f1, f2); + fail("rename to non-empty directory should fail"); + } catch (IOException e) { + assertEquals("f1", JGitTestUtil.read(f1)); // untouched source + assertEquals("f3", JGitTestUtil.read(f3)); // untouched + // empty directories within f2 may or may not have been deleted + } + } + + @Test + public void testRenameOverExistingEmptyDirectory() throws IOException { + File d = new File(trash, "d"); + FileUtils.mkdirs(d); + File f1 = new File(trash, "d/f"); + File f2 = new File(trash, "d/g"); + File d1 = new File(trash, "d/g/h/i"); + FileUtils.mkdirs(d1); + JGitTestUtil.write(f1, "f1"); + // test + FileUtils.rename(f1, f2); + assertFalse(f1.exists()); + assertTrue(f2.exists()); + assertEquals("f1", JGitTestUtil.read(f2)); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java index dc0bbcb38..5fbbda7c5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -171,7 +171,14 @@ public class FileUtils { * Rename a file or folder. If the rename fails and if we are running on a * filesystem where it makes sense to repeat a failing rename then repeat * the rename operation up to 9 times with 100ms sleep time between two - * calls + * calls. Furthermore if the destination exists and is directory hierarchy + * with only directories in it, the whole directory hierarchy will be + * deleted. If the target represents a non-empty directory structure, empty + * subdirectories within that structure may or may not be deleted even if + * the method fails. Furthermore if the destination exists and is a file + * then the file will be deleted and then the rename is retried. + *
+ * This operation is not atomic. * * @see FS#retryFailedLockFileCommit() * @param src @@ -188,6 +195,15 @@ public class FileUtils { while (--attempts >= 0) { if (src.renameTo(dst)) return; + try { + if (!dst.delete()) + delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE); + // On *nix there is no try, you do or do not + if (src.renameTo(dst)) + return; + } catch (IOException e) { + // ignore and continue retry + } try { Thread.sleep(100); } catch (InterruptedException e) {