Merge changes I66527dd1,Iafcfe3f2,I1f7d77fc,I8a75c32a
* changes: GetMergeList: Also allow caching of responses for non-merges Include a magic /MERGE_LIST file for merge commits Patch: Add static method to check if file is magic Don't compare commit message against auto-merge commit
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
@@ -27,6 +29,7 @@ import com.google.gerrit.extensions.client.Side;
|
||||
import com.google.gerrit.extensions.common.CommentInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Patch;
|
||||
import com.google.gerrit.reviewdb.client.PatchLineComment;
|
||||
import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
@@ -210,10 +213,27 @@ public class PatchLineCommentsUtil {
|
||||
public List<PatchLineComment> publishedByPatchSet(ReviewDb db,
|
||||
ChangeNotes notes, PatchSet.Id psId) throws OrmException {
|
||||
if (!migration.readChanges()) {
|
||||
return sort(
|
||||
db.patchComments().publishedByPatchSet(psId).toList());
|
||||
return removeCommentsOnAncestorOfCommitMessage(sort(
|
||||
db.patchComments().publishedByPatchSet(psId).toList()));
|
||||
}
|
||||
return commentsOnPatchSet(notes.load().getComments().values(), psId);
|
||||
return removeCommentsOnAncestorOfCommitMessage(
|
||||
commentsOnPatchSet(notes.load().getComments().values(), psId));
|
||||
}
|
||||
|
||||
/**
|
||||
* For the commit message the A side in a diff view is always empty when a
|
||||
* comparison against an ancestor is done, so there can't be any comments on
|
||||
* this ancestor. However earlier we showed the auto-merge commit message on
|
||||
* side A when for a merge commit a comparison against the auto-merge was
|
||||
* done. From that time there may still be comments on the auto-merge commit
|
||||
* message and those we want to filter out.
|
||||
*/
|
||||
private List<PatchLineComment> removeCommentsOnAncestorOfCommitMessage(
|
||||
List<PatchLineComment> list) {
|
||||
return list.stream()
|
||||
.filter(c -> c.getSide() != 0
|
||||
|| !Patch.COMMIT_MSG.equals(c.getKey().getParentKey().get()))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public List<PatchLineComment> draftByPatchSetAuthor(ReviewDb db,
|
||||
|
||||
@@ -1056,6 +1056,7 @@ public class ChangeJson {
|
||||
if (has(ALL_FILES) || (out.isCurrent && has(CURRENT_FILES))) {
|
||||
out.files = fileInfoJson.toFileInfoMap(c, in);
|
||||
out.files.remove(Patch.COMMIT_MSG);
|
||||
out.files.remove(Patch.MERGE_LIST);
|
||||
}
|
||||
|
||||
if ((out.isCurrent || (out.draft != null && out.draft))
|
||||
|
||||
@@ -54,6 +54,7 @@ import java.util.zip.ZipOutputStream;
|
||||
@Singleton
|
||||
public class FileContentUtil {
|
||||
public static final String TEXT_X_GERRIT_COMMIT_MESSAGE = "text/x-gerrit-commit-message";
|
||||
public static final String TEXT_X_GERRIT_MERGE_LIST = "text/x-gerrit-merge-list";
|
||||
private static final String X_GIT_SYMLINK = "x-git/symlink";
|
||||
private static final String X_GIT_GITLINK = "x-git/gitlink";
|
||||
private static final int MAX_SIZE = 5 << 20;
|
||||
@@ -264,6 +265,9 @@ public class FileContentUtil {
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
return TEXT_X_GERRIT_COMMIT_MESSAGE;
|
||||
}
|
||||
if (Patch.MERGE_LIST.equals(path)) {
|
||||
return TEXT_X_GERRIT_MERGE_LIST;
|
||||
}
|
||||
if (project != null) {
|
||||
for (ProjectState p : project.tree()) {
|
||||
String t = p.getConfig().getMimeTypes().getMimeType(path);
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.PatchSetUtil;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
import com.google.gerrit.server.patch.ComparisonType;
|
||||
import com.google.gerrit.server.patch.Text;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -68,6 +70,12 @@ public class GetContent implements RestReadView<FileResource> {
|
||||
return BinaryResult.create(msg)
|
||||
.setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
|
||||
.base64();
|
||||
} else if (Patch.MERGE_LIST.equals(path)) {
|
||||
byte[] mergeList = getMergeList(
|
||||
rsrc.getRevision().getChangeResource().getNotes());
|
||||
return BinaryResult.create(mergeList)
|
||||
.setContentType(FileContentUtil.TEXT_X_GERRIT_MERGE_LIST)
|
||||
.base64();
|
||||
}
|
||||
return fileContentUtil.getContent(
|
||||
rsrc.getRevision().getControl().getProjectControl().getProjectState(),
|
||||
@@ -92,4 +100,22 @@ public class GetContent implements RestReadView<FileResource> {
|
||||
throw new NoSuchChangeException(changeId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getMergeList(ChangeNotes notes)
|
||||
throws NoSuchChangeException, OrmException, IOException {
|
||||
Change.Id changeId = notes.getChangeId();
|
||||
PatchSet ps = psUtil.current(db.get(), notes);
|
||||
if (ps == null) {
|
||||
throw new NoSuchChangeException(changeId);
|
||||
}
|
||||
|
||||
try (Repository git = gitManager.openRepository(notes.getProjectName());
|
||||
RevWalk revWalk = new RevWalk(git)) {
|
||||
return Text.forMergeList(ComparisonType.againstAutoMerge(),
|
||||
revWalk.getObjectReader(),
|
||||
ObjectId.fromString(ps.getRevision().get())).getContent();
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
throw new NoSuchChangeException(changeId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.patch.MergeListBuilder;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
@@ -46,7 +47,8 @@ public class GetMergeList implements RestReadView<RevisionResource> {
|
||||
private boolean addLinks;
|
||||
|
||||
@Inject
|
||||
GetMergeList(GitRepositoryManager repoManager, ChangeJson.Factory json) {
|
||||
GetMergeList(GitRepositoryManager repoManager,
|
||||
ChangeJson.Factory json) {
|
||||
this.repoManager = repoManager;
|
||||
this.json = json;
|
||||
}
|
||||
@@ -62,7 +64,6 @@ public class GetMergeList implements RestReadView<RevisionResource> {
|
||||
@Override
|
||||
public Response<List<CommitInfo>> apply(RevisionResource rsrc)
|
||||
throws BadRequestException, IOException {
|
||||
List<CommitInfo> result = new ArrayList<>();
|
||||
Project.NameKey p = rsrc.getChange().getProject();
|
||||
try (Repository repo = repoManager.openRepository(p);
|
||||
RevWalk rw = new RevWalk(repo)) {
|
||||
@@ -76,26 +77,22 @@ public class GetMergeList implements RestReadView<RevisionResource> {
|
||||
}
|
||||
|
||||
if (commit.getParentCount() < 2) {
|
||||
return Response.<List<CommitInfo>> ok(ImmutableList.<CommitInfo> of());
|
||||
}
|
||||
|
||||
for (int parent = 0; parent < commit.getParentCount(); parent++) {
|
||||
if (parent == uninterestingParent - 1) {
|
||||
rw.markUninteresting(commit.getParent(parent));
|
||||
} else {
|
||||
rw.markStart(commit.getParent(parent));
|
||||
}
|
||||
return createResponse(rsrc, ImmutableList.<CommitInfo> of());
|
||||
}
|
||||
|
||||
List<RevCommit> commits =
|
||||
MergeListBuilder.build(rw, commit, uninterestingParent);
|
||||
List<CommitInfo> result = new ArrayList<>(commits.size());
|
||||
ChangeJson changeJson = json.create(ChangeJson.NO_OPTIONS);
|
||||
RevCommit c;
|
||||
while ((c = rw.next()) != null) {
|
||||
CommitInfo info =
|
||||
changeJson.toCommit(rsrc.getControl(), rw, c, addLinks, true);
|
||||
result.add(info);
|
||||
for (RevCommit c : commits) {
|
||||
result.add(changeJson.toCommit(rsrc.getControl(), rw, c, addLinks, true));
|
||||
}
|
||||
return createResponse(rsrc, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static Response<List<CommitInfo>> createResponse(
|
||||
RevisionResource rsrc, List<CommitInfo> result) {
|
||||
Response<List<CommitInfo>> r = Response.ok(result);
|
||||
if (rsrc.isCacheable()) {
|
||||
r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
|
||||
|
||||
@@ -324,11 +324,19 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
|
||||
while (mapItr.hasNext()) {
|
||||
Map.Entry<String, List<CommentInput>> ent = mapItr.next();
|
||||
String path = ent.getKey();
|
||||
if (!filePaths.contains(path) && !Patch.COMMIT_MSG.equals(path)) {
|
||||
if (!filePaths.contains(path) && !Patch.isMagic(path)) {
|
||||
throw new BadRequestException(String.format(
|
||||
"file %s not found in revision %s",
|
||||
path, revision.getChange().currentPatchSetId()));
|
||||
}
|
||||
if (Patch.isMagic(path)) {
|
||||
for (CommentInput comment : ent.getValue()) {
|
||||
if (comment.side == Side.PARENT && comment.parent == null) {
|
||||
throw new BadRequestException(
|
||||
String.format("cannot comment on %s on auto-merge", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<CommentInput> list = ent.getValue();
|
||||
if (list == null) {
|
||||
|
||||
@@ -496,7 +496,7 @@ public class EventFactory {
|
||||
List<Patch> list =
|
||||
patchListCache.get(change, patchSet).toPatchList(pId);
|
||||
for (Patch pe : list) {
|
||||
if (!Patch.COMMIT_MSG.equals(pe.getFileName())) {
|
||||
if (!Patch.isMagic(pe.getFileName())) {
|
||||
p.sizeDeletions -= pe.getDeletions();
|
||||
p.sizeInsertions += pe.getInsertions();
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ public abstract class ChangeEmail extends NotificationEmail {
|
||||
detail.append("---\n");
|
||||
PatchList patchList = getPatchList();
|
||||
for (PatchListEntry p : patchList.getPatches()) {
|
||||
if (Patch.COMMIT_MSG.equals(p.getNewName())) {
|
||||
if (Patch.isMagic(p.getNewName())) {
|
||||
continue;
|
||||
}
|
||||
detail.append(p.getChangeType().getCode())
|
||||
|
||||
@@ -75,7 +75,7 @@ public class CommentSender extends ReplyToChangeSender {
|
||||
Set<String> paths = new HashSet<>();
|
||||
for (PatchLineComment c : plc) {
|
||||
Patch.Key p = c.getKey().getParentKey();
|
||||
if (!Patch.COMMIT_MSG.equals(p.getFileName())) {
|
||||
if (!Patch.isMagic(p.getFileName())) {
|
||||
paths.add(p.getFileName());
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,8 @@ public class CommentSender extends ReplyToChangeSender {
|
||||
}
|
||||
if (Patch.COMMIT_MSG.equals(pk.get())) {
|
||||
cmts.append("Commit Message:\n\n");
|
||||
} else if (Patch.MERGE_LIST.equals(pk.get())) {
|
||||
cmts.append("Merge List:\n\n");
|
||||
} else {
|
||||
cmts.append("File ").append(pk.get()).append(":\n\n");
|
||||
}
|
||||
@@ -144,8 +146,7 @@ public class CommentSender extends ReplyToChangeSender {
|
||||
|
||||
if (patchList != null) {
|
||||
try {
|
||||
currentFileData =
|
||||
new PatchFile(repo, patchList, pk.get());
|
||||
currentFileData = new PatchFile(repo, patchList, pk.get());
|
||||
} catch (IOException e) {
|
||||
log.warn(String.format(
|
||||
"Cannot load %s from %s in %s",
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2016 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.patch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
|
||||
import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ComparisonType {
|
||||
|
||||
/** 1-based parent */
|
||||
private final Integer parentNum;
|
||||
|
||||
private final boolean autoMerge;
|
||||
|
||||
public static ComparisonType againstOtherPatchSet() {
|
||||
return new ComparisonType(null, false);
|
||||
}
|
||||
|
||||
public static ComparisonType againstParent(int parentNum) {
|
||||
return new ComparisonType(parentNum, false);
|
||||
}
|
||||
|
||||
public static ComparisonType againstAutoMerge() {
|
||||
return new ComparisonType(null, true);
|
||||
}
|
||||
|
||||
private ComparisonType(Integer parentNum, boolean autoMerge) {
|
||||
this.parentNum = parentNum;
|
||||
this.autoMerge = autoMerge;
|
||||
}
|
||||
|
||||
public boolean isAgainstParentOrAutoMerge() {
|
||||
return isAgainstParent() || isAgainstAutoMerge();
|
||||
}
|
||||
|
||||
public boolean isAgainstParent() {
|
||||
return parentNum != null;
|
||||
}
|
||||
|
||||
public boolean isAgainstAutoMerge() {
|
||||
return autoMerge;
|
||||
}
|
||||
|
||||
public int getParentNum() {
|
||||
checkNotNull(parentNum);
|
||||
return parentNum;
|
||||
}
|
||||
|
||||
void writeTo(OutputStream out) throws IOException {
|
||||
writeVarInt32(out, parentNum != null ? parentNum : 0);
|
||||
writeVarInt32(out, autoMerge ? 1 : 0);
|
||||
}
|
||||
|
||||
static ComparisonType readFrom(InputStream in) throws IOException {
|
||||
int p = readVarInt32(in);
|
||||
Integer parentNum = p > 0 ? p : null;
|
||||
boolean autoMerge = readVarInt32(in) != 0;
|
||||
return new ComparisonType(parentNum, autoMerge);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2016 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.patch;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MergeListBuilder {
|
||||
public static List<RevCommit> build(RevWalk rw, RevCommit merge,
|
||||
int uninterestingParent) throws IOException {
|
||||
rw.reset();
|
||||
rw.parseBody(merge);
|
||||
if (merge.getParentCount() < 2) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
for (int parent = 0; parent < merge.getParentCount(); parent++) {
|
||||
RevCommit parentCommit = merge.getParent(parent);
|
||||
rw.parseBody(parentCommit);
|
||||
if (parent == uninterestingParent - 1) {
|
||||
rw.markUninteresting(parentCommit);
|
||||
} else {
|
||||
rw.markStart(parentCommit);
|
||||
}
|
||||
}
|
||||
|
||||
List<RevCommit> result = new ArrayList<>();
|
||||
RevCommit c;
|
||||
while ((c = rw.next()) != null) {
|
||||
result.add(c);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,8 @@ public class PatchFile {
|
||||
private Text a;
|
||||
private Text b;
|
||||
|
||||
public PatchFile(final Repository repo, final PatchList patchList,
|
||||
final String fileName) throws MissingObjectException,
|
||||
IncorrectObjectTypeException, IOException {
|
||||
public PatchFile(Repository repo, PatchList patchList, String fileName)
|
||||
throws MissingObjectException, IncorrectObjectTypeException, IOException {
|
||||
this.repo = repo;
|
||||
this.entry = patchList.get(fileName);
|
||||
|
||||
@@ -55,7 +54,7 @@ public class PatchFile {
|
||||
final RevCommit bCommit = rw.parseCommit(patchList.getNewId());
|
||||
|
||||
if (Patch.COMMIT_MSG.equals(fileName)) {
|
||||
if (patchList.isAgainstParent()) {
|
||||
if (patchList.getComparisonType().isAgainstParentOrAutoMerge()) {
|
||||
a = Text.EMPTY;
|
||||
} else {
|
||||
// For the initial commit, we have an empty tree on Side A
|
||||
@@ -68,7 +67,16 @@ public class PatchFile {
|
||||
|
||||
aTree = null;
|
||||
bTree = null;
|
||||
} else if (Patch.MERGE_LIST.equals(fileName)) {
|
||||
// For the initial commit, we have an empty tree on Side A
|
||||
RevObject object = rw.parseAny(patchList.getOldId());
|
||||
a = object instanceof RevCommit
|
||||
? Text.forMergeList(patchList.getComparisonType(), reader, object)
|
||||
: Text.EMPTY;
|
||||
b = Text.forMergeList(patchList.getComparisonType(), reader, bCommit);
|
||||
|
||||
aTree = null;
|
||||
bTree = null;
|
||||
} else {
|
||||
if (patchList.getOldId() != null) {
|
||||
aTree = rw.parseTree(patchList.getOldId());
|
||||
|
||||
@@ -58,16 +58,19 @@ public class PatchList implements Serializable {
|
||||
@Nullable
|
||||
private transient ObjectId oldId;
|
||||
private transient ObjectId newId;
|
||||
private transient boolean againstParent;
|
||||
private transient boolean isMerge;
|
||||
private transient ComparisonType comparisonType;
|
||||
private transient int insertions;
|
||||
private transient int deletions;
|
||||
private transient PatchListEntry[] patches;
|
||||
|
||||
public PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId,
|
||||
final boolean againstParent, final PatchListEntry[] patches) {
|
||||
public PatchList(@Nullable AnyObjectId oldId, AnyObjectId newId,
|
||||
boolean isMerge, ComparisonType comparisonType,
|
||||
PatchListEntry[] patches) {
|
||||
this.oldId = oldId != null ? oldId.copy() : null;
|
||||
this.newId = newId.copy();
|
||||
this.againstParent = againstParent;
|
||||
this.isMerge = isMerge;
|
||||
this.comparisonType = comparisonType;
|
||||
|
||||
// We assume index 0 contains the magic commit message entry.
|
||||
if (patches.length > 1) {
|
||||
@@ -97,9 +100,9 @@ public class PatchList implements Serializable {
|
||||
return Collections.unmodifiableList(Arrays.asList(patches));
|
||||
}
|
||||
|
||||
/** @return true if {@link #getOldId} is {@link #getNewId}'s ancestor. */
|
||||
public boolean isAgainstParent() {
|
||||
return againstParent;
|
||||
/** @return the comparison type */
|
||||
public ComparisonType getComparisonType() {
|
||||
return comparisonType;
|
||||
}
|
||||
|
||||
/** @return total number of new lines added. */
|
||||
@@ -144,9 +147,12 @@ public class PatchList implements Serializable {
|
||||
if (Patch.COMMIT_MSG.equals(fileName)) {
|
||||
return 0;
|
||||
}
|
||||
if (isMerge && Patch.MERGE_LIST.equals(fileName)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int high = patches.length;
|
||||
int low = 1;
|
||||
int low = isMerge ? 2 : 1;
|
||||
while (low < high) {
|
||||
final int mid = (low + high) >>> 1;
|
||||
final int cmp = patches[mid].getNewName().compareTo(fileName);
|
||||
@@ -166,7 +172,8 @@ public class PatchList implements Serializable {
|
||||
try (DeflaterOutputStream out = new DeflaterOutputStream(buf)) {
|
||||
writeCanBeNull(out, oldId);
|
||||
writeNotNull(out, newId);
|
||||
writeVarInt32(out, againstParent ? 1 : 0);
|
||||
writeVarInt32(out, isMerge ? 1 : 0);
|
||||
comparisonType.writeTo(out);
|
||||
writeVarInt32(out, insertions);
|
||||
writeVarInt32(out, deletions);
|
||||
writeVarInt32(out, patches.length);
|
||||
@@ -182,7 +189,8 @@ public class PatchList implements Serializable {
|
||||
try (InflaterInputStream in = new InflaterInputStream(buf)) {
|
||||
oldId = readCanBeNull(in);
|
||||
newId = readNotNull(in);
|
||||
againstParent = readVarInt32(in) != 0;
|
||||
isMerge = readVarInt32(in) != 0;
|
||||
comparisonType = ComparisonType.readFrom(in);
|
||||
insertions = readVarInt32(in);
|
||||
deletions = readVarInt32(in);
|
||||
final int cnt = readVarInt32(in);
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PatchListKey implements Serializable {
|
||||
public static final long serialVersionUID = 22L;
|
||||
public static final long serialVersionUID = 24L;
|
||||
|
||||
public static final BiMap<Whitespace, Character> WHITESPACE_TYPES = ImmutableBiMap.of(
|
||||
Whitespace.IGNORE_NONE, 'N',
|
||||
@@ -138,6 +138,10 @@ public class PatchListKey implements Serializable {
|
||||
n.append("..");
|
||||
n.append(newId.name());
|
||||
n.append(" ");
|
||||
if (parentNum != null) {
|
||||
n.append(parentNum);
|
||||
n.append(" ");
|
||||
}
|
||||
n.append(whitespace.name());
|
||||
n.append("]");
|
||||
return n.toString();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.google.gerrit.server.patch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
|
||||
@@ -156,14 +155,19 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
|
||||
if (a == null) {
|
||||
// TODO(sop) Remove this case.
|
||||
// This is a merge commit, compared to its ancestor.
|
||||
// This is an octopus merge commit which should be compared against the
|
||||
// auto-merge. However since we don't support computing the auto-merge
|
||||
// for octopus merge commits, we fall back to diffing against the first
|
||||
// parent, even though this wasn't what was requested.
|
||||
//
|
||||
PatchListEntry[] entries = new PatchListEntry[1];
|
||||
ComparisonType comparisonType = ComparisonType.againstParent(1);
|
||||
PatchListEntry[] entries = new PatchListEntry[2];
|
||||
entries[0] = newCommitMessage(cmp, reader, null, b);
|
||||
return new PatchList(a, b, true, entries);
|
||||
entries[1] = newMergeList(cmp, reader, null, b, comparisonType);
|
||||
return new PatchList(a, b, true, comparisonType, entries);
|
||||
}
|
||||
|
||||
boolean againstParent = isAgainstParent(a, b);
|
||||
ComparisonType comparisonType = getComparisonType(a, b);
|
||||
|
||||
RevCommit aCommit = a instanceof RevCommit ? (RevCommit) a : null;
|
||||
RevTree aTree = rw.parseTree(a);
|
||||
@@ -190,7 +194,13 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
int cnt = diffEntries.size();
|
||||
List<PatchListEntry> entries = new ArrayList<>();
|
||||
entries.add(newCommitMessage(cmp, reader,
|
||||
againstParent ? null : aCommit, b));
|
||||
comparisonType.isAgainstParentOrAutoMerge() ? null : aCommit, b));
|
||||
boolean isMerge = b.getParentCount() > 1;
|
||||
if (isMerge) {
|
||||
entries.add(newMergeList(cmp, reader,
|
||||
comparisonType.isAgainstParentOrAutoMerge() ? null : aCommit, b,
|
||||
comparisonType));
|
||||
}
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
DiffEntry e = diffEntries.get(i);
|
||||
if (paths == null || paths.contains(e.getNewPath())
|
||||
@@ -204,19 +214,23 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
entries.add(newEntry(aTree, fh, newSize, newSize - oldSize));
|
||||
}
|
||||
}
|
||||
return new PatchList(a, b, againstParent,
|
||||
return new PatchList(a, b, isMerge, comparisonType,
|
||||
entries.toArray(new PatchListEntry[entries.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAgainstParent(RevObject a, RevCommit b) {
|
||||
private ComparisonType getComparisonType(RevObject a, RevCommit b) {
|
||||
for (int i = 0; i < b.getParentCount(); i++) {
|
||||
if (b.getParent(i).equals(a)) {
|
||||
return true;
|
||||
return ComparisonType.againstParent(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (key.getOldId() == null && b.getParentCount() > 0) {
|
||||
return ComparisonType.againstAutoMerge();
|
||||
}
|
||||
|
||||
return ComparisonType.againstOtherPatchSet();
|
||||
}
|
||||
|
||||
private static long getFileSize(ObjectReader reader,
|
||||
@@ -278,32 +292,30 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
return diffFormatter.toFileHeader(diffEntry);
|
||||
}
|
||||
|
||||
private PatchListEntry newCommitMessage(final RawTextComparator cmp,
|
||||
final ObjectReader reader,
|
||||
final RevCommit aCommit, final RevCommit bCommit) throws IOException {
|
||||
StringBuilder hdr = new StringBuilder();
|
||||
|
||||
hdr.append("diff --git");
|
||||
if (aCommit != null) {
|
||||
hdr.append(" a/").append(Patch.COMMIT_MSG);
|
||||
} else {
|
||||
hdr.append(" ").append(FileHeader.DEV_NULL);
|
||||
}
|
||||
hdr.append(" b/").append(Patch.COMMIT_MSG);
|
||||
hdr.append("\n");
|
||||
|
||||
if (aCommit != null) {
|
||||
hdr.append("--- a/").append(Patch.COMMIT_MSG).append("\n");
|
||||
} else {
|
||||
hdr.append("--- ").append(FileHeader.DEV_NULL).append("\n");
|
||||
}
|
||||
hdr.append("+++ b/").append(Patch.COMMIT_MSG).append("\n");
|
||||
|
||||
Text aText =
|
||||
aCommit != null ? Text.forCommit(reader, aCommit) : Text.EMPTY;
|
||||
private PatchListEntry newCommitMessage(RawTextComparator cmp,
|
||||
ObjectReader reader, RevCommit aCommit, RevCommit bCommit)
|
||||
throws IOException {
|
||||
Text aText = aCommit != null
|
||||
? Text.forCommit(reader, aCommit)
|
||||
: Text.EMPTY;
|
||||
Text bText = Text.forCommit(reader, bCommit);
|
||||
return createPatchListEntry(cmp, aCommit, aText, bText, Patch.COMMIT_MSG);
|
||||
}
|
||||
|
||||
byte[] rawHdr = hdr.toString().getBytes(UTF_8);
|
||||
private PatchListEntry newMergeList(RawTextComparator cmp,
|
||||
ObjectReader reader, RevCommit aCommit, RevCommit bCommit,
|
||||
ComparisonType comparisonType) throws IOException {
|
||||
Text aText = aCommit != null
|
||||
? Text.forMergeList(comparisonType, reader, aCommit)
|
||||
: Text.EMPTY;
|
||||
Text bText =
|
||||
Text.forMergeList(comparisonType, reader, bCommit);
|
||||
return createPatchListEntry(cmp, aCommit, aText, bText, Patch.MERGE_LIST);
|
||||
}
|
||||
|
||||
private static PatchListEntry createPatchListEntry(RawTextComparator cmp,
|
||||
RevCommit aCommit, Text aText, Text bText, String fileName) {
|
||||
byte[] rawHdr = getRawHeader(aCommit != null, fileName);
|
||||
byte[] aContent = aText.getContent();
|
||||
byte[] bContent = bText.getContent();
|
||||
long size = bContent.length;
|
||||
@@ -315,6 +327,26 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
return new PatchListEntry(fh, edits, size, sizeDelta);
|
||||
}
|
||||
|
||||
private static byte[] getRawHeader(boolean hasA, String fileName) {
|
||||
StringBuilder hdr = new StringBuilder();
|
||||
hdr.append("diff --git");
|
||||
if (hasA) {
|
||||
hdr.append(" a/").append(fileName);
|
||||
} else {
|
||||
hdr.append(" ").append(FileHeader.DEV_NULL);
|
||||
}
|
||||
hdr.append(" b/").append(fileName);
|
||||
hdr.append("\n");
|
||||
|
||||
if (hasA) {
|
||||
hdr.append("--- a/").append(fileName).append("\n");
|
||||
} else {
|
||||
hdr.append("--- ").append(FileHeader.DEV_NULL).append("\n");
|
||||
}
|
||||
hdr.append("+++ b/").append(fileName).append("\n");
|
||||
return hdr.toString().getBytes(UTF_8);
|
||||
}
|
||||
|
||||
private PatchListEntry newEntry(RevTree aTree, FileHeader fileHeader,
|
||||
long size, long sizeDelta) {
|
||||
if (aTree == null // want combined diff
|
||||
|
||||
@@ -66,7 +66,7 @@ class PatchScriptBuilder {
|
||||
private ObjectReader reader;
|
||||
private Change change;
|
||||
private DiffPreferencesInfo diffPrefs;
|
||||
private boolean againstParent;
|
||||
private ComparisonType comparisonType;
|
||||
private ObjectId aId;
|
||||
private ObjectId bId;
|
||||
|
||||
@@ -79,7 +79,8 @@ class PatchScriptBuilder {
|
||||
private int context;
|
||||
|
||||
@Inject
|
||||
PatchScriptBuilder(final FileTypeRegistry ftr, final PatchListCache plc) {
|
||||
PatchScriptBuilder(FileTypeRegistry ftr,
|
||||
PatchListCache plc) {
|
||||
a = new Side();
|
||||
b = new Side();
|
||||
registry = ftr;
|
||||
@@ -106,8 +107,8 @@ class PatchScriptBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void setTrees(final boolean ap, final ObjectId a, final ObjectId b) {
|
||||
againstParent = ap;
|
||||
void setTrees(final ComparisonType ct, final ObjectId a, final ObjectId b) {
|
||||
comparisonType = ct;
|
||||
aId = a;
|
||||
bId = b;
|
||||
}
|
||||
@@ -435,7 +436,8 @@ class PatchScriptBuilder {
|
||||
try {
|
||||
final boolean reuse;
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
if (againstParent && (aId == within || within.equals(aId))) {
|
||||
if (comparisonType.isAgainstParentOrAutoMerge()
|
||||
&& (aId == within || within.equals(aId))) {
|
||||
id = ObjectId.zeroId();
|
||||
src = Text.EMPTY;
|
||||
srcContent = Text.NO_BYTES;
|
||||
@@ -453,7 +455,26 @@ class PatchScriptBuilder {
|
||||
}
|
||||
}
|
||||
reuse = false;
|
||||
|
||||
} else if (Patch.MERGE_LIST.equals(path)) {
|
||||
if (comparisonType.isAgainstParentOrAutoMerge()
|
||||
&& (aId == within || within.equals(aId))) {
|
||||
id = ObjectId.zeroId();
|
||||
src = Text.EMPTY;
|
||||
srcContent = Text.NO_BYTES;
|
||||
mode = FileMode.MISSING;
|
||||
displayMethod = DisplayMethod.NONE;
|
||||
} else {
|
||||
id = within;
|
||||
src = Text.forMergeList(comparisonType, reader, within);
|
||||
srcContent = src.getContent();
|
||||
if (src == Text.EMPTY) {
|
||||
mode = FileMode.MISSING;
|
||||
displayMethod = DisplayMethod.NONE;
|
||||
} else {
|
||||
mode = FileMode.REGULAR_FILE;
|
||||
}
|
||||
}
|
||||
reuse = false;
|
||||
} else {
|
||||
final TreeWalk tw = find(within);
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
|
||||
b.setRepository(git, project);
|
||||
b.setChange(change);
|
||||
b.setDiffPrefs(diffPrefs);
|
||||
b.setTrees(list.isAgainstParent(), list.getOldId(), list.getNewId());
|
||||
b.setTrees(list.getComparisonType(), list.getOldId(), list.getNewId());
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,36 @@ public class Text extends RawText {
|
||||
}
|
||||
}
|
||||
|
||||
public static Text forMergeList(ComparisonType comparisonType,
|
||||
ObjectReader reader, AnyObjectId commitId) throws IOException {
|
||||
try (RevWalk rw = new RevWalk(reader)) {
|
||||
RevCommit c = rw.parseCommit(commitId);
|
||||
StringBuilder b = new StringBuilder();
|
||||
switch (c.getParentCount()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
int uniterestingParent = comparisonType.isAgainstParent()
|
||||
? comparisonType.getParentNum()
|
||||
: 1;
|
||||
|
||||
b.append("Merge List:\n\n");
|
||||
for (RevCommit commit : MergeListBuilder.build(rw, c,
|
||||
uniterestingParent)) {
|
||||
b.append("* ");
|
||||
b.append(reader.abbreviate(commit, 8).name());
|
||||
b.append(" ");
|
||||
b.append(commit.getShortMessage());
|
||||
b.append("\n");
|
||||
}
|
||||
}
|
||||
return new Text(b.toString().getBytes(UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendPersonIdent(StringBuilder b, String field,
|
||||
PersonIdent person) {
|
||||
if (person != null) {
|
||||
|
||||
@@ -47,7 +47,7 @@ public class FilesInCommitCollection implements
|
||||
@Override
|
||||
public FileResource parse(CommitResource parent, IdString id)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
if (Patch.COMMIT_MSG.equals(id.get())) {
|
||||
if (Patch.isMagic(id.get())) {
|
||||
return new FileResource(parent.getProject(), parent.getCommit(),
|
||||
id.get());
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ public class ChangeData {
|
||||
|
||||
r = new ArrayList<>(p.get().getPatches().size());
|
||||
for (PatchListEntry e : p.get().getPatches()) {
|
||||
if (Patch.COMMIT_MSG.equals(e.getNewName())) {
|
||||
if (Patch.isMagic(e.getNewName())) {
|
||||
continue;
|
||||
}
|
||||
switch (e.getChangeType()) {
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
package gerrit;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Patch;
|
||||
import com.google.gerrit.rules.StoredValues;
|
||||
import com.google.gerrit.server.patch.PatchList;
|
||||
import com.google.gerrit.server.patch.PatchListEntry;
|
||||
|
||||
import com.googlecode.prolog_cafe.exceptions.PrologException;
|
||||
import com.googlecode.prolog_cafe.lang.IntegerTerm;
|
||||
@@ -24,6 +26,8 @@ import com.googlecode.prolog_cafe.lang.Predicate;
|
||||
import com.googlecode.prolog_cafe.lang.Prolog;
|
||||
import com.googlecode.prolog_cafe.lang.Term;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exports basic commit statistics.
|
||||
*
|
||||
@@ -48,7 +52,11 @@ public class PRED_commit_stats_3 extends Predicate.P3 {
|
||||
Term a3 = arg3.dereference();
|
||||
|
||||
PatchList pl = StoredValues.PATCH_LIST.get(engine);
|
||||
if (!a1.unify(new IntegerTerm(pl.getPatches().size() - 1),engine.trail)) { //Account for /COMMIT_MSG.
|
||||
// Account for magic files
|
||||
if (!a1.unify(
|
||||
new IntegerTerm(
|
||||
pl.getPatches().size() - countMagicFiles(pl.getPatches())),
|
||||
engine.trail)) {
|
||||
return engine.fail();
|
||||
}
|
||||
if (!a2.unify(new IntegerTerm(pl.getInsertions()),engine.trail)) {
|
||||
@@ -59,4 +67,14 @@ public class PRED_commit_stats_3 extends Predicate.P3 {
|
||||
}
|
||||
return cont;
|
||||
}
|
||||
|
||||
private int countMagicFiles(List<PatchListEntry> entries) {
|
||||
int count = 0;
|
||||
for (PatchListEntry e : entries) {
|
||||
if (Patch.isMagic(e.getNewName())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user