Show commit title, description, author and date on commit detail page

Added GitService.getCommitInfo() using getFullMessage() to preserve
the full commit body. Controller splits the message into title (first
line) and body (remainder) for separate display in the template.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-27 11:24:40 +01:00
parent b0016767e8
commit 8784dfc391
5 changed files with 66 additions and 0 deletions

View File

@@ -103,6 +103,14 @@ public class RepoController
model.addAttribute("name", name);
model.addAttribute("hash", hash);
model.addAttribute("shortHash", hash.substring(0, Math.min(7, hash.length())));
var info = gitService.getCommitInfo(name, hash);
model.addAttribute("commitInfo", info);
String fullMsg = info.message() != null ? info.message().trim() : "";
int newline = fullMsg.indexOf('\n');
String title = newline >= 0 ? fullMsg.substring(0, newline).trim() : fullMsg;
String body = newline >= 0 ? fullMsg.substring(newline).trim() : "";
model.addAttribute("commitTitle", title);
model.addAttribute("commitBody", body);
model.addAttribute("diffs", gitService.getCommitDiff(name, hash));
return "commit";
}

View File

@@ -434,6 +434,33 @@ public class GitService
return sb.toString().stripTrailing();
}
public CommitInfo getCommitInfo(String name, String commitHash) throws IOException
{
try (Git git = openRepository(name))
{
Repository repo = git.getRepository();
ObjectId commitId = repo.resolve(commitHash);
try (var walk = new org.eclipse.jgit.revwalk.RevWalk(repo))
{
RevCommit commit = walk.parseCommit(commitId);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
.withZone(ZoneId.systemDefault());
List<String> parents = Arrays.stream(commit.getParents())
.map(p -> p.getId().abbreviate(7).name())
.toList();
return new CommitInfo(
commit.getId().getName(),
commit.getId().abbreviate(7).name(),
commit.getFullMessage(),
commit.getAuthorIdent().getName(),
fmt.format(Instant.ofEpochSecond(commit.getCommitTime())),
parents,
""
);
}
}
}
public List<DiffInfo> getCommitDiff(String name, String commitHash) throws IOException
{
try (Git git = openRepository(name))

View File

@@ -8,6 +8,20 @@
<p><a th:href="@{/repo/{name}/commits(name=${name})}">&lt; Back to commits</a></p>
<table border="0" cellpadding="4" cellspacing="0">
<tr>
<td><b>Author:</b></td>
<td th:text="${commitInfo.author}"></td>
</tr>
<tr>
<td><b>Date:</b></td>
<td th:text="${commitInfo.date}"></td>
</tr>
</table>
<h3 th:text="${commitTitle}"></h3>
<pre th:if="${!commitBody.isEmpty()}" th:text="${commitBody}"></pre>
<table border="0" cellpadding="4" cellspacing="0">
<tr>
<td>

View File

@@ -246,6 +246,9 @@ class RepoControllerTest
@Test
void commitDetailShowsDiff() throws Exception
{
when(gitService.getCommitInfo("myrepo", "abc1234")).thenReturn(
new be.seeseepuff.webgit.model.CommitInfo("abc1234abc1234", "abc1234", "Fix bug\n\nDetails here", "Alice", "2024-01-01 12:00", List.of(), "")
);
when(gitService.getCommitDiff("myrepo", "abc1234")).thenReturn(List.of(
new be.seeseepuff.webgit.model.DiffInfo("ADD", "/dev/null", "file.txt", "+hello")
));
@@ -254,6 +257,7 @@ class RepoControllerTest
.andExpect(status().isOk())
.andExpect(view().name("commit"))
.andExpect(model().attribute("hash", "abc1234"))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Fix bug")))
.andExpect(content().string(org.hamcrest.Matchers.containsString("file.txt")));
}

View File

@@ -349,6 +349,19 @@ class GitServiceTest
assertNotNull(commits.getFirst().graphLine());
}
@Test
void getCommitInfoReturnsDetails() throws GitAPIException, IOException
{
gitService.cloneRepository(bareRemote.toUri().toString(), "myrepo");
var commits = gitService.listCommits("myrepo");
var info = gitService.getCommitInfo("myrepo", commits.getFirst().hash());
assertNotNull(info);
assertTrue(info.message().contains("Initial commit"));
assertNotNull(info.author());
assertNotNull(info.date());
}
@Test
void listCommitsGraphLineShowsBranchingCorrectly() throws GitAPIException, IOException
{