Support push to refs/users/self in All-Users repository
Allow a user to push to its refs/users/YY/XXXXXXX branch as
refs/users/self. The user branch contains the sharded account ID in
the ref name, but this is an implementation detail that should be
hidden from the user. On the other hand users should be able to push
to their user branch easily.
A user can also push a commit on its user branch for review by pushing
to refs/for/refs/users/self.
Change-Id: I4883cb13d77ef181896d2f1f7c4c234994a1f24a
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index e62b2e9..2d3f98a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -37,6 +37,7 @@
import com.google.gerrit.acceptance.AccountCreator;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.StarsInput;
@@ -57,6 +58,8 @@
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.FakeEmailSender.Message;
import com.google.inject.Inject;
@@ -381,6 +384,63 @@
}
@Test
+ public void pushToUserBranch() throws Exception {
+ // change something in the user preferences to ensure that the user branch
+ // is created
+ GeneralPreferencesInfo input = new GeneralPreferencesInfo();
+ input.changesPerPage =
+ GeneralPreferencesInfo.defaults().changesPerPage + 10;
+ gApi.accounts().self().setPreferences(input);
+
+ removeExclusiveReadPermissionOnAllUsers();
+ String userRefName = RefNames.refsUsers(admin.id);
+ grant(Permission.PUSH, allUsers, userRefName);
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+ allUsersRepo.reset("userRef");
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ push.to(userRefName).assertOkStatus();
+
+ push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
+ }
+
+ @Test
+ public void pushToUserBranchForReview() throws Exception {
+ // change something in the user preferences to ensure that the user branch
+ // is created
+ GeneralPreferencesInfo input = new GeneralPreferencesInfo();
+ input.changesPerPage =
+ GeneralPreferencesInfo.defaults().changesPerPage + 10;
+ gApi.accounts().self().setPreferences(input);
+
+ removeExclusiveReadPermissionOnAllUsers();
+ String userRefName = RefNames.refsUsers(admin.id);
+ grant(Permission.PUSH, allUsers, MagicBranch.NEW_CHANGE + userRefName);
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+ allUsersRepo.reset("userRef");
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
+ r.assertOkStatus();
+ assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
+
+ push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
+ r.assertOkStatus();
+ assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
+ }
+
+ private void removeExclusiveReadPermissionOnAllUsers() throws Exception {
+ ProjectConfig cfg = projectCache.checkedGet(allUsers).getConfig();
+ cfg.getAccessSection(RefNames.REFS_USERS + "*", true)
+ .remove(new Permission(Permission.READ));
+ saveProjectConfig(allUsers, cfg);
+ }
+
+ @Test
public void addGpgKey() throws Exception {
TestKey key = validKeyWithoutExpiration();
String id = key.getKeyIdString();
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 b52844d..f7c2dcc 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
@@ -632,7 +632,7 @@
}
Set<Branch.NameKey> branches = new HashSet<>();
- for (ReceiveCommand c : commands) {
+ for (ReceiveCommand c : batch.getCommands()) {
if (c.getResult() == OK) {
if (c.getType() == ReceiveCommand.Type.UPDATE) { // aka fast-forward
tagCache.updateFastForward(project.getNameKey(),
@@ -890,6 +890,19 @@
continue;
}
+ if (projectControl.getProjectState().isAllUsers()
+ && RefNames.REFS_USERS_SELF.equals(cmd.getRefName())) {
+ final ReceiveCommand orgCmd = cmd;
+ cmd = new ReceiveCommand(cmd.getOldId(), cmd.getNewId(),
+ RefNames.refsUsers(user.getAccountId()), cmd.getType()) {
+ @Override
+ public void setResult(Result s, String m) {
+ super.setResult(s, m);
+ orgCmd.setResult(s, m);
+ }
+ };
+ }
+
Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
if (m.matches()) {
// The referenced change must exist and must still be open.
@@ -1303,6 +1316,10 @@
reject(cmd, "see help");
return;
}
+ if (projectControl.getProjectState().isAllUsers()
+ && RefNames.REFS_USERS_SELF.equals(ref)) {
+ ref = RefNames.refsUsers(user.getAccountId());
+ }
if (!rp.getAdvertisedRefs().containsKey(ref) && !ref.equals(readHEAD(repo))) {
if (ref.startsWith(Constants.R_HEADS)) {
String n = ref.substring(Constants.R_HEADS.length());