1- import { FileSystem } from '../models/FileSystem'
2- import { Cache } from '../models/Cache'
31import { ReadCommitResult , _readCommit } from '../commands/readCommit'
2+
3+ import { Cache } from '../models/Cache'
4+ import { FileSystem } from '../models/FileSystem'
45import { GitRefManager } from '../managers/GitRefManager'
56import { GitShallowManager } from '../managers/GitShallowManager'
7+ import { NotFoundError } from '../errors'
68import { compareAge } from '../utils/compareAge'
9+ import { resolveFileIdInTree } from '../utils/resolveFileIdInTree'
10+ import { resolveFilepath } from '../utils/resolveFilepath'
711
812type LogParams = {
913 fs : FileSystem
@@ -12,24 +16,44 @@ type LogParams = {
1216 ref : string
1317 depth ?: number
1418 since ?: Date
19+ filepath ?: string
20+ force ?: boolean ,
21+ follow ?: boolean ,
1522}
1623
1724/**
1825 * Get commit descriptions from the git history.
1926 *
2027 * @internal
2128 */
22- export async function _log ( { fs, cache, gitdir, ref, depth, since } : LogParams ) : Promise < Array < ReadCommitResult > > {
29+ export async function _log ( {
30+ fs,
31+ cache,
32+ gitdir,
33+ ref,
34+ depth,
35+ since,
36+ filepath,
37+ force,
38+ follow
39+ } : LogParams ) : Promise < Array < ReadCommitResult > > {
2340 const sinceTimestamp =
2441 typeof since === 'undefined'
2542 ? undefined
2643 : Math . floor ( since . valueOf ( ) / 1000 )
2744 // TODO: In the future, we may want to have an API where we return a
2845 // async iterator that emits commits.
29- const commits = [ ]
46+ const commits : ReadCommitResult [ ] = [ ]
3047 const shallowCommits = await GitShallowManager . read ( { fs, gitdir } )
3148 const oid = await GitRefManager . resolve ( { fs, gitdir, ref } )
3249 const tips = [ await _readCommit ( { fs, cache, gitdir, oid } ) ]
50+ let lastFileOid : string | undefined = undefined
51+ let lastCommit : ReadCommitResult | undefined = undefined
52+ let isOk : boolean | undefined = undefined
53+
54+ function endCommit ( commit : ReadCommitResult ) {
55+ if ( isOk && filepath ) commits . push ( commit )
56+ }
3357
3458 while ( tips . length > 0 ) {
3559 const commit = tips . pop ( ) !
@@ -42,10 +66,82 @@ export async function _log({ fs, cache, gitdir, ref, depth, since }: LogParams):
4266 break
4367 }
4468
45- commits . push ( commit )
69+ if ( filepath ) {
70+ let vFileOid
71+ try {
72+ vFileOid = await resolveFilepath ( {
73+ fs,
74+ cache,
75+ gitdir,
76+ oid : commit . commit . tree ! ,
77+ filepath,
78+ } )
79+ if ( lastCommit && lastFileOid !== vFileOid ) {
80+ commits . push ( lastCommit )
81+ }
82+ lastFileOid = vFileOid
83+ lastCommit = commit
84+ isOk = true
85+ } catch ( e ) {
86+ if ( e instanceof NotFoundError ) {
87+ let found : string | string [ ] | boolean | undefined = follow && lastFileOid
88+ if ( found ) {
89+ found = await resolveFileIdInTree ( {
90+ fs,
91+ cache,
92+ gitdir,
93+ oid : commit . commit . tree ! ,
94+ fileId : lastFileOid ! ,
95+ } )
96+ if ( found ) {
97+ if ( Array . isArray ( found ) ) {
98+ if ( lastCommit ) {
99+ const lastFound = await resolveFileIdInTree ( {
100+ fs,
101+ cache,
102+ gitdir,
103+ oid : lastCommit . commit . tree ! ,
104+ fileId : lastFileOid ! ,
105+ } )
106+ if ( Array . isArray ( lastFound ) ) {
107+ found = found . filter ( p => lastFound . indexOf ( p ) === - 1 )
108+ if ( found . length === 1 ) {
109+ found = found [ 0 ]
110+ filepath = found
111+ if ( lastCommit ) commits . push ( lastCommit )
112+ } else {
113+ found = false
114+ if ( lastCommit ) commits . push ( lastCommit )
115+ break
116+ }
117+ }
118+ }
119+ } else {
120+ filepath = found
121+ if ( lastCommit ) commits . push ( lastCommit )
122+ }
123+ }
124+ }
125+ if ( ! found ) {
126+ if ( isOk && lastFileOid ) {
127+ commits . push ( lastCommit ! )
128+ if ( ! force ) break
129+ }
130+ if ( ! force && ! follow ) throw e
131+ }
132+ lastCommit = commit
133+ isOk = false
134+ } else throw e
135+ }
136+ } else {
137+ commits . push ( commit )
138+ }
46139
47140 // Stop the loop if we have enough commits now.
48- if ( depth !== undefined && commits . length === depth ) break
141+ if ( depth !== undefined && commits . length === depth ) {
142+ endCommit ( commit )
143+ break
144+ }
49145
50146 // If this is not a shallow commit...
51147 if ( ! shallowCommits . has ( commit . oid ) ) {
@@ -60,10 +156,13 @@ export async function _log({ fs, cache, gitdir, ref, depth, since }: LogParams):
60156 }
61157
62158 // Stop the loop if there are no more commit parents
63- if ( tips . length === 0 ) break
159+ if ( tips . length === 0 ) {
160+ endCommit ( commit )
161+ }
64162
65163 // Process tips in order by age
66164 tips . sort ( ( a , b ) => compareAge ( a . commit , b . commit ) )
67165 }
166+
68167 return commits
69168}
0 commit comments