mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Merge 4a0446f9e0 into ea91639546
This commit is contained in:
commit
83b9c1bb15
|
|
@ -288,6 +288,11 @@ gui:
|
|||
# If true, show commit hashes alongside branch names in the branches view.
|
||||
showBranchCommitHash: false
|
||||
|
||||
# If true, show GPG signature verification status for each commit in the
|
||||
# commits view. This can slow down commit loading as it requires GPG
|
||||
# verification.
|
||||
showGpgSigningStatus: false
|
||||
|
||||
# Whether to show the divergence from the base branch in the branches view.
|
||||
# One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
|
||||
showDivergenceFromBaseBranch: none
|
||||
|
|
|
|||
|
|
@ -91,8 +91,9 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
|||
defer wg.Done()
|
||||
|
||||
var realCommits []*models.Commit
|
||||
showGpg := self.UserConfig().Gui.ShowGpgSigningStatus
|
||||
realCommits, logErr = loadCommits(self.getLogCmd(opts), opts.FilterPath, func(line string) (*models.Commit, bool) {
|
||||
return self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != ""), false
|
||||
return self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != "", showGpg), false
|
||||
})
|
||||
if logErr == nil {
|
||||
commits = append(commits, realCommits...)
|
||||
|
|
@ -189,12 +190,16 @@ func (self *CommitLoader) MergeRebasingCommits(hashPool *utils.StringPool, commi
|
|||
// then puts them into a commit object
|
||||
// example input:
|
||||
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
||||
func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line string, showDivergence bool) *models.Commit {
|
||||
split := strings.SplitN(line, "\x00", 8)
|
||||
func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line string, showDivergence bool, showGpg bool) *models.Commit {
|
||||
numFields := 8
|
||||
if showGpg {
|
||||
numFields = 9
|
||||
}
|
||||
split := strings.SplitN(line, "\x00", numFields)
|
||||
|
||||
// Ensure we have the minimum required fields (at least 7 for basic functionality)
|
||||
if len(split) < 7 {
|
||||
self.Log.Warnf("Malformed git log line: expected at least 7 fields, got %d. Line: %s", len(split), line)
|
||||
minFields := 7
|
||||
if len(split) < minFields {
|
||||
self.Log.Warnf("Malformed git log line: expected at least %d fields, got %d. Line: %s", minFields, len(split), line)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -209,10 +214,18 @@ func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line
|
|||
}
|
||||
extraInfo := strings.TrimSpace(split[6])
|
||||
|
||||
// message (and the \x00 before it) might not be present if extraInfo is extremely long
|
||||
gpgStatus := ""
|
||||
messageIdx := 7
|
||||
if showGpg {
|
||||
if len(split) > 7 {
|
||||
gpgStatus = split[7]
|
||||
}
|
||||
messageIdx = 8
|
||||
}
|
||||
|
||||
message := ""
|
||||
if len(split) > 7 {
|
||||
message = split[7]
|
||||
if len(split) > messageIdx {
|
||||
message = split[messageIdx]
|
||||
}
|
||||
|
||||
var tags []string
|
||||
|
|
@ -248,6 +261,7 @@ func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line
|
|||
AuthorEmail: authorEmail,
|
||||
Parents: parents,
|
||||
Divergence: divergence,
|
||||
GpgStatus: gpgStatus,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -285,10 +299,16 @@ func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, tod
|
|||
|
||||
// note that we're not filtering these as we do non-rebasing commits just because
|
||||
// I suspect that will cause some damage
|
||||
showGpg := self.UserConfig().Gui.ShowGpgSigningStatus
|
||||
format := prettyFormat
|
||||
if showGpg {
|
||||
format = prettyFormatWithGpg
|
||||
}
|
||||
|
||||
cmdObj := self.cmd.New(
|
||||
NewGitCmd("show").
|
||||
Config("log.showSignature=false").
|
||||
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
|
||||
Arg("--no-patch", "--oneline", "--abbrev=20", format).
|
||||
Arg(commitHashes...).
|
||||
ToArgv(),
|
||||
).DontLog()
|
||||
|
|
@ -298,7 +318,10 @@ func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, tod
|
|||
if line == "" || line[0] != '+' {
|
||||
return false, nil
|
||||
}
|
||||
commit := self.extractCommitFromLine(hashPool, line[1:], false)
|
||||
commit := self.extractCommitFromLine(hashPool, line[1:], false, showGpg)
|
||||
if commit == nil {
|
||||
return false, nil
|
||||
}
|
||||
fullCommits[commit.Hash()] = commit
|
||||
return false, nil
|
||||
})
|
||||
|
|
@ -586,12 +609,18 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
|
|||
refSpec += "..." + opts.RefToShowDivergenceFrom
|
||||
}
|
||||
|
||||
showGpg := self.UserConfig().Gui.ShowGpgSigningStatus
|
||||
format := prettyFormat
|
||||
if showGpg {
|
||||
format = prettyFormatWithGpg
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg(refSpec).
|
||||
ArgIf(gitLogOrder != "default", "--"+gitLogOrder).
|
||||
ArgIf(opts.All, "--all").
|
||||
Arg("--oneline").
|
||||
Arg(prettyFormat).
|
||||
Arg(format).
|
||||
Arg("--abbrev=40").
|
||||
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
|
||||
ArgIf(opts.Limit, "-300").
|
||||
|
|
@ -606,3 +635,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
|
|||
}
|
||||
|
||||
const prettyFormat = `--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%s`
|
||||
const prettyFormatWithGpg = `--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%G?%x00%s`
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ var commitsOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18db
|
|||
|
||||
var singleCommitOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|b21997d6b4cbdf84b149|>|HEAD -> better-tests|better typing for rebase mode`, "|", "\x00")
|
||||
|
||||
var singleCommitOutputWithGpg = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|b21997d6b4cbdf84b149|>|HEAD -> better-tests|G|better typing for rebase mode`, "|", "\x00")
|
||||
|
||||
func TestGetCommits(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
|
|
@ -34,6 +36,7 @@ func TestGetCommits(t *testing.T) {
|
|||
expectedCommitOpts []models.NewCommitOpts
|
||||
expectedError error
|
||||
logOrder string
|
||||
showGpg bool
|
||||
opts GetCommitsOptions
|
||||
mainBranches []string
|
||||
}
|
||||
|
|
@ -61,6 +64,30 @@ func TestGetCommits(t *testing.T) {
|
|||
expectedCommitOpts: []models.NewCommitOpts{},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should return commits with gpg status when enabled",
|
||||
logOrder: "topo-order",
|
||||
showGpg: true,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: &models.Branch{Name: "mybranch"}, IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"rev-list", "refs/heads/mybranch", "^mybranch@{u}"}, "0eea75e8c631fba6b58135697835d58ba4c18dbc\n", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%P%x00%m%x00%D%x00%G?%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutputWithGpg, nil),
|
||||
|
||||
expectedCommitOpts: []models.NewCommitOpts{{
|
||||
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||
Name: "better typing for rebase mode",
|
||||
Status: models.StatusUnpushed,
|
||||
Action: models.ActionNone,
|
||||
Tags: nil,
|
||||
ExtraInfo: "(HEAD -> better-tests)",
|
||||
AuthorName: "Jesse Duffield",
|
||||
AuthorEmail: "jessedduffield@gmail.com",
|
||||
UnixTimestamp: 1640826609,
|
||||
Parents: []string{"b21997d6b4cbdf84b149"},
|
||||
GpgStatus: "G",
|
||||
}},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should return commits if they are present",
|
||||
logOrder: "topo-order",
|
||||
|
|
@ -300,6 +327,7 @@ func TestGetCommits(t *testing.T) {
|
|||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
common := common.NewDummyCommon()
|
||||
common.UserConfig().Git.Log.Order = scenario.logOrder
|
||||
common.UserConfig().Gui.ShowGpgSigningStatus = scenario.showGpg
|
||||
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
|
||||
|
||||
builder := &CommitLoader{
|
||||
|
|
@ -333,6 +361,42 @@ func TestGetCommits(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCommitLoader_getHydratedTodoCommitsWithGpgStatus(t *testing.T) {
|
||||
common := common.NewDummyCommon()
|
||||
common.UserConfig().Gui.ShowGpgSigningStatus = true
|
||||
runner := oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "show", "--no-patch", "--oneline", "--abbrev=20", prettyFormatWithGpg, "0eea75e8c631fba6b58135697835d58ba4c18dbc"}, singleCommitOutputWithGpg, nil)
|
||||
|
||||
hashPool := &utils.StringPool{}
|
||||
loader := &CommitLoader{
|
||||
Common: common,
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(runner),
|
||||
}
|
||||
|
||||
todoCommits := []*models.Commit{models.NewCommit(hashPool, models.NewCommitOpts{
|
||||
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||
Status: models.StatusRebasing,
|
||||
Action: todo.Pick,
|
||||
})}
|
||||
|
||||
commits, err := loader.getHydratedTodoCommits(hashPool, todoCommits, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []*models.Commit{models.NewCommit(hashPool, models.NewCommitOpts{
|
||||
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||
Name: "better typing for rebase mode",
|
||||
Status: models.StatusRebasing,
|
||||
Action: todo.Pick,
|
||||
ExtraInfo: "(HEAD -> better-tests)",
|
||||
AuthorName: "Jesse Duffield",
|
||||
AuthorEmail: "jessedduffield@gmail.com",
|
||||
UnixTimestamp: 1640826609,
|
||||
Parents: []string{"b21997d6b4cbdf84b149"},
|
||||
GpgStatus: "G",
|
||||
})}, commits)
|
||||
runner.CheckForMissingCalls()
|
||||
}
|
||||
|
||||
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||
hashPool := &utils.StringPool{}
|
||||
|
||||
|
|
@ -605,6 +669,7 @@ func TestCommitLoader_extractCommitFromLine(t *testing.T) {
|
|||
testName string
|
||||
line string
|
||||
showDivergence bool
|
||||
showGpg bool
|
||||
expectedCommit *models.Commit
|
||||
}{
|
||||
{
|
||||
|
|
@ -759,6 +824,24 @@ func TestCommitLoader_extractCommitFromLine(t *testing.T) {
|
|||
Divergence: models.DivergenceLeft,
|
||||
}),
|
||||
},
|
||||
{
|
||||
testName: "valid line with gpg status",
|
||||
line: "hash\x00timestamp\x00author\x00email\x00parents\x00<\x00extraInfo\x00G\x00message",
|
||||
showDivergence: true,
|
||||
showGpg: true,
|
||||
expectedCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||
Hash: "hash",
|
||||
Name: "message",
|
||||
Tags: nil,
|
||||
ExtraInfo: "(extraInfo)",
|
||||
UnixTimestamp: 0,
|
||||
AuthorName: "author",
|
||||
AuthorEmail: "email",
|
||||
Parents: []string{"parents"},
|
||||
Divergence: models.DivergenceLeft,
|
||||
GpgStatus: "G",
|
||||
}),
|
||||
},
|
||||
{
|
||||
testName: "empty line",
|
||||
line: "",
|
||||
|
|
@ -785,7 +868,7 @@ func TestCommitLoader_extractCommitFromLine(t *testing.T) {
|
|||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
result := loader.extractCommitFromLine(hashPool, scenario.line, scenario.showDivergence)
|
||||
result := loader.extractCommitFromLine(hashPool, scenario.line, scenario.showDivergence, scenario.showGpg)
|
||||
if scenario.expectedCommit == nil {
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ type Commit struct {
|
|||
Action todo.TodoCommand
|
||||
ActionFlag string // e.g. "-C" for fixup -C
|
||||
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
|
||||
|
||||
// GPG signature status: "G" good, "B" bad, "U" unknown validity,
|
||||
// "X" expired, "Y" expired key, "R" revoked, "E" missing key, "N" none
|
||||
GpgStatus string
|
||||
}
|
||||
|
||||
type NewCommitOpts struct {
|
||||
|
|
@ -77,6 +81,7 @@ type NewCommitOpts struct {
|
|||
UnixTimestamp int64
|
||||
Divergence Divergence
|
||||
Parents []string
|
||||
GpgStatus string
|
||||
}
|
||||
|
||||
func NewCommit(hashPool *utils.StringPool, opts NewCommitOpts) *Commit {
|
||||
|
|
@ -93,6 +98,7 @@ func NewCommit(hashPool *utils.StringPool, opts NewCommitOpts) *Commit {
|
|||
UnixTimestamp: opts.UnixTimestamp,
|
||||
Divergence: opts.Divergence,
|
||||
parents: lo.Map(opts.Parents, func(s string, _ int) *string { return hashPool.Add(s) }),
|
||||
GpgStatus: opts.GpgStatus,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ type GuiConfig struct {
|
|||
CommitHashLength int `yaml:"commitHashLength" jsonschema:"minimum=0"`
|
||||
// If true, show commit hashes alongside branch names in the branches view.
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
// If true, show GPG signature verification status for each commit in the commits view. This can slow down commit loading as it requires GPG verification.
|
||||
ShowGpgSigningStatus bool `yaml:"showGpgSigningStatus"`
|
||||
// Whether to show the divergence from the base branch in the branches view.
|
||||
// One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
|
||||
ShowDivergenceFromBaseBranch string `yaml:"showDivergenceFromBaseBranch" jsonschema:"enum=none,enum=onlyArrow,enum=arrowAndNumber"`
|
||||
|
|
@ -914,6 +916,7 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig {
|
|||
CommitAuthorLongLength: 17,
|
||||
CommitHashLength: 8,
|
||||
ShowBranchCommitHash: false,
|
||||
ShowGpgSigningStatus: false,
|
||||
ShowDivergenceFromBaseBranch: "none",
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
|
|
|
|||
|
|
@ -393,6 +393,20 @@ func displayCommit(
|
|||
actionString = actionColorMap(commit.Action, commit.Status).Sprint(actionStr)
|
||||
}
|
||||
|
||||
gpgString := ""
|
||||
if common.UserConfig().Gui.ShowGpgSigningStatus {
|
||||
switch commit.GpgStatus {
|
||||
case "G", "U":
|
||||
gpgString = style.FgGreen.Sprint("✓")
|
||||
case "B", "R", "X", "Y":
|
||||
gpgString = style.FgRed.Sprint("✗")
|
||||
case "E":
|
||||
gpgString = style.FgYellow.Sprint("~")
|
||||
default:
|
||||
gpgString = style.FgBlue.Sprint("-")
|
||||
}
|
||||
}
|
||||
|
||||
tagString := ""
|
||||
if fullDescription {
|
||||
if commit.ExtraInfo != "" {
|
||||
|
|
@ -439,11 +453,12 @@ func displayCommit(
|
|||
}
|
||||
author := authors.AuthorWithLength(commit.AuthorName, authorLength)
|
||||
|
||||
cols := make([]string, 0, 7)
|
||||
cols := make([]string, 0, 8)
|
||||
cols = append(
|
||||
cols,
|
||||
divergenceString,
|
||||
hashString,
|
||||
gpgString,
|
||||
bisectString,
|
||||
descriptionString,
|
||||
actionString,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
|||
selectedCommitHashPtr *string
|
||||
startIdx int
|
||||
endIdx int
|
||||
showGpg bool
|
||||
showGraph bool
|
||||
bisectInfo *git_commands.BisectInfo
|
||||
expected string
|
||||
|
|
@ -89,6 +90,24 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
|||
hash2 commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "show gpg status when enabled",
|
||||
commitOpts: []models.NewCommitOpts{
|
||||
{Name: "commit1", Hash: "hash1", GpgStatus: "G"},
|
||||
{Name: "commit2", Hash: "hash2", GpgStatus: "N"},
|
||||
},
|
||||
startIdx: 0,
|
||||
endIdx: 2,
|
||||
showGpg: true,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitHashSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
hash1 ✓ commit1
|
||||
hash2 - commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "show local branch head, except the current branch, main branches, or merged branches",
|
||||
commitOpts: []models.NewCommitOpts{
|
||||
|
|
@ -543,6 +562,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
|||
if !focusing || s.focus {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
hashPool := &utils.StringPool{}
|
||||
common.UserConfig().Gui.ShowGpgSigningStatus = s.showGpg
|
||||
|
||||
commits := lo.Map(s.commitOpts,
|
||||
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
|
||||
|
|
|
|||
|
|
@ -822,6 +822,11 @@
|
|||
"description": "If true, show commit hashes alongside branch names in the branches view.",
|
||||
"default": false
|
||||
},
|
||||
"showGpgSigningStatus": {
|
||||
"type": "boolean",
|
||||
"description": "If true, show GPG signature verification status for each commit in the commits view. This can slow down commit loading as it requires GPG verification.",
|
||||
"default": false
|
||||
},
|
||||
"showDivergenceFromBaseBranch": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
|
|||
Loading…
Reference in a new issue