@@ -4,20 +4,49 @@ const path = require('path');
44
55const execa = require ( 'execa' ) ;
66const fs = require ( 'fs-extra' ) ;
7+ const inquirer = require ( 'inquirer' ) ;
78const Listr = require ( 'listr' ) ;
89const input = require ( 'listr-input' ) ;
910
1011const common = require ( './common' ) ;
1112
13+ exports . checkOptions = async function checkOptions ( options ) {
14+ if ( options . sha . length > 1 && options . squash ) {
15+ const { wantSquash } = await inquirer . prompt ( [ {
16+ type : 'confirm' ,
17+ name : 'wantSquash' ,
18+ message : 'Squashing commits should be avoided if possible, because it ' +
19+ 'can make git bisection difficult. Only squash commits if they would ' +
20+ 'break the build when applied individually. Are you sure?' ,
21+ default : false
22+ } ] ) ;
23+
24+ if ( ! wantSquash ) {
25+ return true ;
26+ }
27+ }
28+ } ;
29+
1230exports . doBackport = function doBackport ( options ) {
13- const todo = [ common . getCurrentV8Version ( ) , generatePatch ( ) , applyPatch ( ) ] ;
14- if ( options . bump !== false ) {
15- if ( options . nodeMajorVersion < 9 ) {
16- todo . push ( incrementV8Version ( ) ) ;
17- } else {
18- todo . push ( incrementEmbedderVersion ( ) ) ;
31+ const todo = [
32+ common . getCurrentV8Version ( ) ,
33+ generatePatches ( )
34+ ] ;
35+
36+ if ( options . squash ) {
37+ todo . push ( applyPatches ( ) ) ;
38+ if ( options . bump !== false ) {
39+ if ( options . nodeMajorVersion < 9 ) {
40+ todo . push ( incrementV8Version ( ) ) ;
41+ } else {
42+ todo . push ( incrementEmbedderVersion ( ) ) ;
43+ }
1944 }
45+ todo . push ( commitSquashedBackport ( ) ) ;
46+ } else {
47+ todo . push ( applyAndCommitPatches ( ) ) ;
2048 }
49+
2150 return {
2251 title : 'V8 commit backport' ,
2352 task : ( ) => {
@@ -26,72 +55,174 @@ exports.doBackport = function doBackport(options) {
2655 } ;
2756} ;
2857
29- exports . commitBackport = function commitBackport ( ) {
58+ function commitSquashedBackport ( ) {
3059 return {
31- title : 'Commit patch ' ,
60+ title : 'Commit backport ' ,
3261 task : async ( ctx ) => {
33- const messageTitle = `deps: cherry-pick ${ ctx . fullSha . substring (
34- 0 ,
35- 7
36- ) } from upstream V8`;
37- const indentedMessage = ctx . message . replace ( / \n / g, '\n ' ) ;
38- const messageBody =
39- 'Original commit message:\n\n' +
40- ` ${ indentedMessage } \n\n` +
41- `Refs: https://github.com/v8/v8/commit/${ ctx . fullSha } ` ;
42-
62+ const { patches } = ctx ;
63+ const messageTitle = formatMessageTitle ( patches ) ;
64+ let messageBody ;
65+ if ( patches . length === 1 ) {
66+ const [ patch ] = patches ;
67+ messageBody = formatMessageBody ( patch , false ) ;
68+ } else {
69+ messageBody = '' ;
70+ for ( const patch of patches ) {
71+ const formatted = formatMessageBody ( patch , true ) ;
72+ messageBody += formatted + '\n\n' ;
73+ }
74+ }
4375 await ctx . execGitNode ( 'add' , 'deps/v8' ) ;
4476 await ctx . execGitNode ( 'commit' , '-m' , messageTitle , '-m' , messageBody ) ;
4577 }
4678 } ;
4779} ;
4880
49- function generatePatch ( ) {
81+ function commitPatch ( patch ) {
5082 return {
51- title : 'Generate patch' ,
83+ title : 'Commit patch' ,
5284 task : async ( ctx ) => {
53- const sha = ctx . sha ;
54- const fullSha = ctx . fullSha = await ctx . execGitV8 ( 'rev-parse' , sha ) ;
85+ const messageTitle = formatMessageTitle ( [ patch ] ) ;
86+ const messageBody = formatMessageBody ( patch , false ) ;
87+ await ctx . execGitNode ( 'add' , 'deps/v8' ) ;
88+ await ctx . execGitNode ( 'commit' , '-m' , messageTitle , '-m' , messageBody ) ;
89+ }
90+ } ;
91+ }
92+
93+ function shortSha ( sha ) {
94+ return sha . substring ( 0 , 7 ) ;
95+ }
96+
97+ function formatMessageTitle ( patches ) {
98+ const action =
99+ patches . some ( patch => patch . hadConflicts ) ? 'backport' : 'cherry-pick' ;
100+ if ( patches . length === 1 ) {
101+ return `deps: V8: ${ action } ${ shortSha ( patches [ 0 ] . sha ) } ` ;
102+ } else if ( patches . length === 2 ) {
103+ return `deps: V8: ${ action } ${ shortSha ( patches [ 0 ] . sha ) } and ${
104+ shortSha ( patches [ 1 ] . sha )
105+ } `;
106+ } else if ( patches . length === 3 ) {
107+ return `deps: V8: ${ action } ${ shortSha ( patches [ 0 ] . sha ) } , ${
108+ shortSha ( patches [ 1 ] . sha )
109+ } and ${ shortSha ( patches [ 2 ] . sha ) } `;
110+ } else {
111+ return `deps: V8: ${ action } ${ patches . length } commits` ;
112+ }
113+ }
114+
115+ function formatMessageBody ( patch , prefixTitle ) {
116+ const indentedMessage = patch . message . replace ( / \n / g, '\n ' ) ;
117+ const body =
118+ 'Original commit message:\n\n' +
119+ ` ${ indentedMessage } \n\n` +
120+ `Refs: https://github.com/v8/v8/commit/${ patch . sha } ` ;
121+
122+ if ( prefixTitle ) {
123+ const action = patch . hadConflicts ? 'Backport' : 'Cherry-pick' ;
124+ return `${ action } ${ shortSha ( patch . sha ) } .\n` + body ;
125+ }
126+ return body ;
127+ }
128+
129+ function generatePatches ( ) {
130+ return {
131+ title : 'Generate patches' ,
132+ task : async ( ctx ) => {
133+ const shas = ctx . sha ;
55134 try {
56- const [ patch , message ] = await Promise . all ( [
57- ctx . execGitV8 ( 'format-patch' , '--stdout' , `${ fullSha } ^..${ fullSha } ` ) ,
58- ctx . execGitV8 ( 'log' , '--format=%B' , '-n' , '1' , fullSha )
59- ] ) ;
60- ctx . patch = patch . stdout ;
61- ctx . message = message . stdout ;
135+ const fullShas = await Promise . all (
136+ shas . map ( async ( sha ) => {
137+ const { stdout } = await ctx . execGitV8 ( 'rev-parse' , sha ) ;
138+ return stdout ;
139+ } )
140+ ) ;
141+ ctx . patches = await Promise . all ( fullShas . map ( async ( sha ) => {
142+ const [ patch , message ] = await Promise . all ( [
143+ ctx . execGitV8 ( 'format-patch' , '--stdout' , `${ sha } ^..${ sha } ` ) ,
144+ ctx . execGitV8 ( 'log' , '--format=%B' , '-n' , '1' , sha )
145+ ] ) ;
146+ return {
147+ sha,
148+ data : patch . stdout ,
149+ message : message . stdout
150+ } ;
151+ } ) ) ;
62152 } catch ( e ) {
63153 throw new Error ( e . stderr ) ;
64154 }
65155 }
66156 } ;
67157}
68158
69- function applyPatch ( ) {
159+ function applyPatches ( ) {
70160 return {
71- title : 'Apply patch to deps/v8' ,
161+ title : 'Apply patches to deps/v8' ,
72162 task : async ( ctx ) => {
73- const patch = ctx . patch ;
74- try {
75- await execa ( 'patch' ,
76- [ '-p1' , '--merge' , '--no-backup-if-mismatch' , '--directory=deps/v8' ] ,
77- {
78- cwd : ctx . nodeDir ,
79- input : patch
80- } ) ;
81- } catch ( e ) {
82- return input ( "Resolve merge conflicts and enter 'RESOLVED'" , {
83- validate : value => value === 'RESOLVED'
84- } ) ;
163+ const { patches } = ctx ;
164+ for ( const patch of patches ) {
165+ await applyPatch ( ctx , patch ) ;
85166 }
86167 }
87168 } ;
88169}
89170
171+ function applyAndCommitPatches ( ) {
172+ return {
173+ title : 'Apply and commit patches to deps/v8' ,
174+ task : ( ctx ) => {
175+ return new Listr ( ctx . patches . map ( applyPatchTask ) ) ;
176+ }
177+ } ;
178+ }
179+
180+ function applyPatchTask ( patch ) {
181+ return {
182+ title : `Commit ${ shortSha ( patch . sha ) } ` ,
183+ task : ( ctx ) => {
184+ const todo = [
185+ {
186+ title : 'Apply patch' ,
187+ task : ( ctx ) => applyPatch ( ctx , patch )
188+ }
189+ ] ;
190+ if ( ctx . bump !== false ) {
191+ if ( ctx . nodeMajorVersion < 9 ) {
192+ todo . push ( incrementV8Version ( ) ) ;
193+ } else {
194+ todo . push ( incrementEmbedderVersion ( ) ) ;
195+ }
196+ }
197+ todo . push ( commitPatch ( patch ) ) ;
198+ return new Listr ( todo ) ;
199+ }
200+ } ;
201+ }
202+
203+ async function applyPatch ( ctx , patch ) {
204+ try {
205+ await execa (
206+ 'patch' ,
207+ [ '-p1' , '--merge' , '--no-backup-if-mismatch' , '--directory=deps/v8' ] ,
208+ {
209+ cwd : ctx . nodeDir ,
210+ input : patch . data
211+ }
212+ ) ;
213+ } catch ( e ) {
214+ patch . hadConflicts = true ;
215+ return input ( "Resolve merge conflicts and enter 'RESOLVED'" , {
216+ validate : value => value . toUpperCase ( ) === 'RESOLVED'
217+ } ) ;
218+ }
219+ }
220+
90221function incrementV8Version ( ) {
91222 return {
92223 title : 'Increment V8 version' ,
93224 task : async ( ctx ) => {
94- const incremented = ctx . currentVersion [ 3 ] + 1 ;
225+ const incremented = ++ ctx . currentVersion [ 3 ] ;
95226 const versionHPath = `${ ctx . nodeDir } /deps/v8/include/v8-version.h` ;
96227 let versionH = await fs . readFile ( versionHPath , 'utf8' ) ;
97228 versionH = versionH . replace (
0 commit comments