Refactor magic branch values into MagicBranchInput

Perform a simple refactoring of ReceiveCommits to move input data
related to the single pending magic branch operation into a single
inner class. This makes it clear several fields are actually tied
to the one magic branch we are processing in this invocation.

The refactoring supports a future change to add argument parsing to
a refspec. The magic branch's refspec will get split further to supply
option values into the MagicBranchInput, probably by using args4j and
@Option annotations within the input class.

Change-Id: I3ad5808ed6c484d29a591121cf4d09e9521a61d7
This commit is contained in:
Shawn Pearce 2013-02-24 15:13:27 -08:00
parent cdcc88ddc4
commit 0795c58a31
2 changed files with 83 additions and 59 deletions

View File

@ -27,6 +27,7 @@ import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_RE
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Strings;
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;
@ -226,8 +227,8 @@ public class ReceiveCommits {
} }
}; };
private final Set<Account.Id> reviewerId = new HashSet<Account.Id>(); private Set<Account.Id> reviewersFromCommandLine = Sets.newLinkedHashSet();
private final Set<Account.Id> ccId = new HashSet<Account.Id>(); private Set<Account.Id> ccFromCommandLine = Sets.newLinkedHashSet();
private final IdentifiedUser currentUser; private final IdentifiedUser currentUser;
private final ReviewDb db; private final ReviewDb db;
@ -257,9 +258,7 @@ public class ReceiveCommits {
private final Repository repo; private final Repository repo;
private final ReceivePack rp; private final ReceivePack rp;
private final NoteMap rejectCommits; private final NoteMap rejectCommits;
private ReceiveCommand newChange; private MagicBranchInput magicBranch;
private Branch.NameKey destBranch;
private RefControl destBranchCtl;
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 =
@ -270,8 +269,6 @@ public class ReceiveCommits {
private Map<ObjectId, Ref> refsById; private Map<ObjectId, Ref> refsById;
private Map<String, Ref> allRefs; private Map<String, Ref> allRefs;
private String destTopicName;
private final SubmoduleOp.Factory subOpFactory; private final SubmoduleOp.Factory subOpFactory;
private final List<CommitValidationMessage> messages = new ArrayList<CommitValidationMessage>(); private final List<CommitValidationMessage> messages = new ArrayList<CommitValidationMessage>();
@ -376,12 +373,12 @@ public class ReceiveCommits {
/** Add reviewers for new (or updated) changes. */ /** Add reviewers for new (or updated) changes. */
public void addReviewers(Collection<Account.Id> who) { public void addReviewers(Collection<Account.Id> who) {
reviewerId.addAll(who); reviewersFromCommandLine.addAll(who);
} }
/** Add reviewers for new (or updated) changes. */ /** Add reviewers for new (or updated) changes. */
public void addExtraCC(Collection<Account.Id> who) { public void addExtraCC(Collection<Account.Id> who) {
ccId.addAll(who); ccFromCommandLine.addAll(who);
} }
/** Set a message sender for this operation. */ /** Set a message sender for this operation. */
@ -519,7 +516,7 @@ public class ReceiveCommits {
batch.setRefLogMessage("push", true); batch.setRefLogMessage("push", true);
parseCommands(commands); parseCommands(commands);
if (newChange != null && newChange.getResult() == NOT_ATTEMPTED) { if (magicBranch != null && magicBranch.cmd.getResult() == NOT_ATTEMPTED) {
newChanges = selectNewChanges(); newChanges = selectNewChanges();
} }
preparePatchSetsForReplace(); preparePatchSetsForReplace();
@ -636,7 +633,7 @@ public class ReceiveCommits {
int okToInsert = 0; int okToInsert = 0;
for (ReplaceRequest replace : replaceByChange.values()) { for (ReplaceRequest replace : replaceByChange.values()) {
if (replace.inputCommand == newChange) { if (magicBranch != null && replace.inputCommand == magicBranch.cmd) {
replaceCount++; replaceCount++;
if (replace.cmd != null && replace.cmd.getResult() == OK) { if (replace.cmd != null && replace.cmd.getResult() == OK) {
@ -663,7 +660,7 @@ public class ReceiveCommits {
} }
} }
if (newChange == null || newChange.getResult() != NOT_ATTEMPTED) { if (magicBranch == null || magicBranch.cmd.getResult() != NOT_ATTEMPTED) {
// refs/for/ or refs/drafts/ not used, or it already failed earlier. // refs/for/ or refs/drafts/ not used, or it already failed earlier.
// No need to continue. // No need to continue.
return; return;
@ -678,7 +675,7 @@ public class ReceiveCommits {
if (okToInsert != replaceCount + newChanges.size()) { if (okToInsert != replaceCount + newChanges.size()) {
// One or more new references failed to create. Assume the // One or more new references failed to create. Assume the
// system isn't working correctly anymore and abort. // system isn't working correctly anymore and abort.
reject(newChange, "internal server error"); reject(magicBranch.cmd, "internal server error");
log.error(String.format( log.error(String.format(
"Only %d of %d new change refs created in %s; aborting", "Only %d of %d new change refs created in %s; aborting",
okToInsert, replaceCount + newChanges.size(), project.getName())); okToInsert, replaceCount + newChanges.size(), project.getName()));
@ -688,7 +685,7 @@ public class ReceiveCommits {
try { try {
List<CheckedFuture<?, OrmException>> futures = Lists.newArrayList(); List<CheckedFuture<?, OrmException>> futures = Lists.newArrayList();
for (ReplaceRequest replace : replaceByChange.values()) { for (ReplaceRequest replace : replaceByChange.values()) {
if (replace.inputCommand == newChange) { if (magicBranch != null && replace.inputCommand == magicBranch.cmd) {
futures.add(replace.insertPatchSet()); futures.add(replace.insertPatchSet());
} }
} }
@ -700,13 +697,13 @@ public class ReceiveCommits {
for (CheckedFuture<?, OrmException> f : futures) { for (CheckedFuture<?, OrmException> f : futures) {
f.checkedGet(); f.checkedGet();
} }
newChange.setResult(OK); magicBranch.cmd.setResult(OK);
} catch (OrmException err) { } catch (OrmException err) {
log.error("Can't insert changes for " + project.getName(), err); log.error("Can't insert changes for " + project.getName(), err);
reject(newChange, "internal server error"); reject(magicBranch.cmd, "internal server error");
} catch (IOException err) { } catch (IOException err) {
log.error("Can't read commits for " + project.getName(), err); log.error("Can't read commits for " + project.getName(), err);
reject(newChange, "internal server error"); reject(magicBranch.cmd, "internal server error");
} }
} }
@ -762,7 +759,7 @@ public class ReceiveCommits {
} }
if (MagicBranch.isMagicBranch(cmd.getRefName())) { if (MagicBranch.isMagicBranch(cmd.getRefName())) {
parseNewChangeCommand(cmd); parseMagicBranch(cmd);
continue; continue;
} }
@ -973,15 +970,43 @@ public class ReceiveCommits {
} }
} }
private void parseNewChangeCommand(final ReceiveCommand cmd) { private static class MagicBranchInput {
final ReceiveCommand cmd;
Branch.NameKey dest;
RefControl ctl;
String topic;
Set<Account.Id> reviewer = Sets.newLinkedHashSet();
Set<Account.Id> cc = Sets.newLinkedHashSet();
MagicBranchInput(ReceiveCommand cmd) {
this.cmd = cmd;
}
boolean isRef(Ref ref) {
return ctl != null && ref.getName().equals(ctl.getRefName());
}
boolean isDraft() {
return cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE);
}
MailRecipients getMailRecipients() {
return new MailRecipients(reviewer, cc);
}
}
private void parseMagicBranch(final ReceiveCommand cmd) {
// Permit exactly one new change request per push. // Permit exactly one new change request per push.
// //
if (newChange != null) { if (magicBranch != null) {
reject(cmd, "duplicate request"); reject(cmd, "duplicate request");
return; return;
} }
newChange = cmd; magicBranch = new MagicBranchInput(cmd);
magicBranch.reviewer.addAll(reviewersFromCommandLine);
magicBranch.cc.addAll(ccFromCommandLine);
String destBranchName = MagicBranch.getDestBranchName(cmd.getRefName()); String destBranchName = MagicBranch.getDestBranchName(cmd.getRefName());
if (!destBranchName.startsWith(Constants.R_REFS)) { if (!destBranchName.startsWith(Constants.R_REFS)) {
destBranchName = Constants.R_HEADS + destBranchName; destBranchName = Constants.R_HEADS + destBranchName;
@ -1026,17 +1051,15 @@ public class ReceiveCommits {
} }
if (split < destBranchName.length()) { if (split < destBranchName.length()) {
destTopicName = destBranchName.substring(split + 1); String t = destBranchName.substring(split + 1);
if (destTopicName.isEmpty()) { magicBranch.topic = Strings.emptyToNull(t);
destTopicName = null;
}
} else { } else {
destTopicName = null; magicBranch.topic = null;
} }
destBranch = new Branch.NameKey(project.getNameKey(), // magicBranch.dest = new Branch.NameKey(project.getNameKey(), //
destBranchName.substring(0, split)); destBranchName.substring(0, split));
destBranchCtl = projectControl.controlForRef(destBranch); magicBranch.ctl = projectControl.controlForRef(magicBranch.dest);
if (!destBranchCtl.canUpload()) { if (!magicBranch.ctl.canUpload()) {
errors.put(Error.CODE_REVIEW, cmd.getRefName()); errors.put(Error.CODE_REVIEW, cmd.getRefName());
reject(cmd, "cannot upload review"); reject(cmd, "cannot upload review");
return; return;
@ -1050,7 +1073,7 @@ public class ReceiveCommits {
try { try {
final RevWalk walk = rp.getRevWalk(); final RevWalk walk = rp.getRevWalk();
final RevCommit tip = walk.parseCommit(newChange.getNewId()); final RevCommit tip = walk.parseCommit(magicBranch.cmd.getNewId());
Ref targetRef = rp.getAdvertisedRefs().get(destBranchName); Ref targetRef = rp.getAdvertisedRefs().get(destBranchName);
if (targetRef == null || targetRef.getObjectId() == null) { if (targetRef == null || targetRef.getObjectId() == null) {
// The destination branch does not yet exist. Assume the // The destination branch does not yet exist. Assume the
@ -1067,7 +1090,7 @@ public class ReceiveCommits {
walk.markStart(tip); walk.markStart(tip);
walk.markStart(h); walk.markStart(h);
if (walk.next() == null) { if (walk.next() == null) {
reject(newChange, "no common ancestry"); reject(magicBranch.cmd, "no common ancestry");
return; return;
} }
} finally { } finally {
@ -1075,7 +1098,7 @@ public class ReceiveCommits {
walk.setRevFilter(oldRevFilter); walk.setRevFilter(oldRevFilter);
} }
} catch (IOException e) { } catch (IOException e) {
newChange.setResult(REJECTED_MISSING_OBJECT); magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", e); log.error("Invalid pack upload; one or more objects weren't sent", e);
return; return;
} }
@ -1170,7 +1193,7 @@ public class ReceiveCommits {
walk.sort(RevSort.REVERSE, true); walk.sort(RevSort.REVERSE, true);
try { try {
Set<ObjectId> existing = Sets.newHashSet(); Set<ObjectId> existing = Sets.newHashSet();
walk.markStart(walk.parseCommit(newChange.getNewId())); walk.markStart(walk.parseCommit(magicBranch.cmd.getNewId()));
markHeadsAsUninteresting(walk, existing); markHeadsAsUninteresting(walk, existing);
List<ChangeLookup> pending = Lists.newArrayList(); List<ChangeLookup> pending = Lists.newArrayList();
@ -1186,7 +1209,7 @@ public class ReceiveCommits {
continue; continue;
} }
if (!validCommit(destBranchCtl, newChange, c)) { if (!validCommit(magicBranch.ctl, magicBranch.cmd, c)) {
// Not a change the user can propose? Abort as early as possible. // Not a change the user can propose? Abort as early as possible.
// //
return Collections.emptyList(); return Collections.emptyList();
@ -1202,7 +1225,7 @@ public class ReceiveCommits {
final String idStr = idList.get(idList.size() - 1).trim(); final String idStr = idList.get(idList.size() - 1).trim();
if (idStr.matches("^I00*$")) { if (idStr.matches("^I00*$")) {
// Reject this invalid line from EGit. // Reject this invalid line from EGit.
reject(newChange, "invalid Change-Id"); reject(magicBranch.cmd, "invalid Change-Id");
return Collections.emptyList(); return Collections.emptyList();
} }
@ -1212,7 +1235,7 @@ public class ReceiveCommits {
for (ChangeLookup p : pending) { for (ChangeLookup p : pending) {
if (newChangeIds.contains(p.changeKey)) { if (newChangeIds.contains(p.changeKey)) {
reject(newChange, "squash commits first"); reject(magicBranch.cmd, "squash commits first");
return Collections.emptyList(); return Collections.emptyList();
} }
@ -1223,14 +1246,14 @@ public class ReceiveCommits {
// a different Change-Id. In practice, we should never see // a different Change-Id. In practice, we should never see
// this error message as Change-Id should be unique. // this error message as Change-Id should be unique.
// //
reject(newChange, p.changeKey.get() + " has duplicates"); reject(magicBranch.cmd, p.changeKey.get() + " has duplicates");
return Collections.emptyList(); return Collections.emptyList();
} }
if (changes.size() == 1) { if (changes.size() == 1) {
// Schedule as a replacement to this one matching change. // Schedule as a replacement to this one matching change.
// //
if (requestReplace(newChange, false, changes.get(0), p.commit)) { if (requestReplace(magicBranch.cmd, false, changes.get(0), p.commit)) {
continue; continue;
} else { } else {
return Collections.emptyList(); return Collections.emptyList();
@ -1239,7 +1262,7 @@ public class ReceiveCommits {
if (changes.size() == 0) { if (changes.size() == 0) {
if (!isValidChangeId(p.changeKey.get())) { if (!isValidChangeId(p.changeKey.get())) {
reject(newChange, "invalid Change-Id"); reject(magicBranch.cmd, "invalid Change-Id");
return Collections.emptyList(); return Collections.emptyList();
} }
@ -1251,17 +1274,17 @@ public class ReceiveCommits {
// Should never happen, the core receive process would have // Should never happen, the core receive process would have
// identified the missing object earlier before we got control. // identified the missing object earlier before we got control.
// //
newChange.setResult(REJECTED_MISSING_OBJECT); magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", e); log.error("Invalid pack upload; one or more objects weren't sent", e);
return Collections.emptyList(); return Collections.emptyList();
} catch (OrmException e) { } catch (OrmException e) {
log.error("Cannot query database to locate prior changes", e); log.error("Cannot query database to locate prior changes", e);
reject(newChange, "database error"); reject(magicBranch.cmd, "database error");
return Collections.emptyList(); return Collections.emptyList();
} }
if (newChanges.isEmpty() && replaceByChange.isEmpty()) { if (newChanges.isEmpty() && replaceByChange.isEmpty()) {
reject(newChange, "no new changes"); reject(magicBranch.cmd, "no new changes");
return Collections.emptyList(); return Collections.emptyList();
} }
for (CreateRequest create : newChanges) { for (CreateRequest create : newChanges) {
@ -1277,7 +1300,7 @@ public class ReceiveCommits {
} else if (ref.getName().startsWith("refs/changes/")) { } else if (ref.getName().startsWith("refs/changes/")) {
existing.add(ref.getObjectId()); existing.add(ref.getObjectId());
} else if (ref.getName().startsWith(R_HEADS) } else if (ref.getName().startsWith(R_HEADS)
|| (destBranchCtl != null && ref.getName().equals(destBranchCtl.getRefName()))) { || (magicBranch != null && magicBranch.isRef(ref))) {
try { try {
walk.markUninteresting(walk.parseCommit(ref.getObjectId())); walk.markUninteresting(walk.parseCommit(ref.getObjectId()));
} catch (IOException e) { } catch (IOException e) {
@ -1301,7 +1324,7 @@ public class ReceiveCommits {
ChangeLookup(RevCommit c, Change.Key key) throws OrmException { ChangeLookup(RevCommit c, Change.Key key) throws OrmException {
commit = c; commit = c;
changeKey = key; changeKey = key;
changes = db.changes().byBranchKey(destBranch, key); changes = db.changes().byBranchKey(magicBranch.dest, key);
} }
} }
@ -1319,15 +1342,15 @@ public class ReceiveCommits {
change = new Change(changeKey, change = new Change(changeKey,
new Change.Id(db.nextChangeId()), new Change.Id(db.nextChangeId()),
currentUser.getAccountId(), currentUser.getAccountId(),
destBranch); magicBranch.dest);
change.setTopic(destTopicName); change.setTopic(magicBranch.topic);
ps = new PatchSet(new PatchSet.Id(change.getId(), INITIAL_PATCH_SET_ID)); ps = new PatchSet(new PatchSet.Id(change.getId(), INITIAL_PATCH_SET_ID));
ps.setCreatedOn(change.getCreatedOn()); ps.setCreatedOn(change.getCreatedOn());
ps.setUploader(change.getOwner()); ps.setUploader(change.getOwner());
ps.setRevision(toRevId(c)); ps.setRevision(toRevId(c));
if (MagicBranch.isDraft(newChange.getRefName())) { if (magicBranch.isDraft()) {
change.setStatus(Change.Status.DRAFT); change.setStatus(Change.Status.DRAFT);
ps.setDraft(true); ps.setDraft(true);
} }
@ -1368,7 +1391,10 @@ public class ReceiveCommits {
private void insertChange(ReviewDb db) throws OrmException { private void insertChange(ReviewDb db) throws OrmException {
final Account.Id me = currentUser.getAccountId(); final Account.Id me = currentUser.getAccountId();
final List<FooterLine> footerLines = commit.getFooterLines(); final List<FooterLine> footerLines = commit.getFooterLines();
final MailRecipients recipients = new MailRecipients(reviewerId, ccId); final MailRecipients recipients = new MailRecipients();
if (magicBranch != null) {
recipients.add(magicBranch.getMailRecipients());
}
recipients.add(getRecipientsFromFooters(accountResolver, ps, footerLines)); recipients.add(getRecipientsFromFooters(accountResolver, ps, footerLines));
recipients.remove(me); recipients.remove(me);
@ -1445,10 +1471,10 @@ public class ReceiveCommits {
} }
} }
if (newChange != null && newChange.getResult() != NOT_ATTEMPTED) { if (magicBranch != null && magicBranch.cmd.getResult() != NOT_ATTEMPTED) {
// Cancel creations tied to refs/for/ or refs/drafts/ command. // Cancel creations tied to refs/for/ or refs/drafts/ command.
for (ReplaceRequest req : replaceByChange.values()) { for (ReplaceRequest req : replaceByChange.values()) {
if (req.inputCommand == newChange && req.cmd != null) { if (req.inputCommand == magicBranch.cmd && req.cmd != null) {
req.cmd.setResult(Result.REJECTED_OTHER_REASON, "aborted"); req.cmd.setResult(Result.REJECTED_OTHER_REASON, "aborted");
} }
} }
@ -1616,7 +1642,7 @@ public class ReceiveCommits {
newPatchSet.setCreatedOn(new Timestamp(System.currentTimeMillis())); newPatchSet.setCreatedOn(new Timestamp(System.currentTimeMillis()));
newPatchSet.setUploader(currentUser.getAccountId()); newPatchSet.setUploader(currentUser.getAccountId());
newPatchSet.setRevision(toRevId(newCommit)); newPatchSet.setRevision(toRevId(newCommit));
if (newChange != null && MagicBranch.isDraft(newChange.getRefName())) { if (magicBranch != null && magicBranch.isDraft()) {
newPatchSet.setDraft(true); newPatchSet.setDraft(true);
} }
info = patchSetInfoFactory.get(newCommit, newPatchSet.getId()); info = patchSetInfoFactory.get(newCommit, newPatchSet.getId());
@ -1660,7 +1686,10 @@ public class ReceiveCommits {
PatchSet.Id insertPatchSet(ReviewDb db) throws OrmException { PatchSet.Id insertPatchSet(ReviewDb db) throws OrmException {
final Account.Id me = currentUser.getAccountId(); final Account.Id me = currentUser.getAccountId();
final List<FooterLine> footerLines = newCommit.getFooterLines(); final List<FooterLine> footerLines = newCommit.getFooterLines();
final MailRecipients recipients = new MailRecipients(reviewerId, ccId); final MailRecipients recipients = new MailRecipients();
if (magicBranch != null) {
recipients.add(magicBranch.getMailRecipients());
}
recipients.add(getRecipientsFromFooters(accountResolver, newPatchSet, footerLines)); recipients.add(getRecipientsFromFooters(accountResolver, newPatchSet, footerLines));
recipients.remove(me); recipients.remove(me);
@ -1712,8 +1741,8 @@ public class ReceiveCommits {
return change; return change;
} }
if (destTopicName != null) { if (magicBranch != null && magicBranch.topic != null) {
change.setTopic(destTopicName); change.setTopic(magicBranch.topic);
} }
if (change.getStatus() == Change.Status.DRAFT && newPatchSet.isDraft()) { if (change.getStatus() == Change.Status.DRAFT && newPatchSet.isDraft()) {
// Leave in draft status. // Leave in draft status.

View File

@ -94,11 +94,6 @@ public final class MagicBranch {
return Capable.OK; return Capable.OK;
} }
/** Checks if ref name matches the draft magic branch */
public static boolean isDraft(String refName) {
return refName.startsWith(MagicBranch.NEW_DRAFT_CHANGE);
}
private static Capable checkMagicBranchRef(String branchName, Repository repo, private static Capable checkMagicBranchRef(String branchName, Repository repo,
Project project) { Project project) {
Map<String, Ref> blockingFors; Map<String, Ref> blockingFors;