@@ -2,6 +2,7 @@ package controllers
22
33import (
44 "fmt"
5+ "strings"
56
67 "github.com/fsmiamoto/git-todo-parser/todo"
78 "github.com/go-errors/errors"
@@ -811,11 +812,14 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err
811812 HandleConfirm : func () error {
812813 return self .c .Helpers ().WorkingTree .WithEnsureCommitableFiles (func () error {
813814 self .c .LogAction (self .c .Tr .Actions .CreateFixupCommit )
814- if err := self .c .Git ().Commit .CreateFixupCommit (commit .Sha ); err != nil {
815- return self .c .Error (err )
816- }
815+ return self .c .WithWaitingStatusSync (self .c .Tr .CreatingFixupCommitStatus , func () error {
816+ if err := self .c .Git ().Commit .CreateFixupCommit (commit .Sha ); err != nil {
817+ return self .c .Error (err )
818+ }
817819
818- return self .c .Refresh (types.RefreshOptions {Mode : types .ASYNC })
820+ self .context ().MoveSelectedLine (1 )
821+ return self .c .Refresh (types.RefreshOptions {Mode : types .SYNC })
822+ })
819823 })
820824 },
821825 })
@@ -844,37 +848,89 @@ func (self *LocalCommitsController) squashFixupCommits() error {
844848}
845849
846850func (self * LocalCommitsController ) squashAllFixupsAboveSelectedCommit (commit * models.Commit ) error {
847- return self .c .WithWaitingStatus (self .c .Tr .SquashingStatus , func (gocui.Task ) error {
848- self .c .LogAction (self .c .Tr .Actions .SquashAllAboveFixupCommits )
849- err := self .c .Git ().Rebase .SquashAllAboveFixupCommits (commit )
850- return self .c .Helpers ().MergeAndRebase .CheckMergeOrRebase (err )
851- })
851+ return self .squashFixupsImpl (commit , self .context ().GetSelectedLineIdx ())
852852}
853853
854854func (self * LocalCommitsController ) squashAllFixupsInCurrentBranch () error {
855- commit , err := self .findCommitForSquashFixupsInCurrentBranch ()
855+ commit , rebaseStartIdx , err := self .findCommitForSquashFixupsInCurrentBranch ()
856856 if err != nil {
857857 return self .c .Error (err )
858858 }
859859
860- return self .c .WithWaitingStatus (self .c .Tr .SquashingStatus , func (gocui.Task ) error {
860+ return self .squashFixupsImpl (commit , rebaseStartIdx )
861+ }
862+
863+ func (self * LocalCommitsController ) squashFixupsImpl (commit * models.Commit , rebaseStartIdx int ) error {
864+ selectionOffset := countSquashableCommitsAbove (self .c .Model ().Commits , self .context ().GetSelectedLineIdx (), rebaseStartIdx )
865+ return self .c .WithWaitingStatusSync (self .c .Tr .SquashingStatus , func () error {
861866 self .c .LogAction (self .c .Tr .Actions .SquashAllAboveFixupCommits )
862867 err := self .c .Git ().Rebase .SquashAllAboveFixupCommits (commit )
863- return self .c .Helpers ().MergeAndRebase .CheckMergeOrRebase (err )
868+ self .context ().MoveSelectedLine (- selectionOffset )
869+ return self .c .Helpers ().MergeAndRebase .CheckMergeOrRebaseWithRefreshOptions (
870+ err , types.RefreshOptions {Mode : types .SYNC })
864871 })
865872}
866873
867- func (self * LocalCommitsController ) findCommitForSquashFixupsInCurrentBranch () (* models.Commit , error ) {
874+ func (self * LocalCommitsController ) findCommitForSquashFixupsInCurrentBranch () (* models.Commit , int , error ) {
868875 commits := self .c .Model ().Commits
869876 _ , index , ok := lo .FindIndexOf (commits , func (c * models.Commit ) bool {
870877 return c .IsMerge () || c .Status == models .StatusMerged
871878 })
872879
873880 if ! ok || index == 0 {
874- return nil , errors .New (self .c .Tr .CannotSquashCommitsInCurrentBranch )
881+ return nil , - 1 , errors .New (self .c .Tr .CannotSquashCommitsInCurrentBranch )
882+ }
883+
884+ return commits [index - 1 ], index - 1 , nil
885+ }
886+
887+ // Anticipate how many commits above the selectedIdx are going to get squashed
888+ // by the SquashAllAboveFixupCommits call, so that we can adjust the selection
889+ // afterwards. Let's hope we're matching git's behavior correctly here.
890+ func countSquashableCommitsAbove (commits []* models.Commit , selectedIdx int , rebaseStartIdx int ) int {
891+ result := 0
892+
893+ // For each commit _above_ the selection, ...
894+ for i , commit := range commits [0 :selectedIdx ] {
895+ // ... see if it is a fixup commit, and get the base subject it applies to
896+ if baseSubject , isFixup := isFixupCommit (commit .Name ); isFixup {
897+ // Then, for each commit after the fixup, up to and including the
898+ // rebase start commit, see if we find the base commit
899+ for _ , baseCommit := range commits [i + 1 : rebaseStartIdx + 1 ] {
900+ if strings .HasPrefix (baseCommit .Name , baseSubject ) {
901+ result ++
902+ }
903+ }
904+ }
905+ }
906+ return result
907+ }
908+
909+ // Check whether the given subject line is the subject of a fixup commit, and
910+ // returns (trimmedSubject, true) if so (where trimmedSubject is the subject
911+ // with all fixup prefixes removed), or (subject, false) if not.
912+ func isFixupCommit (subject string ) (string , bool ) {
913+ prefixes := []string {"fixup! " , "squash! " , "amend! " }
914+ trimPrefix := func (s string ) (string , bool ) {
915+ for _ , prefix := range prefixes {
916+ if strings .HasPrefix (s , prefix ) {
917+ return strings .TrimPrefix (s , prefix ), true
918+ }
919+ }
920+ return s , false
921+ }
922+
923+ if subject , wasTrimmed := trimPrefix (subject ); wasTrimmed {
924+ for {
925+ // handle repeated prefixes like "fixup! amend! fixup! Subject"
926+ if subject , wasTrimmed = trimPrefix (subject ); ! wasTrimmed {
927+ break
928+ }
929+ }
930+ return subject , true
875931 }
876932
877- return commits [ index - 1 ], nil
933+ return subject , false
878934}
879935
880936func (self * LocalCommitsController ) createTag (commit * models.Commit ) error {
@@ -1067,7 +1123,7 @@ func (self *LocalCommitsController) canFindCommitForQuickStart() *types.Disabled
10671123}
10681124
10691125func (self * LocalCommitsController ) canFindCommitForSquashFixupsInCurrentBranch () * types.DisabledReason {
1070- if _ , err := self .findCommitForSquashFixupsInCurrentBranch (); err != nil {
1126+ if _ , _ , err := self .findCommitForSquashFixupsInCurrentBranch (); err != nil {
10711127 return & types.DisabledReason {Text : err .Error ()}
10721128 }
10731129
0 commit comments