+ * By default when creating a commit containing only specified
+ * paths an attempt to create an empty commit leads to a
+ * {@link JGitInternalException}. By setting this flag to
+ *
@@ -168,6 +173,7 @@ abstract class BaseDirCacheEditor {
* {@link #finish()}, and only after {@link #entries} is sorted.
*/
protected void replace() {
+ checkNameConflicts();
if (entryCnt < entries.length / 2) {
final DirCacheEntry[] n = new DirCacheEntry[entryCnt];
System.arraycopy(entries, 0, n, 0, entryCnt);
@@ -176,6 +182,76 @@ abstract class BaseDirCacheEditor {
cache.replace(entries, entryCnt);
}
+ private void checkNameConflicts() {
+ int end = entryCnt - 1;
+ for (int eIdx = 0; eIdx < end; eIdx++) {
+ DirCacheEntry e = entries[eIdx];
+ if (e.getStage() != 0) {
+ continue;
+ }
+
+ byte[] ePath = e.path;
+ int prefixLen = lastSlash(ePath) + 1;
+
+ for (int nIdx = eIdx + 1; nIdx < entryCnt; nIdx++) {
+ DirCacheEntry n = entries[nIdx];
+ if (n.getStage() != 0) {
+ continue;
+ }
+
+ byte[] nPath = n.path;
+ if (!startsWith(ePath, nPath, prefixLen)) {
+ // Different prefix; this entry is in another directory.
+ break;
+ }
+
+ int s = nextSlash(nPath, prefixLen);
+ int m = s < nPath.length ? TYPE_TREE : n.getRawMode();
+ int cmp = compareSameName(
+ ePath, prefixLen, ePath.length,
+ nPath, prefixLen, s, m);
+ if (cmp < 0) {
+ break;
+ } else if (cmp == 0) {
+ throw new DirCacheNameConflictException(
+ e.getPathString(),
+ n.getPathString());
+ }
+ }
+ }
+ }
+
+ private static int lastSlash(byte[] path) {
+ for (int i = path.length - 1; i >= 0; i--) {
+ if (path[i] == '/') {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static int nextSlash(byte[] b, int p) {
+ final int n = b.length;
+ for (; p < n; p++) {
+ if (b[p] == '/') {
+ return p;
+ }
+ }
+ return n;
+ }
+
+ private static boolean startsWith(byte[] a, byte[] b, int n) {
+ if (b.length < n) {
+ return false;
+ }
+ for (n--; n >= 0; n--) {
+ if (a[n] != b[n]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Finish, write, commit this change, and release the index lock.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index fa0339544..ecdfe823a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -800,8 +800,11 @@ public class DirCache {
* information. If < 0 the entry does not exist in the index.
* @since 3.4
*/
- public int findEntry(final byte[] p, final int pLen) {
- int low = 0;
+ public int findEntry(byte[] p, int pLen) {
+ return findEntry(0, p, pLen);
+ }
+
+ int findEntry(int low, byte[] p, int pLen) {
int high = entryCnt;
while (low < high) {
int mid = (low + high) >>> 1;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
index da5530666..c10e41608 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
@@ -130,4 +130,9 @@ public class DirCacheBuildIterator extends DirCacheIterator {
if (cur < cnt)
builder.keep(cur, cnt - cur);
}
+
+ @Override
+ protected boolean needsStopWalk() {
+ return ptr < cache.getEntryCount();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 4eb688170..a1e1d15ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -46,6 +46,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1319,11 +1320,12 @@ public class DirCacheCheckout {
if (deleteRecursive && f.isDirectory()) {
FileUtils.delete(f, FileUtils.RECURSIVE);
}
- FileUtils.rename(tmpFile, f);
+ FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
- throw new IOException(MessageFormat.format(
- JGitText.get().renameFileFailed, tmpFile.getPath(),
- f.getPath()));
+ throw new IOException(
+ MessageFormat.format(JGitText.get().renameFileFailed,
+ tmpFile.getPath(), f.getPath()),
+ e);
} finally {
if (tmpFile.exists()) {
FileUtils.delete(tmpFile);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
index 13885d370..c987c964c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
@@ -44,6 +44,10 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.dircache.DirCache.cmp;
+import static org.eclipse.jgit.dircache.DirCacheTree.peq;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -53,6 +57,7 @@ import java.util.List;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.Paths;
/**
* Updates a {@link DirCache} by supplying discrete edit commands.
@@ -72,11 +77,12 @@ public class DirCacheEditor extends BaseDirCacheEditor {
public int compare(final PathEdit o1, final PathEdit o2) {
final byte[] a = o1.path;
final byte[] b = o2.path;
- return DirCache.cmp(a, a.length, b, b.length);
+ return cmp(a, a.length, b, b.length);
}
};
private final List
@@ -179,6 +290,7 @@ public class DirCacheEditor extends BaseDirCacheEditor {
*/
public abstract static class PathEdit {
final byte[] path;
+ boolean replace = true;
/**
* Create a new update command by path name.
@@ -190,6 +302,10 @@ public class DirCacheEditor extends BaseDirCacheEditor {
path = Constants.encode(entryPath);
}
+ PathEdit(byte[] path) {
+ this.path = path;
+ }
+
/**
* Create a new update command for an existing entry instance.
*
@@ -201,6 +317,22 @@ public class DirCacheEditor extends BaseDirCacheEditor {
path = ent.path;
}
+ /**
+ * Configure if a file can replace a directory (or vice versa).
+ *
+ * Default is {@code true} as this is usually the desired behavior.
+ *
+ * @param ok
+ * if true a file can replace a directory, or a directory can
+ * replace a file.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public PathEdit setReplace(boolean ok) {
+ replace = ok;
+ return this;
+ }
+
/**
* Apply the update to a single cache entry matching the path.
*
@@ -212,6 +344,12 @@ public class DirCacheEditor extends BaseDirCacheEditor {
* the path is a new path in the index.
*/
public abstract void apply(DirCacheEntry ent);
+
+ @Override
+ public String toString() {
+ String p = DirCacheEntry.toString(path);
+ return getClass().getSimpleName() + '[' + p + ']';
+ }
}
/**
@@ -272,10 +410,26 @@ public class DirCacheEditor extends BaseDirCacheEditor {
* only the subtree's contents are matched by the command.
* The special case "" (not "/"!) deletes all entries.
*/
- public DeleteTree(final String entryPath) {
- super(
- (entryPath.endsWith("/") || entryPath.length() == 0) ? entryPath //$NON-NLS-1$
- : entryPath + "/"); //$NON-NLS-1$
+ public DeleteTree(String entryPath) {
+ super(entryPath.isEmpty()
+ || entryPath.charAt(entryPath.length() - 1) == '/'
+ ? entryPath
+ : entryPath + '/');
+ }
+
+ DeleteTree(byte[] path) {
+ super(appendSlash(path));
+ }
+
+ private static byte[] appendSlash(byte[] path) {
+ int n = path.length;
+ if (n > 0 && path[n - 1] != '/') {
+ byte[] r = new byte[n + 1];
+ System.arraycopy(path, 0, r, 0, n);
+ r[n] = '/';
+ return r;
+ }
+ return path;
}
public void apply(final DirCacheEntry ent) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index c8bc0960f..4ebf2e0d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -294,6 +294,23 @@ public class DirCacheEntry {
NB.encodeInt16(info, infoOffset + P_FLAGS, flags);
}
+ /**
+ * Duplicate DirCacheEntry with same path and copied info.
+ *
+ * The same path buffer is reused (avoiding copying), however a new info
+ * buffer is created and its contents are copied.
+ *
+ * @param src
+ * entry to clone.
+ * @since 4.2
+ */
+ public DirCacheEntry(DirCacheEntry src) {
+ path = src.path;
+ info = new byte[INFO_LEN];
+ infoOffset = 0;
+ System.arraycopy(src.info, src.infoOffset, info, 0, INFO_LEN);
+ }
+
void write(final OutputStream os) throws IOException {
final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
final int pathLen = path.length;
@@ -745,7 +762,7 @@ public class DirCacheEntry {
}
}
- private static String toString(final byte[] path) {
+ static String toString(final byte[] path) {
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
index c6ea09375..e4db40b88 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
@@ -49,8 +49,10 @@ package org.eclipse.jgit.errors;
import java.io.IOException;
import java.text.MessageFormat;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
/**
@@ -59,6 +61,26 @@ import org.eclipse.jgit.lib.ObjectId;
public class CorruptObjectException extends IOException {
private static final long serialVersionUID = 1L;
+ private ObjectChecker.ErrorType errorType;
+
+ /**
+ * Report a specific error condition discovered in an object.
+ *
+ * @param type
+ * type of error
+ * @param id
+ * identity of the bad object
+ * @param why
+ * description of the error.
+ * @since 4.2
+ */
+ public CorruptObjectException(ObjectChecker.ErrorType type, AnyObjectId id,
+ String why) {
+ super(MessageFormat.format(JGitText.get().objectIsCorrupt3,
+ type.getMessageId(), id.name(), why));
+ this.errorType = type;
+ }
+
/**
* Construct a CorruptObjectException for reporting a problem specified
* object id
@@ -66,8 +88,8 @@ public class CorruptObjectException extends IOException {
* @param id
* @param why
*/
- public CorruptObjectException(final AnyObjectId id, final String why) {
- this(id.toObjectId(), why);
+ public CorruptObjectException(AnyObjectId id, String why) {
+ super(MessageFormat.format(JGitText.get().objectIsCorrupt, id.name(), why));
}
/**
@@ -77,7 +99,7 @@ public class CorruptObjectException extends IOException {
* @param id
* @param why
*/
- public CorruptObjectException(final ObjectId id, final String why) {
+ public CorruptObjectException(ObjectId id, String why) {
super(MessageFormat.format(JGitText.get().objectIsCorrupt, id.name(), why));
}
@@ -87,7 +109,7 @@ public class CorruptObjectException extends IOException {
*
* @param why
*/
- public CorruptObjectException(final String why) {
+ public CorruptObjectException(String why) {
super(why);
}
@@ -105,4 +127,15 @@ public class CorruptObjectException extends IOException {
super(why);
initCause(cause);
}
+
+ /**
+ * Specific error condition identified by {@link ObjectChecker}.
+ *
+ * @return error condition or null.
+ * @since 4.2
+ */
+ @Nullable
+ public ObjectChecker.ErrorType getErrorType() {
+ return errorType;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
similarity index 59%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
index 936fd82bf..5f67e3439 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2009, Jonas Fonseca
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 7073763a7..11aef7fea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -62,6 +62,7 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
@@ -91,7 +92,7 @@ public class DfsPackCompactor {
private final Listtrue
this exception will not be thrown.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public CommitCommand setAllowEmpty(boolean allowEmpty) {
+ this.allowEmpty = Boolean.valueOf(allowEmpty);
+ return this;
+ }
+
/**
* @return the commit message used for the commit
*/
@@ -681,7 +723,7 @@ public class CommitCommand extends GitCommand