Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions internal/github/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func ListOpenPRsMeta(repo string) ([]map[string]any, error) {
out, err := exec.Command("gh", "pr", "list",
"--repo", repo,
"--state", "open",
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus",
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus,commits",
"--limit", "50",
).Output()
if err != nil {
Expand Down Expand Up @@ -133,6 +133,7 @@ func FetchPRDetails(repo string, raw map[string]any) (*PR, error) {
Body: body,
HeadSHA: fmt.Sprintf("%v", raw["headRefOid"]),
HeadRefName: fmt.Sprintf("%v", raw["headRefName"]),
LatestCommitAuthor: latestCommitAuthorFromRaw(raw),
State: state,
MergeStateStatus: mergeStateStatus,
Checks: checks,
Expand Down Expand Up @@ -307,7 +308,7 @@ func ListMergedPRsMeta(repo, currentUser string, since time.Time) ([]map[string]
"--repo", repo,
"--state", "merged",
"--search", fmt.Sprintf("merged:>%s", sinceStr),
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus,state",
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus,state,commits",
"--limit", "50",
).Output()
if err != nil {
Expand Down Expand Up @@ -335,7 +336,7 @@ func FetchPRMeta(repo string, number int) (map[string]any, error) {
out, err := exec.Command("gh", "pr", "view",
fmt.Sprintf("%d", number),
"--repo", repo,
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus,state",
"--json", "number,title,author,url,createdAt,additions,deletions,files,body,reviewRequests,headRefOid,headRefName,mergeStateStatus,state,commits",
).Output()
if err != nil {
return nil, fmt.Errorf("gh pr view %d: %w", number, err)
Expand All @@ -347,6 +348,32 @@ func FetchPRMeta(repo string, number int) (map[string]any, error) {
return raw, nil
}

// latestCommitAuthorFromRaw extracts the login of the most recent commit author
// from a raw PR JSON object containing a "commits" field. Returns "" if absent.
// gh returns commits ordered oldest-first, so the last entry is the most recent.
func latestCommitAuthorFromRaw(raw map[string]any) string {
commits, ok := raw["commits"].([]any)
if !ok || len(commits) == 0 {
return ""
}
last, ok := commits[len(commits)-1].(map[string]any)
if !ok {
return ""
}
authors, ok := last["authors"].([]any)
if !ok || len(authors) == 0 {
return ""
}
first, ok := authors[0].(map[string]any)
if !ok {
return ""
}
if login, ok := first["login"].(string); ok {
return login
}
return ""
}

func getDiff(repo string, number int) (string, error) {
out, err := exec.Command("gh", "pr", "diff", fmt.Sprintf("%d", number), "--repo", repo).Output()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/github/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type PR struct {
Body string
HeadSHA string
HeadRefName string
LatestCommitAuthor string // login of the author of the most recent commit on the head ref
State string // "OPEN", "MERGED", or "CLOSED"
MergeStateStatus string // "CLEAN", "BLOCKED", "DIRTY", "BEHIND", "UNSTABLE", "UNKNOWN", etc.
Checks []CheckStatus
Expand Down
19 changes: 18 additions & 1 deletion internal/tui/model_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,13 @@ func (m *Model) computeHasNewContent(card *PRCard) {
fileNames, fileHunks := diff.FileHunkInfo(card.parsedFiles)
commentDigests := diff.CommentDigestsFromPR(card.PR, m.app.CurrentUser)
inc := reviewstate.ComputeIncremental(fileNames, fileHunks, commentDigests, state)
card.HasNewContent = inc.HasChanges || inc.HasNewComments
hasChanges := inc.HasChanges
if hasChanges && card.PR.LatestCommitAuthor == m.app.CurrentUser {
// The diff changed since last snapshot but the most recent commit is
// ours — don't resurface PRs because of our own pushes.
hasChanges = false
}
card.HasNewContent = hasChanges || inc.HasNewComments
}

// updateIncrementalState computes incremental review state and stores it on the
Expand All @@ -261,6 +267,17 @@ func (m *Model) updateIncrementalState(card *PRCard) {
fileNames, fileHunks := diff.FileHunkInfo(card.parsedFiles)
commentDigests := diff.CommentDigestsFromPR(card.PR, m.app.CurrentUser)
state := reviewstate.ComputeIncremental(fileNames, fileHunks, commentDigests, card.ReviewState)
if state.HasChanges && card.PR.LatestCommitAuthor == m.app.CurrentUser {
// Latest commit is ours — treat all new hunks as already-seen so we
// don't get nagged about our own pushes (visibility & diff badges).
for _, fileHunks := range state.HunkStatus {
for hi := range fileHunks {
fileHunks[hi] = reviewstate.StatusSeen
}
}
state.NewHunkCount = 0
state.HasChanges = false
}
card.HasNewContent = state.HasChanges || state.HasNewComments
logger.Info("incremental state for PR #%d: %d new hunks, %d new comments, %d edited, mode=%v",
card.PR.Number, state.NewHunkCount, state.NewCommentCount, state.EditedCommentCount,
Expand Down