1- import { ParseOptions , ParsedCommit , FileChange } from "../../types" ;
1+ import { ParseOptions , ParsedCommit } from "../../types" ;
22import { parseDiffToStructured } from "./diffToStructured" ;
3+ import { HEADERS , REGEX } from "../../consts" ;
34
4- const HEADERS = {
5- FROM : "From: " ,
6- DATE : "Date: " ,
7- SUBJECT : "Subject: " ,
8- } ;
9-
10- const REGEX = {
11- FROM : / ^ F r o m \s + ( [ 0 - 9 a - f ] { 40 } ) \s / ,
12- AUTHOR_EMAIL : / < ( .* ) > / ,
13- PATCH_HEADER : / ^ \[ P A T C H [ ^ \] ] * \] \s * / ,
14- } ;
15-
16- export function parseGitPatch (
5+ export function parseGitPatch <
6+ O extends ParseOptions < any , any > = ParseOptions < false , false >
7+ > (
178 patch : string ,
18- options : ParseOptions = { }
19- ) : ParsedCommit [ ] {
9+ options : O = { parseDates : false , structuredDiff : false } as O
10+ ) : ParsedCommit < O > [ ] {
11+ type DateType = ParsedCommit < O > [ "date" ] ;
12+ type DiffType = ParsedCommit < O > [ "diff" ] ;
2013 const lines = patch . split ( "\n" ) ;
21- const commits : ParsedCommit [ ] = [ ] ;
14+ const commits : ParsedCommit < O > [ ] = [ ] ;
2215
2316 let currentSha = "" ;
2417 let currentAuthorName = "" ;
@@ -28,40 +21,26 @@ export function parseGitPatch(
2821 let currentDiffLines : string [ ] = [ ] ;
2922 let inMessageSection = false ;
3023 let inDiffSection = false ;
31- let foundDiffStart = false ; // To track when we've hit `diff --git`
24+ let foundDiffStart = false ;
3225
3326 const finalizeCommit = ( ) => {
34- if ( ! currentSha ) return ; // No commit started yet
27+ if ( ! currentSha ) return ;
3528
36- const message = currentMessageLines . join ( "\n" ) . trimEnd ( ) ; // trimEnd to preserve leading/internal newlines
37- let date : string | Date = currentDate ;
38- // Join lines, then trim trailing newlines that might have been added if the original patch ended with multiple blank lines.
39- // Then, ensure a single trailing newline if there's content.
29+ const message = currentMessageLines . join ( "\n" ) . trimEnd ( ) ;
4030 let diffString = currentDiffLines . join ( "\n" ) . replace ( / \n + $ / , "" ) ;
4131 if ( diffString . length > 0 ) {
4232 diffString += "\n" ;
4333 }
44-
45- let diff : string | FileChange [ ] = diffString ;
46-
47- // Process based on options
48- if ( options . parseDates && currentDate ) {
49- try {
50- date = new Date ( currentDate ) ;
51- } catch ( e ) {
52- // Keep original string if date parsing fails
53- console . warn ( `Failed to parse date: ${ currentDate } ` ) ;
54- }
55- }
5634
57- // Process structured diff
58- if (
59- options . structuredDiff &&
60- typeof diff === "string" &&
61- diff . trim ( ) . length > 0
62- ) {
63- diff = parseDiffToStructured ( diff ) ;
64- }
35+ const date = (
36+ options . parseDates && currentDate ? new Date ( currentDate ) : currentDate
37+ ) as DateType ;
38+
39+ const shouldStructurizeDiff =
40+ options . structuredDiff && diffString . trim ( ) . length > 0 ;
41+ const diff = (
42+ shouldStructurizeDiff ? parseDiffToStructured ( diffString ) : diffString
43+ ) as DiffType ;
6544
6645 commits . push ( {
6746 sha : currentSha ,
@@ -72,7 +51,6 @@ export function parseGitPatch(
7251 diff,
7352 } ) ;
7453
75- // Reset for next commit
7654 resetCommitState ( ) ;
7755 } ;
7856
@@ -89,15 +67,13 @@ export function parseGitPatch(
8967 } ;
9068
9169 for ( const line of lines ) {
92- // Detect the start of a new commit
9370 const fromMatch = line . match ( REGEX . FROM ) ;
9471 if ( fromMatch ) {
9572 finalizeCommit ( ) ;
9673 currentSha = fromMatch [ 1 ] ;
9774 continue ;
9875 }
9976
100- // Parse author line: From: Name <email>
10177 if ( line . startsWith ( HEADERS . FROM ) ) {
10278 const authorLine = line . slice ( HEADERS . FROM . length ) . trim ( ) ;
10379 const emailMatch = authorLine . match ( REGEX . AUTHOR_EMAIL ) ;
@@ -110,52 +86,39 @@ export function parseGitPatch(
11086 continue ;
11187 }
11288
113- // Parse date line: Date: ...
11489 if ( line . startsWith ( HEADERS . DATE ) ) {
11590 currentDate = line . slice ( HEADERS . DATE . length ) . trim ( ) ;
11691 continue ;
11792 }
11893
119- // Parse subject line
12094 if ( line . startsWith ( HEADERS . SUBJECT ) ) {
12195 let subject = line . slice ( HEADERS . SUBJECT . length ) . trim ( ) ;
122- // Remove leading "[PATCH ...]" if present
12396 subject = subject . replace ( REGEX . PATCH_HEADER , "" ) ;
12497 currentMessageLines . push ( subject ) ;
12598 inMessageSection = true ;
12699 continue ;
127100 }
128101
129- // Check if we are transitioning to diff section
130102 if ( inMessageSection && line . trim ( ) === "---" ) {
131103 inMessageSection = false ;
132104 inDiffSection = true ;
133105 continue ;
134106 }
135107
136- // If we are in the message section, just append lines to message
137108 if ( inMessageSection ) {
138- // For subject lines, they are already pushed.
139- // For subsequent message lines, they might have leading spaces from the patch format.
140- // We should preserve these as they are part of the message.
141109 currentMessageLines . push ( line ) ;
142110 continue ;
143111 }
144112
145- // If we are in the diff section but haven't found `diff --git` yet
146113 if ( inDiffSection && ! foundDiffStart ) {
147- // Look for the start of the actual diff
148114 if ( line . startsWith ( "diff --git " ) ) {
149115 foundDiffStart = true ;
150116 currentDiffLines . push ( line ) ;
151117 }
152- // Ignore everything until we find `diff --git`
153118 continue ;
154119 }
155120
156- // If we are in diff section and already found `diff --git`
157121 if ( inDiffSection && foundDiffStart ) {
158- // Stop capturing when we hit a line that, after trimming, is `--`
159122 if ( line . trim ( ) === "--" ) {
160123 inDiffSection = false ;
161124 foundDiffStart = false ;
@@ -169,4 +132,4 @@ export function parseGitPatch(
169132 finalizeCommit ( ) ;
170133
171134 return commits ;
172- }
135+ }
0 commit comments