| // Copyright (C) 2008 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.gerrit.server.git; | |
| import static com.google.common.base.Preconditions.checkArgument; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.Ordering; | |
| import com.google.gerrit.common.Nullable; | |
| import com.google.gerrit.entities.Change; | |
| import com.google.gerrit.entities.PatchSet; | |
| import com.google.gerrit.server.notedb.ChangeNotes; | |
| import com.google.gerrit.server.submit.CommitMergeStatus; | |
| import java.io.IOException; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.Serializable; | |
| import java.util.Optional; | |
| import java.util.Set; | |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; | |
| import org.eclipse.jgit.errors.MissingObjectException; | |
| import org.eclipse.jgit.lib.AnyObjectId; | |
| import org.eclipse.jgit.lib.ObjectReader; | |
| import org.eclipse.jgit.lib.Repository; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.eclipse.jgit.revwalk.RevWalk; | |
| /** Extended commit entity with code review specific metadata. */ | |
| public class CodeReviewCommit extends RevCommit implements Serializable { | |
| private static final long serialVersionUID = 1L; | |
| /** | |
| * Default ordering when merging multiple topologically-equivalent commits. | |
| * | |
| * <p>Operates only on these commits and does not take ancestry into account. | |
| * | |
| * <p>Use this in preference to the default order, which comes from {@link AnyObjectId} and only | |
| * orders on SHA-1. | |
| */ | |
| public static final Ordering<CodeReviewCommit> ORDER = | |
| Ordering.natural() | |
| .onResultOf( | |
| (CodeReviewCommit c) -> | |
| c.getPatchsetId() != null ? c.getPatchsetId().changeId().get() : null) | |
| .nullsFirst(); | |
| public static CodeReviewRevWalk newRevWalk(Repository repo) { | |
| return new CodeReviewRevWalk(repo); | |
| } | |
| public static CodeReviewRevWalk newRevWalk(ObjectReader reader) { | |
| return new CodeReviewRevWalk(reader); | |
| } | |
| public static class CodeReviewRevWalk extends RevWalk { | |
| private CodeReviewRevWalk(Repository repo) { | |
| super(repo); | |
| } | |
| private CodeReviewRevWalk(ObjectReader reader) { | |
| super(reader); | |
| } | |
| @Override | |
| protected CodeReviewCommit createCommit(AnyObjectId id) { | |
| return new CodeReviewCommit(id); | |
| } | |
| @Override | |
| public CodeReviewCommit next() | |
| throws MissingObjectException, IncorrectObjectTypeException, IOException { | |
| return (CodeReviewCommit) super.next(); | |
| } | |
| @Override | |
| public void markStart(RevCommit c) | |
| throws MissingObjectException, IncorrectObjectTypeException, IOException { | |
| checkArgument(c instanceof CodeReviewCommit); | |
| super.markStart(c); | |
| } | |
| @Override | |
| public void markUninteresting(RevCommit c) | |
| throws MissingObjectException, IncorrectObjectTypeException, IOException { | |
| checkArgument(c instanceof CodeReviewCommit); | |
| super.markUninteresting(c); | |
| } | |
| @Override | |
| public CodeReviewCommit lookupCommit(AnyObjectId id) { | |
| return (CodeReviewCommit) super.lookupCommit(id); | |
| } | |
| @Override | |
| public CodeReviewCommit parseCommit(AnyObjectId id) | |
| throws MissingObjectException, IncorrectObjectTypeException, IOException { | |
| return (CodeReviewCommit) super.parseCommit(id); | |
| } | |
| } | |
| /** | |
| * Unique key of the PatchSet entity from the code review system. | |
| * | |
| * <p>This value is only available on commits that have a PatchSet represented in the code review | |
| * system. | |
| */ | |
| private PatchSet.Id patchsetId; | |
| private ChangeNotes notes; | |
| /** | |
| * The result status for this commit. | |
| * | |
| * <p>Only valid if {@link #patchsetId} is not null. | |
| */ | |
| private CommitMergeStatus statusCode; | |
| /** | |
| * Message for the status that is returned to the calling user if the status indicates a problem | |
| * that prevents submit. | |
| */ | |
| private transient Optional<String> statusMessage = Optional.empty(); | |
| /** List of files in this commit that contain Git conflict markers. */ | |
| private ImmutableSet<String> filesWithGitConflicts; | |
| public CodeReviewCommit(AnyObjectId id) { | |
| super(id); | |
| } | |
| public ChangeNotes notes() { | |
| return notes; | |
| } | |
| public CommitMergeStatus getStatusCode() { | |
| return statusCode; | |
| } | |
| public void setStatusCode(CommitMergeStatus statusCode) { | |
| this.statusCode = statusCode; | |
| } | |
| public Optional<String> getStatusMessage() { | |
| return statusMessage; | |
| } | |
| public void setStatusMessage(@Nullable String statusMessage) { | |
| this.statusMessage = Optional.ofNullable(statusMessage); | |
| } | |
| public ImmutableSet<String> getFilesWithGitConflicts() { | |
| return filesWithGitConflicts != null ? filesWithGitConflicts : ImmutableSet.of(); | |
| } | |
| public void setFilesWithGitConflicts(@Nullable Set<String> filesWithGitConflicts) { | |
| this.filesWithGitConflicts = | |
| filesWithGitConflicts != null && !filesWithGitConflicts.isEmpty() | |
| ? ImmutableSet.copyOf(filesWithGitConflicts) | |
| : null; | |
| } | |
| public PatchSet.Id getPatchsetId() { | |
| return patchsetId; | |
| } | |
| public void setPatchsetId(PatchSet.Id patchsetId) { | |
| this.patchsetId = patchsetId; | |
| } | |
| public void copyFrom(CodeReviewCommit src) { | |
| notes = src.notes; | |
| patchsetId = src.patchsetId; | |
| statusCode = src.statusCode; | |
| } | |
| public Change change() { | |
| return getNotes().getChange(); | |
| } | |
| public ChangeNotes getNotes() { | |
| return notes; | |
| } | |
| public void setNotes(ChangeNotes notes) { | |
| this.notes = notes; | |
| } | |
| /** Custom serialization due to {@link #statusMessage} not being Serializable by default. */ | |
| private void writeObject(ObjectOutputStream oos) throws IOException { | |
| oos.defaultWriteObject(); | |
| if (this.statusMessage.isPresent()) { | |
| oos.writeUTF(this.statusMessage.get()); | |
| } | |
| } | |
| /** Custom deserialization due to {@link #statusMessage} not being Serializable by default. */ | |
| private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { | |
| ois.defaultReadObject(); | |
| String statusMessage = null; | |
| if (ois.available() > 0) { | |
| statusMessage = ois.readUTF(); | |
| } | |
| this.statusMessage = Optional.ofNullable(statusMessage); | |
| } | |
| } |