Include a magic /MERGE_LIST file for merge commits
The /MERGE_LIST file is generated by Gerrit and is automatically included in all changes for merge commits. It contains a list with the commits that are integrated by accepting the merge commit. When comparing against the auto-merge or a previous patch set it is assumed that the first parent is uninteresting, so that the file lists all commits which are reachable by the other parents, but not by the first parent. If a comparison against a selected parent is done, that parent is marked as uninteresting. This means the content of the file depends on the selection in 'Diff Against' drop-down box. By having the /MERGE_LIST file reviewers can immediately see which commits get integrated by this merge commit. This is important since for merge commits reviewers are supposed to review and approve these commits. Having a file for this allow reviewers to comment on the list and also see diffs between patch sets. In edit mode the /MERGE_LIST file is not editable. Change-Id: Iafcfe3f274ed334e9a40c13de5040a7509389e27 Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
@@ -17,6 +17,8 @@ package com.google.gerrit.acceptance;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.gerrit.acceptance.GitUtil.initSsh;
|
||||
import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||
|
||||
@@ -49,6 +51,8 @@ import com.google.gerrit.extensions.client.ListChangesOption;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.common.ActionInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeType;
|
||||
import com.google.gerrit.extensions.common.DiffInfo;
|
||||
import com.google.gerrit.extensions.common.EditInfo;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
@@ -68,6 +72,7 @@ import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.change.Abandon;
|
||||
import com.google.gerrit.server.change.ChangeResource;
|
||||
import com.google.gerrit.server.change.FileContentUtil;
|
||||
import com.google.gerrit.server.change.RevisionResource;
|
||||
import com.google.gerrit.server.change.Revisions;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
@@ -1098,4 +1103,45 @@ public abstract class AbstractDaemonTest {
|
||||
}
|
||||
assertThat(refValues.keySet()).containsAnyIn(trees.keySet());
|
||||
}
|
||||
|
||||
protected void assertDiffForNewFile(DiffInfo diff, RevCommit commit,
|
||||
String path, String expectedContentSideB) throws Exception {
|
||||
List<String> expectedLines = new ArrayList<>();
|
||||
for (String line : expectedContentSideB.split("\n")) {
|
||||
expectedLines.add(line);
|
||||
}
|
||||
|
||||
assertThat(diff.binary).isNull();
|
||||
assertThat(diff.changeType).isEqualTo(ChangeType.ADDED);
|
||||
assertThat(diff.diffHeader).isNotNull();
|
||||
assertThat(diff.intralineStatus).isNull();
|
||||
assertThat(diff.webLinks).isNull();
|
||||
|
||||
assertThat(diff.metaA).isNull();
|
||||
assertThat(diff.metaB).isNotNull();
|
||||
assertThat(diff.metaB.commitId).isEqualTo(commit.name());
|
||||
|
||||
String expectedContentType = "text/plain";
|
||||
if (COMMIT_MSG.equals(path)) {
|
||||
expectedContentType = FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE;
|
||||
} else if (MERGE_LIST.equals(path)) {
|
||||
expectedContentType = FileContentUtil.TEXT_X_GERRIT_MERGE_LIST;
|
||||
}
|
||||
assertThat(diff.metaB.contentType).isEqualTo(expectedContentType);
|
||||
|
||||
assertThat(diff.metaB.lines).isEqualTo(expectedLines.size());
|
||||
assertThat(diff.metaB.name).isEqualTo(path);
|
||||
assertThat(diff.metaB.webLinks).isNull();
|
||||
|
||||
assertThat(diff.content).hasSize(1);
|
||||
DiffInfo.ContentEntry contentEntry = diff.content.get(0);
|
||||
assertThat(contentEntry.b).containsExactlyElementsIn(expectedLines)
|
||||
.inOrder();
|
||||
assertThat(contentEntry.a).isNull();
|
||||
assertThat(contentEntry.ab).isNull();
|
||||
assertThat(contentEntry.common).isNull();
|
||||
assertThat(contentEntry.editA).isNull();
|
||||
assertThat(contentEntry.editB).isNull();
|
||||
assertThat(contentEntry.skip).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// 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.acceptance.api.change;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
import com.google.gerrit.extensions.common.CommitInfo;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NoHttpd
|
||||
public class GetMergeListIT extends AbstractDaemonTest {
|
||||
|
||||
@Test
|
||||
public void getMergeList() throws Exception {
|
||||
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
|
||||
|
||||
PushOneCommit.Result gp1 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "grand parent 1",
|
||||
ImmutableMap.of("foo", "foo-1.1", "bar", "bar-1.1"))
|
||||
.to("refs/for/master");
|
||||
|
||||
PushOneCommit.Result p1 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "parent 1",
|
||||
ImmutableMap.of("foo", "foo-1.2", "bar", "bar-1.2"))
|
||||
.to("refs/for/master");
|
||||
|
||||
// reset HEAD in order to create a sibling of the first change
|
||||
testRepo.reset(initial);
|
||||
|
||||
PushOneCommit.Result gp2 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "grand parent 1",
|
||||
ImmutableMap.of("foo", "foo-2.1", "bar", "bar-2.1"))
|
||||
.to("refs/for/master");
|
||||
|
||||
PushOneCommit.Result p2 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "parent 2",
|
||||
ImmutableMap.of("foo", "foo-2.2", "bar", "bar-2.2"))
|
||||
.to("refs/for/master");
|
||||
|
||||
PushOneCommit m = pushFactory.create(db, admin.getIdent(), testRepo,
|
||||
"merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
|
||||
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
|
||||
PushOneCommit.Result result = m.to("refs/for/master");
|
||||
result.assertOkStatus();
|
||||
|
||||
List<CommitInfo> mergeList =
|
||||
gApi.changes().id(result.getChangeId()).current().getMergeList().get();
|
||||
assertThat(mergeList).hasSize(2);
|
||||
assertThat(mergeList.get(0).commit).isEqualTo(p2.getCommit().name());
|
||||
assertThat(mergeList.get(1).commit).isEqualTo(gp2.getCommit().name());
|
||||
|
||||
mergeList = gApi.changes().id(result.getChangeId()).current().getMergeList()
|
||||
.withUninterestingParent(2).get();
|
||||
assertThat(mergeList).hasSize(2);
|
||||
assertThat(mergeList.get(0).commit).isEqualTo(p1.getCommit().name());
|
||||
assertThat(mergeList.get(1).commit).isEqualTo(gp1.getCommit().name());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// 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.acceptance.api.change;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
import com.google.gerrit.common.RawInputUtil;
|
||||
import com.google.gerrit.extensions.api.changes.RevisionApi;
|
||||
import com.google.gerrit.extensions.common.CommitInfo;
|
||||
import com.google.gerrit.extensions.common.DiffInfo;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.server.edit.ChangeEditModifier;
|
||||
import com.google.gerrit.server.edit.ChangeEditUtil;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.dircache.InvalidPathException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@NoHttpd
|
||||
public class MergeListIT extends AbstractDaemonTest {
|
||||
|
||||
private String changeId;
|
||||
private RevCommit merge;
|
||||
private RevCommit parent1;
|
||||
private RevCommit grandParent1;
|
||||
private RevCommit parent2;
|
||||
private RevCommit grandParent2;
|
||||
|
||||
@Inject
|
||||
private ChangeEditModifier modifier;
|
||||
|
||||
@Inject
|
||||
private ChangeEditUtil editUtil;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
|
||||
|
||||
PushOneCommit.Result gp1 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "grand parent 1",
|
||||
ImmutableMap.of("foo", "foo-1.1", "bar", "bar-1.1"))
|
||||
.to("refs/for/master");
|
||||
grandParent1 = gp1.getCommit();
|
||||
|
||||
PushOneCommit.Result p1 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "parent 1",
|
||||
ImmutableMap.of("foo", "foo-1.2", "bar", "bar-1.2"))
|
||||
.to("refs/for/master");
|
||||
parent1 = p1.getCommit();
|
||||
|
||||
// reset HEAD in order to create a sibling of the first change
|
||||
testRepo.reset(initial);
|
||||
|
||||
PushOneCommit.Result gp2 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "grand parent 2",
|
||||
ImmutableMap.of("foo", "foo-2.1", "bar", "bar-2.1"))
|
||||
.to("refs/for/master");
|
||||
grandParent2 = gp2.getCommit();
|
||||
|
||||
PushOneCommit.Result p2 = pushFactory
|
||||
.create(db, admin.getIdent(), testRepo, "parent 2",
|
||||
ImmutableMap.of("foo", "foo-2.2", "bar", "bar-2.2"))
|
||||
.to("refs/for/master");
|
||||
parent2 = p2.getCommit();
|
||||
|
||||
PushOneCommit m = pushFactory.create(db, admin.getIdent(), testRepo,
|
||||
"merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
|
||||
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
|
||||
PushOneCommit.Result result = m.to("refs/for/master");
|
||||
result.assertOkStatus();
|
||||
merge = result.getCommit();
|
||||
changeId = result.getChangeId();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMergeList() throws Exception {
|
||||
List<CommitInfo> mergeList = current(changeId).getMergeList().get();
|
||||
assertThat(mergeList).hasSize(2);
|
||||
assertThat(mergeList.get(0).commit).isEqualTo(parent2.name());
|
||||
assertThat(mergeList.get(1).commit).isEqualTo(grandParent2.name());
|
||||
|
||||
mergeList = current(changeId).getMergeList()
|
||||
.withUninterestingParent(2).get();
|
||||
assertThat(mergeList).hasSize(2);
|
||||
assertThat(mergeList.get(0).commit).isEqualTo(parent1.name());
|
||||
assertThat(mergeList.get(1).commit).isEqualTo(grandParent1.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMergeListContent() throws Exception {
|
||||
BinaryResult bin = current(changeId).file(MERGE_LIST).content();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
bin.writeTo(os);
|
||||
String content = new String(os.toByteArray(), UTF_8);
|
||||
assertThat(content).isEqualTo(
|
||||
getMergeListContent(parent2, grandParent2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFileList() throws Exception {
|
||||
assertThat(getFiles(changeId)).contains(MERGE_LIST);
|
||||
assertThat(getFiles(changeId, 1)).contains(MERGE_LIST);
|
||||
assertThat(getFiles(changeId, 2)).contains(MERGE_LIST);
|
||||
|
||||
assertThat(getFiles(createChange().getChangeId()))
|
||||
.doesNotContain(MERGE_LIST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDiffForMergeList() throws Exception {
|
||||
DiffInfo diff = getMergeListDiff(changeId);
|
||||
assertDiffForNewFile(diff, merge, MERGE_LIST,
|
||||
getMergeListContent(parent2, grandParent2));
|
||||
|
||||
diff = getMergeListDiff(changeId, 1);
|
||||
assertDiffForNewFile(diff, merge, MERGE_LIST,
|
||||
getMergeListContent(parent2, grandParent2));
|
||||
|
||||
diff = getMergeListDiff(changeId, 2);
|
||||
assertDiffForNewFile(diff, merge, MERGE_LIST,
|
||||
getMergeListContent(parent1, grandParent1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void editMergeList() throws Exception {
|
||||
ChangeData cd = getOnlyElement(queryProvider.get().byKeyPrefix(changeId));
|
||||
modifier.createEdit(cd.change(), cd.currentPatchSet());
|
||||
|
||||
exception.expect(InvalidPathException.class);
|
||||
exception.expectMessage("Invalid path: " + MERGE_LIST);
|
||||
modifier.modifyFile(editUtil.byChange(cd.change()).get(), MERGE_LIST,
|
||||
RawInputUtil.create("new content"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteMergeList() throws Exception {
|
||||
ChangeData cd = getOnlyElement(queryProvider.get().byKeyPrefix(changeId));
|
||||
modifier.createEdit(cd.change(), cd.currentPatchSet());
|
||||
|
||||
exception.expect(InvalidChangeOperationException.class);
|
||||
exception.expectMessage("no changes were made");
|
||||
modifier.deleteFile(editUtil.byChange(cd.change()).get(), MERGE_LIST);
|
||||
}
|
||||
|
||||
private String getMergeListContent(RevCommit... commits) {
|
||||
StringBuilder mergeList = new StringBuilder("Merge List:\n\n");
|
||||
for (RevCommit c : commits) {
|
||||
mergeList.append("* ")
|
||||
.append(c.abbreviate(8).name())
|
||||
.append(" ")
|
||||
.append(c.getShortMessage())
|
||||
.append("\n");
|
||||
}
|
||||
return mergeList.toString();
|
||||
}
|
||||
|
||||
private Set<String> getFiles(String changeId) throws Exception {
|
||||
return current(changeId).files().keySet();
|
||||
}
|
||||
|
||||
private Set<String> getFiles(String changeId, int parent) throws Exception {
|
||||
return current(changeId).files(parent).keySet();
|
||||
}
|
||||
|
||||
private DiffInfo getMergeListDiff(String changeId) throws Exception {
|
||||
return current(changeId).file(MERGE_LIST).diff();
|
||||
}
|
||||
|
||||
private DiffInfo getMergeListDiff(String changeId, int parent)
|
||||
throws Exception {
|
||||
return current(changeId).file(MERGE_LIST).diff(parent);
|
||||
}
|
||||
|
||||
private RevisionApi current(String changeId) throws Exception {
|
||||
return gApi.changes()
|
||||
.id(changeId)
|
||||
.current();
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,13 @@ import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
|
||||
import static com.google.gerrit.acceptance.PushOneCommit.PATCH;
|
||||
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
@@ -45,7 +47,6 @@ import com.google.gerrit.extensions.client.ChangeStatus;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeType;
|
||||
import com.google.gerrit.extensions.common.CommentInfo;
|
||||
import com.google.gerrit.extensions.common.DiffInfo;
|
||||
import com.google.gerrit.extensions.common.FileInfo;
|
||||
@@ -539,7 +540,7 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
.revision(r.getCommit().name())
|
||||
.files()
|
||||
.keySet()
|
||||
).containsExactly(COMMIT_MSG, "foo", "bar");
|
||||
).containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar");
|
||||
|
||||
// list files against parent 1
|
||||
assertThat(gApi.changes()
|
||||
@@ -547,7 +548,7 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
.revision(r.getCommit().name())
|
||||
.files(1)
|
||||
.keySet()
|
||||
).containsExactly(COMMIT_MSG, "bar");
|
||||
).containsExactly(COMMIT_MSG, MERGE_LIST, "bar");
|
||||
|
||||
// list files against parent 2
|
||||
assertThat(gApi.changes()
|
||||
@@ -555,7 +556,7 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
.revision(r.getCommit().name())
|
||||
.files(2)
|
||||
.keySet()
|
||||
).containsExactly(COMMIT_MSG, "foo");
|
||||
).containsExactly(COMMIT_MSG, MERGE_LIST, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -853,66 +854,40 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
.file(path)
|
||||
.diff();
|
||||
|
||||
List<String> expectedLines = new ArrayList<>();
|
||||
List<String> headers = new ArrayList<>();
|
||||
if (path.equals(COMMIT_MSG)) {
|
||||
RevCommit c = pushResult.getCommit();
|
||||
|
||||
RevCommit parentCommit = c.getParents()[0];
|
||||
String parentCommitId = testRepo.getRevWalk().getObjectReader()
|
||||
.abbreviate(parentCommit.getId(), 8).name();
|
||||
expectedLines.add("Parent: " + parentCommitId + " ("
|
||||
headers.add("Parent: " + parentCommitId + " ("
|
||||
+ parentCommit.getShortMessage() + ")");
|
||||
|
||||
SimpleDateFormat dtfmt =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.US);
|
||||
PersonIdent author = c.getAuthorIdent();
|
||||
dtfmt.setTimeZone(author.getTimeZone());
|
||||
expectedLines.add("Author: " + author.getName() + " <"
|
||||
headers.add("Author: " + author.getName() + " <"
|
||||
+ author.getEmailAddress() + ">");
|
||||
expectedLines.add("AuthorDate: "
|
||||
headers.add("AuthorDate: "
|
||||
+ dtfmt.format(Long.valueOf(author.getWhen().getTime())));
|
||||
|
||||
PersonIdent committer = c.getCommitterIdent();
|
||||
dtfmt.setTimeZone(committer.getTimeZone());
|
||||
expectedLines.add("Commit: " + committer.getName() + " <"
|
||||
headers.add("Commit: " + committer.getName() + " <"
|
||||
+ committer.getEmailAddress() + ">");
|
||||
expectedLines.add("CommitDate: "
|
||||
headers.add("CommitDate: "
|
||||
+ dtfmt.format(Long.valueOf(committer.getWhen().getTime())));
|
||||
expectedLines.add("");
|
||||
headers.add("");
|
||||
}
|
||||
|
||||
for (String line : expectedContentSideB.split("\n")) {
|
||||
expectedLines.add(line);
|
||||
if (!headers.isEmpty()) {
|
||||
String header = Joiner.on("\n").join(headers);
|
||||
expectedContentSideB = header + "\n" + expectedContentSideB;
|
||||
}
|
||||
|
||||
assertThat(diff.binary).isNull();
|
||||
assertThat(diff.changeType).isEqualTo(ChangeType.ADDED);
|
||||
assertThat(diff.diffHeader).isNotNull();
|
||||
assertThat(diff.intralineStatus).isNull();
|
||||
assertThat(diff.webLinks).isNull();
|
||||
|
||||
assertThat(diff.metaA).isNull();
|
||||
assertThat(diff.metaB).isNotNull();
|
||||
assertThat(diff.metaB.commitId).isEqualTo(pushResult.getCommit().name());
|
||||
assertThat(diff.metaB.contentType).isEqualTo(
|
||||
path.equals(COMMIT_MSG)
|
||||
? "text/x-gerrit-commit-message"
|
||||
: "text/plain");
|
||||
assertThat(diff.metaB.lines).isEqualTo(expectedLines.size());
|
||||
assertThat(diff.metaB.name).isEqualTo(path);
|
||||
assertThat(diff.metaB.webLinks).isNull();
|
||||
|
||||
assertThat(diff.content).hasSize(1);
|
||||
DiffInfo.ContentEntry contentEntry = diff.content.get(0);
|
||||
assertThat(contentEntry.b).hasSize(expectedLines.size());
|
||||
for (int i = 0; i < contentEntry.b.size(); i++) {
|
||||
assertThat(contentEntry.b.get(i)).isEqualTo(expectedLines.get(i));
|
||||
}
|
||||
assertThat(contentEntry.a).isNull();
|
||||
assertThat(contentEntry.ab).isNull();
|
||||
assertThat(contentEntry.common).isNull();
|
||||
assertThat(contentEntry.editA).isNull();
|
||||
assertThat(contentEntry.editB).isNull();
|
||||
assertThat(contentEntry.skip).isNull();
|
||||
assertDiffForNewFile(diff, pushResult.getCommit(), path,
|
||||
expectedContentSideB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,12 @@ public class FileInfo extends JavaScriptObject {
|
||||
} else if (Patch.COMMIT_MSG.equals(b.path())) {
|
||||
return 1;
|
||||
}
|
||||
if (Patch.MERGE_LIST.equals(a.path())) {
|
||||
return -1;
|
||||
} else if (Patch.MERGE_LIST.equals(b.path())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Look at file suffixes to check if it makes sense to use a different order
|
||||
int s1 = a.path().lastIndexOf('.');
|
||||
int s2 = b.path().lastIndexOf('.');
|
||||
@@ -76,9 +82,15 @@ public class FileInfo extends JavaScriptObject {
|
||||
}
|
||||
|
||||
public static String getFileName(String path) {
|
||||
String fileName = Patch.COMMIT_MSG.equals(path)
|
||||
? "Commit Message"
|
||||
: path;
|
||||
String fileName;
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
fileName = "Commit Message";
|
||||
} else if (Patch.MERGE_LIST.equals(path)) {
|
||||
fileName = "Merge List";
|
||||
} else {
|
||||
fileName = path;
|
||||
}
|
||||
|
||||
int s = fileName.lastIndexOf('/');
|
||||
return s >= 0 ? fileName.substring(s + 1) : fileName;
|
||||
}
|
||||
|
||||
@@ -486,7 +486,11 @@ public class Dispatcher {
|
||||
} else if ("unified".equals(panel)) {
|
||||
unified(token, baseId, id, side, line);
|
||||
} else if ("edit".equals(panel)) {
|
||||
codemirrorForEdit(token, id, line);
|
||||
if (!Patch.isMagic(id.get()) || Patch.COMMIT_MSG.equals(id.get())) {
|
||||
codemirrorForEdit(token, id, line);
|
||||
} else {
|
||||
Gerrit.display(token, new NotFoundScreen());
|
||||
}
|
||||
} else {
|
||||
Gerrit.display(token, new NotFoundScreen());
|
||||
}
|
||||
|
||||
@@ -267,11 +267,18 @@ public class FileTable extends FlowPanel {
|
||||
if (table != null) {
|
||||
String self = Gerrit.selfRedirect(null);
|
||||
for (FileInfo info : Natives.asList(table.list)) {
|
||||
Window.open(self + "#" + url(info), "_blank", null);
|
||||
if (canOpen(info.path())) {
|
||||
Window.open(self + "#" + url(info), "_blank", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canOpen(String path) {
|
||||
return mode != Mode.EDIT || !Patch.isMagic(path)
|
||||
|| Patch.COMMIT_MSG.equals(path);
|
||||
}
|
||||
|
||||
private void setTable(MyTable table) {
|
||||
clear();
|
||||
add(table);
|
||||
@@ -429,7 +436,10 @@ public class FileTable extends FlowPanel {
|
||||
@Override
|
||||
protected void onOpenRow(int row) {
|
||||
if (1 <= row && row <= list.length()) {
|
||||
Gerrit.display(url(list.get(row - 1)));
|
||||
FileInfo info = list.get(row - 1);
|
||||
if (canOpen(info.path())) {
|
||||
Gerrit.display(url(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,7 +462,10 @@ public class FileTable extends FlowPanel {
|
||||
|
||||
@Override
|
||||
public void onKeyPress(KeyPressEvent event) {
|
||||
Gerrit.display(url(list.get(index)));
|
||||
FileInfo info = list.get(index);
|
||||
if (canOpen(info.path())) {
|
||||
Gerrit.display(url(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -668,20 +681,43 @@ public class FileTable extends FlowPanel {
|
||||
}
|
||||
|
||||
private void columnPath(SafeHtmlBuilder sb, FileInfo info) {
|
||||
sb.openTd()
|
||||
.setStyleName(R.css().pathColumn())
|
||||
.openAnchor();
|
||||
|
||||
String path = info.path();
|
||||
|
||||
sb.openTd()
|
||||
.setStyleName(R.css().pathColumn());
|
||||
|
||||
if (!canOpen(path)) {
|
||||
sb.openDiv();
|
||||
appendPath(path);
|
||||
sb.closeDiv();
|
||||
sb.closeTd();
|
||||
return;
|
||||
}
|
||||
|
||||
sb.openAnchor();
|
||||
|
||||
if (mode == Mode.EDIT && !isEditable(info)) {
|
||||
sb.setAttribute("onclick", RESTORE + "(event," + info._row() + ")");
|
||||
} else {
|
||||
sb.setAttribute("href", "#" + url(info))
|
||||
.setAttribute("onclick", OPEN + "(event," + info._row() + ")");
|
||||
}
|
||||
appendPath(path);
|
||||
sb.closeAnchor();
|
||||
if (info.oldPath() != null) {
|
||||
sb.br();
|
||||
sb.openSpan().setStyleName(R.css().renameCopySource())
|
||||
.append(info.oldPath())
|
||||
.closeSpan();
|
||||
}
|
||||
sb.closeTd();
|
||||
}
|
||||
|
||||
private void appendPath(String path) {
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
sb.append(Util.C.commitMessage());
|
||||
} else if (Patch.MERGE_LIST.equals(path)) {
|
||||
sb.append(Util.C.mergeList());
|
||||
} else if (Gerrit.getUserPreferences().muteCommonPathPrefixes()) {
|
||||
int commonPrefixLen = commonPrefix(path);
|
||||
if (commonPrefixLen > 0) {
|
||||
@@ -694,15 +730,6 @@ public class FileTable extends FlowPanel {
|
||||
} else {
|
||||
sb.append(path);
|
||||
}
|
||||
|
||||
sb.closeAnchor();
|
||||
if (info.oldPath() != null) {
|
||||
sb.br();
|
||||
sb.openSpan().setStyleName(R.css().renameCopySource())
|
||||
.append(info.oldPath())
|
||||
.closeSpan();
|
||||
}
|
||||
sb.closeTd();
|
||||
}
|
||||
|
||||
private int commonPrefix(String path) {
|
||||
|
||||
@@ -176,6 +176,10 @@ class Message extends Composite {
|
||||
if (l != null) {
|
||||
comments.add(new FileComments(clp, ps, Util.C.commitMessage(), l));
|
||||
}
|
||||
l = m.remove(Patch.MERGE_LIST);
|
||||
if (l != null) {
|
||||
comments.add(new FileComments(clp, ps, Util.C.mergeList(), l));
|
||||
}
|
||||
for (Map.Entry<String, List<CommentInfo>> e : m.entrySet()) {
|
||||
comments.add(new FileComments(clp, ps, e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
@@ -422,6 +422,11 @@ public class ReplyBox extends Composite {
|
||||
comments.add(new FileComments(clp, psId,
|
||||
Util.C.commitMessage(), copyPath(Patch.COMMIT_MSG, l)));
|
||||
}
|
||||
l = m.get(Patch.MERGE_LIST);
|
||||
if (l != null) {
|
||||
comments.add(new FileComments(clp, psId, Util.C.commitMessage(),
|
||||
copyPath(Patch.MERGE_LIST, l)));
|
||||
}
|
||||
|
||||
List<String> paths = new ArrayList<>(m.keySet());
|
||||
Collections.sort(paths);
|
||||
|
||||
@@ -63,6 +63,7 @@ public interface ChangeConstants extends Constants {
|
||||
String patchTableColumnComments();
|
||||
String patchTableColumnSize();
|
||||
String commitMessage();
|
||||
String mergeList();
|
||||
|
||||
String patchTablePrev();
|
||||
String patchTableNext();
|
||||
|
||||
@@ -45,6 +45,7 @@ patchTableColumnName = File Path
|
||||
patchTableColumnComments = Comments
|
||||
patchTableColumnSize = Size
|
||||
commitMessage = Commit Message
|
||||
mergeList = Merge List
|
||||
|
||||
patchTablePrev = Previous file
|
||||
patchTableNext = Next file
|
||||
|
||||
@@ -122,6 +122,8 @@ public class Header extends Composite {
|
||||
SafeHtmlBuilder b = new SafeHtmlBuilder();
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
return b.append(Util.C.commitMessage());
|
||||
} else if (Patch.MERGE_LIST.equals(path)) {
|
||||
return b.append(Util.C.mergeList());
|
||||
}
|
||||
|
||||
int s = path.lastIndexOf('/') + 1;
|
||||
|
||||
@@ -22,6 +22,9 @@ public final class Patch {
|
||||
/** Magical file name which represents the commit message. */
|
||||
public static final String COMMIT_MSG = "/COMMIT_MSG";
|
||||
|
||||
/** Magical file name which represents the merge list of a merge commit. */
|
||||
public static final String MERGE_LIST = "/MERGE_LIST";
|
||||
|
||||
/**
|
||||
* Checks if the given path represents a magic file. A magic file is a
|
||||
* generated file that is automatically included into changes. It does not
|
||||
@@ -32,7 +35,7 @@ public final class Patch {
|
||||
* {@code false}.
|
||||
*/
|
||||
public static boolean isMagic(String path) {
|
||||
return COMMIT_MSG.equals(path);
|
||||
return COMMIT_MSG.equals(path) || MERGE_LIST.equals(path);
|
||||
}
|
||||
|
||||
public static class Key extends StringKey<PatchSet.Id> {
|
||||
|
||||
@@ -1064,6 +1064,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)) {
|
||||
@@ -79,27 +80,19 @@ public class GetMergeList implements RestReadView<RevisionResource> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Response<List<CommitInfo>> r = Response.ok(result);
|
||||
if (rsrc.isCacheable()) {
|
||||
r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
|
||||
Response<List<CommitInfo>> r = Response.ok(result);
|
||||
if (rsrc.isCacheable()) {
|
||||
r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -28,15 +29,15 @@ public class ComparisonType {
|
||||
|
||||
private final boolean autoMerge;
|
||||
|
||||
static ComparisonType againstOtherPatchSet() {
|
||||
public static ComparisonType againstOtherPatchSet() {
|
||||
return new ComparisonType(null, false);
|
||||
}
|
||||
|
||||
static ComparisonType againstParent(int parentNum) {
|
||||
public static ComparisonType againstParent(int parentNum) {
|
||||
return new ComparisonType(parentNum, false);
|
||||
}
|
||||
|
||||
static ComparisonType againstAutoMerge() {
|
||||
public static ComparisonType againstAutoMerge() {
|
||||
return new ComparisonType(null, true);
|
||||
}
|
||||
|
||||
@@ -57,6 +58,11 @@ public class ComparisonType {
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,15 +58,18 @@ public class PatchList implements Serializable {
|
||||
@Nullable
|
||||
private transient ObjectId oldId;
|
||||
private transient ObjectId newId;
|
||||
private transient boolean isMerge;
|
||||
private transient ComparisonType comparisonType;
|
||||
private transient int insertions;
|
||||
private transient int deletions;
|
||||
private transient PatchListEntry[] patches;
|
||||
|
||||
public PatchList(@Nullable AnyObjectId oldId, AnyObjectId newId,
|
||||
ComparisonType comparisonType, PatchListEntry[] patches) {
|
||||
boolean isMerge, ComparisonType comparisonType,
|
||||
PatchListEntry[] patches) {
|
||||
this.oldId = oldId != null ? oldId.copy() : null;
|
||||
this.newId = newId.copy();
|
||||
this.isMerge = isMerge;
|
||||
this.comparisonType = comparisonType;
|
||||
|
||||
// We assume index 0 contains the magic commit message entry.
|
||||
@@ -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,6 +172,7 @@ public class PatchList implements Serializable {
|
||||
try (DeflaterOutputStream out = new DeflaterOutputStream(buf)) {
|
||||
writeCanBeNull(out, oldId);
|
||||
writeNotNull(out, newId);
|
||||
writeVarInt32(out, isMerge ? 1 : 0);
|
||||
comparisonType.writeTo(out);
|
||||
writeVarInt32(out, insertions);
|
||||
writeVarInt32(out, deletions);
|
||||
@@ -182,6 +189,7 @@ public class PatchList implements Serializable {
|
||||
try (InflaterInputStream in = new InflaterInputStream(buf)) {
|
||||
oldId = readCanBeNull(in);
|
||||
newId = readNotNull(in);
|
||||
isMerge = readVarInt32(in) != 0;
|
||||
comparisonType = ComparisonType.readFrom(in);
|
||||
insertions = readVarInt32(in);
|
||||
deletions = readVarInt32(in);
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PatchListKey implements Serializable {
|
||||
public static final long serialVersionUID = 23L;
|
||||
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;
|
||||
@@ -161,9 +160,11 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
// 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, ComparisonType.againstParent(1), entries);
|
||||
entries[1] = newMergeList(cmp, reader, null, b, comparisonType);
|
||||
return new PatchList(a, b, true, comparisonType, entries);
|
||||
}
|
||||
|
||||
ComparisonType comparisonType = getComparisonType(a, b);
|
||||
@@ -194,6 +195,12 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
List<PatchListEntry> entries = new ArrayList<>();
|
||||
entries.add(newCommitMessage(cmp, reader,
|
||||
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())
|
||||
@@ -207,7 +214,7 @@ public class PatchListLoader implements Callable<PatchList> {
|
||||
entries.add(newEntry(aTree, fh, newSize, newSize - oldSize));
|
||||
}
|
||||
}
|
||||
return new PatchList(a, b, comparisonType,
|
||||
return new PatchList(a, b, isMerge, comparisonType,
|
||||
entries.toArray(new PatchListEntry[entries.size()]));
|
||||
}
|
||||
}
|
||||
@@ -285,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;
|
||||
@@ -322,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
|
||||
|
||||
@@ -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;
|
||||
@@ -454,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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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