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:
committed by
Jonathan Nieder
parent
8f23c50d6f
commit
22f5360b59
@@ -151,8 +151,15 @@ public class GitUtil {
|
|||||||
|
|
||||||
public static PushResult pushHead(TestRepository<?> testRepo, String ref,
|
public static PushResult pushHead(TestRepository<?> testRepo, String ref,
|
||||||
boolean pushTags, boolean force) throws GitAPIException {
|
boolean pushTags, boolean force) throws GitAPIException {
|
||||||
|
return pushHead(testRepo, ref, pushTags, force, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PushResult pushHead(TestRepository<?> testRepo, String ref,
|
||||||
|
boolean pushTags, boolean force, List<String> pushOptions)
|
||||||
|
throws GitAPIException {
|
||||||
PushCommand pushCmd = testRepo.git().push();
|
PushCommand pushCmd = testRepo.git().push();
|
||||||
pushCmd.setForce(force);
|
pushCmd.setForce(force);
|
||||||
|
pushCmd.setPushOptions(pushOptions);
|
||||||
pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref));
|
pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref));
|
||||||
if (pushTags) {
|
if (pushTags) {
|
||||||
pushCmd.setPushTags();
|
pushCmd.setPushTags();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.gerrit.acceptance.GitUtil.pushHead;
|
import static com.google.gerrit.acceptance.GitUtil.pushHead;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
@@ -136,6 +137,7 @@ public class PushOneCommit {
|
|||||||
private String changeId;
|
private String changeId;
|
||||||
private Tag tag;
|
private Tag tag;
|
||||||
private boolean force;
|
private boolean force;
|
||||||
|
private List<String> pushOptions;
|
||||||
|
|
||||||
private final TestRepository<?>.CommitBuilder commitBuilder;
|
private final TestRepository<?>.CommitBuilder commitBuilder;
|
||||||
|
|
||||||
@@ -275,8 +277,8 @@ public class PushOneCommit {
|
|||||||
}
|
}
|
||||||
tagCommand.call();
|
tagCommand.call();
|
||||||
}
|
}
|
||||||
return new Result(ref, pushHead(testRepo, ref, tag != null, force), c,
|
return new Result(ref,
|
||||||
subject);
|
pushHead(testRepo, ref, tag != null, force, pushOptions), c, subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTag(final Tag tag) {
|
public void setTag(final Tag tag) {
|
||||||
@@ -287,6 +289,14 @@ public class PushOneCommit {
|
|||||||
this.force = force;
|
this.force = force;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getPushOptions() {
|
||||||
|
return pushOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPushOptions(List<String> pushOptions) {
|
||||||
|
this.pushOptions = pushOptions;
|
||||||
|
}
|
||||||
|
|
||||||
public void noParents() {
|
public void noParents() {
|
||||||
commitBuilder.noParents();
|
commitBuilder.noParents();
|
||||||
}
|
}
|
||||||
@@ -326,6 +336,10 @@ public class PushOneCommit {
|
|||||||
return commit;
|
return commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertPushOptions(List<String> pushOptions) {
|
||||||
|
assertEquals(pushOptions, getPushOptions());
|
||||||
|
}
|
||||||
|
|
||||||
public void assertChange(Change.Status expectedStatus,
|
public void assertChange(Change.Status expectedStatus,
|
||||||
String expectedTopic, TestAccount... expectedReviewers)
|
String expectedTopic, TestAccount... expectedReviewers)
|
||||||
throws OrmException, NoSuchChangeException {
|
throws OrmException, NoSuchChangeException {
|
||||||
|
|||||||
@@ -175,6 +175,21 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
|
|||||||
r.assertChange(Change.Status.NEW, topic);
|
r.assertChange(Change.Status.NEW, topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pushForMasterWithTopicOption() throws Exception {
|
||||||
|
String topicOption = "topic=myTopic";
|
||||||
|
List<String> pushOptions = new ArrayList<>();
|
||||||
|
pushOptions.add(topicOption);
|
||||||
|
|
||||||
|
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||||
|
push.setPushOptions(pushOptions);
|
||||||
|
PushOneCommit.Result r = push.to("refs/for/master");
|
||||||
|
|
||||||
|
r.assertOkStatus();
|
||||||
|
r.assertChange(Change.Status.NEW, "myTopic");
|
||||||
|
r.assertPushOptions(pushOptions);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pushForMasterWithNotify() throws Exception {
|
public void pushForMasterWithNotify() throws Exception {
|
||||||
TestAccount user2 = accounts.user2();
|
TestAccount user2 = accounts.user2();
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import com.google.common.collect.FluentIterable;
|
|||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.LinkedListMultimap;
|
import com.google.common.collect.LinkedListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
@@ -315,6 +316,8 @@ public class ReceiveCommits {
|
|||||||
private final RequestId receiveId;
|
private final RequestId receiveId;
|
||||||
private MagicBranchInput magicBranch;
|
private MagicBranchInput magicBranch;
|
||||||
private boolean newChangeForAllNotInTarget;
|
private boolean newChangeForAllNotInTarget;
|
||||||
|
private final ListMultimap<String, String> pushOptions =
|
||||||
|
LinkedListMultimap.create();
|
||||||
|
|
||||||
private List<CreateRequest> newChanges = Collections.emptyList();
|
private List<CreateRequest> newChanges = Collections.emptyList();
|
||||||
private final Map<Change.Id, ReplaceRequest> replaceByChange =
|
private final Map<Change.Id, ReplaceRequest> replaceByChange =
|
||||||
@@ -490,6 +493,7 @@ public class ReceiveCommits {
|
|||||||
advHooks.add(new HackPushNegotiateHook());
|
advHooks.add(new HackPushNegotiateHook());
|
||||||
rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
|
rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
|
||||||
rp.setPostReceiveHook(lazyPostReceive.get());
|
rp.setPostReceiveHook(lazyPostReceive.get());
|
||||||
|
rp.setAllowPushOptions(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
@@ -915,6 +919,18 @@ public class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseCommands(Collection<ReceiveCommand> commands) {
|
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());
|
logDebug("Parsing {} commands", commands.size());
|
||||||
for (ReceiveCommand cmd : commands) {
|
for (ReceiveCommand cmd : commands) {
|
||||||
if (cmd.getResult() != NOT_ATTEMPTED) {
|
if (cmd.getResult() != NOT_ATTEMPTED) {
|
||||||
@@ -1305,14 +1321,14 @@ public class ReceiveCommits {
|
|||||||
return new MailRecipients(reviewer, cc);
|
return new MailRecipients(reviewer, cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
String parse(CmdLineParser clp, Repository repo, Set<String> refs)
|
String parse(CmdLineParser clp, Repository repo, Set<String> refs,
|
||||||
throws CmdLineException {
|
ListMultimap<String, String> pushOptions) throws CmdLineException {
|
||||||
String ref = RefNames.fullName(
|
String ref = RefNames.fullName(
|
||||||
MagicBranch.getDestBranchName(cmd.getRefName()));
|
MagicBranch.getDestBranchName(cmd.getRefName()));
|
||||||
|
|
||||||
|
ListMultimap<String, String> options = LinkedListMultimap.create(pushOptions);
|
||||||
int optionStart = ref.indexOf('%');
|
int optionStart = ref.indexOf('%');
|
||||||
if (0 < optionStart) {
|
if (0 < optionStart) {
|
||||||
ListMultimap<String, String> options = LinkedListMultimap.create();
|
|
||||||
for (String s : COMMAS.split(ref.substring(optionStart + 1))) {
|
for (String s : COMMAS.split(ref.substring(optionStart + 1))) {
|
||||||
int e = s.indexOf('=');
|
int e = s.indexOf('=');
|
||||||
if (0 < e) {
|
if (0 < e) {
|
||||||
@@ -1321,10 +1337,13 @@ public class ReceiveCommits {
|
|||||||
options.put(s, "");
|
options.put(s, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clp.parseOptionMap(options);
|
|
||||||
ref = ref.substring(0, optionStart);
|
ref = ref.substring(0, optionStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.isEmpty()) {
|
||||||
|
clp.parseOptionMap(options);
|
||||||
|
}
|
||||||
|
|
||||||
// Split the destination branch by branch and topic. The topic
|
// Split the destination branch by branch and topic. The topic
|
||||||
// suffix is entirely optional, so it might not even exist.
|
// suffix is entirely optional, so it might not even exist.
|
||||||
String head = readHEAD(repo);
|
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) {
|
private void parseMagicBranch(ReceiveCommand cmd) {
|
||||||
// Permit exactly one new change request per push.
|
// Permit exactly one new change request per push.
|
||||||
if (magicBranch != null) {
|
if (magicBranch != null) {
|
||||||
@@ -1362,8 +1394,10 @@ public class ReceiveCommits {
|
|||||||
String ref;
|
String ref;
|
||||||
CmdLineParser clp = optionParserFactory.create(magicBranch);
|
CmdLineParser clp = optionParserFactory.create(magicBranch);
|
||||||
magicBranch.clp = clp;
|
magicBranch.clp = clp;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ref = magicBranch.parse(clp, repo, rp.getAdvertisedRefs().keySet());
|
ref = magicBranch.parse(
|
||||||
|
clp, repo, rp.getAdvertisedRefs().keySet(), pushOptions);
|
||||||
} catch (CmdLineException e) {
|
} catch (CmdLineException e) {
|
||||||
if (!clp.wasHelpRequestedByOption()) {
|
if (!clp.wasHelpRequestedByOption()) {
|
||||||
logDebug("Invalid branch syntax");
|
logDebug("Invalid branch syntax");
|
||||||
|
|||||||
Reference in New Issue
Block a user