Accept refs/for/ options via git push -o

Push options are a capability in the Git protocol to transport
arbitrary strings (example usage: topic strings) to the server through
command line flags rather than by the existing Gerrit-specific magic
branch "refs/for/master%..." convention.

For example with Git >= 2.10:

  git push -o r=email -o topic=fix-bug42 origin HEAD:refs/for/master

can now be used instead of:

  git push origin HEAD:refs/for/master%r=email,topic=fix-bug42

Change-Id: I44e7214b9342d461b07c4b6dd638970cf5fb622f
Signed-off-by: Dan Wang <dwwang@google.com>
This commit is contained in:
Dan Wang
2016-08-16 16:29:43 -07:00
committed by Jonathan Nieder
parent 8f23c50d6f
commit 22f5360b59
4 changed files with 77 additions and 7 deletions

View File

@@ -42,6 +42,7 @@ import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
@@ -315,6 +316,8 @@ public class ReceiveCommits {
private final RequestId receiveId;
private MagicBranchInput magicBranch;
private boolean newChangeForAllNotInTarget;
private final ListMultimap<String, String> pushOptions =
LinkedListMultimap.create();
private List<CreateRequest> newChanges = Collections.emptyList();
private final Map<Change.Id, ReplaceRequest> replaceByChange =
@@ -490,6 +493,7 @@ public class ReceiveCommits {
advHooks.add(new HackPushNegotiateHook());
rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
rp.setPostReceiveHook(lazyPostReceive.get());
rp.setAllowPushOptions(true);
}
public void init() {
@@ -915,6 +919,18 @@ public class ReceiveCommits {
}
private void parseCommands(Collection<ReceiveCommand> commands) {
List<String> optionList = rp.getPushOptions();
if (optionList != null) {
for (String option : optionList) {
int e = option.indexOf('=');
if (e > 0) {
pushOptions.put(option.substring(0, e), option.substring(e + 1));
} else {
pushOptions.put(option, "");
}
}
}
logDebug("Parsing {} commands", commands.size());
for (ReceiveCommand cmd : commands) {
if (cmd.getResult() != NOT_ATTEMPTED) {
@@ -1305,14 +1321,14 @@ public class ReceiveCommits {
return new MailRecipients(reviewer, cc);
}
String parse(CmdLineParser clp, Repository repo, Set<String> refs)
throws CmdLineException {
String parse(CmdLineParser clp, Repository repo, Set<String> refs,
ListMultimap<String, String> pushOptions) throws CmdLineException {
String ref = RefNames.fullName(
MagicBranch.getDestBranchName(cmd.getRefName()));
ListMultimap<String, String> options = LinkedListMultimap.create(pushOptions);
int optionStart = ref.indexOf('%');
if (0 < optionStart) {
ListMultimap<String, String> options = LinkedListMultimap.create();
for (String s : COMMAS.split(ref.substring(optionStart + 1))) {
int e = s.indexOf('=');
if (0 < e) {
@@ -1321,10 +1337,13 @@ public class ReceiveCommits {
options.put(s, "");
}
}
clp.parseOptionMap(options);
ref = ref.substring(0, optionStart);
}
if (!options.isEmpty()) {
clp.parseOptionMap(options);
}
// Split the destination branch by branch and topic. The topic
// suffix is entirely optional, so it might not even exist.
String head = readHEAD(repo);
@@ -1347,6 +1366,19 @@ public class ReceiveCommits {
}
}
/**
* Gets an unmodifiable view of the pushOptions.
* <p>
* The collection is empty if the client does not support push options, or if
* the client did not send any options.
*
* @return an unmodifiable view of pushOptions.
*/
@Nullable
public ListMultimap<String, String> getPushOptions() {
return ImmutableListMultimap.copyOf(pushOptions);
}
private void parseMagicBranch(ReceiveCommand cmd) {
// Permit exactly one new change request per push.
if (magicBranch != null) {
@@ -1362,8 +1394,10 @@ public class ReceiveCommits {
String ref;
CmdLineParser clp = optionParserFactory.create(magicBranch);
magicBranch.clp = clp;
try {
ref = magicBranch.parse(clp, repo, rp.getAdvertisedRefs().keySet());
ref = magicBranch.parse(
clp, repo, rp.getAdvertisedRefs().keySet(), pushOptions);
} catch (CmdLineException e) {
if (!clp.wasHelpRequestedByOption()) {
logDebug("Invalid branch syntax");