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
showListFooter: true # for seeing the '5 of 20' message in list panels
showRandomTip: true
showBranchCommitHash: false # show commit hashes alongside branch names
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)
showCommandLog: true

View File

@ -128,7 +128,7 @@ func NewGitCommandAux(
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
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)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode)
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()
}
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 {
FastForwardOnly bool
}

View File

@ -1,6 +1,7 @@
package git_commands
import (
"fmt"
"regexp"
"strings"
@ -8,8 +9,10 @@ import (
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/go-git/v5/config"
"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/utils"
"github.com/samber/lo"
)
// context:
@ -36,20 +39,20 @@ type BranchInfo struct {
// BranchLoader returns a list of Branch objects for the current repo
type BranchLoader struct {
*common.Common
getRawBranches func() (string, error)
cmd oscommands.ICmdObjBuilder
getCurrentBranchInfo func() (BranchInfo, error)
config BranchLoaderConfigCommands
}
func NewBranchLoader(
cmn *common.Common,
getRawBranches func() (string, error),
cmd oscommands.ICmdObjBuilder,
getCurrentBranchInfo func() (BranchInfo, error),
config BranchLoaderConfigCommands,
) *BranchLoader {
return &BranchLoader{
Common: cmn,
getRawBranches: getRawBranches,
cmd: cmd,
getCurrentBranchInfo: getCurrentBranchInfo,
config: config,
}
@ -128,8 +131,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
}
split := strings.Split(line, "\x00")
if len(split) != 4 {
// Ignore line if it isn't separated into 4 parts
if len(split) != len(branchFields) {
// Ignore line if it isn't separated into the expected number of parts
// This is probably a warning message, for more info see:
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
return nil, false
@ -139,47 +142,81 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
})
}
// Obtain branch information from parsed line output of getRawBranches()
// split contains the '|' separated tokens in the line of output
func obtainBranch(split []string) *models.Branch {
name := strings.TrimPrefix(split[1], "heads/")
branch := &models.Branch{
Name: name,
Pullables: "?",
Pushables: "?",
Head: split[0] == "*",
}
func (self *BranchLoader) getRawBranches() (string, error) {
format := strings.Join(
lo.Map(branchFields, func(thing string, _ int) string {
return "%(" + thing + ")"
}),
"%00",
)
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]
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 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
// how many commits ahead/behind it is
return branch
return "?", "?", false
}
track := split[3]
if track == "[gone]" {
branch.UpstreamGone = 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 "?", "?", true
}
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

View File

@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestObtainBanch(t *testing.T) {
func TestObtainBranch(t *testing.T) {
type scenario struct {
testName string
input []string
@ -17,29 +17,65 @@ func TestObtainBanch(t *testing.T) {
scenarios := []scenario{
{
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "NoUpstream",
input: []string{"", "a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
testName: "NoUpstream",
input: []string{"", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsHead",
input: []string{"*", "a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: true},
testName: "IsHead",
input: []string{"*", "a_branch", "", "", "subject", "123"},
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "3", Pullables: "2", Head: false},
testName: "IsBehindAndAhead",
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,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]"},
expectedBranch: &models.Branch{Name: "a_branch", UpstreamGone: true, Pushables: "?", Pullables: "?", Head: false},
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
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 {
Name string
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
DisplayName string
Recency string
Pushables string
Pullables string
DisplayName string
// indicator of when the branch was last checked out e.g. '2d', '3m'
Recency 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
// whether this is the current branch. Exactly one branch should have this be true
Head bool
DetachedHead bool
// 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'
UpstreamRemote string
UpstreamBranch string
// subject line in commit message
Subject string
// commit hash
CommitHash string
}
func (b *Branch) FullRefName() string {

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import (
"github.com/jesseduffield/generics/slices"
"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/style"
"github.com/jesseduffield/lazygit/pkg/i18n"
@ -15,15 +16,27 @@ import (
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 {
diffed := branch.Name == diffName
return getBranchDisplayStrings(branch, fullDescription, diffed, tr)
return getBranchDisplayStrings(branch, fullDescription, diffed, tr, userConfig)
})
}
// 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
if b.DisplayName != "" {
displayName = b.DisplayName
@ -43,12 +56,18 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
recencyColor = style.FgGreen
}
res := make([]string, 0, 4)
res := make([]string, 0, 6)
res = append(res, recencyColor.Sprint(b.Recency))
if icons.IsIconEnabled() {
res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b)))
}
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
res = append(res, b.CommitHash)
}
res = append(res, coloredName)
if fullDescription {
res = append(
res,
@ -56,6 +75,7 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
style.FgYellow.Sprint(b.UpstreamRemote),
style.FgYellow.Sprint(b.UpstreamBranch),
),
utils.TruncateWithEllipsis(b.Subject, 60),
)
}
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 {
if len(sha) < 8 {
if len(sha) < COMMIT_HASH_SHORT_SIZE {
return sha
}
return sha[:8]
return sha[:COMMIT_HASH_SHORT_SIZE]
}