import { validate } from '../actions/validate' ; import { cache } from '../lib/config' ; import { MergeConflictCallstackT , StackOntoBaseRebaseStackFrameT , StackOntoFixStackFrameT , } from '../lib/config/merge_conflict_callstack_config' ; import { ExitFailedError , PreconditionsFailedError , RebaseConflictError , ValidationFailedError , } from '../lib/errors' ; import { branchExistsPrecondition , currentBranchPrecondition , } from '../lib/preconditions' ; import { checkoutBranch , getTrunk , gpExecSync , logInfo , rebaseInProgress , uncommittedChanges , } from '../lib/utils' ; import Branch from '../wrapper-classes/branch' ; import { restackBranch } from './fix' ; export async function ontoAction ( args ) { if ( uncommittedChanges ( ) ) { throw new PreconditionsFailedError ( 'Cannot fix with uncommitted changes' ) ; } const originalBranch = currentBranchPrecondition ( ) ; await stackOnto ( originalBranch , args . onto , args . mergeConflictCallstack ) ; checkoutBranch ( originalBranch . name ) ; } async function stackOnto ( currentBranch , onto , mergeConflictCallstack ) { branchExistsPrecondition ( onto ) ; checkBranchCanBeMoved ( currentBranch , onto ) ; validateStack ( ) ; const parent = await getParentForRebaseOnto ( currentBranch , onto ) ; currentBranch . setMetaPrevRef ( currentBranch . getCurrentRef ( ) ) ; const stackOntoContinuationFrame = { op : 'STACK_ONTO_BASE_REBASE_CONTINUATION' as const , currentBranchName : currentBranch . name , onto : onto , } ; gpExecSync ( { command : `git rebase --onto ${ onto } $(git merge-base ${ currentBranch . name } ${ parent . name }) ${ currentBranch . name }` , options : { stdio : 'ignore' } , } , ( err ) => { if ( rebaseInProgress ( ) ) { throw new RebaseConflictError ( `Interactive rebase in progress, cannot fix (${ currentBranch . name }) onto (${ onto }).` , { frame : stackOntoContinuationFrame , parent : mergeConflictCallstack , } ) ; } else { throw new ExitFailedError ( `Rebase failed when moving (${ currentBranch . name }) onto (${ onto }).` , err ) ; } } ) ; await stackOntoBaseRebaseContinuation ( stackOntoContinuationFrame , mergeConflictCallstack ) ; } export async function stackOntoBaseRebaseContinuation ( frame , mergeConflictCallstack ) { const currentBranch = await Branch . branchWithName ( frame . currentBranchName ) ; const onto = frame . onto ; cache . clearAll ( ) ; console . log ( `setting ${ currentBranch . name } parent to ${ onto }` ) ; currentBranch . setParentBranchName ( onto ) ; const stackOntoContinuationFrame = { op : 'STACK_ONTO_FIX_CONTINUATION' as const , currentBranchName : frame . currentBranchName , onto : frame . onto , } ; await restackBranch ( { branch : currentBranch , mergeConflictCallstack : { frame : stackOntoContinuationFrame , parent : mergeConflictCallstack , } , } ) ; await stackOntoFixContinuation ( stackOntoContinuationFrame ) ; } export async function stackOntoFixContinuation ( frame ) { logInfo ( `Successfully moved (${ frame . currentBranchName }) onto (${ frame . onto })` ) ; } function getParentForRebaseOnto ( branch , onto ) { const metaParent = branch . getParentFromMeta ( ) ; if ( metaParent ) { return metaParent ; } branch . setParentBranchName ( onto ) ; return new Branch ( onto ) ; } function validateStack ( ) { try { validate ( 'UPSTACK' ) ; } catch { throw new ValidationFailedError ( `Cannot stack "onto", git branches must match stack.` ) ; } } function checkBranchCanBeMoved ( branch , onto ) { if ( branch . name === getTrunk ( ) . name ) { throw new PreconditionsFailedError ( `Cannot stack (${ branch . name }) onto ${ onto }, (${ branch . name }) is currently set as trunk.` ) ; } }