2018-08-14 11:05:26 +02:00
package gui
import (
2018-09-12 10:23:25 +02:00
"fmt"
2018-08-14 11:05:26 +02:00
2019-02-11 12:30:27 +02:00
"github.com/go-errors/errors"
2018-08-14 11:05:26 +02:00
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
2019-02-19 00:18:30 +02:00
"github.com/jesseduffield/lazygit/pkg/git"
2018-09-17 13:02:30 +02:00
"github.com/jesseduffield/lazygit/pkg/utils"
2018-08-14 11:05:26 +02:00
)
2018-12-06 13:18:17 +02:00
// list panel functions
func ( gui * Gui ) getSelectedCommit ( g * gocui . Gui ) * commands . Commit {
selectedLine := gui . State . Panels . Commits . SelectedLine
if selectedLine == - 1 {
return nil
}
return gui . State . Commits [ selectedLine ]
}
func ( gui * Gui ) handleCommitSelect ( g * gocui . Gui , v * gocui . View ) error {
commit := gui . getSelectedCommit ( g )
if commit == nil {
return gui . renderString ( g , "main" , gui . Tr . SLocalize ( "NoCommitsThisBranch" ) )
}
if err := gui . focusPoint ( 0 , gui . State . Panels . Commits . SelectedLine , v ) ; err != nil {
return err
}
commitText , err := gui . GitCommand . Show ( commit . Sha )
if err != nil {
return err
}
return gui . renderString ( g , "main" , commitText )
}
2018-08-14 11:05:26 +02:00
func ( gui * Gui ) refreshCommits ( g * gocui . Gui ) error {
g . Update ( func ( * gocui . Gui ) error {
2019-02-20 13:52:17 +02:00
builder , err := git . NewCommitListBuilder ( gui . Log , gui . GitCommand , gui . OSCommand , gui . Tr )
2019-02-19 00:18:30 +02:00
if err != nil {
return err
}
commits , err := builder . GetCommits ( )
2018-09-25 12:11:33 +02:00
if err != nil {
return err
}
gui . State . Commits = commits
2018-09-17 13:02:30 +02:00
2018-12-04 10:50:11 +02:00
gui . refreshSelectedLine ( & gui . State . Panels . Commits . SelectedLine , len ( gui . State . Commits ) )
2019-02-16 06:17:44 +02:00
isFocused := gui . g . CurrentView ( ) . Name ( ) == "commits"
list , err := utils . RenderList ( gui . State . Commits , isFocused )
2018-09-17 13:02:30 +02:00
if err != nil {
return err
2018-08-14 11:05:26 +02:00
}
2018-12-06 13:18:17 +02:00
2018-12-08 07:54:54 +02:00
v := gui . getCommitsView ( )
2018-12-06 13:18:17 +02:00
v . Clear ( )
2018-09-17 13:02:30 +02:00
fmt . Fprint ( v , list )
2018-08-14 11:05:26 +02:00
gui . refreshStatus ( g )
2018-12-06 13:18:17 +02:00
if v == g . CurrentView ( ) {
2018-08-14 11:05:26 +02:00
gui . handleCommitSelect ( g , v )
}
return nil
} )
return nil
}
2018-12-06 13:18:17 +02:00
func ( gui * Gui ) handleCommitsNextLine ( g * gocui . Gui , v * gocui . View ) error {
panelState := gui . State . Panels . Commits
gui . changeSelectedLine ( & panelState . SelectedLine , len ( gui . State . Commits ) , false )
2018-12-08 07:54:54 +02:00
if err := gui . resetOrigin ( gui . getMainView ( ) ) ; err != nil {
return err
}
2018-12-06 13:18:17 +02:00
return gui . handleCommitSelect ( gui . g , v )
}
func ( gui * Gui ) handleCommitsPrevLine ( g * gocui . Gui , v * gocui . View ) error {
panelState := gui . State . Panels . Commits
gui . changeSelectedLine ( & panelState . SelectedLine , len ( gui . State . Commits ) , true )
2018-12-08 07:54:54 +02:00
if err := gui . resetOrigin ( gui . getMainView ( ) ) ; err != nil {
return err
}
2018-12-06 13:18:17 +02:00
return gui . handleCommitSelect ( gui . g , v )
}
// specific functions
2018-08-14 11:05:26 +02:00
func ( gui * Gui ) handleResetToCommit ( g * gocui . Gui , commitView * gocui . View ) error {
2018-08-16 07:16:32 +02:00
return gui . createConfirmationPanel ( g , commitView , gui . Tr . SLocalize ( "ResetToCommit" ) , gui . Tr . SLocalize ( "SureResetThisCommit" ) , func ( g * gocui . Gui , v * gocui . View ) error {
2018-12-04 10:50:11 +02:00
commit := gui . getSelectedCommit ( g )
if commit == nil {
panic ( errors . New ( gui . Tr . SLocalize ( "NoCommitsThisBranch" ) ) )
2018-08-14 11:05:26 +02:00
}
if err := gui . GitCommand . ResetToCommit ( commit . Sha ) ; err != nil {
return gui . createErrorPanel ( g , err . Error ( ) )
}
if err := gui . refreshCommits ( g ) ; err != nil {
panic ( err )
}
2018-12-08 07:54:54 +02:00
if err := gui . refreshFiles ( ) ; err != nil {
2018-08-14 11:05:26 +02:00
panic ( err )
}
gui . resetOrigin ( commitView )
2018-12-07 09:52:31 +02:00
gui . State . Panels . Commits . SelectedLine = 0
return gui . handleCommitSelect ( g , commitView )
2018-08-14 11:05:26 +02:00
} , nil )
}
func ( gui * Gui ) handleCommitSquashDown ( g * gocui . Gui , v * gocui . View ) error {
2018-12-04 10:50:11 +02:00
if len ( gui . State . Commits ) <= 1 {
2018-08-16 07:16:32 +02:00
return gui . createErrorPanel ( g , gui . Tr . SLocalize ( "YouNoCommitsToSquash" ) )
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "squash" )
if err != nil {
return err
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
if applied {
return nil
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
gui . createConfirmationPanel ( g , v , gui . Tr . SLocalize ( "Squash" ) , gui . Tr . SLocalize ( "SureSquashThisCommit" ) , func ( g * gocui . Gui , v * gocui . View ) error {
err := gui . GitCommand . InteractiveRebase ( gui . State . Commits , gui . State . Panels . Commits . SelectedLine , "squash" )
return gui . handleGenericMergeCommandResult ( err )
} , nil )
return nil
2018-08-14 11:05:26 +02:00
}
// TODO: move to files panel
2018-09-17 13:02:30 +02:00
func ( gui * Gui ) anyUnStagedChanges ( files [ ] * commands . File ) bool {
2018-08-14 11:05:26 +02:00
for _ , file := range files {
if file . Tracked && file . HasUnstagedChanges {
return true
}
}
return false
}
func ( gui * Gui ) handleCommitFixup ( g * gocui . Gui , v * gocui . View ) error {
2018-12-04 10:50:11 +02:00
if len ( gui . State . Commits ) <= 1 {
2018-08-16 07:16:32 +02:00
return gui . createErrorPanel ( g , gui . Tr . SLocalize ( "YouNoCommitsToSquash" ) )
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "fixup" )
if err != nil {
return err
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
if applied {
return nil
2018-08-14 11:05:26 +02:00
}
2019-02-19 14:36:29 +02:00
gui . createConfirmationPanel ( g , v , gui . Tr . SLocalize ( "Fixup" ) , gui . Tr . SLocalize ( "SureFixupThisCommit" ) , func ( g * gocui . Gui , v * gocui . View ) error {
err := gui . GitCommand . InteractiveRebase ( gui . State . Commits , gui . State . Panels . Commits . SelectedLine , "fixup" )
return gui . handleGenericMergeCommandResult ( err )
2018-08-14 11:05:26 +02:00
} , nil )
return nil
}
func ( gui * Gui ) handleRenameCommit ( g * gocui . Gui , v * gocui . View ) error {
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "reword" )
if err != nil {
return err
}
if applied {
return nil
}
2018-12-06 13:18:17 +02:00
if gui . State . Panels . Commits . SelectedLine != 0 {
2018-08-16 07:16:32 +02:00
return gui . createErrorPanel ( g , gui . Tr . SLocalize ( "OnlyRenameTopCommit" ) )
2018-08-14 11:05:26 +02:00
}
2018-09-07 14:41:01 +02:00
return gui . createPromptPanel ( g , v , gui . Tr . SLocalize ( "renameCommit" ) , func ( g * gocui . Gui , v * gocui . View ) error {
2018-08-14 11:05:26 +02:00
if err := gui . GitCommand . RenameCommit ( v . Buffer ( ) ) ; err != nil {
return gui . createErrorPanel ( g , err . Error ( ) )
}
if err := gui . refreshCommits ( g ) ; err != nil {
panic ( err )
}
return gui . handleCommitSelect ( g , v )
} )
}
2018-08-29 14:27:17 +02:00
func ( gui * Gui ) handleRenameCommitEditor ( g * gocui . Gui , v * gocui . View ) error {
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "reword" )
if err != nil {
return err
}
if applied {
return nil
}
2019-02-18 14:27:54 +02:00
subProcess , err := gui . GitCommand . RewordCommit ( gui . State . Commits , gui . State . Panels . Commits . SelectedLine )
2019-02-18 12:29:43 +02:00
if err != nil {
2019-02-18 14:27:54 +02:00
return gui . createErrorPanel ( gui . g , err . Error ( ) )
2018-08-29 14:27:17 +02:00
}
2019-02-18 12:29:43 +02:00
if subProcess != nil {
gui . SubProcess = subProcess
2018-08-29 14:27:17 +02:00
return gui . Errors . ErrSubProcess
2019-02-18 12:29:43 +02:00
}
2018-08-29 14:27:17 +02:00
return nil
}
2019-02-18 14:27:54 +02:00
2019-02-19 14:36:29 +02:00
// handleMidRebaseCommand sees if the selected commit is in fact a rebasing
// commit meaning you are trying to edit the todo file rather than actually
// begin a rebase. It then updates the todo file with that action
func ( gui * Gui ) handleMidRebaseCommand ( action string ) ( bool , error ) {
2019-02-20 13:52:17 +02:00
// for now we do not support setting 'reword' because it requires an editor
// and that means we either unconditionally wait around for the subprocess to ask for
// our input or we set a lazygit client as the EDITOR env variable and have it
// request us to edit the commit message when prompted.
if action == "reword" {
return true , gui . createErrorPanel ( gui . g , gui . Tr . SLocalize ( "rewordNotSupported" ) )
}
2019-02-19 14:36:29 +02:00
selectedCommit := gui . State . Commits [ gui . State . Panels . Commits . SelectedLine ]
if selectedCommit . Status != "rebasing" {
return false , nil
}
if err := gui . GitCommand . EditRebaseTodo ( gui . State . Panels . Commits . SelectedLine , action ) ; err != nil {
return false , gui . createErrorPanel ( gui . g , err . Error ( ) )
}
return true , gui . refreshCommits ( gui . g )
}
2019-02-20 11:51:24 +02:00
// handleMoveTodoDown like handleMidRebaseCommand but for moving an item up in the todo list
func ( gui * Gui ) handleMoveTodoDown ( index int ) ( bool , error ) {
selectedCommit := gui . State . Commits [ index ]
if selectedCommit . Status != "rebasing" {
return false , nil
}
if gui . State . Commits [ index + 1 ] . Status != "rebasing" {
return true , nil
}
if err := gui . GitCommand . MoveTodoDown ( index ) ; err != nil {
return true , gui . createErrorPanel ( gui . g , err . Error ( ) )
}
return true , gui . refreshCommits ( gui . g )
}
2019-02-18 14:27:54 +02:00
func ( gui * Gui ) handleCommitDelete ( g * gocui . Gui , v * gocui . View ) error {
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "drop" )
if err != nil {
return err
}
if applied {
return nil
}
2019-02-18 14:27:54 +02:00
// TODO: i18n
return gui . createConfirmationPanel ( gui . g , v , "Delete Commit" , "Are you sure you want to delete this commit?" , func ( * gocui . Gui , * gocui . View ) error {
err := gui . GitCommand . InteractiveRebase ( gui . State . Commits , gui . State . Panels . Commits . SelectedLine , "drop" )
return gui . handleGenericMergeCommandResult ( err )
} , nil )
}
func ( gui * Gui ) handleCommitMoveDown ( g * gocui . Gui , v * gocui . View ) error {
2019-02-20 11:51:24 +02:00
index := gui . State . Panels . Commits . SelectedLine
selectedCommit := gui . State . Commits [ index ]
if selectedCommit . Status == "rebasing" {
if gui . State . Commits [ index + 1 ] . Status != "rebasing" {
return nil
}
if err := gui . GitCommand . MoveTodoDown ( index ) ; err != nil {
return gui . createErrorPanel ( gui . g , err . Error ( ) )
}
gui . State . Panels . Commits . SelectedLine ++
return gui . refreshCommits ( gui . g )
}
2019-02-18 14:27:54 +02:00
2019-02-20 11:51:24 +02:00
err := gui . GitCommand . MoveCommitDown ( gui . State . Commits , index )
if err == nil {
gui . State . Panels . Commits . SelectedLine ++
}
2019-02-18 14:27:54 +02:00
return gui . handleGenericMergeCommandResult ( err )
}
func ( gui * Gui ) handleCommitMoveUp ( g * gocui . Gui , v * gocui . View ) error {
2019-02-20 11:51:24 +02:00
index := gui . State . Panels . Commits . SelectedLine
if index == 0 {
return nil
2019-02-18 14:27:54 +02:00
}
2019-02-20 11:51:24 +02:00
selectedCommit := gui . State . Commits [ index ]
if selectedCommit . Status == "rebasing" {
if err := gui . GitCommand . MoveTodoDown ( index - 1 ) ; err != nil {
return gui . createErrorPanel ( gui . g , err . Error ( ) )
}
gui . State . Panels . Commits . SelectedLine --
return gui . refreshCommits ( gui . g )
}
2019-02-18 14:27:54 +02:00
2019-02-20 11:51:24 +02:00
err := gui . GitCommand . MoveCommitDown ( gui . State . Commits , index - 1 )
if err == nil {
gui . State . Panels . Commits . SelectedLine --
}
2019-02-18 14:27:54 +02:00
return gui . handleGenericMergeCommandResult ( err )
}
func ( gui * Gui ) handleCommitEdit ( g * gocui . Gui , v * gocui . View ) error {
2019-02-19 14:36:29 +02:00
applied , err := gui . handleMidRebaseCommand ( "edit" )
if err != nil {
return err
}
if applied {
return nil
}
err = gui . GitCommand . InteractiveRebase ( gui . State . Commits , gui . State . Panels . Commits . SelectedLine , "edit" )
2019-02-18 14:27:54 +02:00
return gui . handleGenericMergeCommandResult ( err )
}
2019-02-19 14:36:29 +02:00
func ( gui * Gui ) handleCommitAmendTo ( g * gocui . Gui , v * gocui . View ) error {
2019-02-20 10:46:27 +02:00
return gui . createConfirmationPanel ( gui . g , v , "Amend Commit" , "Are you sure you want to amend this commit with your staged files?" , func ( * gocui . Gui , * gocui . View ) error {
err := gui . GitCommand . AmendTo ( gui . State . Commits [ gui . State . Panels . Commits . SelectedLine ] . Sha )
return gui . handleGenericMergeCommandResult ( err )
} , nil )
2019-02-19 14:36:29 +02:00
}
func ( gui * Gui ) handleCommitPick ( g * gocui . Gui , v * gocui . View ) error {
applied , err := gui . handleMidRebaseCommand ( "pick" )
if err != nil {
return err
}
if applied {
return nil
}
// at this point we aren't actually rebasing so we will interpret this as an
// attempt to pull. We might revoke this later after enabling configurable keybindings
return gui . pullFiles ( g , v )
}
func ( gui * Gui ) handleCommitRevert ( g * gocui . Gui , v * gocui . View ) error {
if err := gui . GitCommand . Revert ( gui . State . Commits [ gui . State . Panels . Commits . SelectedLine ] . Sha ) ; err != nil {
return gui . createErrorPanel ( gui . g , err . Error ( ) )
}
gui . State . Panels . Commits . SelectedLine ++
return gui . refreshCommits ( gui . g )
}