diff --git a/backend/src/routes/repo-git.ts b/backend/src/routes/repo-git.ts index 8bf46180..b85be5ab 100644 --- a/backend/src/routes/repo-git.ts +++ b/backend/src/routes/repo-git.ts @@ -407,7 +407,8 @@ export function createRepoGitRoutes(database: Database, gitAuthService: GitAuthS } const limit = parseInt(c.req.query('limit') || '10', 10) - const commits = await git.getLog(id, database, limit) + const branch = c.req.query('branch') || undefined + const commits = await git.getLog(id, database, limit, branch) return c.json({ commits }) } catch (error: unknown) { diff --git a/backend/src/services/git/GitService.ts b/backend/src/services/git/GitService.ts index 444bd39a..f58e7fe5 100644 --- a/backend/src/services/git/GitService.ts +++ b/backend/src/services/git/GitService.ts @@ -84,7 +84,7 @@ export class GitService { return this.getFileDiff(repoId, filePath, database, { includeStaged }) } - async getLog(repoId: number, database: Database, limit: number = 10): Promise { + async getLog(repoId: number, database: Database, limit: number = 10, branch?: string): Promise { try { const repo = getRepoById(database, repoId) if (!repo) { @@ -97,7 +97,7 @@ export class GitService { '-C', repoPath, 'log', - `--all`, + branch?.trim() || 'HEAD', `-n`, String(limit), '--format=%H|%an|%ae|%at|%s' diff --git a/backend/test/services/git/GitService.test.ts b/backend/test/services/git/GitService.test.ts index 4ff27237..2d00012a 100644 --- a/backend/test/services/git/GitService.test.ts +++ b/backend/test/services/git/GitService.test.ts @@ -436,6 +436,16 @@ describe('GitService', () => { const result = await service.getLog(1, database, 10) expect(getRepoByIdMock).toHaveBeenCalledWith(database, 1) + expect(executeCommandMock).toHaveBeenNthCalledWith(1, [ + 'git', + '-C', + '/path/to/repo', + 'log', + 'HEAD', + '-n', + '10', + '--format=%H|%an|%ae|%at|%s' + ], { env: {} }) expect(result).toHaveLength(2) expect(result[0]).toEqual({ hash: 'abc123', @@ -467,6 +477,50 @@ describe('GitService', () => { expect(result).toEqual([]) }) + + it('uses the requested branch when provided', async () => { + const mockRepo = { + id: 1, + fullPath: '/path/to/repo', + } + getRepoByIdMock.mockReturnValue(mockRepo as any) + executeCommandMock.mockResolvedValueOnce('').mockResolvedValueOnce('') + + await service.getLog(1, database, 10, 'feature/test') + + expect(executeCommandMock).toHaveBeenNthCalledWith(1, [ + 'git', + '-C', + '/path/to/repo', + 'log', + 'feature/test', + '-n', + '10', + '--format=%H|%an|%ae|%at|%s' + ], { env: {} }) + }) + + it('falls back to HEAD for blank branch values', async () => { + const mockRepo = { + id: 1, + fullPath: '/path/to/repo', + } + getRepoByIdMock.mockReturnValue(mockRepo as any) + executeCommandMock.mockResolvedValueOnce('').mockResolvedValueOnce('') + + await service.getLog(1, database, 10, ' ') + + expect(executeCommandMock).toHaveBeenNthCalledWith(1, [ + 'git', + '-C', + '/path/to/repo', + 'log', + 'HEAD', + '-n', + '10', + '--format=%H|%an|%ae|%at|%s' + ], { env: {} }) + }) }) describe('getCommit', () => { diff --git a/frontend/src/api/git.ts b/frontend/src/api/git.ts index 66ec61d3..d6afc2d8 100644 --- a/frontend/src/api/git.ts +++ b/frontend/src/api/git.ts @@ -35,9 +35,9 @@ export async function fetchGitDiff(repoId: number, path: string): Promise<{ diff return { diff: data } } -export async function fetchGitLog(repoId: number, limit?: number): Promise<{ commits: GitCommit[] }> { +export async function fetchGitLog(repoId: number, limit?: number, branch?: string): Promise<{ commits: GitCommit[] }> { return fetchWrapper(`${API_BASE_URL}/api/repos/${repoId}/git/log`, { - params: { limit }, + params: { limit, branch }, }) } @@ -130,10 +130,10 @@ export function useCommitFileDiff(repoId: number | undefined, commitHash: string }) } -export function useGitLog(repoId: number | undefined, limit?: number) { +export function useGitLog(repoId: number | undefined, limit?: number, branch?: string) { return useQuery({ - queryKey: ['gitLog', repoId, limit], - queryFn: () => repoId ? fetchGitLog(repoId, limit) : Promise.reject(new Error('No repo ID')), + queryKey: ['gitLog', repoId, limit, branch], + queryFn: () => repoId ? fetchGitLog(repoId, limit, branch) : Promise.reject(new Error('No repo ID')), enabled: !!repoId, }) } diff --git a/frontend/src/components/source-control/CommitsTab.tsx b/frontend/src/components/source-control/CommitsTab.tsx index a4b765b0..eb231f22 100644 --- a/frontend/src/components/source-control/CommitsTab.tsx +++ b/frontend/src/components/source-control/CommitsTab.tsx @@ -5,6 +5,7 @@ import { GIT_UI_COLORS } from '@/lib/git-status-styles' interface CommitsTabProps { repoId: number + branch: string onSelectCommit?: (hash: string) => void } @@ -28,8 +29,8 @@ function formatRelativeTime(timestamp: string): string { return `${diffMonths}mo ago` } -export function CommitsTab({ repoId, onSelectCommit }: CommitsTabProps) { - const { data, isLoading, error } = useGitLog(repoId, 50) +export function CommitsTab({ repoId, branch, onSelectCommit }: CommitsTabProps) { + const { data, isLoading, error } = useGitLog(repoId, 50, branch) if (isLoading) { return ( diff --git a/frontend/src/components/source-control/SourceControlPanel.tsx b/frontend/src/components/source-control/SourceControlPanel.tsx index 7da7c995..6451da85 100644 --- a/frontend/src/components/source-control/SourceControlPanel.tsx +++ b/frontend/src/components/source-control/SourceControlPanel.tsx @@ -230,7 +230,7 @@ export function SourceControlPanel({ /> )} {activeTab === 'commits' && currentView === 'default' && ( - + )} {activeTab === 'branches' && currentView === 'default' && (