1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-08 04:04:22 +02:00

Merge pull request #2274 from jesseduffield/show-commit-against-branch

This commit is contained in:
Jesse Duffield 2023-06-01 19:25:00 +10:00 committed by GitHub
commit caab31ff38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 170 additions and 72 deletions

View File

@ -58,6 +58,7 @@ gui:
showFileTree: true # for rendering changes files in a tree format showFileTree: true # for rendering changes files in a tree format
showListFooter: true # for seeing the '5 of 20' message in list panels showListFooter: true # for seeing the '5 of 20' message in list panels
showRandomTip: true showRandomTip: true
showBranchCommitHash: false # show commit hashes alongside branch names
experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you) showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
showCommandLog: true showCommandLog: true

View File

@ -128,7 +128,7 @@ func NewGitCommandAux(
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder) patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
bisectCommands := git_commands.NewBisectCommands(gitCommon) bisectCommands := git_commands.NewBisectCommands(gitCommon)
branchLoader := git_commands.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchInfo, configCommands) branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd) commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode) commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode)
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd) reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)

View File

@ -188,16 +188,6 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
return self.cmd.New(cmdArgs).Run() return self.cmd.New(cmdArgs).Run()
} }
func (self *BranchCommands) GetRawBranches() (string, error) {
cmdArgs := NewGitCmd("for-each-ref").
Arg("--sort=-committerdate").
Arg(`--format=%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)`).
Arg("refs/heads").
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
type MergeOpts struct { type MergeOpts struct {
FastForwardOnly bool FastForwardOnly bool
} }

View File

@ -1,6 +1,7 @@
package git_commands package git_commands
import ( import (
"fmt"
"regexp" "regexp"
"strings" "strings"
@ -8,8 +9,10 @@ import (
"github.com/jesseduffield/generics/slices" "github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/go-git/v5/config" "github.com/jesseduffield/go-git/v5/config"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
) )
// context: // context:
@ -36,20 +39,20 @@ type BranchInfo struct {
// BranchLoader returns a list of Branch objects for the current repo // BranchLoader returns a list of Branch objects for the current repo
type BranchLoader struct { type BranchLoader struct {
*common.Common *common.Common
getRawBranches func() (string, error) cmd oscommands.ICmdObjBuilder
getCurrentBranchInfo func() (BranchInfo, error) getCurrentBranchInfo func() (BranchInfo, error)
config BranchLoaderConfigCommands config BranchLoaderConfigCommands
} }
func NewBranchLoader( func NewBranchLoader(
cmn *common.Common, cmn *common.Common,
getRawBranches func() (string, error), cmd oscommands.ICmdObjBuilder,
getCurrentBranchInfo func() (BranchInfo, error), getCurrentBranchInfo func() (BranchInfo, error),
config BranchLoaderConfigCommands, config BranchLoaderConfigCommands,
) *BranchLoader { ) *BranchLoader {
return &BranchLoader{ return &BranchLoader{
Common: cmn, Common: cmn,
getRawBranches: getRawBranches, cmd: cmd,
getCurrentBranchInfo: getCurrentBranchInfo, getCurrentBranchInfo: getCurrentBranchInfo,
config: config, config: config,
} }
@ -128,8 +131,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
} }
split := strings.Split(line, "\x00") split := strings.Split(line, "\x00")
if len(split) != 4 { if len(split) != len(branchFields) {
// Ignore line if it isn't separated into 4 parts // Ignore line if it isn't separated into the expected number of parts
// This is probably a warning message, for more info see: // This is probably a warning message, for more info see:
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439 // https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
return nil, false return nil, false
@ -139,47 +142,81 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
}) })
} }
// Obtain branch information from parsed line output of getRawBranches() func (self *BranchLoader) getRawBranches() (string, error) {
// split contains the '|' separated tokens in the line of output format := strings.Join(
func obtainBranch(split []string) *models.Branch { lo.Map(branchFields, func(thing string, _ int) string {
name := strings.TrimPrefix(split[1], "heads/") return "%(" + thing + ")"
branch := &models.Branch{ }),
Name: name, "%00",
Pullables: "?", )
Pushables: "?",
Head: split[0] == "*",
}
cmdArgs := NewGitCmd("for-each-ref").
Arg("--sort=-committerdate").
Arg(fmt.Sprintf("--format=%s", format)).
Arg("refs/heads").
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
var branchFields = []string{
"HEAD",
"refname:short",
"upstream:short",
"upstream:track",
"subject",
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
}
// Obtain branch information from parsed line output of getRawBranches()
func obtainBranch(split []string) *models.Branch {
headMarker := split[0]
fullName := split[1]
upstreamName := split[2] upstreamName := split[2]
track := split[3]
subject := split[4]
commitHash := split[5]
name := strings.TrimPrefix(fullName, "heads/")
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
return &models.Branch{
Name: name,
Pushables: pushables,
Pullables: pullables,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
}
}
func parseUpstreamInfo(upstreamName string, track string) (string, string, bool) {
if upstreamName == "" { if upstreamName == "" {
// if we're here then it means we do not have a local version of the remote. // if we're here then it means we do not have a local version of the remote.
// The branch might still be tracking a remote though, we just don't know // The branch might still be tracking a remote though, we just don't know
// how many commits ahead/behind it is // how many commits ahead/behind it is
return branch return "?", "?", false
} }
track := split[3]
if track == "[gone]" { if track == "[gone]" {
branch.UpstreamGone = true return "?", "?", true
} else {
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pushables = match[1]
} else {
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pullables = match[1]
} else {
branch.Pullables = "0"
}
} }
return branch pushables := parseDifference(track, `ahead (\d+)`)
pullables := parseDifference(track, `behind (\d+)`)
return pushables, pullables, false
}
func parseDifference(track string, regexStr string) string {
re := regexp.MustCompile(regexStr)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
return match[1]
} else {
return "0"
}
} }
// TODO: only look at the new reflog commits, and otherwise store the recencies in // TODO: only look at the new reflog commits, and otherwise store the recencies in

View File

@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestObtainBanch(t *testing.T) { func TestObtainBranch(t *testing.T) {
type scenario struct { type scenario struct {
testName string testName string
input []string input []string
@ -17,29 +17,65 @@ func TestObtainBanch(t *testing.T) {
scenarios := []scenario{ scenarios := []scenario{
{ {
testName: "TrimHeads", testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", ""}, input: []string{"", "heads/a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false}, expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
}, },
{ {
testName: "NoUpstream", testName: "NoUpstream",
input: []string{"", "a_branch", "", ""}, input: []string{"", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false}, expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
}, },
{ {
testName: "IsHead", testName: "IsHead",
input: []string{"*", "a_branch", "", ""}, input: []string{"*", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: true}, expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
},
}, },
{ {
testName: "IsBehindAndAhead", testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]"}, input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "3", Pullables: "2", Head: false}, expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "3",
Pullables: "2",
Head: false,
Subject: "subject",
CommitHash: "123",
},
}, },
{ {
testName: "RemoteBranchIsGone", testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]"}, input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
expectedBranch: &models.Branch{Name: "a_branch", UpstreamGone: true, Pushables: "?", Pullables: "?", Head: false}, expectedBranch: &models.Branch{
Name: "a_branch",
UpstreamGone: true,
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
}, },
} }

View File

@ -5,11 +5,16 @@ package models
type Branch struct { type Branch struct {
Name string Name string
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf' // the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
DisplayName string DisplayName string
Recency string // indicator of when the branch was last checked out e.g. '2d', '3m'
Pushables string Recency string
Pullables string // how many commits ahead we are from the remote branch (how many commits we can push)
Pushables string
// how many commits behind we are from the remote branch (how many commits we can pull)
Pullables string
// whether the remote branch is 'gone' i.e. we're tracking a remote branch that has been deleted
UpstreamGone bool UpstreamGone bool
// whether this is the current branch. Exactly one branch should have this be true
Head bool Head bool
DetachedHead bool DetachedHead bool
// if we have a named remote locally this will be the name of that remote e.g. // if we have a named remote locally this will be the name of that remote e.g.
@ -17,6 +22,10 @@ type Branch struct {
// 'git@github.com:tiwood/lazygit.git' // 'git@github.com:tiwood/lazygit.git'
UpstreamRemote string UpstreamRemote string
UpstreamBranch string UpstreamBranch string
// subject line in commit message
Subject string
// commit hash
CommitHash string
} }
func (b *Branch) FullRefName() string { func (b *Branch) FullRefName() string {

View File

@ -49,6 +49,7 @@ type GuiConfig struct {
ShowCommandLog bool `yaml:"showCommandLog"` ShowCommandLog bool `yaml:"showCommandLog"`
ShowBottomLine bool `yaml:"showBottomLine"` ShowBottomLine bool `yaml:"showBottomLine"`
ShowIcons bool `yaml:"showIcons"` ShowIcons bool `yaml:"showIcons"`
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"` ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
CommandLogSize int `yaml:"commandLogSize"` CommandLogSize int `yaml:"commandLogSize"`
SplitDiff string `yaml:"splitDiff"` SplitDiff string `yaml:"splitDiff"`
@ -426,6 +427,7 @@ func GetDefaultConfig() *UserConfig {
ShowRandomTip: true, ShowRandomTip: true,
ShowIcons: false, ShowIcons: false,
ExperimentalShowBranchHeads: false, ExperimentalShowBranchHeads: false,
ShowBranchCommitHash: false,
CommandLogSize: 8, CommandLogSize: 8,
SplitDiff: "auto", SplitDiff: "auto",
SkipRewordInEditorWarning: false, SkipRewordInEditorWarning: false,

View File

@ -25,6 +25,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().Diffing.Ref, c.Modes().Diffing.Ref,
c.Tr, c.Tr,
c.UserConfig,
) )
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/jesseduffield/generics/slices" "github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/i18n"
@ -15,15 +16,27 @@ import (
var branchPrefixColorCache = make(map[string]style.TextStyle) var branchPrefixColorCache = make(map[string]style.TextStyle)
func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string, tr *i18n.TranslationSet) [][]string { func GetBranchListDisplayStrings(
branches []*models.Branch,
fullDescription bool,
diffName string,
tr *i18n.TranslationSet,
userConfig *config.UserConfig,
) [][]string {
return slices.Map(branches, func(branch *models.Branch) []string { return slices.Map(branches, func(branch *models.Branch) []string {
diffed := branch.Name == diffName diffed := branch.Name == diffName
return getBranchDisplayStrings(branch, fullDescription, diffed, tr) return getBranchDisplayStrings(branch, fullDescription, diffed, tr, userConfig)
}) })
} }
// getBranchDisplayStrings returns the display string of branch // getBranchDisplayStrings returns the display string of branch
func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool, tr *i18n.TranslationSet) []string { func getBranchDisplayStrings(
b *models.Branch,
fullDescription bool,
diffed bool,
tr *i18n.TranslationSet,
userConfig *config.UserConfig,
) []string {
displayName := b.Name displayName := b.Name
if b.DisplayName != "" { if b.DisplayName != "" {
displayName = b.DisplayName displayName = b.DisplayName
@ -43,12 +56,18 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
recencyColor = style.FgGreen recencyColor = style.FgGreen
} }
res := make([]string, 0, 4) res := make([]string, 0, 6)
res = append(res, recencyColor.Sprint(b.Recency)) res = append(res, recencyColor.Sprint(b.Recency))
if icons.IsIconEnabled() { if icons.IsIconEnabled() {
res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b))) res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b)))
} }
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
res = append(res, b.CommitHash)
}
res = append(res, coloredName) res = append(res, coloredName)
if fullDescription { if fullDescription {
res = append( res = append(
res, res,
@ -56,6 +75,7 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
style.FgYellow.Sprint(b.UpstreamRemote), style.FgYellow.Sprint(b.UpstreamRemote),
style.FgYellow.Sprint(b.UpstreamBranch), style.FgYellow.Sprint(b.UpstreamBranch),
), ),
utils.TruncateWithEllipsis(b.Subject, 60),
) )
} }
return res return res

View File

@ -149,9 +149,11 @@ func SafeTruncate(str string, limit int) string {
} }
} }
const COMMIT_HASH_SHORT_SIZE = 8
func ShortSha(sha string) string { func ShortSha(sha string) string {
if len(sha) < 8 { if len(sha) < COMMIT_HASH_SHORT_SIZE {
return sha return sha
} }
return sha[:8] return sha[:COMMIT_HASH_SHORT_SIZE]
} }