2022-12-30 14:24:24 +02:00
package helpers
import (
"fmt"
"strings"
"sync"
2023-07-29 01:37:14 +02:00
"time"
2022-12-30 14:24:24 +02:00
"github.com/jesseduffield/generics/set"
2023-07-09 03:32:27 +02:00
"github.com/jesseduffield/gocui"
2022-12-30 14:24:24 +02:00
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
2023-07-24 05:06:42 +02:00
"github.com/samber/lo"
2022-12-30 14:24:24 +02:00
)
type RefreshHelper struct {
2023-03-23 03:35:07 +02:00
c * HelperCommon
2022-12-30 14:24:24 +02:00
refsHelper * RefsHelper
mergeAndRebaseHelper * MergeAndRebaseHelper
patchBuildingHelper * PatchBuildingHelper
stagingHelper * StagingHelper
mergeConflictsHelper * MergeConflictsHelper
2022-09-02 08:50:34 +02:00
worktreeHelper * WorktreeHelper
2023-10-08 19:52:54 +02:00
searchHelper * SearchHelper
2022-12-30 14:24:24 +02:00
}
func NewRefreshHelper (
2023-03-23 03:35:07 +02:00
c * HelperCommon ,
2022-12-30 14:24:24 +02:00
refsHelper * RefsHelper ,
mergeAndRebaseHelper * MergeAndRebaseHelper ,
patchBuildingHelper * PatchBuildingHelper ,
stagingHelper * StagingHelper ,
mergeConflictsHelper * MergeConflictsHelper ,
2022-09-02 08:50:34 +02:00
worktreeHelper * WorktreeHelper ,
2023-10-08 19:52:54 +02:00
searchHelper * SearchHelper ,
2022-12-30 14:24:24 +02:00
) * RefreshHelper {
return & RefreshHelper {
c : c ,
refsHelper : refsHelper ,
mergeAndRebaseHelper : mergeAndRebaseHelper ,
patchBuildingHelper : patchBuildingHelper ,
stagingHelper : stagingHelper ,
mergeConflictsHelper : mergeConflictsHelper ,
2022-09-02 08:50:34 +02:00
worktreeHelper : worktreeHelper ,
2023-10-08 19:52:54 +02:00
searchHelper : searchHelper ,
2022-12-30 14:24:24 +02:00
}
}
func ( self * RefreshHelper ) Refresh ( options types . RefreshOptions ) error {
2023-08-22 13:55:05 +02:00
if options . Mode == types . ASYNC && options . Then != nil {
panic ( "RefreshOptions.Then doesn't work with mode ASYNC" )
}
2023-07-29 01:37:14 +02:00
t := time . Now ( )
defer func ( ) {
self . c . Log . Infof ( fmt . Sprintf ( "Refresh took %s" , time . Since ( t ) ) )
} ( )
2022-12-30 14:24:24 +02:00
if options . Scope == nil {
self . c . Log . Infof (
"refreshing all scopes in %s mode" ,
getModeName ( options . Mode ) ,
)
} else {
self . c . Log . Infof (
"refreshing the following scopes in %s mode: %s" ,
getModeName ( options . Mode ) ,
strings . Join ( getScopeNames ( options . Scope ) , "," ) ,
)
}
f := func ( ) {
var scopeSet * set . Set [ types . RefreshableView ]
if len ( options . Scope ) == 0 {
// not refreshing staging/patch-building unless explicitly requested because we only need
// to refresh those while focused.
scopeSet = set . NewFromSlice ( [ ] types . RefreshableView {
types . COMMITS ,
types . BRANCHES ,
types . FILES ,
types . STASH ,
types . REFLOG ,
types . TAGS ,
types . REMOTES ,
2022-09-01 20:25:41 +02:00
types . WORKTREES ,
2022-12-30 14:24:24 +02:00
types . STATUS ,
types . BISECT_INFO ,
2023-04-29 05:41:29 +02:00
types . STAGING ,
2022-12-30 14:24:24 +02:00
} )
} else {
scopeSet = set . NewFromSlice ( options . Scope )
}
2023-07-29 01:34:17 +02:00
wg := sync . WaitGroup { }
refresh := func ( name string , f func ( ) ) {
2023-07-31 10:32:38 +02:00
// if we're in a demo we don't want any async refreshes because
// everything happens fast and it's better to have everything update
// in the one frame
if ! self . c . InDemo ( ) && options . Mode == types . ASYNC {
2023-07-09 13:09:52 +02:00
self . c . OnWorker ( func ( t gocui . Task ) {
2023-07-09 03:32:27 +02:00
f ( )
} )
2023-07-08 07:22:26 +02:00
} else {
2023-07-29 01:34:17 +02:00
wg . Add ( 1 )
go utils . Safe ( func ( ) {
t := time . Now ( )
defer wg . Done ( )
f ( )
self . c . Log . Infof ( fmt . Sprintf ( "refreshed %s in %s" , name , time . Since ( t ) ) )
} )
2023-07-08 07:22:26 +02:00
}
2022-12-30 14:24:24 +02:00
}
2023-09-26 17:16:11 +02:00
includeWorktreesWithBranches := false
2022-12-30 14:24:24 +02:00
if scopeSet . Includes ( types . COMMITS ) || scopeSet . Includes ( types . BRANCHES ) || scopeSet . Includes ( types . REFLOG ) || scopeSet . Includes ( types . BISECT_INFO ) {
2023-07-29 01:34:17 +02:00
// whenever we change commits, we should update branches because the upstream/downstream
// counts can change. Whenever we change branches we should also change commits
// e.g. in the case of switching branches.
refresh ( "commits and commit files" , self . refreshCommitsAndCommitFiles )
2023-09-26 17:16:11 +02:00
includeWorktreesWithBranches = scopeSet . Includes ( types . WORKTREES )
refresh ( "reflog and branches" , func ( ) { self . refreshReflogAndBranches ( includeWorktreesWithBranches ) } )
2022-12-30 14:24:24 +02:00
} else if scopeSet . Includes ( types . REBASE_COMMITS ) {
// the above block handles rebase commits so we only need to call this one
// if we've asked specifically for rebase commits and not those other things
2023-07-29 01:34:17 +02:00
refresh ( "rebase commits" , func ( ) { _ = self . refreshRebaseCommits ( ) } )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . SUB_COMMITS ) {
2023-07-29 01:34:17 +02:00
refresh ( "sub commits" , func ( ) { _ = self . refreshSubCommitsWithLimit ( ) } )
2022-12-30 14:24:24 +02:00
}
// reason we're not doing this if the COMMITS type is included is that if the COMMITS type _is_ included we will refresh the commit files context anyway
if scopeSet . Includes ( types . COMMIT_FILES ) && ! scopeSet . Includes ( types . COMMITS ) {
2023-07-29 01:34:17 +02:00
refresh ( "commit files" , func ( ) { _ = self . refreshCommitFilesContext ( ) } )
2022-12-30 14:24:24 +02:00
}
2023-07-29 02:20:15 +02:00
fileWg := sync . WaitGroup { }
2022-12-30 14:24:24 +02:00
if scopeSet . Includes ( types . FILES ) || scopeSet . Includes ( types . SUBMODULES ) {
2023-07-29 02:20:15 +02:00
fileWg . Add ( 1 )
refresh ( "files" , func ( ) {
_ = self . refreshFilesAndSubmodules ( )
fileWg . Done ( )
} )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . STASH ) {
2023-07-29 01:34:17 +02:00
refresh ( "stash" , func ( ) { _ = self . refreshStashEntries ( ) } )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . TAGS ) {
2023-07-29 01:34:17 +02:00
refresh ( "tags" , func ( ) { _ = self . refreshTags ( ) } )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . REMOTES ) {
2023-07-29 01:34:17 +02:00
refresh ( "remotes" , func ( ) { _ = self . refreshRemotes ( ) } )
2022-12-30 14:24:24 +02:00
}
2023-09-26 17:16:11 +02:00
if scopeSet . Includes ( types . WORKTREES ) && ! includeWorktreesWithBranches {
2022-09-01 20:25:41 +02:00
refresh ( "worktrees" , func ( ) { _ = self . refreshWorktrees ( ) } )
}
2022-12-30 14:24:24 +02:00
if scopeSet . Includes ( types . STAGING ) {
2023-07-29 02:20:15 +02:00
refresh ( "staging" , func ( ) {
fileWg . Wait ( )
_ = self . stagingHelper . RefreshStagingPanel ( types . OnFocusOpts { } )
} )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . PATCH_BUILDING ) {
2023-07-29 01:34:17 +02:00
refresh ( "patch building" , func ( ) { _ = self . patchBuildingHelper . RefreshPatchBuildingPanel ( types . OnFocusOpts { } ) } )
2022-12-30 14:24:24 +02:00
}
if scopeSet . Includes ( types . MERGE_CONFLICTS ) || scopeSet . Includes ( types . FILES ) {
2023-07-29 01:34:17 +02:00
refresh ( "merge conflicts" , func ( ) { _ = self . mergeConflictsHelper . RefreshMergeState ( ) } )
2022-12-30 14:24:24 +02:00
}
self . refreshStatus ( )
2023-08-22 12:08:41 +02:00
wg . Wait ( )
2022-12-30 14:24:24 +02:00
if options . Then != nil {
options . Then ( )
}
}
if options . Mode == types . BLOCK_UI {
self . c . OnUIThread ( func ( ) error {
f ( )
return nil
} )
} else {
f ( )
}
return nil
}
func getScopeNames ( scopes [ ] types . RefreshableView ) [ ] string {
scopeNameMap := map [ types . RefreshableView ] string {
types . COMMITS : "commits" ,
types . BRANCHES : "branches" ,
types . FILES : "files" ,
types . SUBMODULES : "submodules" ,
types . SUB_COMMITS : "subCommits" ,
types . STASH : "stash" ,
types . REFLOG : "reflog" ,
types . TAGS : "tags" ,
types . REMOTES : "remotes" ,
2022-09-01 20:25:41 +02:00
types . WORKTREES : "worktrees" ,
2022-12-30 14:24:24 +02:00
types . STATUS : "status" ,
types . BISECT_INFO : "bisect" ,
types . STAGING : "staging" ,
types . MERGE_CONFLICTS : "mergeConflicts" ,
}
2023-07-24 05:06:42 +02:00
return lo . Map ( scopes , func ( scope types . RefreshableView , _ int ) string {
2022-12-30 14:24:24 +02:00
return scopeNameMap [ scope ]
} )
}
func getModeName ( mode types . RefreshMode ) string {
switch mode {
case types . SYNC :
return "sync"
case types . ASYNC :
return "async"
case types . BLOCK_UI :
return "block-ui"
default :
return "unknown mode"
}
}
// during startup, the bottleneck is fetching the reflog entries. We need these
// on startup to sort the branches by recency. So we have two phases: INITIAL, and COMPLETE.
// In the initial phase we don't get any reflog commits, but we asynchronously get them
// and refresh the branches after that
func ( self * RefreshHelper ) refreshReflogCommitsConsideringStartup ( ) {
switch self . c . State ( ) . GetRepoState ( ) . GetStartupStage ( ) {
case types . INITIAL :
2023-07-09 13:09:52 +02:00
self . c . OnWorker ( func ( _ gocui . Task ) {
2022-12-30 14:24:24 +02:00
_ = self . refreshReflogCommits ( )
2023-09-26 17:16:11 +02:00
self . refreshBranches ( false )
2022-12-30 14:24:24 +02:00
self . c . State ( ) . GetRepoState ( ) . SetStartupStage ( types . COMPLETE )
} )
case types . COMPLETE :
_ = self . refreshReflogCommits ( )
}
}
2023-09-26 17:16:11 +02:00
func ( self * RefreshHelper ) refreshReflogAndBranches ( refreshWorktrees bool ) {
2023-07-29 01:34:17 +02:00
self . refreshReflogCommitsConsideringStartup ( )
2022-12-30 14:24:24 +02:00
2023-09-26 17:16:11 +02:00
self . refreshBranches ( refreshWorktrees )
2023-07-29 01:34:17 +02:00
}
2022-12-30 14:24:24 +02:00
2023-07-29 01:34:17 +02:00
func ( self * RefreshHelper ) refreshCommitsAndCommitFiles ( ) {
_ = self . refreshCommitsWithLimit ( )
ctx , ok := self . c . Contexts ( ) . CommitFiles . GetParentContext ( )
if ok && ctx . GetKey ( ) == context . LOCAL_COMMITS_CONTEXT_KEY {
// This makes sense when we've e.g. just amended a commit, meaning we get a new commit SHA at the same position.
// However if we've just added a brand new commit, it pushes the list down by one and so we would end up
// showing the contents of a different commit than the one we initially entered.
// Ideally we would know when to refresh the commit files context and when not to,
// or perhaps we could just pop that context off the stack whenever cycling windows.
// For now the awkwardness remains.
commit := self . c . Contexts ( ) . LocalCommits . GetSelected ( )
if commit != nil {
self . c . Contexts ( ) . CommitFiles . SetRef ( commit )
self . c . Contexts ( ) . CommitFiles . SetTitleRef ( commit . RefName ( ) )
_ = self . refreshCommitFilesContext ( )
2022-12-30 14:24:24 +02:00
}
2023-07-29 01:34:17 +02:00
}
2022-12-30 14:24:24 +02:00
}
2023-07-12 08:47:50 +02:00
func ( self * RefreshHelper ) determineCheckedOutBranchName ( ) string {
if rebasedBranch := self . c . Git ( ) . Status . BranchBeingRebased ( ) ; rebasedBranch != "" {
// During a rebase we're on a detached head, so cannot determine the
// branch name in the usual way. We need to read it from the
// ".git/rebase-merge/head-name" file instead.
return strings . TrimPrefix ( rebasedBranch , "refs/heads/" )
}
if bisectInfo := self . c . Git ( ) . Bisect . GetInfo ( ) ; bisectInfo . Bisecting ( ) && bisectInfo . GetStartSha ( ) != "" {
// Likewise, when we're bisecting we're on a detached head as well. In
// this case we read the branch name from the ".git/BISECT_START" file.
return bisectInfo . GetStartSha ( )
}
// In all other cases, get the branch name by asking git what branch is
// checked out. Note that if we're on a detached head (for reasons other
// than rebasing or bisecting, i.e. it was explicitly checked out), then
// this will return its sha.
if branchName , err := self . c . Git ( ) . Branch . CurrentBranchName ( ) ; err == nil {
return branchName
}
// Should never get here unless the working copy is corrupt
return ""
}
2022-12-30 14:24:24 +02:00
func ( self * RefreshHelper ) refreshCommitsWithLimit ( ) error {
self . c . Mutexes ( ) . LocalCommitsMutex . Lock ( )
defer self . c . Mutexes ( ) . LocalCommitsMutex . Unlock ( )
2023-08-19 09:17:12 +02:00
checkedOutBranchName := self . determineCheckedOutBranchName ( )
2023-03-23 03:53:18 +02:00
commits , err := self . c . Git ( ) . Loaders . CommitLoader . GetCommits (
2022-12-30 14:24:24 +02:00
git_commands . GetCommitsOptions {
2023-03-23 03:53:18 +02:00
Limit : self . c . Contexts ( ) . LocalCommits . GetLimitCommits ( ) ,
2022-12-30 14:24:24 +02:00
FilterPath : self . c . Modes ( ) . Filtering . GetPath ( ) ,
IncludeRebaseCommits : true ,
RefName : self . refForLog ( ) ,
2023-08-19 09:17:12 +02:00
RefForPushedStatus : checkedOutBranchName ,
2023-03-23 03:53:18 +02:00
All : self . c . Contexts ( ) . LocalCommits . GetShowWholeGitGraph ( ) ,
2022-12-30 14:24:24 +02:00
} ,
)
if err != nil {
return err
}
self . c . Model ( ) . Commits = commits
2023-07-22 02:33:40 +02:00
self . RefreshAuthors ( commits )
2023-03-23 03:53:18 +02:00
self . c . Model ( ) . WorkingTreeStateAtLastCommitRefresh = self . c . Git ( ) . Status . WorkingTreeState ( )
2023-08-19 09:17:12 +02:00
self . c . Model ( ) . CheckedOutBranch = checkedOutBranchName
2022-12-30 14:24:24 +02:00
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . LocalCommits )
2022-12-30 14:24:24 +02:00
}
func ( self * RefreshHelper ) refreshSubCommitsWithLimit ( ) error {
self . c . Mutexes ( ) . SubCommitsMutex . Lock ( )
defer self . c . Mutexes ( ) . SubCommitsMutex . Unlock ( )
2023-03-23 03:53:18 +02:00
commits , err := self . c . Git ( ) . Loaders . CommitLoader . GetCommits (
2022-12-30 14:24:24 +02:00
git_commands . GetCommitsOptions {
2023-08-05 12:06:37 +02:00
Limit : self . c . Contexts ( ) . SubCommits . GetLimitCommits ( ) ,
FilterPath : self . c . Modes ( ) . Filtering . GetPath ( ) ,
IncludeRebaseCommits : false ,
RefName : self . c . Contexts ( ) . SubCommits . GetRef ( ) . FullRefName ( ) ,
RefToShowDivergenceFrom : self . c . Contexts ( ) . SubCommits . GetRefToShowDivergenceFrom ( ) ,
RefForPushedStatus : self . c . Contexts ( ) . SubCommits . GetRef ( ) . FullRefName ( ) ,
2022-12-30 14:24:24 +02:00
} ,
)
if err != nil {
return err
}
self . c . Model ( ) . SubCommits = commits
2023-07-22 02:33:40 +02:00
self . RefreshAuthors ( commits )
2022-12-30 14:24:24 +02:00
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . SubCommits )
2022-12-30 14:24:24 +02:00
}
2023-07-22 02:33:40 +02:00
func ( self * RefreshHelper ) RefreshAuthors ( commits [ ] * models . Commit ) {
self . c . Mutexes ( ) . AuthorsMutex . Lock ( )
defer self . c . Mutexes ( ) . AuthorsMutex . Unlock ( )
authors := self . c . Model ( ) . Authors
for _ , commit := range commits {
if _ , ok := authors [ commit . AuthorEmail ] ; ! ok {
authors [ commit . AuthorEmail ] = & models . Author {
Email : commit . AuthorEmail ,
Name : commit . AuthorName ,
}
}
}
}
2022-12-30 14:24:24 +02:00
func ( self * RefreshHelper ) refreshCommitFilesContext ( ) error {
2023-03-23 03:53:18 +02:00
ref := self . c . Contexts ( ) . CommitFiles . GetRef ( )
2022-12-30 14:24:24 +02:00
to := ref . RefName ( )
from , reverse := self . c . Modes ( ) . Diffing . GetFromAndReverseArgsForDiff ( ref . ParentRefName ( ) )
2023-03-23 03:53:18 +02:00
files , err := self . c . Git ( ) . Loaders . CommitFileLoader . GetFilesInDiff ( from , to , reverse )
2022-12-30 14:24:24 +02:00
if err != nil {
return self . c . Error ( err )
}
self . c . Model ( ) . CommitFiles = files
2023-03-23 03:53:18 +02:00
self . c . Contexts ( ) . CommitFiles . CommitFileTreeViewModel . SetTree ( )
2022-12-30 14:24:24 +02:00
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . CommitFiles )
2022-12-30 14:24:24 +02:00
}
func ( self * RefreshHelper ) refreshRebaseCommits ( ) error {
self . c . Mutexes ( ) . LocalCommitsMutex . Lock ( )
defer self . c . Mutexes ( ) . LocalCommitsMutex . Unlock ( )
2023-03-23 03:53:18 +02:00
updatedCommits , err := self . c . Git ( ) . Loaders . CommitLoader . MergeRebasingCommits ( self . c . Model ( ) . Commits )
2022-12-30 14:24:24 +02:00
if err != nil {
return err
}
self . c . Model ( ) . Commits = updatedCommits
2023-03-23 03:53:18 +02:00
self . c . Model ( ) . WorkingTreeStateAtLastCommitRefresh = self . c . Git ( ) . Status . WorkingTreeState ( )
2022-12-30 14:24:24 +02:00
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . LocalCommits )
2022-12-30 14:24:24 +02:00
}
func ( self * RefreshHelper ) refreshTags ( ) error {
2023-03-23 03:53:18 +02:00
tags , err := self . c . Git ( ) . Loaders . TagLoader . GetTags ( )
2022-12-30 14:24:24 +02:00
if err != nil {
return self . c . Error ( err )
}
self . c . Model ( ) . Tags = tags
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . Tags )
2022-12-30 14:24:24 +02:00
}
func ( self * RefreshHelper ) refreshStateSubmoduleConfigs ( ) error {
2023-03-23 03:53:18 +02:00
configs , err := self . c . Git ( ) . Submodule . GetConfigs ( )
2022-12-30 14:24:24 +02:00
if err != nil {
return err
}
self . c . Model ( ) . Submodules = configs
return nil
}
// self.refreshStatus is called at the end of this because that's when we can
// be sure there is a State.Model.Branches array to pick the current branch from
2023-09-26 17:16:11 +02:00
func ( self * RefreshHelper ) refreshBranches ( refreshWorktrees bool ) {
2023-07-08 12:47:16 +02:00
self . c . Mutexes ( ) . RefreshingBranchesMutex . Lock ( )
defer self . c . Mutexes ( ) . RefreshingBranchesMutex . Unlock ( )
2022-12-30 14:24:24 +02:00
reflogCommits := self . c . Model ( ) . FilteredReflogCommits
if self . c . Modes ( ) . Filtering . Active ( ) {
// in filter mode we filter our reflog commits to just those containing the path
// however we need all the reflog entries to populate the recencies of our branches
// which allows us to order them correctly. So if we're filtering we'll just
// manually load all the reflog commits here
var err error
2023-03-23 03:53:18 +02:00
reflogCommits , _ , err = self . c . Git ( ) . Loaders . ReflogCommitLoader . GetReflogCommits ( nil , "" )
2022-12-30 14:24:24 +02:00
if err != nil {
self . c . Log . Error ( err )
}
}
2023-03-23 03:53:18 +02:00
branches , err := self . c . Git ( ) . Loaders . BranchLoader . Load ( reflogCommits )
2022-12-30 14:24:24 +02:00
if err != nil {
_ = self . c . Error ( err )
}
self . c . Model ( ) . Branches = branches
2023-09-26 17:16:11 +02:00
if refreshWorktrees {
self . loadWorktrees ( )
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Worktrees ) ; err != nil {
2023-09-26 17:16:11 +02:00
self . c . Log . Error ( err )
}
}
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Branches ) ; err != nil {
2022-12-30 14:24:24 +02:00
self . c . Log . Error ( err )
}
2023-07-11 12:13:40 +02:00
// Need to re-render the commits view because the visualization of local
// branch heads might have changed
2023-10-03 09:36:46 +02:00
self . c . Mutexes ( ) . LocalCommitsMutex . Lock ( )
2023-07-11 12:13:40 +02:00
if err := self . c . Contexts ( ) . LocalCommits . HandleRender ( ) ; err != nil {
self . c . Log . Error ( err )
}
2023-10-03 09:36:46 +02:00
self . c . Mutexes ( ) . LocalCommitsMutex . Unlock ( )
2023-07-11 12:13:40 +02:00
2022-12-30 14:24:24 +02:00
self . refreshStatus ( )
}
func ( self * RefreshHelper ) refreshFilesAndSubmodules ( ) error {
self . c . Mutexes ( ) . RefreshingFilesMutex . Lock ( )
self . c . State ( ) . SetIsRefreshingFiles ( true )
defer func ( ) {
self . c . State ( ) . SetIsRefreshingFiles ( false )
self . c . Mutexes ( ) . RefreshingFilesMutex . Unlock ( )
} ( )
if err := self . refreshStateSubmoduleConfigs ( ) ; err != nil {
return err
}
if err := self . refreshStateFiles ( ) ; err != nil {
return err
}
self . c . OnUIThread ( func ( ) error {
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Submodules ) ; err != nil {
2022-12-30 14:24:24 +02:00
self . c . Log . Error ( err )
}
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Files ) ; err != nil {
2022-12-30 14:24:24 +02:00
self . c . Log . Error ( err )
}
return nil
} )
return nil
}
func ( self * RefreshHelper ) refreshStateFiles ( ) error {
2023-03-23 03:53:18 +02:00
fileTreeViewModel := self . c . Contexts ( ) . Files . FileTreeViewModel
2022-12-30 14:24:24 +02:00
// If git thinks any of our files have inline merge conflicts, but they actually don't,
// we stage them.
// Note that if files with merge conflicts have both arisen and have been resolved
// between refreshes, we won't stage them here. This is super unlikely though,
// and this approach spares us from having to call `git status` twice in a row.
// Although this also means that at startup we won't be staging anything until
// we call git status again.
pathsToStage := [ ] string { }
prevConflictFileCount := 0
for _ , file := range self . c . Model ( ) . Files {
if file . HasMergeConflicts {
prevConflictFileCount ++
}
if file . HasInlineMergeConflicts {
hasConflicts , err := mergeconflicts . FileHasConflictMarkers ( file . Name )
if err != nil {
self . c . Log . Error ( err )
} else if ! hasConflicts {
pathsToStage = append ( pathsToStage , file . Name )
}
}
}
if len ( pathsToStage ) > 0 {
self . c . LogAction ( self . c . Tr . Actions . StageResolvedFiles )
2023-03-23 03:53:18 +02:00
if err := self . c . Git ( ) . WorkingTree . StageFiles ( pathsToStage ) ; err != nil {
2022-12-30 14:24:24 +02:00
return self . c . Error ( err )
}
}
2023-03-23 03:53:18 +02:00
files := self . c . Git ( ) . Loaders . FileLoader .
2022-12-30 14:24:24 +02:00
GetStatusFiles ( git_commands . GetStatusFileOptions { } )
conflictFileCount := 0
for _ , file := range files {
if file . HasMergeConflicts {
conflictFileCount ++
}
}
2023-03-23 03:53:18 +02:00
if self . c . Git ( ) . Status . WorkingTreeState ( ) != enums . REBASE_MODE_NONE && conflictFileCount == 0 && prevConflictFileCount > 0 {
2022-12-30 14:24:24 +02:00
self . c . OnUIThread ( func ( ) error { return self . mergeAndRebaseHelper . PromptToContinueRebase ( ) } )
}
fileTreeViewModel . RWMutex . Lock ( )
// only taking over the filter if it hasn't already been set by the user.
// Though this does make it impossible for the user to actually say they want to display all if
// conflicts are currently being shown. Hmm. Worth it I reckon. If we need to add some
// extra state here to see if the user's set the filter themselves we can do that, but
// I'd prefer to maintain as little state as possible.
if conflictFileCount > 0 {
if fileTreeViewModel . GetFilter ( ) == filetree . DisplayAll {
2023-05-27 11:58:48 +02:00
fileTreeViewModel . SetStatusFilter ( filetree . DisplayConflicted )
2022-12-30 14:24:24 +02:00
}
} else if fileTreeViewModel . GetFilter ( ) == filetree . DisplayConflicted {
2023-05-27 11:58:48 +02:00
fileTreeViewModel . SetStatusFilter ( filetree . DisplayAll )
2022-12-30 14:24:24 +02:00
}
self . c . Model ( ) . Files = files
fileTreeViewModel . SetTree ( )
fileTreeViewModel . RWMutex . Unlock ( )
return nil
}
// the reflogs panel is the only panel where we cache data, in that we only
// load entries that have been created since we last ran the call. This means
// we need to be more careful with how we use this, and to ensure we're emptying
// the reflogs array when changing contexts.
// This method also manages two things: ReflogCommits and FilteredReflogCommits.
// FilteredReflogCommits are rendered in the reflogs panel, and ReflogCommits
// are used by the branches panel to obtain recency values for sorting.
func ( self * RefreshHelper ) refreshReflogCommits ( ) error {
// pulling state into its own variable incase it gets swapped out for another state
// and we get an out of bounds exception
model := self . c . Model ( )
var lastReflogCommit * models . Commit
if len ( model . ReflogCommits ) > 0 {
lastReflogCommit = model . ReflogCommits [ 0 ]
}
refresh := func ( stateCommits * [ ] * models . Commit , filterPath string ) error {
2023-03-23 03:53:18 +02:00
commits , onlyObtainedNewReflogCommits , err := self . c . Git ( ) . Loaders . ReflogCommitLoader .
2022-12-30 14:24:24 +02:00
GetReflogCommits ( lastReflogCommit , filterPath )
if err != nil {
return self . c . Error ( err )
}
if onlyObtainedNewReflogCommits {
* stateCommits = append ( commits , * stateCommits ... )
} else {
* stateCommits = commits
}
return nil
}
if err := refresh ( & model . ReflogCommits , "" ) ; err != nil {
return err
}
if self . c . Modes ( ) . Filtering . Active ( ) {
if err := refresh ( & model . FilteredReflogCommits , self . c . Modes ( ) . Filtering . GetPath ( ) ) ; err != nil {
return err
}
} else {
model . FilteredReflogCommits = model . ReflogCommits
}
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . ReflogCommits )
2022-12-30 14:24:24 +02:00
}
func ( self * RefreshHelper ) refreshRemotes ( ) error {
2023-03-23 03:53:18 +02:00
prevSelectedRemote := self . c . Contexts ( ) . Remotes . GetSelected ( )
2022-12-30 14:24:24 +02:00
2023-03-23 03:53:18 +02:00
remotes , err := self . c . Git ( ) . Loaders . RemoteLoader . GetRemotes ( )
2022-12-30 14:24:24 +02:00
if err != nil {
return self . c . Error ( err )
}
self . c . Model ( ) . Remotes = remotes
// we need to ensure our selected remote branches aren't now outdated
if prevSelectedRemote != nil && self . c . Model ( ) . RemoteBranches != nil {
// find remote now
for _ , remote := range remotes {
if remote . Name == prevSelectedRemote . Name {
self . c . Model ( ) . RemoteBranches = remote . Branches
break
}
}
}
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Remotes ) ; err != nil {
2022-12-30 14:24:24 +02:00
return err
}
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . RemoteBranches ) ; err != nil {
2022-12-30 14:24:24 +02:00
return err
}
return nil
}
2023-09-26 17:16:11 +02:00
func ( self * RefreshHelper ) loadWorktrees ( ) {
2022-09-01 20:25:41 +02:00
worktrees , err := self . c . Git ( ) . Loaders . Worktrees . GetWorktrees ( )
if err != nil {
2023-07-17 11:23:35 +02:00
self . c . Log . Error ( err )
self . c . Model ( ) . Worktrees = [ ] * models . Worktree { }
2022-09-01 20:25:41 +02:00
}
self . c . Model ( ) . Worktrees = worktrees
2023-09-26 17:16:11 +02:00
}
func ( self * RefreshHelper ) refreshWorktrees ( ) error {
self . loadWorktrees ( )
2022-09-01 20:25:41 +02:00
2023-07-29 09:30:40 +02:00
// need to refresh branches because the branches view shows worktrees against
// branches
2023-10-08 19:52:54 +02:00
if err := self . refreshView ( self . c . Contexts ( ) . Branches ) ; err != nil {
2023-07-29 09:30:40 +02:00
return err
}
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . Worktrees )
2022-09-01 20:25:41 +02:00
}
2022-12-30 14:24:24 +02:00
func ( self * RefreshHelper ) refreshStashEntries ( ) error {
2023-03-23 03:53:18 +02:00
self . c . Model ( ) . StashEntries = self . c . Git ( ) . Loaders . StashLoader .
2022-12-30 14:24:24 +02:00
GetStashEntries ( self . c . Modes ( ) . Filtering . GetPath ( ) )
2023-10-08 19:52:54 +02:00
return self . refreshView ( self . c . Contexts ( ) . Stash )
2022-12-30 14:24:24 +02:00
}
// never call this on its own, it should only be called from within refreshCommits()
func ( self * RefreshHelper ) refreshStatus ( ) {
self . c . Mutexes ( ) . RefreshingStatusMutex . Lock ( )
defer self . c . Mutexes ( ) . RefreshingStatusMutex . Unlock ( )
currentBranch := self . refsHelper . GetCheckedOutRef ( )
if currentBranch == nil {
// need to wait for branches to refresh
return
}
2023-03-23 03:53:18 +02:00
workingTreeState := self . c . Git ( ) . Status . WorkingTreeState ( )
2023-07-17 11:59:51 +02:00
linkedWorktreeName := self . worktreeHelper . GetLinkedWorktreeName ( )
2023-07-17 06:10:07 +02:00
2023-07-28 10:27:14 +02:00
repoName := self . c . Git ( ) . RepoPaths . RepoName ( )
2023-07-17 06:38:08 +02:00
2023-10-08 18:09:29 +02:00
status := presentation . FormatStatus ( repoName , currentBranch , types . ItemOperationNone , linkedWorktreeName , workingTreeState , self . c . Tr )
2022-12-30 14:24:24 +02:00
self . c . SetViewContent ( self . c . Views ( ) . Status , status )
}
func ( self * RefreshHelper ) refForLog ( ) string {
2023-03-23 03:53:18 +02:00
bisectInfo := self . c . Git ( ) . Bisect . GetInfo ( )
2022-12-30 14:24:24 +02:00
self . c . Model ( ) . BisectInfo = bisectInfo
if ! bisectInfo . Started ( ) {
return "HEAD"
}
// need to see if our bisect's current commit is reachable from our 'new' ref.
2023-03-23 03:53:18 +02:00
if bisectInfo . Bisecting ( ) && ! self . c . Git ( ) . Bisect . ReachableFromStart ( bisectInfo ) {
2022-12-30 14:24:24 +02:00
return bisectInfo . GetNewSha ( )
}
return bisectInfo . GetStartSha ( )
}
2023-10-08 19:52:54 +02:00
func ( self * RefreshHelper ) refreshView ( context types . Context ) error {
self . searchHelper . ReApplyFilter ( context )
return self . c . PostRefreshUpdate ( context )
}