diff a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/CommitFormatters.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/CommitFormatters.java
--- /dev/null
+++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/CommitFormatters.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.skara.bots.notify;
+
+import org.openjdk.skara.forge.HostedRepository;
+import org.openjdk.skara.vcs.*;
+
+import java.io.*;
+import java.time.format.DateTimeFormatter;
+
+class CommitFormatters {
+    static String commitToTextBrief(HostedRepository repository, Commit commit) {
+        var writer = new StringWriter();
+        var printer = new PrintWriter(writer);
+
+        printer.println("Changeset: " + commit.hash().abbreviate());
+        printer.println("Author:    " + commit.author().name() + " <" + commit.author().email() + ">");
+        if (!commit.author().equals(commit.committer())) {
+            printer.println("Committer: " + commit.committer().name() + " <" + commit.committer().email() + ">");
+        }
+        printer.println("Date:      " + commit.date().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss +0000")));
+        printer.println("URL:       " + repository.webUrl(commit.hash()));
+
+        return writer.toString();
+    }
+
+    private static String patchToText(Patch patch) {
+        if (patch.status().isAdded()) {
+            return "+ " + patch.target().path().orElseThrow();
+        } else if (patch.status().isDeleted()) {
+            return "- " + patch.source().path().orElseThrow();
+        } else if (patch.status().isModified()) {
+            return "! " + patch.target().path().orElseThrow();
+        } else {
+            return "= " + patch.target().path().orElseThrow();
+        }
+    }
+
+    static String commitToText(HostedRepository repository, Commit commit) {
+        var writer = new StringWriter();
+        var printer = new PrintWriter(writer);
+
+        printer.print(commitToTextBrief(repository, commit));
+        printer.println();
+        printer.println(String.join("\n", commit.message()));
+        printer.println();
+
+        for (var diff : commit.parentDiffs()) {
+            for (var patch : diff.patches()) {
+                printer.println(patchToText(patch));
+            }
+        }
+
+        return writer.toString();
+    }
+}
diff a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/IssueUpdater.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/IssueUpdater.java
--- /dev/null
+++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/IssueUpdater.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.skara.bots.notify;
+
+import org.openjdk.skara.forge.HostedRepository;
+import org.openjdk.skara.issuetracker.Issue;
+import org.openjdk.skara.issuetracker.IssueProject;
+import org.openjdk.skara.vcs.*;
+import org.openjdk.skara.vcs.openjdk.*;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+public class IssueUpdater implements UpdateConsumer {
+    private final IssueProject issueProject;
+    private final Logger log = Logger.getLogger("org.openjdk.skara.bots.notify");
+
+    IssueUpdater(IssueProject issueProject) {
+        this.issueProject = issueProject;
+    }
+
+    @Override
+    public void handleCommits(HostedRepository repository, List<Commit> commits, Branch branch) {
+        for (var commit : commits) {
+            var commitNotification = CommitFormatters.commitToTextBrief(repository, commit);
+            var commitMessage = CommitMessageParsers.v1.parse(commit);
+            for (var commitIssue : commitMessage.issues()) {
+                var issue = issueProject.issue(commitIssue.id());
+                if (issue.isEmpty()) {
+                    log.severe("Cannot update issue " + commitIssue.id() + " with commit " + commit.hash().abbreviate()
+                                       + " - issue not found in issue project");
+                    continue;
+                }
+                issue.get().addComment(commitNotification);
+                issue.get().setState(Issue.State.CLOSED);
+            }
+        }
+    }
+
+    @Override
+    public void handleOpenJDKTagCommits(HostedRepository repository, List<Commit> commits, OpenJDKTag tag, Tag.Annotated annotated) {
+
+    }
+
+    @Override
+    public void handleTagCommit(HostedRepository repository, Commit commit, Tag tag, Tag.Annotated annotation) {
+
+    }
+
+    @Override
+    public void handleNewBranch(HostedRepository repository, List<Commit> commits, Branch parent, Branch branch) {
+
+    }
+}
diff a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/JNotifyBotFactory.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/JNotifyBotFactory.java
--- a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/JNotifyBotFactory.java
+++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/JNotifyBotFactory.java
@@ -109,10 +109,14 @@
                     var allowedDomains = author == null ? Pattern.compile(mailinglist.get("domains").asString()) : null;
                     updaters.add(new MailingListUpdater(listServer.getList(recipient), recipientAddress, sender, author,
                                                         includeBranchNames, mode, headers, allowedDomains));
                 }
             }
+            if (repo.value().contains("issues")) {
+                var issueProject = configuration.issueProject(repo.value().get("issues").asString());
+                updaters.add(new IssueUpdater(issueProject));
+            }
 
             if (updaters.isEmpty()) {
                 log.warning("No consumers configured for notify bot repository: " + repoName);
                 continue;
             }
diff a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/MailingListUpdater.java b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/MailingListUpdater.java
--- a/bots/notify/src/main/java/org/openjdk/skara/bots/notify/MailingListUpdater.java
+++ b/bots/notify/src/main/java/org/openjdk/skara/bots/notify/MailingListUpdater.java
@@ -63,55 +63,10 @@
         this.mode = mode;
         this.headers = headers;
         this.allowedAuthorDomains = allowedAuthorDomains;
     }
 
-    private String patchToText(Patch patch) {
-        if (patch.status().isAdded()) {
-            return "+ " + patch.target().path().orElseThrow();
-        } else if (patch.status().isDeleted()) {
-            return "- " + patch.source().path().orElseThrow();
-        } else if (patch.status().isModified()) {
-            return "! " + patch.target().path().orElseThrow();
-        } else {
-            return "= " + patch.target().path().orElseThrow();
-        }
-    }
-
-    private String commitToTextBrief(HostedRepository repository, Commit commit) {
-        var writer = new StringWriter();
-        var printer = new PrintWriter(writer);
-
-        printer.println("Changeset: " + commit.hash().abbreviate());
-        printer.println("Author:    " + commit.author().name() + " <" + commit.author().email() + ">");
-        if (!commit.author().equals(commit.committer())) {
-            printer.println("Committer: " + commit.committer().name() + " <" + commit.committer().email() + ">");
-        }
-        printer.println("Date:      " + commit.date().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss +0000")));
-        printer.println("URL:       " + repository.webUrl(commit.hash()));
-
-        return writer.toString();
-    }
-
-    private String commitToText(HostedRepository repository, Commit commit) {
-        var writer = new StringWriter();
-        var printer = new PrintWriter(writer);
-
-        printer.print(commitToTextBrief(repository, commit));
-        printer.println();
-        printer.println(String.join("\n", commit.message()));
-        printer.println();
-
-        for (var diff : commit.parentDiffs()) {
-            for (var patch : diff.patches()) {
-                printer.println(patchToText(patch));
-            }
-        }
-
-        return writer.toString();
-    }
-
     private String tagAnnotationToText(HostedRepository repository, Tag.Annotated annotation) {
         var writer = new StringWriter();
         var printer = new PrintWriter(writer);
 
         printer.println("Tagged by: " + annotation.author().name() + " <" + annotation.author().email() + ">");
@@ -199,11 +154,11 @@
                 ret.add(commit);
                 continue;
             }
             var rfr = rfrCandidates.get(0);
 
-            var body = commitToText(repository, commit);
+            var body = CommitFormatters.commitToText(repository, commit);
             var email = Email.reply(rfr, "Re: [Integrated] " + rfr.subject(), body)
                              .sender(sender)
                              .author(commitToAuthor(commit))
                              .recipient(recipient)
                              .headers(headers)
@@ -221,11 +176,11 @@
 
         var writer = new StringWriter();
         var printer = new PrintWriter(writer);
 
         for (var commit : commits) {
-            printer.println(commitToText(repository, commit));
+            printer.println(CommitFormatters.commitToText(repository, commit));
         }
 
         var subject = commitsToSubject(repository, commits, branch);
         var lastCommit = commits.get(commits.size() - 1);
         var commitAddress = filteredAuthor(EmailAddress.from(lastCommit.committer().name(), lastCommit.committer().email()));
@@ -264,11 +219,11 @@
 
         var taggedCommit = commits.get(commits.size() - 1);
         if (annotation != null) {
             printer.println(tagAnnotationToText(repository, annotation));
         }
-        printer.println(commitToTextBrief(repository, taggedCommit));
+        printer.println(CommitFormatters.commitToTextBrief(repository, taggedCommit));
 
         printer.println("The following commits are included in " + tag.tag());
         printer.println("========================================================");
         for (var commit : commits) {
             printer.print(commit.hash().abbreviate());
@@ -302,11 +257,11 @@
         var printer = new PrintWriter(writer);
 
         if (annotation != null) {
             printer.println(tagAnnotationToText(repository, annotation));
         }
-        printer.println(commitToTextBrief(repository, commit));
+        printer.println(CommitFormatters.commitToTextBrief(repository, commit));
 
         var subject = tagToSubject(repository, commit.hash(), tag);
         var email = Email.create(subject, writer.toString())
                          .sender(sender)
                          .recipient(recipient)
diff a/bots/notify/src/test/java/org/openjdk/skara/bots/notify/UpdaterTests.java b/bots/notify/src/test/java/org/openjdk/skara/bots/notify/UpdaterTests.java
--- a/bots/notify/src/test/java/org/openjdk/skara/bots/notify/UpdaterTests.java
+++ b/bots/notify/src/test/java/org/openjdk/skara/bots/notify/UpdaterTests.java
@@ -718,6 +718,44 @@
             assertEquals(email.recipients(), List.of(listAddress));
             assertEquals("git: test: created branch newbranch2 based on the branch newbranch1 containing 0 unique commits", email.subject());
             assertEquals("The new branch newbranch2 is currently identical to the newbranch1 branch.", email.body());
         }
     }
+
+    @Test
+    void testIssue(TestInfo testInfo) throws IOException {
+        try (var credentials = new HostCredentials(testInfo);
+             var tempFolder = new TemporaryDirectory()) {
+            var repo = credentials.getHostedRepository();
+            var repoFolder = tempFolder.path().resolve("repo");
+            var localRepo = CheckableRepository.init(repoFolder, repo.repositoryType());
+            credentials.commitLock(localRepo);
+            localRepo.pushAll(repo.url());
+
+            var tagStorage = createTagStorage(repo);
+            var branchStorage = createBranchStorage(repo);
+            var storageFolder = tempFolder.path().resolve("storage");
+
+            var issueProject = credentials.getIssueProject();
+            var updater = new IssueUpdater(issueProject);
+            var notifyBot = new JNotifyBot(repo, storageFolder, Pattern.compile("master"), tagStorage, branchStorage, List.of(updater));
+
+            // Initialize history
+            TestBotRunner.runPeriodicItems(notifyBot);
+
+            // Create an issue and commit a fix
+            var issue = issueProject.createIssue("This is an issue", List.of("Indeed"));
+            var editHash = CheckableRepository.appendAndCommit(localRepo, "Another line", issue.id() + ": Fix that issue");
+            localRepo.push(editHash, repo.url(), "master");
+            TestBotRunner.runPeriodicItems(notifyBot);
+
+            // The changeset should be reflected in a comment
+            var comments = issue.comments();
+            assertEquals(1, comments.size());
+            var comment = comments.get(0);
+            assertTrue(comment.body().contains(editHash.abbreviate()));
+
+            // There should be no open issues
+            assertEquals(0, issueProject.issues().size());
+        }
+    }
 }
