Allow to create a change for the initial commit of refs/meta/config

Normally you can only push commits for review to branches that exist.
But there is an exception for the branch to which HEAD in the remote
repository points (usually master). The same is true on submit, changes
can only be submitted if the destination branch exists, except if the
destination branch is the branch to which HEAD points. Relax this
further and also allow to push an initial commit for review to the
refs/meta/config branch and also allow to submit such a change.

This is useful to setup an initial project configuration when the
refs/meta/config branch is missing.

Actually already today it is possible to create a change with an initial
commit for the refs/meta/config branch, but then this change is not
submittable because the destination branch is missing. The creation of
such a change is possible by editing the access rights in the WebUI and
then clicking on the 'Save for Review' button. This creates a change for
the refs/meta/config branch regardless of whether the branch exists.

Change-Id: I2e28fdd5384149d0ee32b10641652803c73842ab
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin 2017-08-18 12:50:46 +02:00
parent 9f3b888fcf
commit f5a46a4109
4 changed files with 76 additions and 3 deletions

View File

@ -62,6 +62,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.receive.ReceiveConstants;
@ -83,8 +84,11 @@ import java.util.regex.Pattern;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
@ -182,6 +186,69 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
}
@Test
public void pushInitialCommitForRefsMetaConfigBranch() throws Exception {
// delete refs/meta/config
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG);
u.setForceUpdate(true);
u.setExpectedOldObjectId(repo.resolve(RefNames.REFS_CONFIG));
assertThat(u.delete(rw)).isEqualTo(Result.FORCED);
}
RevCommit c =
testRepo
.commit()
.message("Initial commit")
.author(admin.getIdent())
.committer(admin.getIdent())
.insertChangeId()
.create();
String id = GitUtil.getChangeId(testRepo, c).get();
testRepo.reset(c);
String r = "refs/for/" + RefNames.REFS_CONFIG;
PushResult pr = pushHead(testRepo, r, false);
assertPushOk(pr, r);
ChangeInfo change = gApi.changes().id(id).info();
assertThat(change.branch).isEqualTo(RefNames.REFS_CONFIG);
assertThat(change.status).isEqualTo(ChangeStatus.NEW);
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.resolve(RefNames.REFS_CONFIG)).isNull();
}
gApi.changes().id(change.id).current().review(ReviewInput.approve());
gApi.changes().id(change.id).current().submit();
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.resolve(RefNames.REFS_CONFIG)).isEqualTo(c);
}
}
@Test
public void pushInitialCommitForNormalNonExistingBranchFails() throws Exception {
RevCommit c =
testRepo
.commit()
.message("Initial commit")
.author(admin.getIdent())
.committer(admin.getIdent())
.insertChangeId()
.create();
testRepo.reset(c);
String r = "refs/for/foo";
PushResult pr = pushHead(testRepo, r, false);
assertPushRejected(pr, r, "branch foo not found");
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.resolve("foo")).isNull();
}
}
@Test
public void output() throws Exception {
String url = canonicalWebUrl.get();

View File

@ -15,6 +15,7 @@
package com.google.gerrit.server;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@ -37,7 +38,8 @@ public class ProjectUtil {
try (Repository repo = repoManager.openRepository(branch.getParentKey())) {
boolean exists = repo.getRefDatabase().exactRef(branch.get()) != null;
if (!exists) {
exists = repo.getFullBranch().equals(branch.get());
exists =
repo.getFullBranch().equals(branch.get()) || RefNames.REFS_CONFIG.equals(branch.get());
}
return exists;
}

View File

@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.Maps;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@ -135,7 +136,8 @@ public class MergeOpRepoManager implements AutoCloseable {
update = or.repo.updateRef(name.get());
if (update.getOldObjectId() != null) {
oldTip = or.rw.parseCommit(update.getOldObjectId());
} else if (Objects.equals(or.repo.getFullBranch(), name.get())) {
} else if (Objects.equals(or.repo.getFullBranch(), name.get())
|| Objects.equals(RefNames.REFS_CONFIG, name.get())) {
oldTip = null;
update.setExpectedOldObjectId(ObjectId.zeroId());
} else {

View File

@ -1430,7 +1430,9 @@ class ReceiveCommits {
logDebug("Handling {}", RefNames.REFS_USERS_SELF);
ref = RefNames.refsUsers(user.getAccountId());
}
if (!rp.getAdvertisedRefs().containsKey(ref) && !ref.equals(readHEAD(repo))) {
if (!rp.getAdvertisedRefs().containsKey(ref)
&& !ref.equals(readHEAD(repo))
&& !ref.equals(RefNames.REFS_CONFIG)) {
logDebug("Ref {} not found", ref);
if (ref.startsWith(Constants.R_HEADS)) {
String n = ref.substring(Constants.R_HEADS.length());