Skip to content

Commit 53d54d3

Browse files
committed
feat: open file diff
1 parent 801aab1 commit 53d54d3

File tree

14 files changed

+195
-129
lines changed

14 files changed

+195
-129
lines changed
File renamed without changes.

src/git/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,15 @@ export class GitService {
6262
throw error
6363
}
6464
}
65+
66+
async getParentCommit(commitHash: string): Promise<string | null> {
67+
try {
68+
const result = await this.git.raw(['rev-parse', `${commitHash}^`])
69+
return result.trim()
70+
}
71+
catch (error) {
72+
console.error('Failed to get parent commit:', error)
73+
return null
74+
}
75+
}
6576
}

src/git/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export interface Commit extends ListLogLine {
1212
additions: number
1313
deletions: number
1414
}
15+
files?: Array<{
16+
path: string
17+
status: string
18+
}>
1519
}
1620

1721
export interface CommitStats {

src/index.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,72 @@
11
import * as vscode from 'vscode'
22

33
import { GitPanelViewProvider } from './views/webview'
4-
import { GitChangesProvider } from './views/diff'
5-
import { GitService } from './git'
6-
import { DiffViewService } from './views/diff/DiffViewService'
4+
import { DiffProvider } from './views/diff'
75

6+
import { GitService } from './git'
7+
import { StorageService } from './storage'
88
import { logger } from './utils'
99

1010
export function activate(context: vscode.ExtensionContext) {
1111
logger.info('Git Panel Activated')
1212

13+
const storageService = StorageService.initialize(context)
14+
storageService.clearCommits()
15+
1316
const gitService = new GitService()
14-
const diffViewService = DiffViewService.initialize(gitService)
1517

16-
const provider = new GitPanelViewProvider(vscode.Uri.file(__dirname), context, gitService)
18+
const provider = new GitPanelViewProvider(vscode.Uri.file(__dirname), gitService)
1719
vscode.window.registerWebviewViewProvider(GitPanelViewProvider.viewType, provider)
1820

19-
const gitChangesProvider = GitChangesProvider.getInstance()
21+
const diffProvider = DiffProvider.getInstance()
2022
vscode.window.createTreeView('git-panel.changes', {
21-
treeDataProvider: gitChangesProvider,
23+
treeDataProvider: diffProvider,
2224
showCollapseAll: true,
2325
})
2426

25-
context.subscriptions.push({
26-
dispose: () => {
27-
diffViewService.dispose()
28-
},
27+
// Register openDiff command
28+
context.subscriptions.push(
29+
vscode.commands.registerCommand('vscGitPanel.openDiff', async (fileInfo: { path: string, status: string }) => {
30+
const commit = diffProvider.getSelectedCommitHash()
31+
if (!commit) {
32+
return
33+
}
34+
35+
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri
36+
if (!workspaceRoot) {
37+
return
38+
}
39+
40+
const uri = vscode.Uri.joinPath(workspaceRoot, fileInfo.path)
41+
const title = `${fileInfo.path} (${commit})`
42+
43+
// For modified files, show diff between current commit and its parent
44+
if (fileInfo.status === 'M') {
45+
const parentCommit = await gitService.getParentCommit(commit)
46+
if (parentCommit) {
47+
const leftUri = toGitUri(uri, parentCommit, fileInfo)
48+
const rightUri = toGitUri(uri, commit, fileInfo)
49+
await vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title)
50+
return
51+
}
52+
}
53+
54+
// For added files, show the entire file content
55+
if (fileInfo.status === 'A') {
56+
const gitUri = toGitUri(uri, commit, fileInfo)
57+
await vscode.commands.executeCommand('vscode.open', gitUri)
58+
}
59+
}),
60+
)
61+
}
62+
63+
// Helper function to create Git URIs
64+
function toGitUri(uri: vscode.Uri, ref: string, fileInfo: { path: string }): vscode.Uri {
65+
return uri.with({
66+
scheme: 'git',
67+
query: JSON.stringify({
68+
path: fileInfo.path,
69+
ref,
70+
}),
2971
})
3072
}

src/storage.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,49 @@
1-
import type { ListLogLine } from 'simple-git'
21
import type * as vscode from 'vscode'
2+
import type { Commit } from '@/git/types'
33

44
export class StorageService {
55
private static readonly COMMITS_KEY = 'git-panel.commits'
6+
private static instance: StorageService
7+
private context: vscode.ExtensionContext
68

7-
constructor(private context: vscode.ExtensionContext) {}
9+
private constructor(context: vscode.ExtensionContext) {
10+
this.context = context
11+
}
12+
13+
static initialize(context: vscode.ExtensionContext): StorageService {
14+
if (!StorageService.instance) {
15+
StorageService.instance = new StorageService(context)
16+
}
17+
return StorageService.instance
18+
}
19+
20+
static getInstance(): StorageService {
21+
if (!StorageService.instance) {
22+
throw new Error('StorageService must be initialized with context first')
23+
}
24+
return StorageService.instance
25+
}
826

9-
saveCommits(commits: ListLogLine[]) {
27+
saveCommits(commits: Commit[]) {
1028
this.context.globalState.update(StorageService.COMMITS_KEY, commits)
1129
}
1230

13-
getCommits(): ListLogLine[] {
14-
return this.context.globalState.get<ListLogLine[]>(StorageService.COMMITS_KEY) || []
31+
updateCommitFiles(commitHash: string, files: Array<{ status: string, path: string }>) {
32+
const commits = this.getCommits()
33+
const commit = commits.find(c => c.hash === commitHash)
34+
if (commit) {
35+
commit.files = files
36+
this.saveCommits(commits)
37+
}
38+
}
39+
40+
getCommits(): Commit[] {
41+
return this.context.globalState.get<Commit[]>(StorageService.COMMITS_KEY) || []
42+
}
43+
44+
getCommit(hash: string): Commit | undefined {
45+
const commits = this.getCommits()
46+
return commits.find(commit => commit.hash === hash)
1547
}
1648

1749
clearCommits() {

src/views/diff/GitChangesProvider.ts renamed to src/views/diff/DiffProvider.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
import * as vscode from 'vscode'
2-
import { GitService } from '../../git'
3-
import { CommitNode } from './CommitNode'
2+
import { CommitNode } from './entity/CommitNode'
43
import { FileTreeProvider } from './FileTreeProvider'
54
import type { CommitDetails } from './types'
5+
import { StorageService } from '@/storage'
6+
import { GitService } from '@/git'
67

7-
export class GitChangesProvider implements vscode.TreeDataProvider<CommitNode> {
8+
export class DiffProvider implements vscode.TreeDataProvider<CommitNode> {
89
private _onDidChangeTreeData: vscode.EventEmitter<CommitNode | undefined | null | void> = new vscode.EventEmitter<CommitNode | undefined | null | void>()
910
readonly onDidChangeTreeData: vscode.Event<CommitNode | undefined | null | void> = this._onDidChangeTreeData.event
1011
private gitService: GitService
12+
private storageService: StorageService
1113
private fileTreeProvider: FileTreeProvider
1214
private selectedCommitHash?: string
13-
private static instance: GitChangesProvider
15+
private static instance: DiffProvider
1416

1517
private constructor() {
1618
this.gitService = new GitService()
17-
this.fileTreeProvider = new FileTreeProvider(this.gitService)
19+
this.storageService = StorageService.getInstance()
20+
this.fileTreeProvider = new FileTreeProvider(this.gitService, this.storageService)
1821
}
1922

20-
static getInstance(): GitChangesProvider {
21-
if (!GitChangesProvider.instance) {
22-
GitChangesProvider.instance = new GitChangesProvider()
23+
static getInstance(): DiffProvider {
24+
if (!DiffProvider.instance) {
25+
DiffProvider.instance = new DiffProvider()
2326
}
24-
return GitChangesProvider.instance
27+
return DiffProvider.instance
2528
}
2629

2730
refresh(commitHash?: string): void {
@@ -35,19 +38,33 @@ export class GitChangesProvider implements vscode.TreeDataProvider<CommitNode> {
3538

3639
private async getCommitByHash(hash?: string): Promise<CommitDetails | null> {
3740
try {
38-
const history = await this.gitService.getHistory()
39-
if (history.all.length === 0) {
40-
return null
41+
if (!hash) {
42+
throw new Error('Commit hash is required')
4143
}
4244

43-
const commit = history.all.find(c => c.hash === hash) || history.all[0]
44-
return {
45-
hash: commit.hash,
46-
authorName: commit.author_name,
47-
authorEmail: commit.author_email,
48-
date: commit.date,
49-
stats: commit.stats,
45+
// First try to get from cache
46+
let commit = this.storageService.getCommit(hash)
47+
48+
if (!commit) {
49+
// Only fetch all commits if not found in cache
50+
const history = await this.gitService.getHistory()
51+
const historyCommit = history.all.find(c => c.hash === hash)
52+
if (!historyCommit) {
53+
return null
54+
}
55+
56+
commit = {
57+
hash: historyCommit.hash,
58+
authorName: historyCommit.author_name,
59+
authorEmail: historyCommit.author_email,
60+
date: historyCommit.date,
61+
message: historyCommit.message,
62+
body: historyCommit.body,
63+
stats: historyCommit.stats,
64+
}
5065
}
66+
67+
return commit
5168
}
5269
catch (error) {
5370
console.error('Error getting commit details:', error)
@@ -56,17 +73,15 @@ export class GitChangesProvider implements vscode.TreeDataProvider<CommitNode> {
5673
}
5774

5875
async getChildren(element?: CommitNode): Promise<CommitNode[]> {
59-
if (!element) {
76+
if (!element && this.selectedCommitHash) {
6077
const commitDetails = await this.getCommitByHash(this.selectedCommitHash)
6178

6279
if (!commitDetails) {
6380
return []
6481
}
6582

66-
// Refresh the file tree provider with current commit hash
6783
this.fileTreeProvider.refresh(commitDetails.hash)
6884

69-
// Get changed files from FileTreeProvider
7085
const changedFiles = await this.fileTreeProvider.getChildren()
7186

7287
return [
@@ -97,10 +112,12 @@ export class GitChangesProvider implements vscode.TreeDataProvider<CommitNode> {
97112
),
98113
]
99114
}
100-
else if (element.children) {
101-
return element.children as CommitNode[]
102-
}
103115

104-
return []
116+
return element?.children as CommitNode[] || []
117+
}
118+
119+
// Getter for selectedCommitHash
120+
public getSelectedCommitHash(): string | undefined {
121+
return this.selectedCommitHash
105122
}
106123
}

src/views/diff/DiffViewService.ts

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)