summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorLee Worrall <worrall.la@gmail.com>2020-10-10 11:31:19 +0800
committerThomas Wolf <thomas.wolf@paranor.ch>2020-11-26 21:14:08 +0100
commit9ebbfe93bb3ba0ea170d330fe227f1d8182d7d64 (patch)
tree52d2401beac6b58d2eb485cd9aebe7958d3469b2 /org.eclipse.jgit
parentdfa29458e7b187b45e9fb48791ddb71ffac5ad93 (diff)
downloadjgit-9ebbfe93bb3ba0ea170d330fe227f1d8182d7d64.tar.gz
jgit-9ebbfe93bb3ba0ea170d330fe227f1d8182d7d64.zip
Add support for reading symrefs from pack capabilities
A SymbolicRef is added to the advertised refs for any symref in capabilities whose target is an advertised ref; this may replace an existing entry, such as HEAD. When cloning, if any advertised HEAD is symbolic then use the target rather than looking for an advertised ref with a matching objectId. Add --symref option to LsRemote command. Bug: 514052 Change-Id: Idfb48e6f6e8dcfe57a6896883fe6d84d533aa9d0 Signed-off-by: Lee Worrall <worrall.la@gmail.com>
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java105
2 files changed, 109 insertions, 0 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 30d7f9adc4..aba86fc361 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -413,6 +413,10 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return null;
}
+ if (idHEAD != null && idHEAD.isSymbolic()) {
+ return idHEAD.getTarget();
+ }
+
Ref master = result.getAdvertisedRef(Constants.R_HEADS
+ Constants.MASTER);
ObjectId objectId = master != null ? master.getObjectId() : null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index 1417faee80..3a36398629 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -21,8 +21,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -35,6 +38,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.TimeoutInputStream;
import org.eclipse.jgit.util.io.TimeoutOutputStream;
@@ -49,6 +53,8 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream;
*/
abstract class BasePackConnection extends BaseConnection {
+ protected static final String CAPABILITY_SYMREF_PREFIX = "symref="; //$NON-NLS-1$
+
/** The repository this transport fetches into, or pushes out of. */
protected final Repository local;
@@ -228,10 +234,109 @@ abstract class BasePackConnection extends BaseConnection {
throw duplicateAdvertisement(name);
}
}
+ updateWithSymRefs(avail, extractSymRefsFromCapabilities(remoteCapablities));
available(avail);
}
/**
+ * Finds values in the given capabilities of the form:
+ *
+ * <pre>
+ * symref=<em>source</em>:<em>target</em>
+ * </pre>
+ *
+ * And returns a Map of source->target entries.
+ *
+ * @param capabilities
+ * the capabilities lines
+ * @return a Map of the symref entries from capabilities
+ * @throws NullPointerException
+ * if capabilities, or any entry in it, is null
+ */
+ static Map<String, String> extractSymRefsFromCapabilities(Collection<String> capabilities) {
+ final Map<String, String> symRefs = new LinkedHashMap<>();
+ for (String option : capabilities) {
+ if (option.startsWith(CAPABILITY_SYMREF_PREFIX)) {
+ String[] symRef = option
+ .substring(CAPABILITY_SYMREF_PREFIX.length())
+ .split(":", 2); //$NON-NLS-1$
+ if (symRef.length == 2) {
+ symRefs.put(symRef[0], symRef[1]);
+ }
+ }
+ }
+ return symRefs;
+ }
+
+ /**
+ * Updates the given refMap with {@link SymbolicRef}s defined by the given
+ * symRefs.
+ * <p>
+ * For each entry, symRef, in symRefs, whose value is a key in refMap, adds
+ * a new entry to refMap with that same key and value of a new
+ * {@link SymbolicRef} with source=symRef.key and
+ * target=refMap.get(symRef.value), then removes that entry from symRefs.
+ * <p>
+ * If refMap already contains an entry for symRef.key, it is replaced.
+ * </p>
+ * </p>
+ * <p>
+ * For example, given:
+ * </p>
+ *
+ * <pre>
+ * refMap.put("refs/heads/main", ref);
+ * symRefs.put("HEAD", "refs/heads/main");
+ * </pre>
+ *
+ * then:
+ *
+ * <pre>
+ * updateWithSymRefs(refMap, symRefs);
+ * </pre>
+ *
+ * has the <em>effect</em> of:
+ *
+ * <pre>
+ * refMap.put("HEAD",
+ * new SymbolicRef("HEAD", refMap.get(symRefs.remove("HEAD"))))
+ * </pre>
+ * <p>
+ * Any entry in symRefs whose value is not a key in refMap is ignored. Any
+ * circular symRefs are ignored.
+ * </p>
+ * <p>
+ * Upon completion, symRefs will contain only any unresolvable entries.
+ * </p>
+ *
+ * @param refMap
+ * a non-null, modifiable, Map to update, and the provider of
+ * symref targets.
+ * @param symRefs
+ * a non-null, modifiable, Map of symrefs.
+ * @throws NullPointerException
+ * if refMap or symRefs is null
+ */
+ static void updateWithSymRefs(Map<String, Ref> refMap, Map<String, String> symRefs) {
+ boolean haveNewRefMapEntries = !refMap.isEmpty();
+ while (!symRefs.isEmpty() && haveNewRefMapEntries) {
+ haveNewRefMapEntries = false;
+ final Iterator<Map.Entry<String, String>> iterator = symRefs.entrySet().iterator();
+ while (iterator.hasNext()) {
+ final Map.Entry<String, String> symRef = iterator.next();
+ if (!symRefs.containsKey(symRef.getValue())) { // defer forward reference
+ final Ref r = refMap.get(symRef.getValue());
+ if (r != null) {
+ refMap.put(symRef.getKey(), new SymbolicRef(symRef.getKey(), r));
+ haveNewRefMapEntries = true;
+ iterator.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Create an exception to indicate problems finding a remote repository. The
* caller is expected to throw the returned exception.
*