@ -61,11 +61,13 @@ import java.util.regex.Pattern;
import org.eclipse.jgit.api.RebaseResult.Status ;
import org.eclipse.jgit.api.RebaseResult.Status ;
import org.eclipse.jgit.api.ResetCommand.ResetType ;
import org.eclipse.jgit.api.ResetCommand.ResetType ;
import org.eclipse.jgit.api.errors.CheckoutConflictException ;
import org.eclipse.jgit.api.errors.CheckoutConflictException ;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException ;
import org.eclipse.jgit.api.errors.GitAPIException ;
import org.eclipse.jgit.api.errors.GitAPIException ;
import org.eclipse.jgit.api.errors.InvalidRebaseStepException ;
import org.eclipse.jgit.api.errors.InvalidRebaseStepException ;
import org.eclipse.jgit.api.errors.InvalidRefNameException ;
import org.eclipse.jgit.api.errors.InvalidRefNameException ;
import org.eclipse.jgit.api.errors.JGitInternalException ;
import org.eclipse.jgit.api.errors.JGitInternalException ;
import org.eclipse.jgit.api.errors.NoHeadException ;
import org.eclipse.jgit.api.errors.NoHeadException ;
import org.eclipse.jgit.api.errors.NoMessageException ;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException ;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException ;
import org.eclipse.jgit.api.errors.RefNotFoundException ;
import org.eclipse.jgit.api.errors.RefNotFoundException ;
import org.eclipse.jgit.api.errors.UnmergedPathsException ;
import org.eclipse.jgit.api.errors.UnmergedPathsException ;
@ -75,6 +77,7 @@ import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout ;
import org.eclipse.jgit.dircache.DirCacheCheckout ;
import org.eclipse.jgit.dircache.DirCacheIterator ;
import org.eclipse.jgit.dircache.DirCacheIterator ;
import org.eclipse.jgit.internal.JGitText ;
import org.eclipse.jgit.internal.JGitText ;
import org.eclipse.jgit.lib.AbbreviatedObjectId ;
import org.eclipse.jgit.lib.AnyObjectId ;
import org.eclipse.jgit.lib.AnyObjectId ;
import org.eclipse.jgit.lib.Constants ;
import org.eclipse.jgit.lib.Constants ;
import org.eclipse.jgit.lib.NullProgressMonitor ;
import org.eclipse.jgit.lib.NullProgressMonitor ;
@ -197,6 +200,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private boolean stopAfterInitialization = false ;
private boolean stopAfterInitialization = false ;
private RevCommit newHead ;
private boolean lastStepWasForward ;
/ * *
/ * *
* @param repo
* @param repo
* /
* /
@ -220,8 +227,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
* /
* /
public RebaseResult call ( ) throws GitAPIException , NoHeadException ,
public RebaseResult call ( ) throws GitAPIException , NoHeadException ,
RefNotFoundException , WrongRepositoryStateException {
RefNotFoundException , WrongRepositoryStateException {
RevCommit newHead = null ;
newHead = null ;
boolean lastStepWasForward = false ;
lastStepWasForward = false ;
checkCallable ( ) ;
checkCallable ( ) ;
checkParameters ( ) ;
checkParameters ( ) ;
try {
try {
@ -261,7 +268,19 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
if ( operation = = Operation . CONTINUE ) {
if ( operation = = Operation . CONTINUE ) {
newHead = continueRebase ( ) ;
newHead = continueRebase ( ) ;
List < RebaseTodoLine > doneLines = repo . readRebaseTodo (
rebaseState . getPath ( DONE ) , true ) ;
RebaseTodoLine step = doneLines . get ( doneLines . size ( ) - 1 ) ;
if ( newHead ! = null
& & step . getAction ( ) ! = Action . PICK ) {
RebaseTodoLine newStep = new RebaseTodoLine (
step . getAction ( ) ,
AbbreviatedObjectId . fromObjectId ( newHead ) ,
step . getShortMessage ( ) ) ;
RebaseResult result = processStep ( newStep , false ) ;
if ( result ! = null )
return result ;
}
File amendFile = rebaseState . getFile ( AMEND ) ;
File amendFile = rebaseState . getFile ( AMEND ) ;
boolean amendExists = amendFile . exists ( ) ;
boolean amendExists = amendFile . exists ( ) ;
if ( amendExists ) {
if ( amendExists ) {
@ -280,8 +299,6 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
if ( operation = = Operation . SKIP )
if ( operation = = Operation . SKIP )
newHead = checkoutCurrentHead ( ) ;
newHead = checkoutCurrentHead ( ) ;
ObjectReader or = repo . newObjectReader ( ) ;
List < RebaseTodoLine > steps = repo . readRebaseTodo (
List < RebaseTodoLine > steps = repo . readRebaseTodo (
rebaseState . getPath ( GIT_REBASE_TODO ) , false ) ;
rebaseState . getPath ( GIT_REBASE_TODO ) , false ) ;
if ( steps . size ( ) = = 0 ) {
if ( steps . size ( ) = = 0 ) {
@ -296,81 +313,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
for ( int i = 0 ; i < steps . size ( ) ; i + + ) {
for ( int i = 0 ; i < steps . size ( ) ; i + + ) {
RebaseTodoLine step = steps . get ( i ) ;
RebaseTodoLine step = steps . get ( i ) ;
popSteps ( 1 ) ;
popSteps ( 1 ) ;
if ( Action . COMMENT . equals ( step . getAction ( ) ) )
RebaseResult result = processStep ( step , true ) ;
continue ;
if ( result ! = null ) {
Collection < ObjectId > ids = or . resolve ( step . getCommit ( ) ) ;
return result ;
if ( ids . size ( ) ! = 1 )
throw new JGitInternalException (
"Could not resolve uniquely the abbreviated object ID" ) ;
RevCommit commitToPick = walk
. parseCommit ( ids . iterator ( ) . next ( ) ) ;
if ( monitor . isCancelled ( ) )
return new RebaseResult ( commitToPick , Status . STOPPED ) ;
try {
monitor . beginTask ( MessageFormat . format (
JGitText . get ( ) . applyingCommit ,
commitToPick . getShortMessage ( ) ) ,
ProgressMonitor . UNKNOWN ) ;
// if the first parent of commitToPick is the current HEAD,
// we do a fast-forward instead of cherry-pick to avoid
// unnecessary object rewriting
newHead = tryFastForward ( commitToPick ) ;
lastStepWasForward = newHead ! = null ;
if ( ! lastStepWasForward ) {
// TODO if the content of this commit is already merged
// here we should skip this step in order to avoid
// confusing pseudo-changed
String ourCommitName = getOurCommitName ( ) ;
CherryPickResult cherryPickResult = new Git ( repo )
. cherryPick ( ) . include ( commitToPick )
. setOurCommitName ( ourCommitName )
. setReflogPrefix ( "rebase:" ) . call ( ) ; //$NON-NLS-1$
switch ( cherryPickResult . getStatus ( ) ) {
case FAILED :
if ( operation = = Operation . BEGIN )
return abort ( new RebaseResult (
cherryPickResult . getFailingPaths ( ) ) ) ;
else
return stop ( commitToPick , Status . STOPPED ) ;
case CONFLICTING :
return stop ( commitToPick , Status . STOPPED ) ;
case OK :
newHead = cherryPickResult . getNewHead ( ) ;
}
}
boolean isSquash = false ;
switch ( step . getAction ( ) ) {
case PICK :
continue ; // continue rebase process on pick command
case REWORD :
String oldMessage = commitToPick . getFullMessage ( ) ;
String newMessage = interactiveHandler
. modifyCommitMessage ( oldMessage ) ;
newHead = new Git ( repo ) . commit ( ) . setMessage ( newMessage )
. setAmend ( true ) . call ( ) ;
continue ;
case EDIT :
rebaseState . createFile ( AMEND , commitToPick . name ( ) ) ;
return stop ( commitToPick , Status . EDIT ) ;
case COMMENT :
break ;
case SQUASH :
isSquash = true ;
//$FALL-THROUGH$
case FIXUP :
resetSoftToParent ( ) ;
RebaseTodoLine nextStep = ( i > = steps . size ( ) - 1 ? null
: steps . get ( i + 1 ) ) ;
File messageFixupFile = rebaseState . getFile ( MESSAGE_FIXUP ) ;
File messageSquashFile = rebaseState
. getFile ( MESSAGE_SQUASH ) ;
if ( isSquash & & messageFixupFile . exists ( ) )
messageFixupFile . delete ( ) ;
newHead = doSquashFixup ( isSquash , commitToPick ,
nextStep , messageFixupFile , messageSquashFile ) ;
}
} finally {
monitor . endTask ( ) ;
}
}
}
}
return finishRebase ( newHead , lastStepWasForward ) ;
return finishRebase ( newHead , lastStepWasForward ) ;
@ -381,6 +326,98 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
}
}
}
private RebaseResult processStep ( RebaseTodoLine step , boolean shouldPick )
throws IOException , GitAPIException {
if ( Action . COMMENT . equals ( step . getAction ( ) ) )
return null ;
ObjectReader or = repo . newObjectReader ( ) ;
Collection < ObjectId > ids = or . resolve ( step . getCommit ( ) ) ;
if ( ids . size ( ) ! = 1 )
throw new JGitInternalException (
"Could not resolve uniquely the abbreviated object ID" ) ;
RevCommit commitToPick = walk . parseCommit ( ids . iterator ( ) . next ( ) ) ;
if ( shouldPick ) {
if ( monitor . isCancelled ( ) )
return new RebaseResult ( commitToPick , Status . STOPPED ) ;
RebaseResult result = cherryPickCommit ( commitToPick ) ;
if ( result ! = null )
return result ;
}
boolean isSquash = false ;
switch ( step . getAction ( ) ) {
case PICK :
return null ; // continue rebase process on pick command
case REWORD :
String oldMessage = commitToPick . getFullMessage ( ) ;
String newMessage = interactiveHandler
. modifyCommitMessage ( oldMessage ) ;
newHead = new Git ( repo ) . commit ( ) . setMessage ( newMessage )
. setAmend ( true ) . call ( ) ;
return null ;
case EDIT :
rebaseState . createFile ( AMEND , commitToPick . name ( ) ) ;
return stop ( commitToPick , Status . EDIT ) ;
case COMMENT :
break ;
case SQUASH :
isSquash = true ;
//$FALL-THROUGH$
case FIXUP :
resetSoftToParent ( ) ;
List < RebaseTodoLine > steps = repo . readRebaseTodo (
rebaseState . getPath ( GIT_REBASE_TODO ) , false ) ;
RebaseTodoLine nextStep = steps . size ( ) > 0 ? steps . get ( 0 ) : null ;
File messageFixupFile = rebaseState . getFile ( MESSAGE_FIXUP ) ;
File messageSquashFile = rebaseState . getFile ( MESSAGE_SQUASH ) ;
if ( isSquash & & messageFixupFile . exists ( ) )
messageFixupFile . delete ( ) ;
newHead = doSquashFixup ( isSquash , commitToPick , nextStep ,
messageFixupFile , messageSquashFile ) ;
}
return null ;
}
private RebaseResult cherryPickCommit ( RevCommit commitToPick )
throws IOException , GitAPIException , NoMessageException ,
UnmergedPathsException , ConcurrentRefUpdateException ,
WrongRepositoryStateException , NoHeadException {
try {
monitor . beginTask ( MessageFormat . format (
JGitText . get ( ) . applyingCommit ,
commitToPick . getShortMessage ( ) ) , ProgressMonitor . UNKNOWN ) ;
// if the first parent of commitToPick is the current HEAD,
// we do a fast-forward instead of cherry-pick to avoid
// unnecessary object rewriting
newHead = tryFastForward ( commitToPick ) ;
lastStepWasForward = newHead ! = null ;
if ( ! lastStepWasForward ) {
// TODO if the content of this commit is already merged
// here we should skip this step in order to avoid
// confusing pseudo-changed
String ourCommitName = getOurCommitName ( ) ;
CherryPickResult cherryPickResult = new Git ( repo ) . cherryPick ( )
. include ( commitToPick ) . setOurCommitName ( ourCommitName )
. setReflogPrefix ( "rebase:" ) . call ( ) ; //$NON-NLS-1$
switch ( cherryPickResult . getStatus ( ) ) {
case FAILED :
if ( operation = = Operation . BEGIN )
return abort ( new RebaseResult (
cherryPickResult . getFailingPaths ( ) ) ) ;
else
return stop ( commitToPick , Status . STOPPED ) ;
case CONFLICTING :
return stop ( commitToPick , Status . STOPPED ) ;
case OK :
newHead = cherryPickResult . getNewHead ( ) ;
}
}
return null ;
} finally {
monitor . endTask ( ) ;
}
}
private RebaseResult finishRebase ( RevCommit newHead ,
private RebaseResult finishRebase ( RevCommit newHead ,
boolean lastStepWasForward ) throws IOException {
boolean lastStepWasForward ) throws IOException {
String headName = rebaseState . readFile ( HEAD_NAME ) ;
String headName = rebaseState . readFile ( HEAD_NAME ) ;