Merge "Add ChangeKind for trivial rebase with commit message update."
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 29d1b85..857725d 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -339,7 +339,7 @@
Gerrit currently supports the following predicates:
[[changekind]]
-==== changekind:{NO_CHANGE,NO_CODE_CHANGE,MERGE_FIRST_PARENT_UPDATE,REWORK,TRIVIAL_REBASE}
+==== changekind:{NO_CHANGE,NO_CODE_CHANGE,MERGE_FIRST_PARENT_UPDATE,REWORK,TRIVIAL_REBASE,TRIVIAL_REBASE_WITH_MESSAGE_UPDATE}
Matches if the diff between two patch sets was of a certain change kind:
@@ -410,6 +410,10 @@
For the pre-installed Code-Review label this predicate is used by
default.
+* [[trivial_rebase_with_message_update]]`TRIVIAL_REBASE_WITH_MESSAGE_UPDATE`:
++
+Same as TRIVIAL_REBASE, but commit message can be different.
+
* [[rework]]`REWORK`:
+
Matches all kind of change kinds because any other change kind
diff --git a/Documentation/json.txt b/Documentation/json.txt
index dc82ad1..94f9de9 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -118,7 +118,11 @@
REWORK;; Nontrivial content changes.
- TRIVIAL_REBASE;; Conflict-free merge between the new parent and the prior patch set.
+ TRIVIAL_REBASE;; Conflict-free merge between the new parent and the prior patch set; same commit
+ message.
+
+ TRIVIAL_REBASE_WITH_MESSAGE_UPDATE;; Conflict-free merge between the new parent and the prior
+ patch set.
MERGE_FIRST_PARENT_UPDATE;; Conflict-free change of first (left) parent of a merge commit.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 6e14415..c6e5623 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -8900,7 +8900,8 @@
|===========================
|Field Name ||Description
|`kind` ||The change kind. Valid values are `REWORK`, `TRIVIAL_REBASE`,
-`MERGE_FIRST_PARENT_UPDATE`, `NO_CODE_CHANGE`, and `NO_CHANGE`.
+`TRIVIAL_REBASE_WITH_MESSAGE_UPDATE`, `MERGE_FIRST_PARENT_UPDATE`,
+`NO_CODE_CHANGE`, and `NO_CHANGE`.
|`_number` ||The patch set number, or `edit` if the patch set is an edit.
|`created` ||
The link:rest-api.html#timestamp[timestamp] of when the patch set was
diff --git a/java/com/google/gerrit/extensions/client/ChangeKind.java b/java/com/google/gerrit/extensions/client/ChangeKind.java
index 6240bba..6c6069d 100644
--- a/java/com/google/gerrit/extensions/client/ChangeKind.java
+++ b/java/com/google/gerrit/extensions/client/ChangeKind.java
@@ -22,6 +22,12 @@
/** Conflict-free merge between the new parent and the prior patch set. */
TRIVIAL_REBASE,
+ /**
+ * Conflict-free merge between the new parent and the prior patch set, accompanied with a change
+ * to commit message.
+ */
+ TRIVIAL_REBASE_WITH_MESSAGE_UPDATE,
+
/** Conflict-free change of first (left) parent of a merge commit. */
MERGE_FIRST_PARENT_UPDATE,
@@ -39,6 +45,8 @@
return true;
case TRIVIAL_REBASE:
return isTrivialRebase();
+ case TRIVIAL_REBASE_WITH_MESSAGE_UPDATE:
+ return isTrivialRebaseWithMessageUpdate();
case MERGE_FIRST_PARENT_UPDATE:
return isMergeFirstParentUpdate(isMerge);
case NO_CHANGE:
@@ -59,6 +67,14 @@
return this == NO_CHANGE || this == TRIVIAL_REBASE;
}
+ public boolean isTrivialRebaseWithMessageUpdate() {
+ // TRIVIAL_REBASE is more strict condition and hence matched as well
+ return this == NO_CHANGE
+ || this == NO_CODE_CHANGE
+ || this == TRIVIAL_REBASE
+ || this == TRIVIAL_REBASE_WITH_MESSAGE_UPDATE;
+ }
+
public boolean isMergeFirstParentUpdate(boolean isMerge) {
if (!isMerge) {
return false;
diff --git a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 74aa373..90752c0 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -193,7 +193,7 @@
@Override
public ChangeKind call() throws IOException {
if (Objects.equals(key.prior(), key.next())) {
- return ChangeKind.NO_CODE_CHANGE;
+ return ChangeKind.NO_CHANGE;
}
RevWalk rw = alreadyOpenRw;
@@ -210,15 +210,10 @@
RevCommit next = rw.parseCommit(key.next());
rw.parseBody(next);
- if (!next.getFullMessage().equals(prior.getFullMessage())) {
- if (isSameDeltaAndTree(rw, prior, next)) {
- return ChangeKind.NO_CODE_CHANGE;
- }
- return ChangeKind.REWORK;
- }
+ boolean commitMessageChanged = !next.getFullMessage().equals(prior.getFullMessage());
if (isSameDeltaAndTree(rw, prior, next)) {
- return ChangeKind.NO_CHANGE;
+ return commitMessageChanged ? ChangeKind.NO_CODE_CHANGE : ChangeKind.NO_CHANGE;
}
if (prior.getParentCount() == 0 || next.getParentCount() == 0) {
@@ -243,9 +238,11 @@
if (merger.merge(next.getParent(0), prior)
&& merger.getResultTreeId().equals(next.getTree())) {
if (prior.getParentCount() == 1) {
- return ChangeKind.TRIVIAL_REBASE;
+ return commitMessageChanged
+ ? ChangeKind.TRIVIAL_REBASE_WITH_MESSAGE_UPDATE
+ : ChangeKind.TRIVIAL_REBASE;
}
- return ChangeKind.MERGE_FIRST_PARENT_UPDATE;
+ return commitMessageChanged ? ChangeKind.REWORK : ChangeKind.MERGE_FIRST_PARENT_UPDATE;
}
} catch (LargeObjectException e) {
// Some object is too large for the merge attempt to succeed. Assume
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index c0ffde3..2036078 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -453,6 +453,8 @@
+ ".";
case TRIVIAL_REBASE:
return ": Patch Set " + priorPatchSetId.get() + " was rebased.";
+ case TRIVIAL_REBASE_WITH_MESSAGE_UPDATE:
+ return ": Patch Set " + priorPatchSetId.get() + " was rebased. Commit message was updated.";
case NO_CHANGE:
return ": New patch set was added with same tree, parent "
+ (commit.getParentCount() != 1 ? "trees" : "tree")
diff --git a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
index 01537e0..0c451ac 100644
--- a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
+++ b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
@@ -20,13 +20,33 @@
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.proto.testing.SerializedClassSubject;
import com.google.gerrit.server.cache.proto.Cache.ChangeKindKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
+import com.google.gerrit.server.change.ChangeKindCacheImpl.NoCache;
+import com.google.gerrit.testing.InMemoryRepositoryManager;
+import com.google.gerrit.testing.InMemoryRepositoryManager.Repo;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
import org.junit.Test;
public class ChangeKindCacheImplTest {
+ private InMemoryRepositoryManager repoManager;
+ private ChangeKindCache changeKindCache;
+
+ @Before
+ public void setUp() {
+ repoManager = new InMemoryRepositoryManager();
+ // For simplicity, we use non-caching version, and as long as we call the method that doesn't
+ // use ChangeData, we can provide null instead of constructing a factory.
+ changeKindCache = new NoCache(new Config(), null, repoManager);
+ }
+
@Test
public void keySerializer() throws Exception {
ChangeKindCacheImpl.Key key =
@@ -57,4 +77,134 @@
ImmutableMap.of(
"prior", ObjectId.class, "next", ObjectId.class, "strategyName", String.class));
}
+
+ @Test
+ public void commitMessageChanged() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().create();
+ RevCommit firstRev = p.commit().parent(root).message("Commit message").create();
+ RevCommit secondRev = p.commit().parent(root).message("Commit message update").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.NO_CODE_CHANGE);
+ }
+
+ @Test
+ public void sameObject_noChange() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().create();
+ RevCommit rev =
+ p.commit().parent(root).message("Commit message").add("test.md", "Hello").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, rev, rev))
+ .isEqualTo(ChangeKind.NO_CHANGE);
+ }
+
+ @Test
+ public void sameContent_noChange() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("test.md", "Hello").create();
+ RevCommit secondRev =
+ p.commit().parent(root).message("Commit message").add("test.md", "Hello").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.NO_CHANGE);
+ }
+
+ @Test
+ public void contentChanged_rework() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("test.md", "Hello").create();
+ RevCommit secondRev =
+ p.commit().parent(root).message("Commit message").add("test.md", "Goodbye").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.REWORK);
+ }
+
+ @Test
+ public void mergeConflict_rework() throws Exception {
+ // Delete a change in one of the parents
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().add("foo", "foo-text").create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("foo", "bar-text").create();
+ // File was deleted, but the commit is still writing new content to it.
+ RevCommit newRoot = p.commit().parent(root).rm("foo").create();
+ RevCommit secondRev =
+ p.commit().parent(newRoot).message("Commit message").add("foo", "bar-text").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.REWORK);
+ }
+
+ @Test
+ public void rebaseThenEdit_rework() throws Exception {
+ // Delete a change in one of the parents
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().add("foo", "foo-text").create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("foo", "bar-text").create();
+ // Unrelated file was added.
+ RevCommit newRoot = p.commit().parent(root).add("baz", "baz-text").create();
+ RevCommit secondRev =
+ p.commit().parent(newRoot).message("Commit message").add("foo", "foobar-text").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.REWORK);
+ }
+
+ @Test
+ public void trivialRebase() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().add("foo", "foo-text").create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("foo", "bar-text").create();
+ // Unrelated file was added.
+ RevCommit newRoot = p.commit().parent(root).add("baz", "baz-text").create();
+ RevCommit secondRev =
+ p.commit().parent(newRoot).message("Commit message").add("foo", "bar-text").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.TRIVIAL_REBASE);
+ }
+
+ @Test
+ public void trivialRebaseCommitMessage() throws Exception {
+ TestRepository<Repo> p = newRepo("p");
+ RevCommit root = p.commit().add("foo", "foo-text").create();
+ RevCommit firstRev =
+ p.commit().parent(root).message("Commit message").add("foo", "bar-text").create();
+ // Unrelated file was added.
+ RevCommit newRoot = p.commit().parent(root).add("baz", "baz-text").create();
+ RevCommit secondRev =
+ p.commit().parent(newRoot).message("Commit subject").add("foo", "bar-text").create();
+
+ assertThat(
+ changeKindCache.getChangeKind(
+ p.getRepository().getDescription().getProject(), null, null, firstRev, secondRev))
+ .isEqualTo(ChangeKind.TRIVIAL_REBASE_WITH_MESSAGE_UPDATE);
+ }
+
+ private TestRepository<Repo> newRepo(String name) throws Exception {
+ return new TestRepository<>(repoManager.createRepository(Project.nameKey(name)));
+ }
}
diff --git a/polygerrit-ui/app/api/rest-api.ts b/polygerrit-ui/app/api/rest-api.ts
index 134cdd3..c354d2e 100644
--- a/polygerrit-ui/app/api/rest-api.ts
+++ b/polygerrit-ui/app/api/rest-api.ts
@@ -174,6 +174,7 @@
export enum RevisionKind {
REWORK = 'REWORK',
TRIVIAL_REBASE = 'TRIVIAL_REBASE',
+ TRIVIAL_REBASE_WITH_MESSAGE_UPDATE = 'TRIVIAL_REBASE_WITH_MESSAGE_UPDATE',
MERGE_FIRST_PARENT_UPDATE = 'MERGE_FIRST_PARENT_UPDATE',
NO_CODE_CHANGE = 'NO_CODE_CHANGE',
NO_CHANGE = 'NO_CHANGE',