Search in sources :

Example 6 with FooterLine

use of org.eclipse.jgit.revwalk.FooterLine in project gerrit by GerritCodeReview.

the class CommitRewriter method checkCommitModification.

/**
 * In NoteDb, all the meta information is stored in footer lines. If we accidentally drop some of
 * the footer lines, the original meta information will be lost, and the change might become
 * unparsable.
 *
 * <p>While we can not verify the entire commit content, we at least make sure that the resulting
 * commit has the same author, committer and footer lines are in the same order and contain same
 * footer keys as the original commit.
 *
 * <p>Commit message and footer values might have been rewritten.
 */
private void checkCommitModification(RevCommit originalCommit, byte[] newCommitContent) throws IOException {
    RevCommit newCommit = RevCommit.parse(newCommitContent);
    PersonIdent newAuthorIdent = newCommit.getAuthorIdent();
    PersonIdent originalAuthorIdent = originalCommit.getAuthorIdent();
    // The new commit must have same author and committer ident as the original commit.
    if (!verifyPersonIdent(newAuthorIdent, originalAuthorIdent)) {
        throw new IllegalStateException(String.format("New author %s does not match original author %s", newAuthorIdent.toExternalString(), originalAuthorIdent.toExternalString()));
    }
    PersonIdent newCommitterIdent = newCommit.getCommitterIdent();
    PersonIdent originalCommitterIdent = originalCommit.getCommitterIdent();
    if (!verifyPersonIdent(newCommitterIdent, originalCommitterIdent)) {
        throw new IllegalStateException(String.format("New committer %s does not match original committer %s", newCommitterIdent.toExternalString(), originalCommitterIdent.toExternalString()));
    }
    List<FooterLine> newFooterLines = newCommit.getFooterLines();
    List<FooterLine> originalFooterLines = originalCommit.getFooterLines();
    // Number and order of footer lines must remain the same, the value may have changed.
    if (newFooterLines.size() != originalFooterLines.size()) {
        String diff = computeDiff(originalCommit.getRawBuffer(), newCommitContent);
        throw new IllegalStateException(String.format("Expected footer lines in new commit to match original footer lines. Diff %s", diff));
    }
    for (int i = 0; i < newFooterLines.size(); i++) {
        FooterLine newFooterLine = newFooterLines.get(i);
        FooterLine originalFooterLine = originalFooterLines.get(i);
        if (!newFooterLine.getKey().equals(originalFooterLine.getKey())) {
            String diff = computeDiff(originalCommit.getRawBuffer(), newCommitContent);
            throw new IllegalStateException(String.format("Expected footer lines in new commit to match original footer lines. Diff %s", diff));
        }
    }
}
Also used : PersonIdent(org.eclipse.jgit.lib.PersonIdent) FooterLine(org.eclipse.jgit.revwalk.FooterLine) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

Example 7 with FooterLine

use of org.eclipse.jgit.revwalk.FooterLine in project gerrit by GerritCodeReview.

the class CommitRewriter method fixedCommitMessage.

/**
 * Fixes commit body case by case, so it does not contain user data. Returns fixed commit message,
 * or {@link Optional#empty} if no fixes were applied.
 */
private Optional<String> fixedCommitMessage(RevCommit revCommit, ChangeFixProgress fixProgress) throws ConfigInvalidException {
    byte[] raw = revCommit.getRawBuffer();
    Charset enc = RawParseUtils.parseEncoding(raw);
    Optional<CommitMessageRange> commitMessageRange = ChangeNoteUtil.parseCommitMessageRange(revCommit);
    if (!commitMessageRange.isPresent()) {
        throw new ConfigInvalidException("Failed to parse commit message " + revCommit.getName());
    }
    String changeSubject = RawParseUtils.decode(enc, raw, commitMessageRange.get().subjectStart(), commitMessageRange.get().subjectEnd());
    Optional<String> fixedChangeMessage = Optional.empty();
    String originalChangeMessage = null;
    if (commitMessageRange.isPresent() && commitMessageRange.get().hasChangeMessage()) {
        originalChangeMessage = RawParseUtils.decode(enc, raw, commitMessageRange.get().changeMessageStart(), commitMessageRange.get().changeMessageEnd() + 1).trim();
    }
    List<FooterLine> footerLines = revCommit.getFooterLines();
    StringBuilder footerLinesBuilder = new StringBuilder();
    boolean anyFootersFixed = false;
    for (FooterLine fl : footerLines) {
        String footerKey = fl.getKey();
        String footerValue = fl.getValue();
        if (footerKey.equalsIgnoreCase(FOOTER_TAG.getName())) {
            fixProgress.tag = footerValue;
        } else if (footerKey.equalsIgnoreCase(FOOTER_ASSIGNEE.getName())) {
            Account.Id oldAssignee = fixProgress.assigneeId;
            FixIdentResult fixedAssignee = null;
            if (footerValue.equals("")) {
                fixProgress.assigneeId = null;
            } else {
                fixedAssignee = getFixedIdentString(fixProgress, footerValue);
                fixProgress.assigneeId = fixedAssignee.accountId;
            }
            if (!fixedChangeMessage.isPresent()) {
                fixedChangeMessage = fixAssigneeChangeMessage(fixProgress, Optional.ofNullable(oldAssignee), Optional.ofNullable(fixProgress.assigneeId), originalChangeMessage);
            }
            if (fixedAssignee != null && fixedAssignee.fixedIdentString.isPresent()) {
                addFooter(footerLinesBuilder, footerKey, fixedAssignee.fixedIdentString.get());
                anyFootersFixed = true;
                continue;
            }
        } else if (Arrays.stream(ReviewerStateInternal.values()).anyMatch(state -> footerKey.equalsIgnoreCase(state.getFooterKey().getName()))) {
            if (!fixedChangeMessage.isPresent()) {
                fixedChangeMessage = fixReviewerChangeMessage(originalChangeMessage);
            }
            FixIdentResult fixedReviewer = getFixedIdentString(fixProgress, footerValue);
            if (fixedReviewer.fixedIdentString.isPresent()) {
                addFooter(footerLinesBuilder, footerKey, fixedReviewer.fixedIdentString.get());
                anyFootersFixed = true;
                continue;
            }
        } else if (footerKey.equalsIgnoreCase(FOOTER_REAL_USER.getName())) {
            FixIdentResult fixedRealUser = getFixedIdentString(fixProgress, footerValue);
            if (fixedRealUser.fixedIdentString.isPresent()) {
                addFooter(footerLinesBuilder, footerKey, fixedRealUser.fixedIdentString.get());
                anyFootersFixed = true;
                continue;
            }
        } else if (footerKey.equalsIgnoreCase(FOOTER_LABEL.getName())) {
            int uuidStart = footerValue.indexOf(", ");
            int voterIdentStart = footerValue.indexOf(' ', uuidStart != -1 ? uuidStart + 2 : 0);
            FixIdentResult fixedVoter = null;
            if (voterIdentStart > 0) {
                String originalIdentString = footerValue.substring(voterIdentStart + 1);
                fixedVoter = getFixedIdentString(fixProgress, originalIdentString);
            }
            if (!fixedChangeMessage.isPresent()) {
                fixedChangeMessage = fixRemoveVoteChangeMessage(fixProgress, fixedVoter == null ? fixProgress.updateAuthorId : Optional.of(fixedVoter.accountId), originalChangeMessage);
            }
            if (fixedVoter != null && fixedVoter.fixedIdentString.isPresent()) {
                String fixedLabelVote = footerValue.substring(0, voterIdentStart) + " " + fixedVoter.fixedIdentString.get();
                addFooter(footerLinesBuilder, footerKey, fixedLabelVote);
                anyFootersFixed = true;
                continue;
            }
        } else if (footerKey.equalsIgnoreCase(FOOTER_SUBMITTED_WITH.getName())) {
            // Record format:
            // Submitted-with: OK
            // Submitted-with: OK: Code-Review: User Name <accountId@serverId>
            int voterIdentStart = StringUtils.ordinalIndexOf(footerValue, ": ", 2);
            if (voterIdentStart >= 0) {
                String originalIdentString = footerValue.substring(voterIdentStart + 2);
                FixIdentResult fixedVoter = getFixedIdentString(fixProgress, originalIdentString);
                if (fixedVoter.fixedIdentString.isPresent()) {
                    String fixedLabelVote = footerValue.substring(0, voterIdentStart) + ": " + fixedVoter.fixedIdentString.get();
                    addFooter(footerLinesBuilder, footerKey, fixedLabelVote);
                    anyFootersFixed = true;
                    continue;
                }
            }
        } else if (footerKey.equalsIgnoreCase(FOOTER_ATTENTION.getName())) {
            AttentionStatusInNoteDb originalAttentionSetUpdate = gson.fromJson(footerValue, AttentionStatusInNoteDb.class);
            FixIdentResult fixedAttentionAccount = getFixedIdentString(fixProgress, originalAttentionSetUpdate.personIdent);
            Optional<String> fixedReason = fixAttentionSetReason(originalAttentionSetUpdate.reason);
            if (fixedAttentionAccount.fixedIdentString.isPresent() || fixedReason.isPresent()) {
                AttentionStatusInNoteDb fixedAttentionSetUpdate = new AttentionStatusInNoteDb(fixedAttentionAccount.fixedIdentString.isPresent() ? fixedAttentionAccount.fixedIdentString.get() : originalAttentionSetUpdate.personIdent, originalAttentionSetUpdate.operation, fixedReason.isPresent() ? fixedReason.get() : originalAttentionSetUpdate.reason);
                addFooter(footerLinesBuilder, footerKey, gson.toJson(fixedAttentionSetUpdate));
                anyFootersFixed = true;
                continue;
            }
        }
        addFooter(footerLinesBuilder, footerKey, footerValue);
    }
    // getPossibleAccountReplacement)
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixReviewerChangeMessage(originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixRemoveVotesChangeMessage(fixProgress, originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixRemoveVoteChangeMessage(fixProgress, Optional.empty(), originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixAssigneeChangeMessage(fixProgress, Optional.empty(), Optional.empty(), originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixSubmitChangeMessage(originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixDeleteChangeMessageCommitMessage(originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent()) {
        fixedChangeMessage = fixCodeOwnersOnReviewChangeMessage(fixProgress.updateAuthorId, originalChangeMessage);
    }
    if (!fixedChangeMessage.isPresent() && Objects.equals(fixProgress.tag, CODE_OWNER_ADD_REVIEWER_TAG)) {
        fixedChangeMessage = fixCodeOwnersOnAddReviewerChangeMessage(fixProgress, originalChangeMessage);
    }
    if (!anyFootersFixed && !fixedChangeMessage.isPresent()) {
        return Optional.empty();
    }
    StringBuilder fixedCommitBuilder = new StringBuilder();
    fixedCommitBuilder.append(changeSubject);
    fixedCommitBuilder.append("\n\n");
    if (commitMessageRange.get().hasChangeMessage()) {
        fixedCommitBuilder.append(fixedChangeMessage.orElse(originalChangeMessage));
        fixedCommitBuilder.append("\n\n");
    }
    fixedCommitBuilder.append(footerLinesBuilder);
    return Optional.of(fixedCommitBuilder.toString());
}
Also used : ConfigInvalidException(org.eclipse.jgit.errors.ConfigInvalidException) Charset(java.nio.charset.Charset) FooterLine(org.eclipse.jgit.revwalk.FooterLine) AttentionStatusInNoteDb(com.google.gerrit.server.notedb.ChangeNoteUtil.AttentionStatusInNoteDb) CommitMessageRange(com.google.gerrit.server.notedb.ChangeNoteUtil.CommitMessageRange) ObjectId(org.eclipse.jgit.lib.ObjectId) ExternalId(com.google.gerrit.server.account.externalids.ExternalId)

Aggregations

FooterLine (org.eclipse.jgit.revwalk.FooterLine)7 Account (com.google.gerrit.entities.Account)2 FooterKey (org.eclipse.jgit.revwalk.FooterKey)2 LabelType (com.google.gerrit.common.data.LabelType)1 Change (com.google.gerrit.entities.Change)1 PatchSetApproval (com.google.gerrit.entities.PatchSetApproval)1 UnprocessableEntityException (com.google.gerrit.extensions.restapi.UnprocessableEntityException)1 Account (com.google.gerrit.reviewdb.client.Account)1 Change (com.google.gerrit.reviewdb.client.Change)1 PatchSetApproval (com.google.gerrit.reviewdb.client.PatchSetApproval)1 ExternalId (com.google.gerrit.server.account.externalids.ExternalId)1 AttentionStatusInNoteDb (com.google.gerrit.server.notedb.ChangeNoteUtil.AttentionStatusInNoteDb)1 CommitMessageRange (com.google.gerrit.server.notedb.ChangeNoteUtil.CommitMessageRange)1 Charset (java.nio.charset.Charset)1 ArrayList (java.util.ArrayList)1 Optional (java.util.Optional)1 ConfigInvalidException (org.eclipse.jgit.errors.ConfigInvalidException)1 ObjectId (org.eclipse.jgit.lib.ObjectId)1 PersonIdent (org.eclipse.jgit.lib.PersonIdent)1 RevCommit (org.eclipse.jgit.revwalk.RevCommit)1