mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-06-20 01:19:23 +02:00
Show commit merged/push statuses against branches
When viewing branches in an enlargened window, we now colour the commit shas based on whether the branch has been merged to a main branch and whether it's been pushed to the upstream. This isn't using the exact same code as we use for commits, so it's possible we'll get a colour mismatch when the user checks out a branch and views the commits in the commits view, but if we come across that situation we'll just need to fix it.
This commit is contained in:
@@ -47,6 +47,7 @@ type GitCommand struct {
|
||||
|
||||
type Loaders struct {
|
||||
BranchLoader *git_commands.BranchLoader
|
||||
MergedBranchLoader *git_commands.MergedBranchLoader
|
||||
CommitFileLoader *git_commands.CommitFileLoader
|
||||
CommitLoader *git_commands.CommitLoader
|
||||
FileLoader *git_commands.FileLoader
|
||||
@@ -162,6 +163,7 @@ func NewGitCommandAux(
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
mergedBranchLoader := git_commands.NewMergedBranchLoader(gitCommon)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
|
||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||
@@ -200,6 +202,7 @@ func NewGitCommandAux(
|
||||
Worktrees: worktreeLoader,
|
||||
StashLoader: stashLoader,
|
||||
TagLoader: tagLoader,
|
||||
MergedBranchLoader: mergedBranchLoader,
|
||||
},
|
||||
RepoPaths: repoPaths,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// Returns a set of branch names which are merged into a main branch.
|
||||
// This
|
||||
type MergedBranchLoader struct {
|
||||
c *GitCommon
|
||||
}
|
||||
|
||||
func NewMergedBranchLoader(c *GitCommon) *MergedBranchLoader {
|
||||
return &MergedBranchLoader{c: c}
|
||||
}
|
||||
|
||||
// TODO: check against upstreams, and share code with commit loader that determines main branches
|
||||
func (self *MergedBranchLoader) Load() *set.Set[string] {
|
||||
set := set.New[string]()
|
||||
|
||||
mainBranches := self.c.UserConfig.Git.MainBranches
|
||||
results := utils.ConcurrentMap(mainBranches, func(mainBranch string) []string {
|
||||
mergedBranches, err := self.GetMergedBranches(mainBranch)
|
||||
if err != nil {
|
||||
self.c.Log.Warnf("Failed to get merged branches for %s: %s", mainBranch, err)
|
||||
return nil
|
||||
}
|
||||
return mergedBranches
|
||||
})
|
||||
|
||||
for i := 0; i < len(mainBranches); i++ {
|
||||
for _, mergedBranch := range results[i] {
|
||||
set.Add(mergedBranch)
|
||||
}
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (self *MergedBranchLoader) GetMergedBranches(mainBranch string) ([]string, error) {
|
||||
// git for-each-ref --merged master --format '%(refname)' refs/heads/
|
||||
cmdArgs := NewGitCmd("for-each-ref").Arg(
|
||||
"--merged",
|
||||
mainBranch,
|
||||
"--format",
|
||||
"%(refname)",
|
||||
"refs/heads/",
|
||||
).ToArgv()
|
||||
|
||||
output, err := self.c.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := utils.SplitLines(output)
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
@@ -33,6 +33,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
c.Tr,
|
||||
c.UserConfig,
|
||||
c.Model().Worktrees,
|
||||
c.Model().MergedBranches,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -429,25 +429,43 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool) {
|
||||
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
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
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
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Model().Branches = branches
|
||||
self.c.Model().Branches = branches
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
mergedBranches := self.c.Git().Loaders.MergedBranchLoader.Load()
|
||||
|
||||
self.c.Model().MergedBranches = mergedBranches
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if refreshWorktrees {
|
||||
self.loadWorktrees()
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
@@ -384,6 +385,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
BisectInfo: git_commands.NewNullBisectInfo(),
|
||||
FilesTrie: patricia.NewTrie(),
|
||||
Authors: map[string]*models.Author{},
|
||||
MergedBranches: set.New[string](),
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath),
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@@ -26,10 +27,11 @@ func GetBranchListDisplayStrings(
|
||||
tr *i18n.TranslationSet,
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
mergedBranches *set.Set[string],
|
||||
) [][]string {
|
||||
return lo.Map(branches, func(branch *models.Branch, _ int) []string {
|
||||
diffed := branch.Name == diffName
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, tr, userConfig, worktrees)
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, tr, userConfig, worktrees, mergedBranches)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,6 +44,7 @@ func getBranchDisplayStrings(
|
||||
tr *i18n.TranslationSet,
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
mergedBranches *set.Set[string],
|
||||
) []string {
|
||||
displayName := b.Name
|
||||
if b.DisplayName != "" {
|
||||
@@ -74,7 +77,15 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
|
||||
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
|
||||
res = append(res, utils.ShortSha(b.CommitHash))
|
||||
var commitStyle style.TextStyle
|
||||
if b.HasCommitsToPush() {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusUnpushed)
|
||||
} else if mergedBranches.Includes(b.FullRefName()) {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusMerged)
|
||||
} else {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusPushed)
|
||||
}
|
||||
res = append(res, commitStyle.Sprint(utils.ShortSha(b.CommitHash)))
|
||||
}
|
||||
|
||||
res = append(res, coloredName)
|
||||
|
||||
@@ -413,20 +413,8 @@ func getShaColor(
|
||||
}
|
||||
|
||||
diffed := commit.Sha != "" && commit.Sha == diffName
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch commit.Status {
|
||||
case models.StatusUnpushed:
|
||||
shaColor = style.FgRed
|
||||
case models.StatusPushed:
|
||||
shaColor = style.FgYellow
|
||||
case models.StatusMerged:
|
||||
shaColor = style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
shaColor = style.FgBlue
|
||||
case models.StatusReflog:
|
||||
shaColor = style.FgBlue
|
||||
default:
|
||||
}
|
||||
|
||||
shaColor := getShaStyleFromStatus(commit.Status)
|
||||
|
||||
if diffed {
|
||||
shaColor = theme.DiffTerminalColor
|
||||
@@ -439,6 +427,23 @@ func getShaColor(
|
||||
return shaColor
|
||||
}
|
||||
|
||||
func getShaStyleFromStatus(status models.CommitStatus) style.TextStyle {
|
||||
switch status {
|
||||
case models.StatusUnpushed:
|
||||
return style.FgRed
|
||||
case models.StatusPushed:
|
||||
return style.FgYellow
|
||||
case models.StatusMerged:
|
||||
return style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
return style.FgBlue
|
||||
case models.StatusReflog:
|
||||
return style.FgBlue
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
func actionColorMap(action todo.TodoCommand) style.TextStyle {
|
||||
switch action {
|
||||
case todo.Pick:
|
||||
|
||||
+12
-9
@@ -1,6 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -223,15 +224,17 @@ type MenuItem struct {
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
// set of branch names of the form 'refs/heads/mybranch'
|
||||
MergedBranches *set.Set[string]
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// when in filtering mode we only include the ones that match the given path
|
||||
|
||||
Reference in New Issue
Block a user