Allow opening new changes on existing commits
The %base argument can be used with refs/for/ to identify a specific
revision the server should start to look for new commits at. Any
commits in the range $base..$tip will be opened as a new change,
even if the commit already has another change on a different branch.
Change-Id: Ic2ea9b7c53df3b99d29043cd6a6fd68214f3398f
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 83ce6ce..cbb152c3 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -353,6 +353,20 @@
set the merge can be reattempted by using `%submit` again.
+[[base]]
+Selecting Merge Base
+~~~~~~~~~~~~~~~~~~~~
+
+By default new changes are opened only for new unique commits
+that have never before been seen by the Gerrit server. Clients
+may override that behavior and force new changes to be created
+by setting the merge base SHA-1 using the '%base' argument:
+
+====
+ git push ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/master%base=$(git rev-parse origin/master)
+====
+
+
repo upload
-----------
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 45c6e5a..05ee2d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -1003,6 +1003,10 @@
RefControl ctl;
Set<Account.Id> reviewer = Sets.newLinkedHashSet();
Set<Account.Id> cc = Sets.newLinkedHashSet();
+ RevCommit baseCommit;
+
+ @Option(name = "--base", metaVar = "BASE", usage = "merge base of changes")
+ ObjectId base;
@Option(name = "--topic", metaVar = "NAME", usage = "attach topic to changes")
String topic;
@@ -1147,13 +1151,31 @@
reject(cmd, "submit not allowed");
}
+ RevWalk walk = rp.getRevWalk();
+ if (magicBranch.base != null) {
+ try {
+ magicBranch.baseCommit = walk.parseCommit(magicBranch.base);
+ } catch (IncorrectObjectTypeException notCommit) {
+ reject(cmd, "base must be a commit");
+ return;
+ } catch (MissingObjectException e) {
+ reject(cmd, "base not found");
+ return;
+ } catch (IOException e) {
+ log.warn(String.format(
+ "Project %s cannot read %s",
+ project.getName(), magicBranch.base.name()), e);
+ reject(cmd, "internal server error");
+ return;
+ }
+ }
+
// Validate that the new commits are connected with the target
// branch. If they aren't, we want to abort. We do this check by
// looking to see if we can compute a merge base between the new
// commits and the target branch head.
//
try {
- final RevWalk walk = rp.getRevWalk();
final RevCommit tip = walk.parseCommit(magicBranch.cmd.getNewId());
Ref targetRef = rp.getAdvertisedRefs().get(magicBranch.ctl.getRefName());
if (targetRef == null || targetRef.getObjectId() == null) {
@@ -1283,10 +1305,14 @@
try {
Set<ObjectId> existing = Sets.newHashSet();
walk.markStart(walk.parseCommit(magicBranch.cmd.getNewId()));
- markHeadsAsUninteresting(
- walk,
- existing,
- magicBranch.ctl != null ? magicBranch.ctl.getRefName() : null);
+ if (magicBranch.baseCommit != null) {
+ walk.markUninteresting(magicBranch.baseCommit);
+ } else {
+ markHeadsAsUninteresting(
+ walk,
+ existing,
+ magicBranch.ctl != null ? magicBranch.ctl.getRefName() : null);
+ }
List<ChangeLookup> pending = Lists.newArrayList();
final Set<Change.Key> newChangeIds = new HashSet<Change.Key>();