Merge "Add types/global import to GrApp"
diff --git a/java/com/google/gerrit/server/ChangeUtil.java b/java/com/google/gerrit/server/ChangeUtil.java
index d9edf42..2c5b539 100644
--- a/java/com/google/gerrit/server/ChangeUtil.java
+++ b/java/com/google/gerrit/server/ChangeUtil.java
@@ -124,7 +124,10 @@
    * @throws BadRequestException if the new commit message is null or empty
    */
   public static void ensureChangeIdIsCorrect(
-      boolean requireChangeId, String currentChangeId, String newCommitMessage)
+      boolean requireChangeId,
+      String currentChangeId,
+      String newCommitMessage,
+      UrlFormatter urlFormatter)
       throws ResourceConflictException, BadRequestException {
     RevCommit revCommit =
         RevCommit.parse(
@@ -133,7 +136,7 @@
     // Check that the commit message without footers is not empty
     CommitMessageUtil.checkAndSanitizeCommitMessage(revCommit.getShortMessage());
 
-    List<String> changeIdFooters = revCommit.getFooterLines(FooterConstants.CHANGE_ID);
+    List<String> changeIdFooters = getChangeIdsFromFooter(revCommit, urlFormatter);
     if (requireChangeId && changeIdFooters.isEmpty()) {
       throw new ResourceConflictException("missing Change-Id footer");
     }
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index a2ce866..94ba9cd 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.MergeConflictException;
@@ -36,6 +37,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.UrlFormatter;
 import com.google.gerrit.server.edit.tree.ChangeFileContentModification;
 import com.google.gerrit.server.edit.tree.DeleteFileModification;
 import com.google.gerrit.server.edit.tree.RenameFileModification;
@@ -103,6 +105,7 @@
   private final PatchSetUtil patchSetUtil;
   private final ProjectCache projectCache;
   private final NoteDbEdits noteDbEdits;
+  private final DynamicItem<UrlFormatter> urlFormatter;
 
   @Inject
   ChangeEditModifier(
@@ -113,7 +116,8 @@
       ChangeEditUtil changeEditUtil,
       PatchSetUtil patchSetUtil,
       ProjectCache projectCache,
-      GitReferenceUpdated gitReferenceUpdated) {
+      GitReferenceUpdated gitReferenceUpdated,
+      DynamicItem<UrlFormatter> urlFormatter) {
     this.currentUser = currentUser;
     this.permissionBackend = permissionBackend;
     this.zoneId = gerritIdent.getZoneId();
@@ -121,6 +125,7 @@
     this.patchSetUtil = patchSetUtil;
     this.projectCache = projectCache;
     noteDbEdits = new NoteDbEdits(gitReferenceUpdated, zoneId, indexer, currentUser);
+    this.urlFormatter = urlFormatter;
   }
 
   /**
@@ -515,7 +520,8 @@
           "New commit message cannot be same as existing commit message");
     }
 
-    ChangeUtil.ensureChangeIdIsCorrect(requireChangeId, currentChangeId, newCommitMessage);
+    ChangeUtil.ensureChangeIdIsCorrect(
+        requireChangeId, currentChangeId, newCommitMessage, urlFormatter.get());
 
     return newCommitMessage;
   }
diff --git a/java/com/google/gerrit/server/restapi/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
index 4a4f546..41710a6 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.CommitMessageInput;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -34,6 +35,7 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.NotifyResolver;
 import com.google.gerrit.server.change.PatchSetInserter;
+import com.google.gerrit.server.config.UrlFormatter;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.ChangePermission;
@@ -72,6 +74,7 @@
   private final PatchSetUtil psUtil;
   private final NotifyResolver notifyResolver;
   private final ProjectCache projectCache;
+  private final DynamicItem<UrlFormatter> urlFormatter;
 
   @Inject
   PutMessage(
@@ -83,7 +86,8 @@
       @GerritPersonIdent PersonIdent gerritIdent,
       PatchSetUtil psUtil,
       NotifyResolver notifyResolver,
-      ProjectCache projectCache) {
+      ProjectCache projectCache,
+      DynamicItem<UrlFormatter> urlFormatter) {
     this.updateFactory = updateFactory;
     this.repositoryManager = repositoryManager;
     this.userProvider = userProvider;
@@ -93,6 +97,7 @@
     this.psUtil = psUtil;
     this.notifyResolver = notifyResolver;
     this.projectCache = projectCache;
+    this.urlFormatter = urlFormatter;
   }
 
   @Override
@@ -116,7 +121,8 @@
             .orElseThrow(illegalState(resource.getProject()))
             .is(BooleanProjectConfig.REQUIRE_CHANGE_ID),
         resource.getChange().getKey().get(),
-        sanitizedCommitMessage);
+        sanitizedCommitMessage,
+        urlFormatter.get());
 
     try (Repository repository = repositoryManager.openRepository(resource.getProject());
         RevWalk revWalk = new RevWalk(repository);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index ddb98d9..d3ab2bb 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -3840,6 +3840,29 @@
   }
 
   @Test
+  public void changeCommitMessageFromChangeIdToLinkFooter() throws Exception {
+    PushOneCommit.Result r = createChange();
+    r.assertOkStatus();
+    assertThat(getCommitMessage(r.getChangeId()))
+        .isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
+
+    requestScopeOperations.setApiUser(admin.id());
+    String newMessage =
+        "modified commit by "
+            + admin.id()
+            + "\n\nLink: "
+            + canonicalWebUrl.get()
+            + "id/"
+            + r.getChangeId()
+            + "\n";
+    gApi.changes().id(r.getChangeId()).setMessage(newMessage);
+    RevisionApi rApi = gApi.changes().id(r.getChangeId()).current();
+    assertThat(rApi.files().keySet()).containsExactly("/COMMIT_MSG", "a.txt");
+    assertThat(getCommitMessage(r.getChangeId())).isEqualTo(newMessage);
+    assertThat(rApi.description()).isEqualTo("Edit commit message");
+  }
+
+  @Test
   public void changeCommitMessageWithNoChangeIdSucceedsIfChangeIdNotRequired() throws Exception {
     ConfigInput configInput = new ConfigInput();
     configInput.requireChangeId = InheritableBoolean.FALSE;
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index cc72924..4168164 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -325,6 +325,21 @@
   }
 
   @Test
+  public void updateCommitMessageByEditingMagicCommitMsgFileChangingChangeIdFooterToLinkFooter()
+      throws Exception {
+    createEmptyEditFor(changeId);
+    String updatedCommitMsg =
+        "Foo Bar\n\n\n\nLink: " + canonicalWebUrl.get() + "id/" + changeId + "\n";
+    gApi.changes()
+        .id(changeId)
+        .edit()
+        .modifyFile(Patch.COMMIT_MSG, RawInputUtil.create(updatedCommitMsg.getBytes(UTF_8)));
+    assertThat(getEdit(changeId)).isPresent();
+    ensureSameBytes(
+        getFileContentOfEdit(changeId, Patch.COMMIT_MSG), updatedCommitMsg.getBytes(UTF_8));
+  }
+
+  @Test
   public void updateCommitMessageByEditingMagicCommitMsgFileWithoutContent() throws Exception {
     createEmptyEditFor(changeId);
     BadRequestException ex =