Browse Source

Write packed-refs directly when cloning

When we are cloning we have no refs at all yet, and there cannot
(or at least should not) be any other thread doing something with
refs yet.

Locking loose refs is thus not needed, since there are no loose
refs yet and nothing should be trying to create them concurrently.

Let's skip the whole loose ref locking when we are cloning a repository.
As a result, JGit will write the refs directly to the packed-refs
file, and will not create the refs/remotes/ directories nor the
lock files underneath when cloning and packed refs are used. Since
no lock files are created, any problems on case-insensitive file
systems with tag or branch names that differ only in case are avoided
during cloning.

Detect if we are cloning based on the following heuristics:
* HEAD is a dangling symref
* There is no loose ref
* There is no packed-refs file

Note, however, that there may still be problems with such tag or
branch names later on. This is primarily a five-minutes-past-twelve
stop-gap measure to resolve the referenced bug, which affects the
Oxygen.2 release.

Bug: 528497
Change-Id: I57860c29c210568165276a123b855e462b6a107a
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-4.9
Thomas Wolf 7 years ago committed by Matthias Sohn
parent
commit
c86327979f
  1. 16
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
  2. 26
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java

16
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java

@ -179,11 +179,19 @@ class PackedBatchRefUpdate extends BatchRefUpdate {
Map<String, LockFile> locks = null;
refdb.inProcessPackedRefsLock.lock();
try {
locks = lockLooseRefs(pending);
if (locks == null) {
return;
PackedRefList oldPackedList;
if (!refdb.isInClone()) {
locks = lockLooseRefs(pending);
if (locks == null) {
return;
}
oldPackedList = refdb.pack(locks);
} else {
// During clone locking isn't needed since no refs exist yet.
// This also helps to avoid problems with refs only differing in
// case on a case insensitive filesystem (bug 528497)
oldPackedList = refdb.getPackedRefs();
}
PackedRefList oldPackedList = refdb.pack(locks);
RefList<Ref> newRefs = applyUpdates(walk, oldPackedList, pending);
if (newRefs == null) {
return;

26
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java

@ -65,6 +65,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.nio.file.Files;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.text.MessageFormat;
@ -901,7 +902,7 @@ public class RefDirectory extends RefDatabase {
return ref;
}
private PackedRefList getPackedRefs() throws IOException {
PackedRefList getPackedRefs() throws IOException {
boolean trustFolderStat = getRepository().getConfig().getBoolean(
ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
@ -1188,6 +1189,29 @@ public class RefDirectory extends RefDatabase {
&& buf[4] == ' ';
}
/**
* Detect if we are in a clone command execution
*
* @return {@code true} if we are currently cloning a repository
* @throws IOException
*/
boolean isInClone() throws IOException {
return hasDanglingHead() && !packedRefsFile.exists() && !hasLooseRef();
}
private boolean hasDanglingHead() throws IOException {
Ref head = exactRef(Constants.HEAD);
if (head != null) {
ObjectId id = head.getObjectId();
return id == null || id.equals(ObjectId.zeroId());
}
return false;
}
private boolean hasLooseRef() throws IOException {
return Files.walk(refsDir.toPath()).anyMatch(Files::isRegularFile);
}
/** If the parent should fire listeners, fires them. */
void fireRefsChanged() {
final int last = lastNotifiedModCnt.get();

Loading…
Cancel
Save