diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index ae5baa9c47..4a298a3b21 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -10,7 +10,9 @@ import (
"html/template"
"net/http"
"path"
+ "sort"
"strings"
+ "time"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
@@ -28,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
@@ -83,11 +86,12 @@ func Commits(ctx *context.Context) {
ctx.ServerError("CommitsByRange", err)
return
}
- ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ processedCommits, err := processGitCommits(ctx, commits)
if err != nil {
ctx.ServerError("processGitCommits", err)
return
}
+ ctx.Data["GroupCommits"] = GroupCommitsByDate(processedCommits)
commitIDs := make([]string, 0, len(commits))
for _, c := range commits {
commitIDs = append(commitIDs, c.ID.String())
@@ -199,11 +203,12 @@ func SearchCommits(ctx *context.Context) {
return
}
ctx.Data["CommitCount"] = len(commits)
- ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ processedCommits, err := processGitCommits(ctx, commits)
if err != nil {
ctx.ServerError("processGitCommits", err)
return
}
+ ctx.Data["GroupCommits"] = GroupCommitsByDate(processedCommits)
ctx.Data["Keyword"] = query
if all {
@@ -245,11 +250,12 @@ func FileHistory(ctx *context.Context) {
ctx.ServerError("CommitsByFileAndRange", err)
return
}
- ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ processedCommits, err := processGitCommits(ctx, commits)
if err != nil {
ctx.ServerError("processGitCommits", err)
return
}
+ ctx.Data["GroupCommits"] = GroupCommitsByDate(processedCommits)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
@@ -463,3 +469,57 @@ func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_m
}
return commits, nil
}
+
+// GroupedCommits defines the structure for grouped commits.
+type GroupedCommits struct {
+ Date timeutil.TimeStamp
+ Commits []*git_model.SignCommitWithStatuses
+}
+
+// GroupCommitsByDate groups the commits by date (in days) using UTC timezone.
+func GroupCommitsByDate(commits []*git_model.SignCommitWithStatuses) []GroupedCommits {
+ // Use Unix timestamp of date as key (truncated to day)
+ grouped := make(map[int64][]*git_model.SignCommitWithStatuses)
+
+ for _, commit := range commits {
+ var sigTime time.Time
+ if commit.Committer != nil {
+ sigTime = commit.Committer.When
+ } else if commit.Author != nil {
+ sigTime = commit.Author.When
+ }
+
+ // Convert time to UTC timezone first
+ sigTimeUTC := sigTime.UTC()
+
+ // Truncate time to date part (remove hours, minutes, seconds)
+ year, month, day := sigTimeUTC.Date()
+ dateOnly := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
+ dateUnix := dateOnly.Unix()
+
+ grouped[dateUnix] = append(grouped[dateUnix], commit)
+ }
+
+ // Create result slice with pre-allocated capacity
+ result := make([]GroupedCommits, 0, len(grouped))
+
+ // Collect all dates and sort them
+ dates := make([]int64, 0, len(grouped))
+ for dateUnix := range grouped {
+ dates = append(dates, dateUnix)
+ }
+ // Sort dates in descending order (most recent first)
+ sort.Slice(dates, func(i, j int) bool {
+ return dates[i] > dates[j]
+ })
+
+ // Build result in sorted order
+ for _, dateUnix := range dates {
+ result = append(result, GroupedCommits{
+ Date: timeutil.TimeStamp(dateUnix),
+ Commits: grouped[dateUnix],
+ })
+ }
+
+ return result
+}
diff --git a/routers/web/repo/commit_test.go b/routers/web/repo/commit_test.go
new file mode 100644
index 0000000000..355edddb7b
--- /dev/null
+++ b/routers/web/repo/commit_test.go
@@ -0,0 +1,114 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "testing"
+ "time"
+
+ "code.gitea.io/gitea/models/asymkey"
+ git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGroupCommitsByDate(t *testing.T) {
+ // Create test data
+ // These two commits represent the same moment but in different timezones
+ // commit1: 2025-04-10T08:00:00+08:00
+ // commit2: 2025-04-09T23:00:00-01:00
+ // Their UTC time is both 2025-04-10T00:00:00Z
+
+ // Create the first commit (Asia timezone +8)
+ asiaTimezone := time.FixedZone("Asia/Shanghai", 8*60*60)
+ commit1Time := time.Date(2025, 4, 10, 8, 0, 0, 0, asiaTimezone)
+ commit1 := &git_model.SignCommitWithStatuses{
+ SignCommit: &asymkey.SignCommit{
+ UserCommit: &user.UserCommit{
+ Commit: &git.Commit{
+ Committer: &git.Signature{
+ When: commit1Time,
+ },
+ },
+ },
+ },
+ }
+
+ // Create the second commit (Western timezone -1)
+ westTimezone := time.FixedZone("West", -1*60*60)
+ commit2Time := time.Date(2025, 4, 9, 23, 0, 0, 0, westTimezone)
+ commit2 := &git_model.SignCommitWithStatuses{
+ SignCommit: &asymkey.SignCommit{
+ UserCommit: &user.UserCommit{
+ Commit: &git.Commit{
+ Committer: &git.Signature{
+ When: commit2Time,
+ },
+ },
+ },
+ },
+ }
+
+ // Verify that the two timestamps actually represent the same moment
+ assert.Equal(t, commit1Time.Unix(), commit2Time.Unix(), "The two commits should have the same Unix timestamp")
+
+ // Test the modified grouping behavior
+ commits := []*git_model.SignCommitWithStatuses{commit1, commit2}
+ grouped := GroupCommitsByDate(commits)
+
+ // Output the grouping results for observation
+ t.Logf("Number of grouped results: %d", len(grouped))
+ for i, group := range grouped {
+ t.Logf("Group %d: Date %s, Number of commits %d", i, time.Unix(int64(group.Date), 0).Format("2006-01-02"), len(group.Commits))
+ for j, c := range group.Commits {
+ t.Logf(" Commit %d: Time %s", j, c.SignCommit.UserCommit.Commit.Committer.When.Format(time.RFC3339))
+ }
+ }
+
+ // After modification, these two commits should be grouped together as they are on the same day in UTC timezone
+ assert.Len(t, grouped, 1, "After modification, the two commits should be grouped together")
+
+ // Verify the group date (should be 2025-04-10, the date in UTC timezone)
+ utcDate := time.Date(2025, 4, 10, 0, 0, 0, 0, time.UTC)
+ assert.Equal(t, timeutil.TimeStamp(utcDate.Unix()), grouped[0].Date)
+ assert.Len(t, grouped[0].Commits, 2)
+
+ // Verify that both commits are in this group
+ commitMap := make(map[*git_model.SignCommitWithStatuses]bool)
+ for _, c := range grouped[0].Commits {
+ commitMap[c] = true
+ }
+ assert.True(t, commitMap[commit1], "The first commit should be in the group")
+ assert.True(t, commitMap[commit2], "The second commit should be in the group")
+
+ // Add a commit with a different date for testing
+ nextDayTimezone := time.FixedZone("NextDay", 0)
+ commit3Time := time.Date(2025, 4, 11, 0, 0, 0, 0, nextDayTimezone)
+ commit3 := &git_model.SignCommitWithStatuses{
+ SignCommit: &asymkey.SignCommit{
+ UserCommit: &user.UserCommit{
+ Commit: &git.Commit{
+ Committer: &git.Signature{
+ When: commit3Time,
+ },
+ },
+ },
+ },
+ }
+
+ // Test with commits from different dates
+ commits = append(commits, commit3)
+ grouped = GroupCommitsByDate(commits)
+
+ // Now there should be two groups
+ assert.Len(t, grouped, 2, "There should be two different date groups")
+
+ // Verify date sorting (descending, most recent date first)
+ assert.True(t, time.Unix(int64(grouped[0].Date), 0).After(time.Unix(int64(grouped[1].Date), 0)),
+ "Dates should be sorted in descending order")
+}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 8b99dd95da..3c595aed13 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -665,7 +665,7 @@ func PrepareCompareDiff(
ctx.ServerError("processGitCommits", err)
return false
}
- ctx.Data["Commits"] = commits
+ ctx.Data["GroupCommits"] = GroupCommitsByDate(commits)
ctx.Data["CommitCount"] = len(commits)
title := ci.HeadBranch
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 43ddc265cf..f9db2fd1b7 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -627,7 +627,7 @@ func ViewPullCommits(ctx *context.Context) {
ctx.ServerError("processGitCommits", err)
return
}
- ctx.Data["Commits"] = commits
+ ctx.Data["GroupCommits"] = GroupCommitsByDate(commits)
ctx.Data["CommitCount"] = len(commits)
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
diff --git a/templates/repo/commits_list_group_by_date.tmpl b/templates/repo/commits_list_group_by_date.tmpl
new file mode 100644
index 0000000000..492a36ea67
--- /dev/null
+++ b/templates/repo/commits_list_group_by_date.tmpl
@@ -0,0 +1,111 @@
+{{range $index, $groupCommit := .GroupCommits}}
+
+
+
+
+ {{svg "octicon-git-commit"}}
+
+
+
+
+
+ Commits on {{DateUtils.AbsoluteShort $groupCommit.Date}}
+
+
+
+ {{range $groupCommit.Commits}}
+ {{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}}
+ -
+
+
+
+ {{if $.PageIsWiki}}
+ {{.Summary | ctx.RenderUtils.RenderEmoji}}
+ {{else}}
+ {{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
+ {{ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.Repository.ComposeCommentMetas ctx)}}
+ {{end}}
+
+ {{if IsMultilineCommitMessage .Message}}
+
+ {{end}}
+ {{if IsMultilineCommitMessage .Message}}
+ {{ctx.RenderUtils.RenderCommitBody .Message ($.Repository.ComposeCommentMetas ctx)}}
+ {{end}}
+ {{if $.CommitsTagsMap}}
+ {{range (index $.CommitsTagsMap .ID.String)}}
+ {{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .TagName "IsRelease" (not .IsTag) -}}
+ {{end}}
+ {{end}}
+
+
+
+
+ {{$userName := .Author.Name}}
+ {{if .User}}
+ {{if and .User.FullName DefaultShowFullName}}
+ {{$userName = .User.FullName}}
+ {{end}}
+ {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}
{{$userName}}
+ {{else}}
+ {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}}
+
{{$userName}}
+ {{end}}
+
+
+ {{if .Committer}}
+ committed
+ {{else}}
+ authored
+ {{end}}
+
+ {{if .Committer}}
+ {{DateUtils.TimeSince .Committer.When}}
+ {{else}}
+ {{DateUtils.TimeSince .Author.When}}
+ {{end}}
+ {{if .Statuses}}
+
ยท
+ {{end}}
+ {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
+
+
+
+ {{end}}
+
+
+
+
+
+{{end}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index a0c5eacdd4..0da89e82dd 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -29,8 +29,8 @@
{{end}}
-{{if and .Commits (gt .CommitCount 0)}}
- {{template "repo/commits_list" .}}
+{{if and .GroupCommits (gt .CommitCount 0)}}
+ {{template "repo/commits_list_group_by_date" .}}
{{end}}
{{template "base/paginate" .}}
diff --git a/tests/integration/git_general_test.go b/tests/integration/git_general_test.go
index ed60bdb58a..14ad267612 100644
--- a/tests/integration/git_general_test.go
+++ b/tests/integration/git_general_test.go
@@ -707,7 +707,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge a").Last().Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go
index 4d43847f1b..f84ef5b4d9 100644
--- a/tests/integration/pull_status_test.go
+++ b/tests/integration/pull_status_test.go
@@ -49,7 +49,7 @@ func TestPullCreate_CommitStatus(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge a").Last().Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -87,12 +87,12 @@ func TestPullCreate_CommitStatus(t *testing.T) {
resp = session.MakeRequest(t, req, http.StatusOK)
doc = NewHTMLParser(t, resp.Body)
- commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href")
+ commitURL, exists = doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge a").Last().Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
assert.Equal(t, commitID, path.Base(commitURL))
- cls, ok := doc.doc.Find("#commits-table tbody tr td.message .commit-status").Last().Attr("class")
+ cls, ok := doc.doc.Find(".timeline.commits-list-group-by-date .description .commit-status").Last().Attr("class")
assert.True(t, ok)
assert.Contains(t, cls, statesIcons[status])
}
diff --git a/tests/integration/repo_commits_search_test.go b/tests/integration/repo_commits_search_test.go
index 9b05e36399..0a1a26fea5 100644
--- a/tests/integration/repo_commits_search_test.go
+++ b/tests/integration/repo_commits_search_test.go
@@ -22,7 +22,7 @@ func testRepoCommitsSearch(t *testing.T, query, commit string) {
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
- sel := doc.doc.Find("#commits-table tbody tr td.sha a")
+ sel := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge a")
assert.Equal(t, commit, strings.TrimSpace(sel.Text()))
}
diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go
index dee0aa6176..898b8a6e53 100644
--- a/tests/integration/repo_commits_test.go
+++ b/tests/integration/repo_commits_test.go
@@ -33,7 +33,7 @@ func TestRepoCommits(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
- commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
}
@@ -50,7 +50,7 @@ func Test_ReposGitCommitListNotMaster(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
commits := []string{}
- doc.doc.Find("#commits-table .commit-id-short").Each(func(i int, s *goquery.Selection) {
+ doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge .commit-id-short").Each(func(i int, s *goquery.Selection) {
commitURL, exists := s.Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -63,7 +63,7 @@ func Test_ReposGitCommitListNotMaster(t *testing.T) {
assert.Equal(t, "5099b81332712fe655e34e8dd63574f503f61811", commits[2])
userNames := []string{}
- doc.doc.Find("#commits-table .author-wrapper").Each(func(i int, s *goquery.Selection) {
+ doc.doc.Find(".timeline.commits-list-group-by-date .description .author-wrapper").Each(func(i int, s *goquery.Selection) {
userPath, exists := s.Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, userPath)
@@ -87,7 +87,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -105,7 +105,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
doc = NewHTMLParser(t, resp.Body)
// Check if commit status is displayed in message column (.tippy-target to ignore the tippy trigger)
- sel := doc.doc.Find("#commits-table .message .tippy-target .commit-status")
+ sel := doc.doc.Find(".timeline.commits-list-group-by-date .description .tippy-target .commit-status")
assert.Equal(t, 1, sel.Length())
for _, class := range classes {
assert.True(t, sel.HasClass(class))
@@ -181,7 +181,7 @@ func TestRepoCommitsStatusParallel(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -216,7 +216,7 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// Get first commit URL
- commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
+ commitURL, exists := doc.doc.Find(".timeline.commits-list-group-by-date .commit-sign-badge .commit-id-short").Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
@@ -241,6 +241,6 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
doc = NewHTMLParser(t, resp.Body)
// Check that the data-global-init="initCommitStatuses" (for trigger) and commit-status (svg) are present
- sel := doc.doc.Find(`#commits-table .message [data-global-init="initCommitStatuses"] .commit-status`)
+ sel := doc.doc.Find(`.timeline.commits-list-group-by-date .description [data-global-init="initCommitStatuses"] .commit-status`)
assert.Equal(t, 1, sel.Length())
}
diff --git a/tests/integration/view_test.go b/tests/integration/view_test.go
index 9ed3e30857..8891ca432c 100644
--- a/tests/integration/view_test.go
+++ b/tests/integration/view_test.go
@@ -48,9 +48,9 @@ func TestCommitListActions(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- AssertHTMLElement(t, htmlDoc, `.commit-list .copy-commit-id`, true)
- AssertHTMLElement(t, htmlDoc, `.commit-list .view-single-diff`, false)
- AssertHTMLElement(t, htmlDoc, `.commit-list .view-commit-path`, true)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .copy-commit-id`, true)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .view-single-diff`, false)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .view-commit-path`, true)
})
t.Run("RepoFileHistory", func(t *testing.T) {
@@ -60,8 +60,8 @@ func TestCommitListActions(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- AssertHTMLElement(t, htmlDoc, `.commit-list .copy-commit-id`, true)
- AssertHTMLElement(t, htmlDoc, `.commit-list .view-single-diff`, true)
- AssertHTMLElement(t, htmlDoc, `.commit-list .view-commit-path`, true)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .copy-commit-id`, true)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .view-single-diff`, true)
+ AssertHTMLElement(t, htmlDoc, `.timeline.commits-list-group-by-date .view-commit-path`, true)
})
}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 306db34300..6bdbdda0a2 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2281,3 +2281,110 @@ tbody.commit-list {
.branch-selector-dropdown .scrolling.menu .loading-indicator {
height: 4em;
}
+
+.commits-list-group-by-date.timeline {
+ display: flex;
+ margin-left: 16px;
+ position: relative;
+ padding-bottom: 6px;
+}
+
+.commits-list-group-by-date.timeline[data-index="0"] {
+ margin-top: 6px;
+}
+
+.commits-list-group-by-date.timeline::before {
+ background-color: var(--color-timeline);
+ bottom: 0;
+ content: "";
+ display: block;
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: 2px;
+}
+
+.commits-list-group-by-date.timeline .timeline-badge-wrapper {
+ position: relative;
+ z-index: 1;
+}
+
+.commits-list-group-by-date.timeline .timeline-badge {
+ border-style: solid;
+ border-radius: var(--border-radius-full);
+ color: var(--color-text);
+ background-color: var(--color-box-body);
+ float: left;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ margin-left: -16px;
+ width: 34px;
+ height: 34px;
+ overflow: hidden;
+}
+
+.commits-list-group-by-date.timeline .timeline-heading {
+ font-size: 15px;
+ font-weight: var(--font-weight-normal);
+ color: var(--color-text-light-2);
+ margin: 0;
+}
+
+.commits-list-group-by-date.timeline .timeline-body {
+ max-width: 100%;
+ flex: auto;
+ padding-left: 5px;
+}
+
+.commits-list-group-by-date.timeline .timeline-list-container {
+ border: 1px solid var(--color-secondary);
+ background: var(--color-box-body);
+ border-radius: var(--border-radius);
+}
+
+.commits-list-group-by-date.timeline .commits-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ flex: auto;
+}
+
+.commits-list-group-by-date.timeline .commits-list-item {
+ list-style: none;
+ display: grid;
+ gap: .5rem;
+ min-height: 2rem;
+ position: relative;
+ --core-grid-template-columns: minmax(30%, 1fr);
+ --last-grid-template-column: minmax(0, max-content);
+ grid-template-columns: var(--core-grid-template-columns) var(--last-grid-template-column);
+ grid-template-areas: "primary metadata" "main-content metadata";
+ grid-template-rows: repeat(2, auto);
+}
+
+.commits-list-group-by-date.timeline .commits-list-item:not(:last-child) {
+ border-bottom: 1px solid var(--color-secondary);
+}
+
+.commits-list-group-by-date.timeline .commits-list-item:hover {
+ background-color: var(--color-hover)
+}
+
+.commits-list-group-by-date.timeline .commits-list-item .title {
+ grid-area: primary;
+}
+
+.commits-list-group-by-date.timeline .commits-list-item .description {
+ grid-area: main-content;
+}
+
+.commits-list-group-by-date.timeline .commits-list-item .metadata {
+ grid-area: metadata;
+}
+
+.commits-list-group-by-date.timeline .author img {
+ width: 16px;
+ height: 16px;
+}