diff options
author | Lee Worrall <worrall.la@gmail.com> | 2020-10-10 11:31:19 +0800 |
---|---|---|
committer | Thomas Wolf <thomas.wolf@paranor.ch> | 2020-11-26 21:14:08 +0100 |
commit | 9ebbfe93bb3ba0ea170d330fe227f1d8182d7d64 (patch) | |
tree | 52d2401beac6b58d2eb485cd9aebe7958d3469b2 /org.eclipse.jgit | |
parent | dfa29458e7b187b45e9fb48791ddb71ffac5ad93 (diff) | |
download | jgit-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.java | 4 | ||||
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java | 105 |
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. * |