/*
* Copyright (C) 2010, Google Inc.
* Copyright (C) 2010, Matthias Sohn
* This operation is not atomic.
*
* @see FS#retryFailedLockFileCommit()
* @param src
* the old {@code File}
* @param dst
* the new {@code File}
* @throws IOException
* if the rename has failed
* @since 3.0
*/
public static void rename(final File src, final File dst)
throws IOException {
int attempts = FS.DETECTED.retryFailedLockFileCommit() ? 10 : 1;
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) {
throw new IOException(MessageFormat.format(
JGitText.get().renameFileFailed, src.getAbsolutePath(),
dst.getAbsolutePath()));
}
}
throw new IOException(MessageFormat.format(
JGitText.get().renameFileFailed, src.getAbsolutePath(),
dst.getAbsolutePath()));
}
/**
* Creates the directory named by this abstract pathname.
*
* @param d
* directory to be created
* @throws IOException
* if creation of {@code d} fails. This may occur if {@code d}
* did exist when the method was called. This can therefore
* cause IOExceptions during race conditions when multiple
* concurrent threads all try to create the same directory.
*/
public static void mkdir(final File d)
throws IOException {
mkdir(d, false);
}
/**
* Creates the directory named by this abstract pathname.
*
* @param d
* directory to be created
* @param skipExisting
* if {@code true} skip creation of the given directory if it
* already exists in the file system
* @throws IOException
* if creation of {@code d} fails. This may occur if {@code d}
* did exist when the method was called. This can therefore
* cause IOExceptions during race conditions when multiple
* concurrent threads all try to create the same directory.
*/
public static void mkdir(final File d, boolean skipExisting)
throws IOException {
if (!d.mkdir()) {
if (skipExisting && d.isDirectory())
return;
throw new IOException(MessageFormat.format(
JGitText.get().mkDirFailed, d.getAbsolutePath()));
}
}
/**
* Creates the directory named by this abstract pathname, including any
* necessary but nonexistent parent directories. Note that if this operation
* fails it may have succeeded in creating some of the necessary parent
* directories.
*
* @param d
* directory to be created
* @throws IOException
* if creation of {@code d} fails. This may occur if {@code d}
* did exist when the method was called. This can therefore
* cause IOExceptions during race conditions when multiple
* concurrent threads all try to create the same directory.
*/
public static void mkdirs(final File d) throws IOException {
mkdirs(d, false);
}
/**
* Creates the directory named by this abstract pathname, including any
* necessary but nonexistent parent directories. Note that if this operation
* fails it may have succeeded in creating some of the necessary parent
* directories.
*
* @param d
* directory to be created
* @param skipExisting
* if {@code true} skip creation of the given directory if it
* already exists in the file system
* @throws IOException
* if creation of {@code d} fails. This may occur if {@code d}
* did exist when the method was called. This can therefore
* cause IOExceptions during race conditions when multiple
* concurrent threads all try to create the same directory.
*/
public static void mkdirs(final File d, boolean skipExisting)
throws IOException {
if (!d.mkdirs()) {
if (skipExisting && d.isDirectory())
return;
throw new IOException(MessageFormat.format(
JGitText.get().mkDirsFailed, d.getAbsolutePath()));
}
}
/**
* Atomically creates a new, empty file named by this abstract pathname if
* and only if a file with this name does not yet exist. The check for the
* existence of the file and the creation of the file if it does not exist
* are a single operation that is atomic with respect to all other
* filesystem activities that might affect the file.
*
* Note: this method should not be used for file-locking, as the resulting
* protocol cannot be made to work reliably. The {@link FileLock} facility
* should be used instead.
*
* @param f
* the file to be created
* @throws IOException
* if the named file already exists or if an I/O error occurred
*/
public static void createNewFile(File f) throws IOException {
if (!f.createNewFile())
throw new IOException(MessageFormat.format(
JGitText.get().createNewFileFailed, f));
}
/**
* Create a symbolic link
*
* @param path
* @param target
* @throws IOException
* @since 3.0
*/
public static void createSymLink(File path, String target)
throws IOException {
FS.DETECTED.createSymLink(path, target);
}
/**
* @param path
* @return the target of the symbolic link, or null if it is not a symbolic
* link
* @throws IOException
* @since 3.0
*/
public static String readSymLink(File path) throws IOException {
return FS.DETECTED.readSymLink(path);
}
/**
* Create a temporary directory.
*
* @param prefix
* @param suffix
* @param dir
* The parent dir, can be null to use system default temp dir.
* @return the temp dir created.
* @throws IOException
* @since 3.4
*/
public static File createTempDir(String prefix, String suffix, File dir)
throws IOException {
final int RETRIES = 1; // When something bad happens, retry once.
for (int i = 0; i < RETRIES; i++) {
File tmp = File.createTempFile(prefix, suffix, dir);
if (!tmp.delete())
continue;
if (!tmp.mkdir())
continue;
return tmp;
}
throw new IOException(JGitText.get().cannotCreateTempDir);
}
/**
* This will try and make a given path relative to another.
*
* For example, if this is called with the two following paths :
*
*
*
*
* This will return "..\\another_project\\pom.xml".
* base = "c:\\Users\\jdoe\\eclipse\\git\\project"
* other = "c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml"
*
* This method uses {@link File#separator} to split the paths into segments. *
*
* Note that this will return the empty String if base
* and other
are equal.
*
other
should be
* relativized. This will be assumed to denote the path to a
* folder and not a file.
* @param other
* The path that will be made relative to base
.
* @return A relative path that, when resolved against base
,
* will yield the original other
.
* @since 3.7
*/
public static String relativize(String base, String other) {
if (base.equals(other))
return ""; //$NON-NLS-1$
final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
final String[] baseSegments = base.split(Pattern.quote(File.separator));
final String[] otherSegments = other.split(Pattern
.quote(File.separator));
int commonPrefix = 0;
while (commonPrefix < baseSegments.length
&& commonPrefix < otherSegments.length) {
if (ignoreCase
&& baseSegments[commonPrefix]
.equalsIgnoreCase(otherSegments[commonPrefix]))
commonPrefix++;
else if (!ignoreCase
&& baseSegments[commonPrefix]
.equals(otherSegments[commonPrefix]))
commonPrefix++;
else
break;
}
final StringBuilder builder = new StringBuilder();
for (int i = commonPrefix; i < baseSegments.length; i++)
builder.append("..").append(File.separator); //$NON-NLS-1$
for (int i = commonPrefix; i < otherSegments.length; i++) {
builder.append(otherSegments[i]);
if (i < otherSegments.length - 1)
builder.append(File.separator);
}
return builder.toString();
}
}