55
66import * as dayjs from 'dayjs' ;
77import * as advancedFormat from 'dayjs/plugin/advancedFormat' ;
8- import { CancellationToken , Disposable , Event , EventEmitter , ThemeIcon , Timeline , TimelineChangeEvent , TimelineCursor , TimelineItem , TimelineProvider , Uri , workspace } from 'vscode' ;
8+ import { CancellationToken , Disposable , Event , EventEmitter , ThemeIcon , Timeline , TimelineChangeEvent , TimelineItem , TimelineOptions , TimelineProvider , Uri , workspace } from 'vscode' ;
99import { Model } from './model' ;
1010import { Repository } from './repository' ;
1111import { debounce } from './decorators' ;
@@ -87,7 +87,7 @@ export class GitTimelineProvider implements TimelineProvider {
8787 this . _disposable . dispose ( ) ;
8888 }
8989
90- async provideTimeline ( uri : Uri , _cursor : TimelineCursor , _token : CancellationToken ) : Promise < Timeline > {
90+ async provideTimeline ( uri : Uri , options : TimelineOptions , _token : CancellationToken ) : Promise < Timeline > {
9191 // console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
9292
9393 const repo = this . _model . getRepository ( uri ) ;
@@ -112,109 +112,152 @@ export class GitTimelineProvider implements TimelineProvider {
112112
113113 // TODO[ECA]: Ensure that the uri is a file -- if not we could get the history of the repo?
114114
115- const commits = await repo . logFile ( uri ) ;
115+ let limit : number | undefined ;
116+ if ( typeof options . limit === 'string' ) {
117+ try {
118+ const result = await this . _model . git . exec ( repo . root , [ 'rev-list' , '--count' , `${ options . limit } ..` , '--' , uri . fsPath ] ) ;
119+ if ( ! result . exitCode ) {
120+ // Ask for 1 more than so we can determine if there are more commits
121+ limit = Number ( result . stdout ) + 1 ;
122+ }
123+ }
124+ catch {
125+ limit = undefined ;
126+ }
127+ } else {
128+ // If we are not getting everything, ask for 1 more than so we can determine if there are more commits
129+ limit = options . limit === undefined ? undefined : options . limit + 1 ;
130+ }
131+
132+
133+ const commits = await repo . logFile ( uri , {
134+ maxEntries : limit ,
135+ hash : options . cursor ,
136+ reverse : options . before ,
137+ // sortByAuthorDate: true
138+ } ) ;
139+
140+ const more = limit === undefined || options . before ? false : commits . length >= limit ;
141+ const paging = commits . length ? {
142+ more : more ,
143+ cursors : {
144+ before : commits [ 0 ] ?. hash ,
145+ after : commits [ commits . length - ( more ? 1 : 2 ) ] ?. hash
146+ }
147+ } : undefined ;
148+
149+ // If we asked for an extra commit, strip it off
150+ if ( limit !== undefined && commits . length >= limit ) {
151+ commits . splice ( commits . length - 1 , 1 ) ;
152+ }
116153
117154 let dateFormatter : dayjs . Dayjs ;
118155 const items = commits . map < GitTimelineItem > ( c => {
119- dateFormatter = dayjs ( c . authorDate ) ;
156+ const date = c . commitDate ; // c.authorDate
157+
158+ dateFormatter = dayjs ( date ) ;
120159
121- const item = new GitTimelineItem ( c . hash , `${ c . hash } ^` , c . message , c . authorDate ?. getTime ( ) ?? 0 , c . hash , 'git:file:commit' ) ;
160+ const item = new GitTimelineItem ( c . hash , `${ c . hash } ^` , c . message , date ?. getTime ( ) ?? 0 , c . hash , 'git:file:commit' ) ;
122161 item . iconPath = new ( ThemeIcon as any ) ( 'git-commit' ) ;
123162 item . description = c . authorName ;
124163 item . detail = `${ c . authorName } (${ c . authorEmail } ) \u2014 ${ c . hash . substr ( 0 , 8 ) } \n${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } \n\n${ c . message } ` ;
125164 item . command = {
126165 title : 'Open Comparison' ,
127166 command : 'git.timeline.openDiff' ,
128- arguments : [ uri , this . id , item ]
167+ arguments : [ item , uri , this . id ]
129168 } ;
130169
131170 return item ;
132171 } ) ;
133172
134- const index = repo . indexGroup . resourceStates . find ( r => r . resourceUri . fsPath === uri . fsPath ) ;
135- if ( index ) {
136- const date = this . _repoStatusDate ?? new Date ( ) ;
137- dateFormatter = dayjs ( date ) ;
138-
139- let status ;
140- switch ( index . type ) {
141- case Status . INDEX_MODIFIED :
142- status = 'Modified' ;
143- break ;
144- case Status . INDEX_ADDED :
145- status = 'Added' ;
146- break ;
147- case Status . INDEX_DELETED :
148- status = 'Deleted' ;
149- break ;
150- case Status . INDEX_RENAMED :
151- status = 'Renamed' ;
152- break ;
153- case Status . INDEX_COPIED :
154- status = 'Copied' ;
155- break ;
156- default :
157- status = '' ;
158- break ;
173+ if ( options . cursor === undefined || options . before ) {
174+ const index = repo . indexGroup . resourceStates . find ( r => r . resourceUri . fsPath === uri . fsPath ) ;
175+ if ( index ) {
176+ const date = this . _repoStatusDate ?? new Date ( ) ;
177+ dateFormatter = dayjs ( date ) ;
178+
179+ let status ;
180+ switch ( index . type ) {
181+ case Status . INDEX_MODIFIED :
182+ status = 'Modified' ;
183+ break ;
184+ case Status . INDEX_ADDED :
185+ status = 'Added' ;
186+ break ;
187+ case Status . INDEX_DELETED :
188+ status = 'Deleted' ;
189+ break ;
190+ case Status . INDEX_RENAMED :
191+ status = 'Renamed' ;
192+ break ;
193+ case Status . INDEX_COPIED :
194+ status = 'Copied' ;
195+ break ;
196+ default :
197+ status = '' ;
198+ break ;
199+ }
200+
201+ const item = new GitTimelineItem ( '~' , 'HEAD' , 'Staged Changes' , date . getTime ( ) , 'index' , 'git:file:index' ) ;
202+ // TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
203+ item . iconPath = new ( ThemeIcon as any ) ( 'git-commit' ) ;
204+ item . description = 'You' ;
205+ item . detail = `You \u2014 Index\n${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } \n${ status } ` ;
206+ item . command = {
207+ title : 'Open Comparison' ,
208+ command : 'git.timeline.openDiff' ,
209+ arguments : [ item , uri , this . id ]
210+ } ;
211+
212+ items . splice ( 0 , 0 , item ) ;
159213 }
160214
161- const item = new GitTimelineItem ( '~' , 'HEAD' , 'Staged Changes' , date . getTime ( ) , 'index' , 'git:file:index' ) ;
162- // TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
163- item . iconPath = new ( ThemeIcon as any ) ( 'git-commit' ) ;
164- item . description = 'You' ;
165- item . detail = `You \u2014 Index\n${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } \n${ status } ` ;
166- item . command = {
167- title : 'Open Comparison' ,
168- command : 'git.timeline.openDiff' ,
169- arguments : [ uri , this . id , item ]
170- } ;
171-
172- items . push ( item ) ;
173- }
174-
175-
176- const working = repo . workingTreeGroup . resourceStates . find ( r => r . resourceUri . fsPath === uri . fsPath ) ;
177- if ( working ) {
178- const date = new Date ( ) ;
179- dateFormatter = dayjs ( date ) ;
180-
181- let status ;
182- switch ( working . type ) {
183- case Status . INDEX_MODIFIED :
184- status = 'Modified' ;
185- break ;
186- case Status . INDEX_ADDED :
187- status = 'Added' ;
188- break ;
189- case Status . INDEX_DELETED :
190- status = 'Deleted' ;
191- break ;
192- case Status . INDEX_RENAMED :
193- status = 'Renamed' ;
194- break ;
195- case Status . INDEX_COPIED :
196- status = 'Copied' ;
197- break ;
198- default :
199- status = '' ;
200- break ;
215+ const working = repo . workingTreeGroup . resourceStates . find ( r => r . resourceUri . fsPath === uri . fsPath ) ;
216+ if ( working ) {
217+ const date = new Date ( ) ;
218+ dateFormatter = dayjs ( date ) ;
219+
220+ let status ;
221+ switch ( working . type ) {
222+ case Status . INDEX_MODIFIED :
223+ status = 'Modified' ;
224+ break ;
225+ case Status . INDEX_ADDED :
226+ status = 'Added' ;
227+ break ;
228+ case Status . INDEX_DELETED :
229+ status = 'Deleted' ;
230+ break ;
231+ case Status . INDEX_RENAMED :
232+ status = 'Renamed' ;
233+ break ;
234+ case Status . INDEX_COPIED :
235+ status = 'Copied' ;
236+ break ;
237+ default :
238+ status = '' ;
239+ break ;
240+ }
241+
242+ const item = new GitTimelineItem ( '' , index ? '~' : 'HEAD' , 'Uncommited Changes' , date . getTime ( ) , 'working' , 'git:file:working' ) ;
243+ // TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
244+ item . iconPath = new ( ThemeIcon as any ) ( 'git-commit' ) ;
245+ item . description = 'You' ;
246+ item . detail = `You \u2014 Working Tree\n${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } \n${ status } ` ;
247+ item . command = {
248+ title : 'Open Comparison' ,
249+ command : 'git.timeline.openDiff' ,
250+ arguments : [ item , uri , this . id ]
251+ } ;
252+
253+ items . splice ( 0 , 0 , item ) ;
201254 }
202-
203- const item = new GitTimelineItem ( '' , index ? '~' : 'HEAD' , 'Uncommited Changes' , date . getTime ( ) , 'working' , 'git:file:working' ) ;
204- // TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
205- item . iconPath = new ( ThemeIcon as any ) ( 'git-commit' ) ;
206- item . description = 'You' ;
207- item . detail = `You \u2014 Working Tree\n${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } \n${ status } ` ;
208- item . command = {
209- title : 'Open Comparison' ,
210- command : 'git.timeline.openDiff' ,
211- arguments : [ uri , this . id , item ]
212- } ;
213-
214- items . push ( item ) ;
215255 }
216256
217- return { items : items } ;
257+ return {
258+ items : items ,
259+ paging : paging
260+ } ;
218261 }
219262
220263 private onRepositoriesChanged ( _repo : Repository ) {
@@ -241,6 +284,6 @@ export class GitTimelineProvider implements TimelineProvider {
241284
242285 @debounce ( 500 )
243286 private fireChanged ( ) {
244- this . _onDidChange . fire ( { } ) ;
287+ this . _onDidChange . fire ( { reset : true } ) ;
245288 }
246289}
0 commit comments