11import  chalk  from  "chalk" 
2+ import  console  from  "console" 
23import  {  renameSync  }  from  "fs" 
34import  { 
45  copySync , 
@@ -36,6 +37,7 @@ import { resolveRelativeFileDependencies } from "./resolveRelativeFileDependenci
3637import  {  spawnSafeSync  }  from  "./spawnSafe" 
3738import  { 
3839  clearPatchApplicationState , 
40+   getPatchApplicationState , 
3941  PatchState , 
4042  savePatchApplicationState , 
4143  STATE_FILE_NAME , 
@@ -78,22 +80,54 @@ export function makePatch({
7880    return 
7981  } 
8082
83+   const  state  =  getPatchApplicationState ( packageDetails ) 
84+   const  isRebasing  =  state ?. isRebasing  ??  false 
85+   // TODO: verify applied patch hashes 
86+   // TODO: handle case for --rebase 0 
87+   // TODO: handle empty diffs while rebasing 
88+   if  ( 
89+     mode . type  ===  "overwrite_last"  && 
90+     isRebasing  && 
91+     state ?. patches . length  ===  0 
92+   )  { 
93+     mode  =  {  type : "append" ,  name : "initial"  } 
94+   } 
95+ 
8196  const  existingPatches  = 
8297    getGroupedPatches ( patchDir ) . pathSpecifierToPatchFiles [ 
8398      packageDetails . pathSpecifier 
8499    ]  ||  [ ] 
85100
101+   // apply all existing patches if appending 
102+   // otherwise apply all but the last 
103+   const  previouslyAppliedPatches  =  state ?. patches . filter ( ( p )  =>  p . didApply ) 
104+   const  patchesToApplyBeforeDiffing : PatchedPackageDetails [ ]  =  isRebasing 
105+     ? mode . type  ===  "append" 
106+       ? existingPatches . slice ( 0 ,  previouslyAppliedPatches ! . length ) 
107+       : state ! . patches [ state ! . patches . length  -  1 ] . didApply 
108+       ? existingPatches . slice ( 0 ,  previouslyAppliedPatches ! . length  -  1 ) 
109+       : existingPatches . slice ( 0 ,  previouslyAppliedPatches ! . length ) 
110+     : mode . type  ===  "append" 
111+     ? existingPatches 
112+     : existingPatches . slice ( 0 ,  - 1 ) 
113+ 
86114  if  ( createIssue  &&  mode . type  ===  "append" )  { 
87115    console . error ( "--create-issue is not compatible with --append." ) 
88116    process . exit ( 1 ) 
89117  } 
90118
119+   if  ( createIssue  &&  isRebasing )  { 
120+     console . error ( "--create-issue is not compatible with rebasing." ) 
121+     process . exit ( 1 ) 
122+   } 
123+ 
91124  const  numPatchesAfterCreate  = 
92125    mode . type  ===  "append"  ||  existingPatches . length  ===  0 
93126      ? existingPatches . length  +  1 
94127      : existingPatches . length 
95128  const  vcs  =  getPackageVCSDetails ( packageDetails ) 
96129  const  canCreateIssue  = 
130+     ! isRebasing  && 
97131    shouldRecommendIssue ( vcs )  && 
98132    numPatchesAfterCreate  ===  1  && 
99133    mode . type  !==  "append" 
@@ -224,11 +258,7 @@ export function makePatch({
224258    // remove ignored files first 
225259    removeIgnoredFiles ( tmpRepoPackagePath ,  includePaths ,  excludePaths ) 
226260
227-     // apply all existing patches if appending 
228-     // otherwise apply all but the last 
229-     const  patchesToApplyBeforeCommit  = 
230-       mode . type  ===  "append"  ? existingPatches  : existingPatches . slice ( 0 ,  - 1 ) 
231-     for  ( const  patchDetails  of  patchesToApplyBeforeCommit )  { 
261+     for  ( const  patchDetails  of  patchesToApplyBeforeDiffing )  { 
232262      if  ( 
233263        ! applyPatch ( { 
234264          patchDetails, 
@@ -339,10 +369,10 @@ export function makePatch({
339369    } 
340370
341371    // maybe delete existing 
342-     if  ( mode . type  ===  "overwrite_last" )  { 
343-       const  prevPatch  =  existingPatches [ existingPatches . length   -   1 ]   as 
344-         |   PatchedPackageDetails 
345-          |  undefined 
372+     if  ( ! isRebasing   &&   mode . type  ===  "overwrite_last" )  { 
373+       const  prevPatch  =  patchesToApplyBeforeDiffing [ 
374+         patchesToApplyBeforeDiffing . length   -   1 
375+       ]   as   PatchedPackageDetails  |  undefined 
346376      if  ( prevPatch )  { 
347377        const  patchFilePath  =  join ( appPath ,  patchDir ,  prevPatch . patchFilename ) 
348378        try  { 
@@ -351,7 +381,7 @@ export function makePatch({
351381          // noop 
352382        } 
353383      } 
354-     }  else  if  ( existingPatches . length  ===  1 )  { 
384+     }  else  if  ( ! isRebasing   &&   existingPatches . length  ===  1 )  { 
355385      // if we are appending to an existing patch that doesn't have a sequence number let's rename it 
356386      const  prevPatch  =  existingPatches [ 0 ] 
357387      if  ( prevPatch . sequenceNumber  ===  undefined )  { 
@@ -370,9 +400,9 @@ export function makePatch({
370400      } 
371401    } 
372402
373-     const  lastPatch  =  existingPatches [ existingPatches . length   -   1 ]   as 
374-       |   PatchedPackageDetails 
375-        |  undefined 
403+     const  lastPatch  =  existingPatches [ 
404+       state  ?  state . patches . length   -   1  :  existingPatches . length   -   1 
405+     ]   as   PatchedPackageDetails  |  undefined 
376406    const  sequenceName  = 
377407      mode . type  ===  "append"  ? mode . name  : lastPatch ?. sequenceName 
378408    const  sequenceNumber  = 
@@ -396,10 +426,33 @@ export function makePatch({
396426    console . log ( 
397427      `${ chalk . green ( "✔" ) }   Created file ${ join ( patchDir ,  patchFileName ) }  \n` , 
398428    ) 
399-     const  prevState : PatchState [ ]  =  ( mode . type  ===  "append" 
400-       ? existingPatches 
401-       : existingPatches . slice ( 0 ,  - 1 ) 
402-     ) . map ( 
429+ 
430+     // if we inserted a new patch into a sequence we may need to update the sequence numbers 
431+     if  ( isRebasing  &&  mode . type  ===  "append" )  { 
432+       const  patchesToNudge  =  existingPatches . slice ( state ! . patches . length ) 
433+       if  ( sequenceNumber  ===  undefined )  { 
434+         throw  new  Error ( "sequenceNumber is undefined while rebasing" ) 
435+       } 
436+       if  ( 
437+         patchesToNudge [ 0 ] ?. sequenceNumber  !==  undefined  && 
438+         patchesToNudge [ 0 ] . sequenceNumber  <=  sequenceNumber 
439+       )  { 
440+         let  next  =  sequenceNumber  +  1 
441+         for  ( const  p  of  patchesToNudge )  { 
442+           const  newName  =  createPatchFileName ( { 
443+             packageDetails, 
444+             packageVersion, 
445+             sequenceName : p . sequenceName , 
446+             sequenceNumber : next ++ , 
447+           } ) 
448+           const  oldPath  =  join ( appPath ,  patchDir ,  p . patchFilename ) 
449+           const  newPath  =  join ( appPath ,  patchDir ,  newName ) 
450+           renameSync ( oldPath ,  newPath ) 
451+         } 
452+       } 
453+     } 
454+ 
455+     const  prevState : PatchState [ ]  =  patchesToApplyBeforeDiffing . map ( 
403456      ( p ) : PatchState  =>  ( { 
404457        patchFilename : p . patchFilename , 
405458        didApply : true , 
@@ -414,15 +467,61 @@ export function makePatch({
414467        patchContentHash : hashFile ( patchPath ) , 
415468      } , 
416469    ] 
417-     if  ( nextState . length  >  1 )  { 
470+ 
471+     // if any patches come after this one we just made, we should reapply them 
472+     let  didFailWhileFinishingRebase  =  false 
473+     if  ( isRebasing )  { 
474+       const  previouslyUnappliedPatches  =  existingPatches . slice ( 
475+         // if we overwrote a previously failing patch we should not include that in here 
476+         previouslyAppliedPatches ! . length  + 
477+           ( mode . type  ===  "overwrite_last"  && 
478+           ! state ?. patches [ state . patches . length  -  1 ] . didApply 
479+             ? 1 
480+             : 0 ) , 
481+       ) 
482+       if  ( previouslyUnappliedPatches . length )  { 
483+         console . log ( `Fast forwarding...` ) 
484+         for  ( const  patch  of  previouslyUnappliedPatches )  { 
485+           const  patchFilePath  =  join ( appPath ,  patchDir ,  patch . patchFilename ) 
486+           if  ( 
487+             ! applyPatch ( { 
488+               patchDetails : patch , 
489+               patchDir, 
490+               patchFilePath, 
491+               reverse : false , 
492+               cwd : tmpRepo . name , 
493+             } ) 
494+           )  { 
495+             didFailWhileFinishingRebase  =  true 
496+             logPatchSequenceError ( {  patchDetails : patch  } ) 
497+             nextState . push ( { 
498+               patchFilename : patch . patchFilename , 
499+               didApply : false , 
500+               patchContentHash : hashFile ( patchFilePath ) , 
501+             } ) 
502+             break 
503+           }  else  { 
504+             console . log ( `  ${ chalk . green ( "✔" ) }   ${ patch . patchFilename }  ` ) 
505+             nextState . push ( { 
506+               patchFilename : patch . patchFilename , 
507+               didApply : true , 
508+               patchContentHash : hashFile ( patchFilePath ) , 
509+             } ) 
510+           } 
511+         } 
512+       } 
513+     } 
514+ 
515+     if  ( isRebasing  ||  numPatchesAfterCreate  >  1 )  { 
418516      savePatchApplicationState ( { 
419517        packageDetails, 
420518        patches : nextState , 
421-         isRebasing : false , 
519+         isRebasing : didFailWhileFinishingRebase , 
422520      } ) 
423521    }  else  { 
424522      clearPatchApplicationState ( packageDetails ) 
425523    } 
524+ 
426525    if  ( canCreateIssue )  { 
427526      if  ( createIssue )  { 
428527        openIssueCreationLink ( { 
@@ -466,3 +565,27 @@ function createPatchFileName({
466565
467566  return  `${ nameAndVersion } ${ num } ${ name }  .patch` 
468567} 
568+ 
569+ export  function  logPatchSequenceError ( { 
570+   patchDetails, 
571+ } : { 
572+   patchDetails : PatchedPackageDetails 
573+ } )  { 
574+   console . log ( ` 
575+ ${ chalk . red . bold ( "⛔ ERROR" ) } 
576+ 
577+ Failed to apply patch file ${ chalk . bold ( patchDetails . patchFilename ) }  . 
578+ 
579+ If this patch file is no longer useful, delete it and run 
580+ 
581+   ${ chalk . bold ( `patch-package` ) }  
582+    
583+ Otherwise you should open ${  
584+     patchDetails . path  
585+   }  , manually apply the changes from the patch file, and run
586+ 
587+   ${ chalk . bold ( `patch-package ${ patchDetails . pathSpecifier }  ` ) }  
588+ 
589+ to update the patch file. 
590+ ` ) 
591+ } 
0 commit comments