1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-10 04:07:18 +02:00
lazygit/pkg/gui/controllers/helpers/refresh_helper.go

656 lines
20 KiB
Go
Raw Normal View History

package helpers
import (
"fmt"
"strings"
"sync"
2023-07-29 01:37:14 +02:00
"time"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
Use first class task objects instead of global counter The global counter approach is easy to understand but it's brittle and depends on implicit behaviour that is not very discoverable. With a global counter, if any goroutine accidentally decrements the counter twice, we'll think lazygit is idle when it's actually busy. Likewise if a goroutine accidentally increments the counter twice we'll think lazygit is busy when it's actually idle. With the new approach we have a map of tasks where each task can either be busy or not. We create a new task and add it to the map when we spawn a worker goroutine (among other things) and we remove it once the task is done. The task can also be paused and continued for situations where we switch back and forth between running a program and asking for user input. In order for this to work with `git push` (and other commands that require credentials) we need to obtain the task from gocui when we create the worker goroutine, and then pass it along to the commands package to pause/continue the task as required. This is MUCH more discoverable than the old approach which just decremented and incremented the global counter from within the commands package, but it's at the cost of expanding some function signatures (arguably a good thing). Likewise, whenever you want to call WithWaitingStatus or WithLoaderPanel the callback will now have access to the task for pausing/ continuing. We only need to actually make use of this functionality in a couple of places so it's a high price to pay, but I don't know if I want to introduce a WithWaitingStatusTask and WithLoaderPanelTask function (open to suggestions).
2023-07-09 03:32:27 +02:00
"github.com/jesseduffield/gocui"
"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"
)
type RefreshHelper struct {
c *HelperCommon
refsHelper *RefsHelper
mergeAndRebaseHelper *MergeAndRebaseHelper
patchBuildingHelper *PatchBuildingHelper
stagingHelper *StagingHelper
mergeConflictsHelper *MergeConflictsHelper
worktreeHelper *WorktreeHelper
fileWatcher types.IFileWatcher
}
func NewRefreshHelper(
c *HelperCommon,
refsHelper *RefsHelper,
mergeAndRebaseHelper *MergeAndRebaseHelper,
patchBuildingHelper *PatchBuildingHelper,
stagingHelper *StagingHelper,
mergeConflictsHelper *MergeConflictsHelper,
worktreeHelper *WorktreeHelper,
fileWatcher types.IFileWatcher,
) *RefreshHelper {
return &RefreshHelper{
c: c,
refsHelper: refsHelper,
mergeAndRebaseHelper: mergeAndRebaseHelper,
patchBuildingHelper: patchBuildingHelper,
stagingHelper: stagingHelper,
mergeConflictsHelper: mergeConflictsHelper,
worktreeHelper: worktreeHelper,
fileWatcher: fileWatcher,
}
}
func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
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)))
}()
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,
types.WORKTREES,
types.STATUS,
types.BISECT_INFO,
types.STAGING,
})
} else {
scopeSet = set.NewFromSlice(options.Scope)
}
wg := sync.WaitGroup{}
refresh := func(name string, f func()) {
if options.Mode == types.ASYNC {
self.c.OnWorker(func(t gocui.Task) {
Use first class task objects instead of global counter The global counter approach is easy to understand but it's brittle and depends on implicit behaviour that is not very discoverable. With a global counter, if any goroutine accidentally decrements the counter twice, we'll think lazygit is idle when it's actually busy. Likewise if a goroutine accidentally increments the counter twice we'll think lazygit is busy when it's actually idle. With the new approach we have a map of tasks where each task can either be busy or not. We create a new task and add it to the map when we spawn a worker goroutine (among other things) and we remove it once the task is done. The task can also be paused and continued for situations where we switch back and forth between running a program and asking for user input. In order for this to work with `git push` (and other commands that require credentials) we need to obtain the task from gocui when we create the worker goroutine, and then pass it along to the commands package to pause/continue the task as required. This is MUCH more discoverable than the old approach which just decremented and incremented the global counter from within the commands package, but it's at the cost of expanding some function signatures (arguably a good thing). Likewise, whenever you want to call WithWaitingStatus or WithLoaderPanel the callback will now have access to the task for pausing/ continuing. We only need to actually make use of this functionality in a couple of places so it's a high price to pay, but I don't know if I want to introduce a WithWaitingStatusTask and WithLoaderPanelTask function (open to suggestions).
2023-07-09 03:32:27 +02:00
f()
})
} else {
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)))
})
}
}
if scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) || scopeSet.Includes(types.REFLOG) || scopeSet.Includes(types.BISECT_INFO) {
// 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)
refresh("reflog and branches", self.refreshReflogAndBranches)
} 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
refresh("rebase commits", func() { _ = self.refreshRebaseCommits() })
}
if scopeSet.Includes(types.SUB_COMMITS) {
refresh("sub commits", func() { _ = self.refreshSubCommitsWithLimit() })
}
// 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) {
refresh("commit files", func() { _ = self.refreshCommitFilesContext() })
}
fileWg := sync.WaitGroup{}
if scopeSet.Includes(types.FILES) || scopeSet.Includes(types.SUBMODULES) {
fileWg.Add(1)
refresh("files", func() {
_ = self.refreshFilesAndSubmodules()
fileWg.Done()
})
}
if scopeSet.Includes(types.STASH) {
refresh("stash", func() { _ = self.refreshStashEntries() })
}
if scopeSet.Includes(types.TAGS) {
refresh("tags", func() { _ = self.refreshTags() })
}
if scopeSet.Includes(types.REMOTES) {
refresh("remotes", func() { _ = self.refreshRemotes() })
}
if scopeSet.Includes(types.WORKTREES) {
refresh("worktrees", func() { _ = self.refreshWorktrees() })
}
if scopeSet.Includes(types.STAGING) {
refresh("staging", func() {
fileWg.Wait()
_ = self.stagingHelper.RefreshStagingPanel(types.OnFocusOpts{})
})
}
if scopeSet.Includes(types.PATCH_BUILDING) {
refresh("patch building", func() { _ = self.patchBuildingHelper.RefreshPatchBuildingPanel(types.OnFocusOpts{}) })
}
if scopeSet.Includes(types.MERGE_CONFLICTS) || scopeSet.Includes(types.FILES) {
refresh("merge conflicts", func() { _ = self.mergeConflictsHelper.RefreshMergeState() })
}
self.refreshStatus()
if options.Then != nil {
options.Then()
}
wg.Wait()
}
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",
types.WORKTREES: "worktrees",
types.STATUS: "status",
types.BISECT_INFO: "bisect",
types.STAGING: "staging",
types.MERGE_CONFLICTS: "mergeConflicts",
}
return slices.Map(scopes, func(scope types.RefreshableView) string {
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:
self.c.OnWorker(func(_ gocui.Task) {
_ = self.refreshReflogCommits()
self.refreshBranches()
self.c.State().GetRepoState().SetStartupStage(types.COMPLETE)
})
case types.COMPLETE:
_ = self.refreshReflogCommits()
}
}
func (self *RefreshHelper) refreshReflogAndBranches() {
self.refreshReflogCommitsConsideringStartup()
self.refreshBranches()
}
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()
}
}
}
func (self *RefreshHelper) refreshCommitsWithLimit() error {
self.c.Mutexes().LocalCommitsMutex.Lock()
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
2023-03-23 03:53:18 +02:00
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
git_commands.GetCommitsOptions{
2023-03-23 03:53:18 +02:00
Limit: self.c.Contexts().LocalCommits.GetLimitCommits(),
FilterPath: self.c.Modes().Filtering.GetPath(),
IncludeRebaseCommits: true,
RefName: self.refForLog(),
2023-03-23 03:53:18 +02:00
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
},
)
if err != nil {
return err
}
self.c.Model().Commits = commits
self.RefreshAuthors(commits)
2023-03-23 03:53:18 +02:00
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
}
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(
git_commands.GetCommitsOptions{
2023-03-23 03:53:18 +02:00
Limit: self.c.Contexts().SubCommits.GetLimitCommits(),
FilterPath: self.c.Modes().Filtering.GetPath(),
IncludeRebaseCommits: false,
2023-03-23 03:53:18 +02:00
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
},
)
if err != nil {
return err
}
self.c.Model().SubCommits = commits
self.RefreshAuthors(commits)
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
}
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,
}
}
}
}
func (self *RefreshHelper) refreshCommitFilesContext() error {
2023-03-23 03:53:18 +02:00
ref := self.c.Contexts().CommitFiles.GetRef()
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)
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()
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().CommitFiles)
}
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)
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()
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
}
func (self *RefreshHelper) refreshTags() error {
2023-03-23 03:53:18 +02:00
tags, err := self.c.Git().Loaders.TagLoader.GetTags()
if err != nil {
return self.c.Error(err)
}
self.c.Model().Tags = tags
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().Tags)
}
func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
2023-03-23 03:53:18 +02:00
configs, err := self.c.Git().Submodule.GetConfigs()
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
func (self *RefreshHelper) refreshBranches() {
self.c.Mutexes().RefreshingBranchesMutex.Lock()
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
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, "")
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)
if err != nil {
_ = self.c.Error(err)
}
self.c.Model().Branches = branches
2023-03-23 03:53:18 +02:00
if err := self.c.PostRefreshUpdate(self.c.Contexts().Branches); err != nil {
self.c.Log.Error(err)
}
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-03-23 03:53:18 +02:00
if err := self.c.PostRefreshUpdate(self.c.Contexts().Submodules); err != nil {
self.c.Log.Error(err)
}
2023-03-23 03:53:18 +02:00
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
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
// 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 {
return self.c.Error(err)
}
}
2023-03-23 03:53:18 +02:00
files := self.c.Git().Loaders.FileLoader.
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 {
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)
}
} else if fileTreeViewModel.GetFilter() == filetree.DisplayConflicted {
2023-05-27 11:58:48 +02:00
fileTreeViewModel.SetStatusFilter(filetree.DisplayAll)
}
self.c.Model().Files = files
fileTreeViewModel.SetTree()
fileTreeViewModel.RWMutex.Unlock()
if err := self.fileWatcher.AddFilesToFileWatcher(files); err != nil {
return err
}
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.
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-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().ReflogCommits)
}
func (self *RefreshHelper) refreshRemotes() error {
2023-03-23 03:53:18 +02:00
prevSelectedRemote := self.c.Contexts().Remotes.GetSelected()
2023-03-23 03:53:18 +02:00
remotes, err := self.c.Git().Loaders.RemoteLoader.GetRemotes()
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-03-23 03:53:18 +02:00
if err := self.c.PostRefreshUpdate(self.c.Contexts().Remotes); err != nil {
return err
}
2023-03-23 03:53:18 +02:00
if err := self.c.PostRefreshUpdate(self.c.Contexts().RemoteBranches); err != nil {
return err
}
return nil
}
func (self *RefreshHelper) refreshWorktrees() error {
worktrees, err := self.c.Git().Loaders.Worktrees.GetWorktrees()
if err != nil {
self.c.Log.Error(err)
self.c.Model().Worktrees = []*models.Worktree{}
return nil
}
self.c.Model().Worktrees = worktrees
return self.c.PostRefreshUpdate(self.c.Contexts().Worktrees)
}
func (self *RefreshHelper) refreshStashEntries() error {
2023-03-23 03:53:18 +02:00
self.c.Model().StashEntries = self.c.Git().Loaders.StashLoader.
GetStashEntries(self.c.Modes().Filtering.GetPath())
2023-03-23 03:53:18 +02:00
return self.c.PostRefreshUpdate(self.c.Contexts().Stash)
}
// 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()
linkedWorktreeName := self.worktreeHelper.GetLinkedWorktreeName()
repoName := git_commands.GetCurrentRepoName()
status := presentation.FormatStatus(repoName, currentBranch, linkedWorktreeName, workingTreeState, self.c.Tr)
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()
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) {
return bisectInfo.GetNewSha()
}
return bisectInfo.GetStartSha()
}