11import { convertPRCommitsToCommitNodes , convertPRDetailToCommitRaw } from "./pullRequest" ;
22import type { CommitDict , CommitNode , CSMDictionary , CSMNode , PullRequest , PullRequestDict , StemDict } from "./types" ;
33
4- const buildCSMNode = ( baseCommitNode : CommitNode , commitDict : CommitDict , stemDict : StemDict ) : CSMNode => {
4+ /**
5+ *
6+ * @param baseCommitNode merge-commit node
7+ * @param commitDict commit-node dictionary
8+ * @returns
9+ */
10+ const buildCSMNode = ( baseCommitNode : CommitNode , commitDict : CommitDict ) : CSMNode => {
511 const mergeParentCommit = commitDict . get ( baseCommitNode . commit . parents [ 1 ] ) ;
612 if ( ! mergeParentCommit ) {
713 return {
@@ -10,46 +16,51 @@ const buildCSMNode = (baseCommitNode: CommitNode, commitDict: CommitDict, stemDi
1016 } ;
1117 }
1218
13- const squashCommitNodes : CommitNode [ ] = [ ] ;
14-
15- const squashTaskQueue : CommitNode [ ] = [ mergeParentCommit ] ;
16- while ( squashTaskQueue . length > 0 ) {
17- // get target
18- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
19- const squashStartNode = squashTaskQueue . shift ( ) ! ;
19+ // 1. stop point: first-parent of baseCommitNode
20+ const baseAncestors = new Set < string > ( ) ;
21+ const baseParentsQueue : CommitNode [ ] = [ ] ;
22+ const firstParent = commitDict . get ( baseCommitNode . commit . parents [ 0 ] ) ;
23+ if ( firstParent ) {
24+ baseParentsQueue . push ( firstParent ) ;
25+ }
2026
21- // get target's stem
22- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
23- const squashStemId = squashStartNode . stemId ! ;
24- const squashStem = stemDict . get ( squashStemId ) ;
25- if ( ! squashStem ) {
27+ while ( baseParentsQueue . length > 0 ) {
28+ const node = baseParentsQueue . shift ( ) ;
29+ if ( ! node || baseAncestors . has ( node . commit . id ) ) {
2630 continue ;
2731 }
32+ baseAncestors . add ( node . commit . id ) ;
33+ for ( const pId of node . commit . parents ) {
34+ const parentNode = commitDict . get ( pId ) ;
35+ if ( parentNode ) {
36+ baseParentsQueue . push ( parentNode ) ;
37+ }
38+ }
39+ }
2840
29- // prepare squash
30- const squashStemLastIndex = squashStem . nodes . length - 1 ;
31- const squashStartNodeIndex = squashStem . nodes . findIndex ( ( { commit : { id } } ) => id === squashStartNode . commit . id ) ;
32- const spliceCount = squashStemLastIndex - squashStartNodeIndex + 1 ;
41+ // 2. find squash-commits by DFS
42+ const squashCommitNodes : CommitNode [ ] = [ ] ;
43+ const squashTaskStack : CommitNode [ ] = [ mergeParentCommit ] ;
44+ const visited = new Set < string > ( ) ;
3345
34- // squash
35- const spliceCommitNodes = squashStem . nodes . splice ( squashStartNodeIndex , spliceCount ) ;
36- squashCommitNodes . push ( ...spliceCommitNodes ) ;
46+ while ( squashTaskStack . length > 0 ) {
47+ const currentNode = squashTaskStack . pop ( ) ! ; // LIFO
3748
38- // check nested-merge
39- const nestedMergeParentCommitIds = spliceCommitNodes
40- . filter ( ( node ) => node . commit . parents . length > 1 )
41- . map ( ( node ) => node . commit . parents )
42- . reduce ( ( pCommitIds , parents ) => [ ...pCommitIds , ...parents ] , [ ] ) ;
43- const nestedMergeParentCommits = nestedMergeParentCommitIds
44- . map ( ( commitId ) => commitDict . get ( commitId ) )
45- . filter ( ( node ) : node is CommitNode => node !== undefined )
46- . filter ( ( node ) => node . stemId !== baseCommitNode . stemId && node . stemId !== squashStemId ) ;
49+ if ( ! currentNode || visited . has ( currentNode . commit . id ) || baseAncestors . has ( currentNode . commit . id ) ) {
50+ continue ;
51+ }
4752
48- squashTaskQueue . push ( ...nestedMergeParentCommits ) ;
53+ visited . add ( currentNode . commit . id ) ;
54+ squashCommitNodes . push ( currentNode ) ;
55+ // add parents to stack
56+ for ( const parentId of currentNode . commit . parents ) {
57+ const parentNode = commitDict . get ( parentId ) ;
58+ if ( parentNode ) {
59+ squashTaskStack . push ( parentNode ) ;
60+ }
61+ }
4962 }
5063
51- squashCommitNodes . sort ( ( a , b ) => a . commit . sequence - b . commit . sequence ) ;
52-
5364 return {
5465 base : baseCommitNode ,
5566 source : squashCommitNodes ,
@@ -101,9 +112,9 @@ export const buildCSMDict = (
101112 ) ;
102113
103114 const csmDict : CSMDictionary = { } ;
104- const stemNodes = masterStem . nodes . reverse ( ) ; // start on root -node
115+ const stemNodes = masterStem . nodes ; // start on latest -node
105116 csmDict [ baseBranchName ] = stemNodes . map ( ( commitNode ) => {
106- const csmNode = buildCSMNode ( commitNode , commitDict , stemDict ) ;
117+ const csmNode = buildCSMNode ( commitNode , commitDict ) ;
107118 const pr = prDictByMergedCommitSha . get ( csmNode . base . commit . id ) ;
108119 return pr ? buildCSMNodeWithPullRequest ( csmNode , pr ) : csmNode ;
109120 } ) ;
0 commit comments