diff options
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.java | 181 |
1 files changed, 122 insertions, 59 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 c8fd8e7..246a653 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java +++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtUtil.java @@ -24,6 +24,7 @@ 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.ChangeMessagesUtil; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.project.NoSuchRefException; @@ -74,6 +75,10 @@ public class QtUtil { public static final String R_HEADS = "refs/heads/"; public static final String R_STAGING = "refs/staging/"; public static final String R_BUILDS = "refs/builds/"; + public static final String TAG_CI = ChangeMessagesUtil.AUTOGENERATED_TAG_PREFIX + "qt:ci"; + public static final String TAG_ADMINCHANGE = ChangeMessagesUtil.AUTOGENERATED_TAG_PREFIX + "qt:adminchange"; + public static final String TAG_REOPENED = ChangeMessagesUtil.AUTOGENERATED_TAG_PREFIX + "qt:reopened"; + private final Provider<InternalChangeQuery> queryProvider; private final GitReferenceUpdated referenceUpdated; @@ -282,6 +287,7 @@ public class QtUtil { return null; } + // Step backwards from the ref and return change list in the same order private List<ChangeData> arrangeOrderLikeInRef(Repository git, ObjectId refObj, ObjectId tipObj, @@ -307,6 +313,7 @@ public class QtUtil { ChangeData change = findChangeFromList(changeId, changeList); if (change != null) results.add(0, change); + // It can always be trusted that parent in index 0 is the correct one commit = revWalk.parseCommit(commit.getParent(0)); } } while (commit != null && !commit.equals(tipObj) && count < 100); @@ -330,16 +337,76 @@ public class QtUtil { projectKey, srcId, newId, - true, // allowFastForward + false, // allowFastForward null, // newStatus null, // defaultMessage null, // inputMessage - null // tag + TAG_CI // tag ).toObjectId(); } return newId; } + // Step backwards from staging head and return 1st commit in integrating status + private ObjectId findIntegrationHead(Repository git, + ObjectId stagingHead, + ObjectId branchHead, + List<ChangeData> integratingChanges) + throws MissingObjectException, IOException { + + if (stagingHead.equals(branchHead)) return branchHead; + + RevWalk revWalk = new RevWalk(git); + RevCommit commit = revWalk.parseCommit(stagingHead); + int count = 0; + do { + count++; + String changeId = getChangeId(commit); + ChangeData change = findChangeFromList(changeId, integratingChanges); + if (change != null) return commit; + + if (commit.getParentCount() > 0) { + // It can always be trusted that parent in index 0 is the correct one + commit = revWalk.parseCommit(commit.getParent(0)); + } else commit = null; + + } while (commit != null && !commit.equals(branchHead) && count < 100); + + return branchHead; + } + + // Step backwards from staging head and find commit that can be reused + private ObjectId findReusableStagingHead(Repository git, + ObjectId stagingHead, + ObjectId integrationHead, + List<ChangeData> stagedChanges) + throws MissingObjectException, IOException { + + if (stagingHead.equals(integrationHead)) return integrationHead; + + ObjectId reusableHead = null; + RevWalk revWalk = new RevWalk(git); + RevCommit commit = revWalk.parseCommit(stagingHead); + int count = 0; + do { + count++; + String changeId = getChangeId(commit); + ChangeData change = findChangeFromList(changeId, stagedChanges); + if (change != null) { + if (reusableHead == null) reusableHead = commit; + } else reusableHead = null; + + if (commit.getParentCount() > 0) { + // It can always be trusted that parent in index 0 is the correct one + commit = revWalk.parseCommit(commit.getParent(0)); + } else commit = null; + + } while (commit != null && !commit.equals(integrationHead) && count < 100); + + if (reusableHead == null) reusableHead = integrationHead; + return reusableHead; + } + public void rebuildStagingBranch(Repository git, IdentifiedUser user, final Project.NameKey projectKey, @@ -349,87 +416,73 @@ public class QtUtil { 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; + List<ChangeData> changes_to_cherrypick = null; + ObjectId oldStageRef = null; + ObjectId branchRef = null; + ObjectId newStageRef = null; + ObjectId integratingRef = null; + String stagingBranchName = null; try { - branchName = stagingBranchKey.get(); - oldStageRefObjId = git.resolve(branchName); - branchObjId = git.resolve(destBranchShortKey.get()); + stagingBranchName = stagingBranchKey.get(); + oldStageRef = git.resolve(stagingBranchName); + branchRef = 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); + changes_integrating = query.byBranchStatus(destBranchShortKey, Change.Status.INTEGRATING); query = queryProvider.get(); - unsorted_list = query.byBranchStatus(destBranchShortKey, Change.Status.STAGED); - changes_staged = arrangeOrderLikeInRef(git, oldStageRefObjId, branchObjId, unsorted_list); + changes_staged = query.byBranchStatus(destBranchShortKey, Change.Status.STAGED); } catch (IOException e) { - logger.atSevere().log("qtcodereview: rebuild staging ref %s db failed. IOException %s", + logger.atSevere().log("qtcodereview: rebuild staging ref %s db query failed. Exception %s", stagingBranchKey, e); - throw new MergeConflictException("fatal: IOException"); + throw new MergeConflictException("fatal: " + e.getMessage()); } 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"); + if (result == null) throw new NoSuchRefException("Cannot create staging ref: " + stagingBranchName); + logger.atInfo().log("qtcodereview: rebuild staging ref reset to %s with result %s", branchRef, result); + integratingRef = findIntegrationHead(git, oldStageRef, branchRef, changes_integrating); + logger.atInfo().log("qtcodereview: rebuild staging integration ref is %s", integratingRef); + newStageRef = findReusableStagingHead(git, oldStageRef, integratingRef, changes_staged); + logger.atInfo().log("qtcodereview: rebuild staging reused staging ref is %s", newStageRef); + changes_to_cherrypick = arrangeOrderLikeInRef(git, oldStageRef, newStageRef, changes_staged); + } catch (NoSuchRefException | IOException e) { + logger.atSevere().log("qtcodereview: rebuild staging ref reset %s failed. Exception %s", + stagingBranchKey, e); + throw new MergeConflictException("fatal: " + e.getMessage()); } try { - newObjId = pickChangesToStagingRef(git, projectKey, changes_integrating, newStageRefObjId); - newStageRefObjId = newObjId; + newStageRef = pickChangesToStagingRef(git, projectKey, changes_to_cherrypick, newStageRef); } 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, Change.Status.STAGED, message, null, null, null); - try (BatchUpdate u = updateFactory.create(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 (UpdateException | RestApiException ex) { - logger.atSevere().log("qtcodereview: staging ref rebuild. Failed to update change status %s", ex); + logger.atInfo().log("qtcodereview: rebuild staging ref %s merge conflict", stagingBranchKey); + newStageRef = integratingRef; + String message = "Merge conflict in staging branch. Status changed back to new. Please stage again."; + QtChangeUpdateOp op = qtUpdateFactory.create(Change.Status.NEW, Change.Status.STAGED, message, null, null, null); + try (BatchUpdate u = updateFactory.create(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 (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 refUpdate = git.updateRef(stagingBranchName); + refUpdate.setNewObjectId(newStageRef); refUpdate.update(); // send ref updated event only if it changed - if (!newStageRefObjId.equals(oldStageRefObjId)) { - referenceUpdated.fire(projectKey, branchName, oldStageRefObjId, - newStageRefObjId, user.state()); + if (!newStageRef.equals(oldStageRef)) { + referenceUpdated.fire(projectKey, stagingBranchName, oldStageRef, newStageRef, user.state()); } } catch (IOException e) { logger.atSevere().log("qtcodereview: rebuild %s failed to update ref %s", stagingBranchKey, e); @@ -468,7 +521,14 @@ public class QtUtil { Iterator<RevCommit> i = revWalk.iterator(); while (i.hasNext()) { RevCommit commit = i.next(); - List<ChangeData> changes = queryProvider.get().byBranchCommit(destination, commit.name()); + String changeId = getChangeId(commit); + List<ChangeData> changes = null; + + if (changeId != null) { + Change.Key key = new Change.Key(changeId); + changes = queryProvider.get().byBranchKey(destination, key); + } + if (changes != null && !changes.isEmpty()) { if (changes.size() > 1) logger.atWarning().log("qtcodereview: commit belongs to multiple changes: %s", commit.name()); ChangeData cd = changes.get(0); @@ -517,7 +577,7 @@ public class QtUtil { final CommitBuilder mergeCommit = new CommitBuilder(); mergeCommit.setTreeId(merger.getResultTreeId()); - mergeCommit.setParentIds(mergeTip, toMerge); + mergeCommit.setParentIds(mergeTip, toMerge); // important: mergeTip must be parent index 0 mergeCommit.setAuthor(toMerge.getAuthorIdent()); mergeCommit.setCommitter(committerIdent); mergeCommit.setMessage(message); @@ -570,6 +630,9 @@ public class QtUtil { RefUpdate refUpdate = git.updateRef(destination.get()); refUpdate.setNewObjectId(mergeCommit); return refUpdate.update(); + } catch (Exception e) { + logger.atWarning().log("qtcodereview: merge failed, %s", e); + return null; } finally { revWalk.dispose(); } |