aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java')
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
index fe35a3c..bf14223 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
@@ -16,12 +16,27 @@
package com.googlesource.gerrit.plugins.qtcodereview;
+import com.google.gerrit.common.FooterConstants;
+import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.project.NoSuchRefException;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.submit.IntegrationException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -35,6 +50,8 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
/**
@@ -48,6 +65,19 @@ public class QtUtil {
public static final String R_HEADS = "refs/heads/";
public static final String R_STAGING = "refs/staging/";
+ private final Provider<InternalChangeQuery> queryProvider;
+ private final GitReferenceUpdated referenceUpdated;
+ private final QtCherryPickPatch qtCherryPickPatch;
+
+ @Inject
+ QtUtil(Provider<InternalChangeQuery> queryProvider,
+ GitReferenceUpdated referenceUpdated,
+ QtCherryPickPatch qtCherryPickPatch) {
+ this.queryProvider = queryProvider;
+ this.referenceUpdated = referenceUpdated;
+ this.qtCherryPickPatch = qtCherryPickPatch;
+ }
+
public static class MergeConflictException extends Exception {
private static final long serialVersionUID = 1L;
public MergeConflictException(final String message) {
@@ -55,6 +85,32 @@ public class QtUtil {
}
}
+ public static Project.NameKey getProjectKey(final String project) {
+ String projectName = project;
+ if (project.endsWith(Constants.DOT_GIT_EXT)) {
+ projectName = project.substring(0, project.length() - Constants.DOT_GIT_EXT.length());
+ }
+ return new Project.NameKey(projectName);
+ }
+
+ /**
+ * Creates a branch key without any prefix.
+ * @param project Project for the branch key.
+ * @param prefix Prefix to remove.
+ * @param branch Branch name with or without prefix.
+ * @return Branch name key without prefix.
+ */
+ public static Branch.NameKey getNameKeyShort(final String project,
+ final String prefix,
+ final String branch) {
+ final Project.NameKey projectKey = getProjectKey(project);
+ if (branch.startsWith(prefix)) {
+ return new Branch.NameKey(projectKey, branch.substring(prefix.length()));
+ } else {
+ return new Branch.NameKey(projectKey, branch);
+ }
+ }
+
/**
* Gets a staging branch for a branch.
* @param branch Branch under refs/heads. E.g. refs/heads/master. Can be short
@@ -121,6 +177,134 @@ public class QtUtil {
return result;
}
+ private String getChangeId(RevCommit commit) {
+ List<String> changeIds = commit.getFooterLines(FooterConstants.CHANGE_ID);
+ String changeId = null;
+ if (!changeIds.isEmpty()) changeId = changeIds.get(0);
+ return changeId;
+ }
+
+ private ChangeData findChangeFromList(String changeId, List<ChangeData> changes)
+ throws OrmException {
+ for (ChangeData item : changes) {
+ if (item.change().getKey().get().equals(changeId)) return item;
+ }
+ return null;
+ }
+
+ private List<ChangeData> arrangeOrderLikeInRef(Repository git,
+ ObjectId refObj,
+ ObjectId tipObj,
+ List<ChangeData> changeList)
+ throws MissingObjectException, OrmException,
+ IOException {
+ List<ChangeData> results = new ArrayList<ChangeData>();
+ if (refObj.equals(tipObj)) return results;
+
+ RevWalk revWalk = new RevWalk(git);
+ RevCommit commit = revWalk.parseCommit(refObj);
+ int count = 0;
+ do {
+ count++;
+ String changeId = getChangeId(commit);
+
+ if (commit.getParentCount() == 0) {
+ commit = null; // something is going wrong, just exit
+ } else {
+ if (changeId == null && commit.getParentCount() > 1) {
+ changeId = getChangeId(revWalk.parseCommit(commit.getParent(1)));
+ }
+ ChangeData change = findChangeFromList(changeId, changeList);
+ if (change != null) results.add(0, change);
+
+ commit = revWalk.parseCommit(commit.getParent(0));
+ }
+ } while (commit != null && !commit.equals(tipObj) && count < 100);
+
+ if (count == 100) return null;
+ return results;
+ }
+
+ private ObjectId pickChangestoStagingRef(Repository git,
+ final Project.NameKey projectKey,
+ List<ChangeData> changes,
+ ObjectId tipObj)
+ throws OrmException, IOException, IntegrationException {
+ ObjectId newId = tipObj;
+ for (ChangeData item : changes) {
+ Change change = item.change();
+ logger.atInfo().log("qtcodereview: rebuilding add %s", change);
+ PatchSet p = item.currentPatchSet();
+ ObjectId srcId = git.resolve(p.getRevision().get());
+ newId = qtCherryPickPatch.cherryPickPatch(item,
+ projectKey,
+ srcId,
+ newId,
+ true, // allowFastForward
+ null, // newStatus
+ null, // defaultMessage
+ null, // inputMessage
+ null // tag
+ ).toObjectId();
+ }
+ return newId;
+ }
+
+ public void rebuildStagingBranch(Repository git,
+ IdentifiedUser user,
+ final Project.NameKey projectKey,
+ final Branch.NameKey stagingBranchKey,
+ final Branch.NameKey destBranchShortKey)
+ throws MergeConflictException {
+ try {
+ ObjectId oldStageRefObjId = git.resolve(stagingBranchKey.get());
+ ObjectId branchObjId = git.resolve(destBranchShortKey.get());
+
+ InternalChangeQuery query = queryProvider.get();
+ List<ChangeData> changes_integrating = query.byBranchStatus(destBranchShortKey, Change.Status.INTEGRATING);
+
+ query = queryProvider.get();
+ List<ChangeData> changes_staged = query.byBranchStatus(destBranchShortKey, Change.Status.STAGED);
+
+ List<ChangeData> changes_allowed = new ArrayList<ChangeData>();
+ changes_allowed.addAll(changes_integrating);
+ changes_allowed.addAll(changes_staged);
+ List<ChangeData> changes = arrangeOrderLikeInRef(git, oldStageRefObjId, branchObjId, changes_allowed);
+
+ logger.atInfo().log("qtcodereview: rebuild reset %s back to %s", stagingBranchKey, destBranchShortKey);
+ Result result = QtUtil.createStagingBranch(git, destBranchShortKey);
+ if (result == null) throw new NoSuchRefException("Cannot create staging ref: " + stagingBranchKey.get());
+ logger.atInfo().log("qtcodereview: rebuild reset result %s",result);
+
+ ObjectId newId = pickChangestoStagingRef(git,
+ projectKey,
+ changes,
+ git.resolve(stagingBranchKey.get()));
+
+ RefUpdate refUpdate = git.updateRef(stagingBranchKey.get());
+ refUpdate.setNewObjectId(newId);
+ refUpdate.update();
+
+ // send ref updated event only if it changed
+ if (!newId.equals(oldStageRefObjId)) {
+ referenceUpdated.fire(projectKey, stagingBranchKey.get(), oldStageRefObjId, newId, user.state());
+ }
+
+ } catch (OrmException e) {
+ logger.atSevere().log("qtcodereview: rebuild %s failed. Failed to access database %s", stagingBranchKey, e);
+ throw new MergeConflictException("fatal: Failed to access database");
+ } catch (IOException e) {
+ logger.atSevere().log("qtcodereview: rebuild %s failed. IOException %s", stagingBranchKey, e);
+ throw new MergeConflictException("fatal: IOException");
+ } catch (NoSuchRefException e) {
+ logger.atSevere().log("qtcodereview: rebuild %s failed. No such ref %s", stagingBranchKey, e);
+ throw new MergeConflictException("fatal: NoSuchRefException");
+ } catch(IntegrationException e) {
+ logger.atSevere().log("qtcodereview: rebuild %s failed. IntegrationException %s", stagingBranchKey, e);
+ throw new MergeConflictException("fatal: IntegrationException");
+ }
+ }
+
public static RevCommit merge(PersonIdent committerIdent,
Repository git,
ObjectInserter objInserter,