1- import { TreeItemCollapsibleState } from 'vscode'
21import type { TreeViewNode } from 'reactive-vscode'
32import { computed , createSingletonComposable , ref , useTreeView } from 'reactive-vscode'
3+ import { TreeItemCollapsibleState } from 'vscode'
44
55import { CommitNode } from './entity/CommitNode'
66import { useFileTreeView } from './FileTreeView'
7+
78import type { CommitDetails } from './types'
9+
810import { useGitService } from '@/git'
911import { useStorage } from '@/storage'
1012import { EXTENSION_SYMBOL } from '@/constant'
@@ -13,80 +15,155 @@ export const useDiffTreeView = createSingletonComposable(() => {
1315 const git = useGitService ( )
1416 const storage = useStorage ( )
1517 const fileTree = useFileTreeView ( )
16- const selectedCommitHash = ref ( '' )
17- const commitDetails = ref < CommitDetails | null > ( null )
18+ const selectedCommitHashes = ref < string [ ] > ( [ ] )
19+ const commitDetailsList = ref < CommitDetails [ ] > ( [ ] )
1820 const files = ref < TreeViewNode [ ] > ( [ ] )
1921
20- async function loadCommitDetails ( hash : string ) {
22+ async function loadCommitDetails ( hashes : string [ ] ) {
2123 try {
22- let commit = storage . getCommit ( hash )
24+ if ( hashes . length === 0 ) {
25+ commitDetailsList . value = [ ]
26+ files . value = [ ]
27+ return
28+ }
29+
30+ const commitList : CommitDetails [ ] = [ ]
31+ const allCommitFiles : Map < string , TreeViewNode [ ] > = new Map ( )
32+ let totalFiles = 0
2333
24- if ( ! commit ) {
25- const { logResult, operations, branches } = await git . getHistory ( )
26- const historyCommit = logResult . all . find ( c => c . hash === hash )
27- if ( ! historyCommit )
28- return
34+ for ( const hash of hashes ) {
35+ let commit = storage . getCommit ( hash )
2936
30- commit = {
31- ...historyCommit ,
37+ if ( ! commit ) {
38+ const { logResult, operations, branches } = await git . getHistory ( )
39+ const historyCommit = logResult . all . find ( c => c . hash === hash )
40+ if ( ! historyCommit )
41+ continue
42+
43+ commit = {
44+ ...historyCommit ,
45+ }
46+
47+ storage . saveCommits ( { operations, branches, logResult } )
3248 }
3349
34- storage . saveCommits ( { operations, branches, logResult } )
50+ commitList . push ( commit )
51+
52+ const { files : commitFiles , total } = await fileTree . getChildren ( hash )
53+
54+ allCommitFiles . set ( hash , commitFiles )
55+ totalFiles += total
3556 }
3657
37- commitDetails . value = commit
38- const { files : commitFiles , total } = await fileTree . getChildren ( hash )
58+ commitDetailsList . value = commitList
3959
40- files . value = [
41- {
42- treeItem : new CommitNode (
43- 'Changed Files' ,
44- `${ total } Files Changed` ,
45- total > 0 ? TreeItemCollapsibleState . Expanded : TreeItemCollapsibleState . None ,
46- 'files' ,
47- ) ,
48- children : commitFiles ,
49- } ,
50- ]
60+ // Aggregate file changes for all selected commits
61+ if ( hashes . length === 1 ) {
62+ // Single commit view - show files directly
63+ files . value = [
64+ {
65+ treeItem : new CommitNode (
66+ 'Changed Files' ,
67+ `${ totalFiles } Files Changed` ,
68+ TreeItemCollapsibleState . Expanded ,
69+ 'files' ,
70+ ) ,
71+ children : allCommitFiles . get ( hashes [ 0 ] ) || [ ] ,
72+ } ,
73+ ]
74+ }
75+ else {
76+ // Multiple commits view - group files by commit
77+ const commitNodes : TreeViewNode [ ] = [ ]
78+
79+ for ( const hash of hashes ) {
80+ const commit = commitList . find ( c => c . hash === hash )
81+ if ( ! commit )
82+ continue
83+
84+ const commitFiles = allCommitFiles . get ( hash ) || [ ]
85+ const shortHash = hash . substring ( 0 , 7 )
86+
87+ commitNodes . push ( {
88+ treeItem : new CommitNode (
89+ `${ commitFiles . length } Files Changed` ,
90+ `${ shortHash } ` ,
91+ TreeItemCollapsibleState . Expanded ,
92+ 'git-commit' ,
93+ ) ,
94+ children : commitFiles ,
95+ } )
96+ }
97+
98+ files . value = [
99+ {
100+ treeItem : new CommitNode (
101+ 'Selected Commits' ,
102+ `${ hashes . length } Commits Selected` ,
103+ TreeItemCollapsibleState . Expanded ,
104+ 'git-commit' ,
105+ ) ,
106+ children : commitNodes ,
107+ } ,
108+ ]
109+ }
51110 }
52111 catch ( error ) {
53112 console . error ( 'Error getting commit details:' , error )
54- commitDetails . value = null
113+ commitDetailsList . value = [ ]
55114 files . value = [ ]
56115 }
57116 }
58117
59118 const treeNodes = computed < TreeViewNode [ ] > ( ( ) => {
60- if ( ! commitDetails . value )
119+ if ( commitDetailsList . value . length === 0 )
61120 return [ ]
62121
63- return [
64- {
65- treeItem : new CommitNode (
66- 'Author' ,
67- `${ commitDetails . value . authorName } <${ commitDetails . value . authorEmail } >` ,
68- TreeItemCollapsibleState . None ,
69- 'person' ,
70- ) ,
71- } ,
72- {
73- treeItem : new CommitNode (
74- 'Date' ,
75- new Date ( commitDetails . value . date ) . toLocaleString ( ) ,
76- TreeItemCollapsibleState . None ,
77- 'calendar' ,
78- ) ,
79- } ,
80- {
81- treeItem : new CommitNode (
82- 'Hash' ,
83- commitDetails . value . hash ,
84- TreeItemCollapsibleState . None ,
85- 'git-commit' ,
86- ) ,
87- } ,
88- ...files . value ,
89- ]
122+ if ( commitDetailsList . value . length === 1 ) {
123+ // Single commit view
124+ const commit = commitDetailsList . value [ 0 ]
125+ return [
126+ {
127+ treeItem : new CommitNode (
128+ 'Author' ,
129+ `${ commit . authorName } <${ commit . authorEmail } >` ,
130+ TreeItemCollapsibleState . None ,
131+ 'person' ,
132+ ) ,
133+ } ,
134+ {
135+ treeItem : new CommitNode (
136+ 'Date' ,
137+ new Date ( commit . date ) . toLocaleString ( ) ,
138+ TreeItemCollapsibleState . None ,
139+ 'calendar' ,
140+ ) ,
141+ } ,
142+ {
143+ treeItem : new CommitNode (
144+ 'Hash' ,
145+ commit . hash ,
146+ TreeItemCollapsibleState . None ,
147+ 'git-commit' ,
148+ ) ,
149+ } ,
150+ ...files . value ,
151+ ]
152+ }
153+ else {
154+ // Multiple commits view - summary information
155+ return [
156+ {
157+ treeItem : new CommitNode (
158+ 'Multiple Commits Selected' ,
159+ `${ commitDetailsList . value . length } commits` ,
160+ TreeItemCollapsibleState . None ,
161+ 'git-commit' ,
162+ ) ,
163+ } ,
164+ ...files . value ,
165+ ]
166+ }
90167 } )
91168
92169 const tree = useTreeView (
@@ -97,18 +174,22 @@ export const useDiffTreeView = createSingletonComposable(() => {
97174 } ,
98175 )
99176
100- async function refresh ( hash : string ) {
101- if ( hash === selectedCommitHash . value )
177+ async function refresh ( hashes : string [ ] ) {
178+ const currentHashes = new Set ( selectedCommitHashes . value )
179+ const newHashes = new Set ( hashes )
180+
181+ if ( currentHashes . size === newHashes . size
182+ && [ ...currentHashes ] . every ( hash => newHashes . has ( hash ) ) ) {
102183 return
184+ }
103185
104- selectedCommitHash . value = hash
105- fileTree . refresh ( hash )
106- await loadCommitDetails ( hash )
186+ selectedCommitHashes . value = [ ...hashes ]
187+ await loadCommitDetails ( hashes )
107188 }
108189
109190 return {
110191 tree,
111192 refresh,
112- selectedCommitHash ,
193+ selectedCommitHashes ,
113194 }
114195} )
0 commit comments