aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJukka Jokiniva <jukka.jokiniva@qt.io>2019-04-09 22:22:52 +0300
committerJukka Jokiniva <jukka.jokiniva@qt.io>2019-04-10 13:52:26 +0000
commitd6c284088e8bd946cd5c62f5c5ac73fdb2b893d0 (patch)
tree2ed5cfee41f4d9fc92140c6f7d71f37f48e4f42f
parent66f4f8365032567692cbb2e72016c9f0dda2d009 (diff)
Handle merge conflict when rebuilding the staging ref
Fixes: QTBI-1641 Change-Id: I3f8abd56dd4991a28acd97e6360232a935ab48c7 Reviewed-by: Paul Wicking <paul.wicking@qt.io>
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java162
-rw-r--r--src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommonFlowsIT.java37
-rw-r--r--src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtUnStageIT.java23
3 files changed, 172 insertions, 50 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 b2da710..b94190b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java
@@ -18,17 +18,22 @@ package com.googlesource.gerrit.plugins.qtcodereview;
import com.google.gerrit.common.FooterConstants;
import com.google.common.collect.Lists;
+import com.google.gerrit.extensions.restapi.RestApiException;
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.reviewdb.server.ReviewDb;
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.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -72,17 +77,26 @@ public class QtUtil {
public static final String R_STAGING = "refs/staging/";
public static final String R_BUILDS = "refs/builds/";
+ private final Provider<ReviewDb> dbProvider;
private final Provider<InternalChangeQuery> queryProvider;
private final GitReferenceUpdated referenceUpdated;
+ private final BatchUpdate.Factory updateFactory;
private final QtCherryPickPatch qtCherryPickPatch;
+ private final QtChangeUpdateOp.Factory qtUpdateFactory;
@Inject
- QtUtil(Provider<InternalChangeQuery> queryProvider,
+ QtUtil(Provider<ReviewDb> dbProvider,
+ Provider<InternalChangeQuery> queryProvider,
GitReferenceUpdated referenceUpdated,
- QtCherryPickPatch qtCherryPickPatch) {
+ BatchUpdate.Factory updateFactory,
+ QtCherryPickPatch qtCherryPickPatch,
+ QtChangeUpdateOp.Factory qtUpdateFactory) {
+ this.dbProvider = dbProvider;
this.queryProvider = queryProvider;
this.referenceUpdated = referenceUpdated;
+ this.updateFactory = updateFactory;
this.qtCherryPickPatch = qtCherryPickPatch;
+ this.qtUpdateFactory = qtUpdateFactory;
}
public static class MergeConflictException extends Exception {
@@ -307,7 +321,7 @@ public class QtUtil {
return results;
}
- private ObjectId pickChangestoStagingRef(Repository git,
+ private ObjectId pickChangesToStagingRef(Repository git,
final Project.NameKey projectKey,
List<ChangeData> changes,
ObjectId tipObj)
@@ -338,53 +352,101 @@ public class QtUtil {
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");
- }
+ InternalChangeQuery query = null;
+ List<ChangeData> changes_integrating = null;
+ List<ChangeData> changes_staged = null;
+ ObjectId oldStageRefObjId = null;
+ ObjectId branchObjId = null;
+ ObjectId newStageRefObjId = null;
+ ObjectId newObjId = null;
+ String branchName = null;
+
+ try {
+ branchName = stagingBranchKey.get();
+ oldStageRefObjId = git.resolve(branchName);
+ branchObjId = git.resolve(destBranchShortKey.get());
+
+ query = queryProvider.get();
+ List<ChangeData> unsorted_list = query.byBranchStatus(destBranchShortKey, Change.Status.INTEGRATING);
+ changes_integrating = arrangeOrderLikeInRef(git, oldStageRefObjId, branchObjId, unsorted_list);
+
+ query = queryProvider.get();
+ unsorted_list = query.byBranchStatus(destBranchShortKey, Change.Status.STAGED);
+ changes_staged = arrangeOrderLikeInRef(git, oldStageRefObjId, branchObjId, unsorted_list);
+ } catch (OrmException e) {
+ logger.atSevere().log("qtcodereview: rebuild staging ref %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 staging ref %s db failed. IOException %s",
+ stagingBranchKey, e);
+ throw new MergeConflictException("fatal: IOException");
+ }
+
+ try {
+ logger.atInfo().log("qtcodereview: rebuild staging ref reset %s back to %s",
+ stagingBranchKey, destBranchShortKey);
+ Result result = QtUtil.createStagingBranch(git, destBranchShortKey);
+ if (result == null) throw new NoSuchRefException("Cannot create staging ref: " + branchName);
+ logger.atInfo().log("qtcodereview: rebuild staging ref reset result %s", result);
+ newStageRefObjId = git.resolve(branchName);
+ } catch (NoSuchRefException e) {
+ logger.atSevere().log("qtcodereview: rebuild staging ref reset %s failed. No such ref %s",
+ stagingBranchKey, e);
+ throw new MergeConflictException("fatal: NoSuchRefException");
+ } catch (IOException e) {
+ logger.atSevere().log("qtcodereview: rebuild staging ref reset %s failed. IOException %s",
+ stagingBranchKey, e);
+ throw new MergeConflictException("fatal: IOException");
+ }
+
+ try {
+ newObjId = pickChangesToStagingRef(git, projectKey, changes_integrating, newStageRefObjId);
+ newStageRefObjId = newObjId;
+ } catch(Exception e) {
+ logger.atSevere().log("qtcodereview: rebuild staging ref %s. failed to cherry pick integrating changes %s",
+ stagingBranchKey, e);
+ newObjId = null;
+ }
+
+ if (newObjId != null) {
+ try {
+ newObjId = pickChangesToStagingRef(git, projectKey, changes_staged, newObjId);
+ newStageRefObjId = newObjId;
+ } catch(Exception e) {
+ newObjId = null;
+ logger.atInfo().log("qtcodereview: rebuild staging ref %s merge conflict", stagingBranchKey);
+ String message = "Merge conflict in staging branch. Status changed back to new. Please stage again.";
+ QtChangeUpdateOp op = qtUpdateFactory.create(Change.Status.NEW, message, null, null, null);
+ try (BatchUpdate u = updateFactory.create(dbProvider.get(), projectKey, user, TimeUtil.nowTs())) {
+ for (ChangeData item: changes_staged) {
+ Change change = item.change();
+ logger.atInfo().log("qtcodereview: staging ref rebuild merge conflict. Change %s back to NEW", change);
+ u.addOp(change.getId(), op);
+ }
+ u.execute();
+ } catch (OrmException ex) {
+ logger.atSevere().log("qtcodereview: staging ref rebuild. Failed to access database %s", ex);
+ } catch (UpdateException | RestApiException ex) {
+ logger.atSevere().log("qtcodereview: staging ref rebuild. Failed to update change status %s", ex);
+ }
+ }
+ }
+
+ try {
+ RefUpdate refUpdate = git.updateRef(branchName);
+ refUpdate.setNewObjectId(newStageRefObjId);
+ refUpdate.update();
+
+ // send ref updated event only if it changed
+ if (!newStageRefObjId.equals(oldStageRefObjId)) {
+ referenceUpdated.fire(projectKey, branchName, oldStageRefObjId,
+ newStageRefObjId, user.state());
+ }
+ } catch (IOException e) {
+ logger.atSevere().log("qtcodereview: rebuild %s failed to update ref %s", stagingBranchKey, e);
+ throw new MergeConflictException("fatal: IOException");
+ }
}
/**
diff --git a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommonFlowsIT.java b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommonFlowsIT.java
index 8068163..2469d62 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommonFlowsIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommonFlowsIT.java
@@ -347,4 +347,41 @@ public class QtCommonFlowsIT extends QtCodeReviewIT {
assertThat(updatedHead).isEqualTo(initialHead);
}
+ @Test
+ public void unStage_MergeConflict_While_Building() throws Exception {
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result c1 = pushCommit("master", "commitmsg1", "file1", "content1");
+ testRepo.reset(initialHead);
+ PushOneCommit.Result c2 = pushCommit("master", "commitmsg2", "file2", "content2");
+ testRepo.reset(initialHead);
+ PushOneCommit.Result c3 = pushCommit("master", "commitmsg3", "thesamefile", "content3");
+ // c4 depends on c3
+ PushOneCommit.Result c4 = pushCommit("master", "commitmsg4", "thesamefile", "conflict content3");
+ approve(c1.getChangeId());
+ approve(c2.getChangeId());
+ approve(c3.getChangeId());
+ approve(c4.getChangeId());
+
+ QtStage(c1);
+ QtNewBuild("master", "master-build-500");
+ RevCommit stagingExpected = getRemoteHead(project, R_STAGING + "master");
+
+ QtStage(c2);
+ QtStage(c3);
+ QtStage(c4);
+
+ QtUnStage(c3);
+ Change change = c1.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.INTEGRATING);
+ change = c2.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+ change = c3.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+ change = c4.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+
+ RevCommit stagingHeadMaster = getRemoteHead(project, R_STAGING + "master");
+ assertThat(stagingHeadMaster.getId()).isEqualTo(stagingExpected.getId());
+ }
+
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtUnStageIT.java b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtUnStageIT.java
index 633515b..06b249e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtUnStageIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtUnStageIT.java
@@ -212,6 +212,29 @@ public class QtUnStageIT extends QtCodeReviewIT {
}
@Test
+ public void multiChange_UnStage_MergeConflict() throws Exception {
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result c1 = pushCommit("master", "commitmsg1", "thesamefile", "content1");
+ // c2 depends on c1
+ PushOneCommit.Result c2 = pushCommit("master", "commitdependingon1", "thesamefile", "conflict");
+ testRepo.reset(initialHead);
+ PushOneCommit.Result c3 = pushCommit("master", "commitmsg3", "file3", "content3");
+
+ approve(c1.getChangeId());
+ QtStage(c1);
+ approve(c2.getChangeId());
+ QtStage(c2);
+ approve(c3.getChangeId());
+ QtStage(c3);
+
+ RevCommit stagingHead = qtUnStageExpectCommit(c1, initialHead);
+ Change change = c2.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+ change = c3.getChange().change();
+ assertThat(change.getStatus()).isEqualTo(Change.Status.NEW);
+ }
+
+ @Test
public void errorUnStage_No_Permission() throws Exception {
PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
approve(c.getChangeId());