Skip to content

Conversation

@fjamesprice
Copy link

@fjamesprice fjamesprice commented Jan 28, 2026

Summary

When commits are made locally over multiple days and then pushed at once, the contribution heatmap currently displays all commits on the push date rather than their actual author dates. This PR fixes that behavior.

Changes:

  • Add OriginalUnix field to the Action model to store the original commit timestamp
  • Group commits by date when creating push actions - each date gets its own action record
  • Update the heatmap query to use COALESCE(NULLIF(original_unix, 0), created_unix) to prefer the original date when available
  • Add database migration (v326) for the new column

How it works

When a push contains commits from multiple days, separate action records are created for each date. Each commit now appears on its actual author date in the heatmap, matching user expectations and GitHub's behavior.

Backward Compatibility

  • Existing actions without OriginalUnix will continue to use created_unix (the push date) via COALESCE
  • Only new push actions will benefit from the improved date tracking
  • No breaking changes to the API or existing behavior

Test Plan

  • Push commits made on different days in a single push
  • Verify heatmap shows contributions on the commit author dates, not push date
  • Verify mirror sync pushes also use commit author dates
  • Verify existing actions (without OriginalUnix) still display correctly

Fixes #36471

Related to #14051 / #11861 (locked)

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Jan 28, 2026
@github-actions github-actions bot added modifies/go Pull requests that update Go code modifies/migrations labels Jan 28, 2026
When commits are made locally over multiple days and then pushed at once,
the contribution heatmap now displays them on their actual author dates
rather than the push date.

Changes:
- Add OriginalUnix field to Action model to store the original content timestamp
- Set OriginalUnix to the earliest commit author date when creating push actions
- Update heatmap query to use COALESCE(original_unix, created_unix)
- Add database migration for the new column

Fixes go-gitea#14051

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@fjamesprice fjamesprice force-pushed the feature/heatmap-commit-dates branch from 0ee2edf to 974f890 Compare January 28, 2026 02:14
Instead of creating a single action record with all commits grouped
under the earliest date, this creates separate action records for
each unique date. Each commit now appears on its actual author date
in the heatmap.

The original_unix field stores the actual commit timestamp (not
truncated to midnight) so the frontend can properly display it in
the user's timezone.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts contribution heatmap bucketing to use commit author timestamps (when available) rather than push timestamps, fixing multi-day local commit batches showing up on a single push day.

Changes:

  • Adds OriginalUnix to Action and a DB migration to persist original timestamps.
  • Splits push (and mirror sync push) actions into per-day action records and stores an original commit timestamp on each.
  • Updates the heatmap grouping query to prefer original_unix via COALESCE(NULLIF(original_unix, 0), created_unix).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
services/feed/notifier.go Creates per-day action records for pushes/sync pushes and sets OriginalUnix based on commit timestamps.
models/migrations/v1_26/v326.go Adds original_unix column (indexed) to the action table.
models/migrations/migrations.go Registers migration 326.
models/activities/user_heatmap.go Uses original_unix (fallback created_unix) for heatmap grouping.
models/activities/action.go Adds OriginalUnix field to the Action model.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +41 to +47
// Use original_unix if available (for commit actions, this is the commit author date),
// otherwise fall back to created_unix (the push/action timestamp).
groupBy := "COALESCE(NULLIF(original_unix, 0), created_unix) / 900 * 900"
groupByName := "timestamp" // We need this extra case because mssql doesn't allow grouping by alias
switch {
case setting.Database.Type.IsMySQL():
groupBy = "created_unix DIV 900 * 900"
groupBy = "COALESCE(NULLIF(original_unix, 0), created_unix) DIV 900 * 900"
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The heatmap query now groups by COALESCE(NULLIF(original_unix, 0), created_unix), but it still filters rows using created_unix > ... later in this function. That means a newly-created action with an old original_unix can produce heatmap buckets far outside the intended (366+7)-day window. Consider filtering on the same coalesced timestamp (or an equivalent condition that respects original_unix when present) to keep the returned timestamps within the heatmap range.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +47
// Use original_unix if available (for commit actions, this is the commit author date),
// otherwise fall back to created_unix (the push/action timestamp).
groupBy := "COALESCE(NULLIF(original_unix, 0), created_unix) / 900 * 900"
groupByName := "timestamp" // We need this extra case because mssql doesn't allow grouping by alias
switch {
case setting.Database.Type.IsMySQL():
groupBy = "created_unix DIV 900 * 900"
groupBy = "COALESCE(NULLIF(original_unix, 0), created_unix) DIV 900 * 900"
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is existing test coverage for the heatmap query, but no tests asserting the new original_unix behavior (preferring commit author dates and handling mixed created_unix/original_unix). Adding/adjusting a unit test fixture with original_unix != 0 and a different created_unix would help prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines 375 to 378
// Create separate action records for each date
for _, dayCommits := range commitsByDate {
// Create a PushCommits struct for this day's commits
dayPushCommits := &repository.PushCommits{
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for _, dayCommits := range commitsByDate iterates over a map, so the order of created action records is nondeterministic. This can lead to unstable ordering in activity feeds/notifications for multi-day pushes. Consider iterating over sorted day keys (and optionally sorting commits within each day) to make action creation deterministic.

Copilot uses AI. Check for mistakes.
Comment on lines 336 to 343
// Group commits by date (truncated to day) so each day's commits appear
// on the correct date in the heatmap, not all on the push date.
commitsByDate := make(map[int64][]*repository.PushCommit)
for _, commit := range commits.Commits {
// Truncate to start of day (UTC)
dayTimestamp := commit.Timestamp.UTC().Truncate(24 * time.Hour).Unix()
commitsByDate[dayTimestamp] = append(commitsByDate[dayTimestamp], commit)
}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit grouping and per-day action creation logic is duplicated between PushCommits and SyncPushCommits. Consider extracting the grouping + per-day action emission into a shared helper to reduce duplication and keep future fixes (eg ordering, timestamp selection) consistent.

Copilot uses AI. Check for mistakes.
Comment on lines 500 to 503
// Create separate action records for each date
for _, dayCommits := range commitsByDate {
dayPushCommits := &repository.PushCommits{
Commits: dayCommits,
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for _, dayCommits := range commitsByDate iterates over a map, so the order of created action records is nondeterministic. This can lead to unstable ordering in activity feeds/notifications for multi-day mirror sync pushes. Consider iterating over sorted day keys to make action creation deterministic.

Copilot uses AI. Check for mistakes.
fjamesprice and others added 3 commits January 30, 2026 00:02
- Fix heatmap query to filter by COALESCE(original_unix, created_unix)
  instead of created_unix, so old commits pushed recently stay in range
- Extract shared groupCommitsByDay() and notifyPushActions() helpers to
  deduplicate logic between PushCommits and SyncPushCommits
- Sort day keys for deterministic action creation order
- Add test fixture (action id:10) with original_unix != created_unix
  and update heatmap test expectations to verify original_unix is used

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The new action fixture (id:10) for testing original_unix in heatmap
also appears in feed queries for user 2 and repo 2. Update feed test
expectations to account for the additional action record.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update TestUserHeatmap to expect both the original action and the new
action with original_unix, which appears at a different timestamp in
the heatmap.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. modifies/go Pull requests that update Go code modifies/migrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heatmap shows commits on push date instead of commit author date

2 participants