mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-10 11:10:18 +02:00
Add command to show divergence from upstream (#2871)
This commit is contained in:
commit
a63d5891e2
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -68,6 +69,8 @@ type GetCommitsOptions struct {
|
|||||||
RefForPushedStatus string // the ref to use for determining pushed/unpushed status
|
RefForPushedStatus string // the ref to use for determining pushed/unpushed status
|
||||||
// determines if we show the whole git graph i.e. pass the '--all' flag
|
// determines if we show the whole git graph i.e. pass the '--all' flag
|
||||||
All bool
|
All bool
|
||||||
|
// If non-empty, show divergence from this ref (left-right log)
|
||||||
|
RefToShowDivergenceFrom string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommits obtains the commits of the current branch
|
// GetCommits obtains the commits of the current branch
|
||||||
@ -93,17 +96,21 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit := self.extractCommitFromLine(line)
|
commit := self.extractCommitFromLine(line, opts.RefToShowDivergenceFrom != "")
|
||||||
commits = append(commits, commit)
|
commits = append(commits, commit)
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var ancestor string
|
var ancestor string
|
||||||
|
var remoteAncestor string
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
ancestor = self.getMergeBase(opts.RefName)
|
ancestor = self.getMergeBase(opts.RefName)
|
||||||
|
if opts.RefToShowDivergenceFrom != "" {
|
||||||
|
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
passedFirstPushedCommit := false
|
passedFirstPushedCommit := false
|
||||||
@ -137,8 +144,23 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
|||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ancestor != "" {
|
if opts.RefToShowDivergenceFrom != "" {
|
||||||
commits = setCommitMergedStatuses(ancestor, commits)
|
sort.SliceStable(commits, func(i, j int) bool {
|
||||||
|
// In the divergence view we want incoming commits to come first
|
||||||
|
return commits[i].Divergence > commits[j].Divergence
|
||||||
|
})
|
||||||
|
|
||||||
|
_, localSectionStart, found := lo.FindIndexOf(commits, func(commit *models.Commit) bool {
|
||||||
|
return commit.Divergence == models.DivergenceLeft
|
||||||
|
})
|
||||||
|
if !found {
|
||||||
|
localSectionStart = len(commits)
|
||||||
|
}
|
||||||
|
|
||||||
|
setCommitMergedStatuses(remoteAncestor, commits[:localSectionStart])
|
||||||
|
setCommitMergedStatuses(ancestor, commits[localSectionStart:])
|
||||||
|
} else {
|
||||||
|
setCommitMergedStatuses(ancestor, commits)
|
||||||
}
|
}
|
||||||
|
|
||||||
return commits, nil
|
return commits, nil
|
||||||
@ -179,8 +201,8 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
|
|||||||
// then puts them into a commit object
|
// then puts them into a commit object
|
||||||
// example input:
|
// example input:
|
||||||
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
||||||
func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool) *models.Commit {
|
||||||
split := strings.SplitN(line, "\x00", 7)
|
split := strings.SplitN(line, "\x00", 8)
|
||||||
|
|
||||||
sha := split[0]
|
sha := split[0]
|
||||||
unixTimestamp := split[1]
|
unixTimestamp := split[1]
|
||||||
@ -189,6 +211,10 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
|||||||
extraInfo := strings.TrimSpace(split[4])
|
extraInfo := strings.TrimSpace(split[4])
|
||||||
parentHashes := split[5]
|
parentHashes := split[5]
|
||||||
message := split[6]
|
message := split[6]
|
||||||
|
divergence := models.DivergenceNone
|
||||||
|
if showDivergence {
|
||||||
|
divergence = lo.Ternary(split[7] == "<", models.DivergenceLeft, models.DivergenceRight)
|
||||||
|
}
|
||||||
|
|
||||||
tags := []string{}
|
tags := []string{}
|
||||||
|
|
||||||
@ -222,6 +248,7 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
|||||||
AuthorName: authorName,
|
AuthorName: authorName,
|
||||||
AuthorEmail: authorEmail,
|
AuthorEmail: authorEmail,
|
||||||
Parents: parents,
|
Parents: parents,
|
||||||
|
Divergence: divergence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +278,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
|||||||
|
|
||||||
fullCommits := map[string]*models.Commit{}
|
fullCommits := map[string]*models.Commit{}
|
||||||
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit := self.extractCommitFromLine(line)
|
commit := self.extractCommitFromLine(line, false)
|
||||||
fullCommits[commit.Sha] = commit
|
fullCommits[commit.Sha] = commit
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
@ -495,7 +522,11 @@ func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*models.Commit {
|
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
|
||||||
|
if ancestor == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
passedAncestor := false
|
passedAncestor := false
|
||||||
for i, commit := range commits {
|
for i, commit := range commits {
|
||||||
// some commits aren't really commits and don't have sha's, such as the update-ref todo
|
// some commits aren't really commits and don't have sha's, such as the update-ref todo
|
||||||
@ -509,7 +540,6 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*model
|
|||||||
commits[i].Status = models.StatusMerged
|
commits[i].Status = models.StatusMerged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commits
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getMergeBase(refName string) string {
|
func (self *CommitLoader) getMergeBase(refName string) string {
|
||||||
@ -622,8 +652,13 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
|||||||
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
config := self.UserConfig.Git.Log
|
config := self.UserConfig.Git.Log
|
||||||
|
|
||||||
|
refSpec := opts.RefName
|
||||||
|
if opts.RefToShowDivergenceFrom != "" {
|
||||||
|
refSpec += "..." + opts.RefToShowDivergenceFrom
|
||||||
|
}
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("log").
|
cmdArgs := NewGitCmd("log").
|
||||||
Arg(opts.RefName).
|
Arg(refSpec).
|
||||||
ArgIf(config.Order != "default", "--"+config.Order).
|
ArgIf(config.Order != "default", "--"+config.Order).
|
||||||
ArgIf(opts.All, "--all").
|
ArgIf(opts.All, "--all").
|
||||||
Arg("--oneline").
|
Arg("--oneline").
|
||||||
@ -632,6 +667,7 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
|||||||
ArgIf(opts.Limit, "-300").
|
ArgIf(opts.Limit, "-300").
|
||||||
ArgIf(opts.FilterPath != "", "--follow").
|
ArgIf(opts.FilterPath != "", "--follow").
|
||||||
Arg("--no-show-signature").
|
Arg("--no-show-signature").
|
||||||
|
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
|
||||||
Arg("--").
|
Arg("--").
|
||||||
ArgIf(opts.FilterPath != "", opts.FilterPath).
|
ArgIf(opts.FilterPath != "", opts.FilterPath).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
@ -639,4 +675,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
|||||||
return self.cmd.New(cmdArgs).DontLog()
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s`
|
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m`
|
||||||
|
@ -45,7 +45,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -57,7 +57,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -72,7 +72,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||||
// here it's testing which of the configured main branches have an upstream
|
// here it's testing which of the configured main branches have an upstream
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||||
@ -209,7 +209,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
// here it's testing which of the configured main branches exist; neither does
|
// here it's testing which of the configured main branches exist; neither does
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||||
@ -246,7 +246,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
// here it's testing which of the configured main branches exist
|
// here it's testing which of the configured main branches exist
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||||
@ -282,7 +282,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -294,7 +294,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -548,7 +548,8 @@ func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
|
|||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(scenario.testName, func(t *testing.T) {
|
t.Run(scenario.testName, func(t *testing.T) {
|
||||||
expectedCommits := setCommitMergedStatuses(scenario.ancestor, scenario.commits)
|
expectedCommits := scenario.commits
|
||||||
|
setCommitMergedStatuses(scenario.ancestor, expectedCommits)
|
||||||
assert.Equal(t, scenario.expectedCommits, expectedCommits)
|
assert.Equal(t, scenario.expectedCommits, expectedCommits)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Branch : A git branch
|
// Branch : A git branch
|
||||||
// duplicating this for now
|
// duplicating this for now
|
||||||
type Branch struct {
|
type Branch struct {
|
||||||
@ -43,6 +45,22 @@ func (b *Branch) ParentRefName() string {
|
|||||||
return b.RefName() + "^"
|
return b.RefName() + "^"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Branch) FullUpstreamRefName() string {
|
||||||
|
if b.UpstreamRemote == "" || b.UpstreamBranch == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("refs/remotes/%s/%s", b.UpstreamRemote, b.UpstreamBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Branch) ShortUpstreamRefName() string {
|
||||||
|
if b.UpstreamRemote == "" || b.UpstreamBranch == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s", b.UpstreamRemote, b.UpstreamBranch)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Branch) ID() string {
|
func (b *Branch) ID() string {
|
||||||
return b.RefName()
|
return b.RefName()
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,17 @@ const (
|
|||||||
ActionConflict = todo.Comment + 1
|
ActionConflict = todo.Comment + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Divergence int
|
||||||
|
|
||||||
|
// For a divergence log (left/right comparison of two refs) this is set to
|
||||||
|
// either DivergenceLeft or DivergenceRight for each commit; for normal
|
||||||
|
// commit views it is always DivergenceNone.
|
||||||
|
const (
|
||||||
|
DivergenceNone Divergence = iota
|
||||||
|
DivergenceLeft
|
||||||
|
DivergenceRight
|
||||||
|
)
|
||||||
|
|
||||||
// Commit : A git commit
|
// Commit : A git commit
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
Sha string
|
Sha string
|
||||||
@ -41,6 +52,7 @@ type Commit struct {
|
|||||||
AuthorName string // something like 'Jesse Duffield'
|
AuthorName string // something like 'Jesse Duffield'
|
||||||
AuthorEmail string // something like 'jessedduffield@gmail.com'
|
AuthorEmail string // something like 'jessedduffield@gmail.com'
|
||||||
UnixTimestamp int64
|
UnixTimestamp int64
|
||||||
|
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
|
||||||
|
|
||||||
// SHAs of parent commits (will be multiple if it's a merge commit)
|
// SHAs of parent commits (will be multiple if it's a merge commit)
|
||||||
Parents []string
|
Parents []string
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubCommitsContext struct {
|
type SubCommitsContext struct {
|
||||||
@ -73,12 +73,41 @@ func NewSubCommitsContext(
|
|||||||
selectedCommitSha,
|
selectedCommitSha,
|
||||||
startIdx,
|
startIdx,
|
||||||
endIdx,
|
endIdx,
|
||||||
shouldShowGraph(c),
|
// Don't show the graph in the left/right view; we'd like to, but
|
||||||
|
// it's too complicated:
|
||||||
|
shouldShowGraph(c) && viewModel.GetRefToShowDivergenceFrom() == "",
|
||||||
git_commands.NewNullBisectInfo(),
|
git_commands.NewNullBisectInfo(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNonModelItems := func() []*NonModelItem {
|
||||||
|
result := []*NonModelItem{}
|
||||||
|
if viewModel.GetRefToShowDivergenceFrom() != "" {
|
||||||
|
_, upstreamIdx, found := lo.FindIndexOf(
|
||||||
|
c.Model().SubCommits, func(c *models.Commit) bool { return c.Divergence == models.DivergenceRight })
|
||||||
|
if !found {
|
||||||
|
upstreamIdx = 0
|
||||||
|
}
|
||||||
|
result = append(result, &NonModelItem{
|
||||||
|
Index: upstreamIdx,
|
||||||
|
Content: fmt.Sprintf("--- %s ---", c.Tr.DivergenceSectionHeaderRemote),
|
||||||
|
})
|
||||||
|
|
||||||
|
_, localIdx, found := lo.FindIndexOf(
|
||||||
|
c.Model().SubCommits, func(c *models.Commit) bool { return c.Divergence == models.DivergenceLeft })
|
||||||
|
if !found {
|
||||||
|
localIdx = len(c.Model().SubCommits)
|
||||||
|
}
|
||||||
|
result = append(result, &NonModelItem{
|
||||||
|
Index: localIdx,
|
||||||
|
Content: fmt.Sprintf("--- %s ---", c.Tr.DivergenceSectionHeaderLocal),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
ctx := &SubCommitsContext{
|
ctx := &SubCommitsContext{
|
||||||
c: c,
|
c: c,
|
||||||
SubCommitsViewModel: viewModel,
|
SubCommitsViewModel: viewModel,
|
||||||
@ -96,6 +125,7 @@ func NewSubCommitsContext(
|
|||||||
ListRenderer: ListRenderer{
|
ListRenderer: ListRenderer{
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
getDisplayStrings: getDisplayStrings,
|
getDisplayStrings: getDisplayStrings,
|
||||||
|
getNonModelItems: getNonModelItems,
|
||||||
},
|
},
|
||||||
c: c,
|
c: c,
|
||||||
refreshViewportOnChange: true,
|
refreshViewportOnChange: true,
|
||||||
@ -112,7 +142,8 @@ func NewSubCommitsContext(
|
|||||||
|
|
||||||
type SubCommitsViewModel struct {
|
type SubCommitsViewModel struct {
|
||||||
// name of the ref that the sub-commits are shown for
|
// name of the ref that the sub-commits are shown for
|
||||||
ref types.Ref
|
ref types.Ref
|
||||||
|
refToShowDivergenceFrom string
|
||||||
*ListViewModel[*models.Commit]
|
*ListViewModel[*models.Commit]
|
||||||
|
|
||||||
limitCommits bool
|
limitCommits bool
|
||||||
@ -127,6 +158,14 @@ func (self *SubCommitsViewModel) GetRef() types.Ref {
|
|||||||
return self.ref
|
return self.ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SubCommitsViewModel) SetRefToShowDivergenceFrom(ref string) {
|
||||||
|
self.refToShowDivergenceFrom = ref
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SubCommitsViewModel) GetRefToShowDivergenceFrom() string {
|
||||||
|
return self.refToShowDivergenceFrom
|
||||||
|
}
|
||||||
|
|
||||||
func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
|
func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
|
||||||
self.showBranchHeads = value
|
self.showBranchHeads = value
|
||||||
}
|
}
|
||||||
@ -160,10 +199,6 @@ func (self *SubCommitsContext) GetCommits() []*models.Commit {
|
|||||||
return self.getModel()
|
return self.getModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SubCommitsContext) Title() string {
|
|
||||||
return fmt.Sprintf(self.c.Tr.SubCommitsDynamicTitle, utils.TruncateWithEllipsis(self.ref.RefName(), 50))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *SubCommitsContext) SetLimitCommits(value bool) {
|
func (self *SubCommitsContext) SetLimitCommits(value bool) {
|
||||||
self.limitCommits = value
|
self.limitCommits = value
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,13 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
helperCommon,
|
helperCommon,
|
||||||
func() *status.StatusManager { return gui.statusManager },
|
func() *status.StatusManager { return gui.statusManager },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setSubCommits := func(commits []*models.Commit) {
|
||||||
|
gui.Mutexes.SubCommitsMutex.Lock()
|
||||||
|
defer gui.Mutexes.SubCommitsMutex.Unlock()
|
||||||
|
|
||||||
|
gui.State.Model.SubCommits = commits
|
||||||
|
}
|
||||||
gui.helpers = &helpers.Helpers{
|
gui.helpers = &helpers.Helpers{
|
||||||
Refs: refsHelper,
|
Refs: refsHelper,
|
||||||
Host: helpers.NewHostHelper(helperCommon),
|
Host: helpers.NewHostHelper(helperCommon),
|
||||||
@ -111,8 +118,9 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
modeHelper,
|
modeHelper,
|
||||||
appStatusHelper,
|
appStatusHelper,
|
||||||
),
|
),
|
||||||
Search: helpers.NewSearchHelper(helperCommon),
|
Search: helpers.NewSearchHelper(helperCommon),
|
||||||
Worktree: worktreeHelper,
|
Worktree: worktreeHelper,
|
||||||
|
SubCommits: helpers.NewSubCommitsHelper(helperCommon, refreshHelper, setSubCommits),
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.CustomCommandsClient = custom_commands.NewClient(
|
gui.CustomCommandsClient = custom_commands.NewClient(
|
||||||
@ -206,13 +214,6 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
controllers.AttachControllers(context, sideWindowControllerFactory.Create(context))
|
controllers.AttachControllers(context, sideWindowControllerFactory.Create(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubCommits := func(commits []*models.Commit) {
|
|
||||||
gui.Mutexes.SubCommitsMutex.Lock()
|
|
||||||
defer gui.Mutexes.SubCommitsMutex.Unlock()
|
|
||||||
|
|
||||||
gui.State.Model.SubCommits = commits
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, context := range []controllers.CanSwitchToSubCommits{
|
for _, context := range []controllers.CanSwitchToSubCommits{
|
||||||
gui.State.Contexts.Branches,
|
gui.State.Contexts.Branches,
|
||||||
gui.State.Contexts.RemoteBranches,
|
gui.State.Contexts.RemoteBranches,
|
||||||
@ -220,7 +221,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
gui.State.Contexts.ReflogCommits,
|
gui.State.Contexts.ReflogCommits,
|
||||||
} {
|
} {
|
||||||
controllers.AttachControllers(context, controllers.NewSwitchToSubCommitsController(
|
controllers.AttachControllers(context, controllers.NewSwitchToSubCommitsController(
|
||||||
common, setSubCommits, context,
|
common, context,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
@ -141,6 +142,27 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
|
|||||||
return self.c.Menu(types.CreateMenuOptions{
|
return self.c.Menu(types.CreateMenuOptions{
|
||||||
Title: self.c.Tr.Actions.SetUnsetUpstream,
|
Title: self.c.Tr.Actions.SetUnsetUpstream,
|
||||||
Items: []*types.MenuItem{
|
Items: []*types.MenuItem{
|
||||||
|
{
|
||||||
|
LabelColumns: []string{self.c.Tr.ViewDivergenceFromUpstream},
|
||||||
|
OnPress: func() error {
|
||||||
|
branch := self.context().GetSelected()
|
||||||
|
if branch == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !branch.RemoteBranchStoredLocally() {
|
||||||
|
return self.c.ErrorMsg(self.c.Tr.DivergenceNoUpstream)
|
||||||
|
}
|
||||||
|
return self.c.Helpers().SubCommits.ViewSubCommits(helpers.ViewSubCommitsOpts{
|
||||||
|
Ref: branch,
|
||||||
|
TitleRef: fmt.Sprintf("%s <-> %s", branch.RefName(), branch.ShortUpstreamRefName()),
|
||||||
|
RefToShowDivergenceFrom: branch.FullUpstreamRefName(),
|
||||||
|
Context: self.context(),
|
||||||
|
ShowBranchHeads: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Key: 'v',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
LabelColumns: []string{self.c.Tr.UnsetUpstream},
|
LabelColumns: []string{self.c.Tr.UnsetUpstream},
|
||||||
OnPress: func() error {
|
OnPress: func() error {
|
||||||
|
@ -49,6 +49,7 @@ type Helpers struct {
|
|||||||
WindowArrangement *WindowArrangementHelper
|
WindowArrangement *WindowArrangementHelper
|
||||||
Search *SearchHelper
|
Search *SearchHelper
|
||||||
Worktree *WorktreeHelper
|
Worktree *WorktreeHelper
|
||||||
|
SubCommits *SubCommitsHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStubHelpers() *Helpers {
|
func NewStubHelpers() *Helpers {
|
||||||
@ -83,5 +84,6 @@ func NewStubHelpers() *Helpers {
|
|||||||
WindowArrangement: &WindowArrangementHelper{},
|
WindowArrangement: &WindowArrangementHelper{},
|
||||||
Search: &SearchHelper{},
|
Search: &SearchHelper{},
|
||||||
Worktree: &WorktreeHelper{},
|
Worktree: &WorktreeHelper{},
|
||||||
|
SubCommits: &SubCommitsHelper{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,11 +334,12 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
|||||||
|
|
||||||
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
||||||
git_commands.GetCommitsOptions{
|
git_commands.GetCommitsOptions{
|
||||||
Limit: self.c.Contexts().SubCommits.GetLimitCommits(),
|
Limit: self.c.Contexts().SubCommits.GetLimitCommits(),
|
||||||
FilterPath: self.c.Modes().Filtering.GetPath(),
|
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||||
IncludeRebaseCommits: false,
|
IncludeRebaseCommits: false,
|
||||||
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
||||||
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
|
||||||
|
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
73
pkg/gui/controllers/helpers/sub_commits_helper.go
Normal file
73
pkg/gui/controllers/helpers/sub_commits_helper.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubCommitsHelper struct {
|
||||||
|
c *HelperCommon
|
||||||
|
|
||||||
|
refreshHelper *RefreshHelper
|
||||||
|
setSubCommits func([]*models.Commit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubCommitsHelper(
|
||||||
|
c *HelperCommon,
|
||||||
|
refreshHelper *RefreshHelper,
|
||||||
|
setSubCommits func([]*models.Commit),
|
||||||
|
) *SubCommitsHelper {
|
||||||
|
return &SubCommitsHelper{
|
||||||
|
c: c,
|
||||||
|
refreshHelper: refreshHelper,
|
||||||
|
setSubCommits: setSubCommits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ViewSubCommitsOpts struct {
|
||||||
|
Ref types.Ref
|
||||||
|
RefToShowDivergenceFrom string
|
||||||
|
TitleRef string
|
||||||
|
Context types.Context
|
||||||
|
ShowBranchHeads bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
|
||||||
|
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
||||||
|
git_commands.GetCommitsOptions{
|
||||||
|
Limit: true,
|
||||||
|
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||||
|
IncludeRebaseCommits: false,
|
||||||
|
RefName: opts.Ref.FullRefName(),
|
||||||
|
RefForPushedStatus: opts.Ref.FullRefName(),
|
||||||
|
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubCommits(commits)
|
||||||
|
self.refreshHelper.RefreshAuthors(commits)
|
||||||
|
|
||||||
|
subCommitsContext := self.c.Contexts().SubCommits
|
||||||
|
subCommitsContext.SetSelectedLineIdx(0)
|
||||||
|
subCommitsContext.SetParentContext(opts.Context)
|
||||||
|
subCommitsContext.SetWindowName(opts.Context.GetWindowName())
|
||||||
|
subCommitsContext.SetTitleRef(utils.TruncateWithEllipsis(opts.TitleRef, 50))
|
||||||
|
subCommitsContext.SetRef(opts.Ref)
|
||||||
|
subCommitsContext.SetRefToShowDivergenceFrom(opts.RefToShowDivergenceFrom)
|
||||||
|
subCommitsContext.SetLimitCommits(true)
|
||||||
|
subCommitsContext.SetShowBranchHeads(opts.ShowBranchHeads)
|
||||||
|
subCommitsContext.ClearSearchString()
|
||||||
|
subCommitsContext.GetView().ClearSearch()
|
||||||
|
|
||||||
|
err = self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.PushContext(self.c.Contexts().SubCommits)
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,20 +17,16 @@ type SwitchToSubCommitsController struct {
|
|||||||
baseController
|
baseController
|
||||||
c *ControllerCommon
|
c *ControllerCommon
|
||||||
context CanSwitchToSubCommits
|
context CanSwitchToSubCommits
|
||||||
|
|
||||||
setSubCommits func([]*models.Commit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSwitchToSubCommitsController(
|
func NewSwitchToSubCommitsController(
|
||||||
controllerCommon *ControllerCommon,
|
controllerCommon *ControllerCommon,
|
||||||
setSubCommits func([]*models.Commit),
|
|
||||||
context CanSwitchToSubCommits,
|
context CanSwitchToSubCommits,
|
||||||
) *SwitchToSubCommitsController {
|
) *SwitchToSubCommitsController {
|
||||||
return &SwitchToSubCommitsController{
|
return &SwitchToSubCommitsController{
|
||||||
baseController: baseController{},
|
baseController: baseController{},
|
||||||
c: controllerCommon,
|
c: controllerCommon,
|
||||||
context: context,
|
context: context,
|
||||||
setSubCommits: setSubCommits,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,40 +52,12 @@ func (self *SwitchToSubCommitsController) viewCommits() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to populate my sub commits
|
return self.c.Helpers().SubCommits.ViewSubCommits(helpers.ViewSubCommitsOpts{
|
||||||
commits, err := self.c.Git().Loaders.CommitLoader.GetCommits(
|
Ref: ref,
|
||||||
git_commands.GetCommitsOptions{
|
TitleRef: ref.RefName(),
|
||||||
Limit: true,
|
Context: self.context,
|
||||||
FilterPath: self.c.Modes().Filtering.GetPath(),
|
ShowBranchHeads: self.context.ShowBranchHeadsInSubCommits(),
|
||||||
IncludeRebaseCommits: false,
|
})
|
||||||
RefName: ref.FullRefName(),
|
|
||||||
RefForPushedStatus: ref.FullRefName(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setSubCommits(commits)
|
|
||||||
self.c.Helpers().Refresh.RefreshAuthors(commits)
|
|
||||||
|
|
||||||
subCommitsContext := self.c.Contexts().SubCommits
|
|
||||||
subCommitsContext.SetSelectedLineIdx(0)
|
|
||||||
subCommitsContext.SetParentContext(self.context)
|
|
||||||
subCommitsContext.SetWindowName(self.context.GetWindowName())
|
|
||||||
subCommitsContext.SetTitleRef(ref.Description())
|
|
||||||
subCommitsContext.SetRef(ref)
|
|
||||||
subCommitsContext.SetLimitCommits(true)
|
|
||||||
subCommitsContext.SetShowBranchHeads(self.context.ShowBranchHeadsInSubCommits())
|
|
||||||
subCommitsContext.ClearSearchString()
|
|
||||||
subCommitsContext.GetView().ClearSearch()
|
|
||||||
|
|
||||||
err = self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.c.PushContext(self.c.Contexts().SubCommits)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwitchToSubCommitsController) Context() types.Context {
|
func (self *SwitchToSubCommitsController) Context() types.Context {
|
||||||
|
@ -359,7 +359,9 @@ func displayCommit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
cols := make([]string, 0, 7)
|
cols := make([]string, 0, 7)
|
||||||
if icons.IsIconEnabled() {
|
if commit.Divergence != models.DivergenceNone {
|
||||||
|
cols = append(cols, shaColor.Sprint(lo.Ternary(commit.Divergence == models.DivergenceLeft, "↑", "↓")))
|
||||||
|
} else if icons.IsIconEnabled() {
|
||||||
cols = append(cols, shaColor.Sprint(icons.IconForCommit(commit)))
|
cols = append(cols, shaColor.Sprint(icons.IconForCommit(commit)))
|
||||||
}
|
}
|
||||||
cols = append(cols, shaColor.Sprint(commit.ShortSha()))
|
cols = append(cols, shaColor.Sprint(commit.ShortSha()))
|
||||||
@ -430,6 +432,8 @@ func getShaColor(
|
|||||||
shaColor = theme.DiffTerminalColor
|
shaColor = theme.DiffTerminalColor
|
||||||
} else if cherryPickedCommitShaSet.Includes(commit.Sha) {
|
} else if cherryPickedCommitShaSet.Includes(commit.Sha) {
|
||||||
shaColor = theme.CherryPickedCommitTextStyle
|
shaColor = theme.CherryPickedCommitTextStyle
|
||||||
|
} else if commit.Divergence == models.DivergenceRight && commit.Status != models.StatusMerged {
|
||||||
|
shaColor = style.FgBlue
|
||||||
}
|
}
|
||||||
|
|
||||||
return shaColor
|
return shaColor
|
||||||
|
@ -348,6 +348,10 @@ type TranslationSet struct {
|
|||||||
SetAsUpstream string
|
SetAsUpstream string
|
||||||
SetUpstream string
|
SetUpstream string
|
||||||
UnsetUpstream string
|
UnsetUpstream string
|
||||||
|
ViewDivergenceFromUpstream string
|
||||||
|
DivergenceNoUpstream string
|
||||||
|
DivergenceSectionHeaderLocal string
|
||||||
|
DivergenceSectionHeaderRemote string
|
||||||
SetUpstreamTitle string
|
SetUpstreamTitle string
|
||||||
SetUpstreamMessage string
|
SetUpstreamMessage string
|
||||||
EditRemote string
|
EditRemote string
|
||||||
@ -1128,6 +1132,10 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
SetAsUpstream: "Set as upstream of checked-out branch",
|
SetAsUpstream: "Set as upstream of checked-out branch",
|
||||||
SetUpstream: "Set upstream of selected branch",
|
SetUpstream: "Set upstream of selected branch",
|
||||||
UnsetUpstream: "Unset upstream of selected branch",
|
UnsetUpstream: "Unset upstream of selected branch",
|
||||||
|
ViewDivergenceFromUpstream: "View divergence from upstream",
|
||||||
|
DivergenceNoUpstream: "Cannot show divergence of a branch that has no (locally tracked) upstream",
|
||||||
|
DivergenceSectionHeaderLocal: "Local",
|
||||||
|
DivergenceSectionHeaderRemote: "Remote",
|
||||||
SetUpstreamTitle: "Set upstream branch",
|
SetUpstreamTitle: "Set upstream branch",
|
||||||
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",
|
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",
|
||||||
EditRemote: "Edit remote",
|
EditRemote: "Edit remote",
|
||||||
|
@ -39,6 +39,18 @@ func (self *TextMatcher) DoesNotContain(target string) *TextMatcher {
|
|||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *TextMatcher) DoesNotContainAnyOf(targets []string) *TextMatcher {
|
||||||
|
self.appendRule(matcherRule[string]{
|
||||||
|
name: fmt.Sprintf("does not contain any of '%s'", targets),
|
||||||
|
testFn: func(value string) (bool, string) {
|
||||||
|
return lo.NoneBy(targets, func(target string) bool { return strings.Contains(value, target) }),
|
||||||
|
fmt.Sprintf("Expected none of '%s' to be found in '%s'", targets, value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
func (self *TextMatcher) MatchesRegexp(target string) *TextMatcher {
|
func (self *TextMatcher) MatchesRegexp(target string) *TextMatcher {
|
||||||
self.appendRule(matcherRule[string]{
|
self.appendRule(matcherRule[string]{
|
||||||
name: fmt.Sprintf("matches regular expression '%s'", target),
|
name: fmt.Sprintf("matches regular expression '%s'", target),
|
||||||
@ -107,6 +119,10 @@ func DoesNotContain(target string) *TextMatcher {
|
|||||||
return AnyString().DoesNotContain(target)
|
return AnyString().DoesNotContain(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DoesNotContainAnyOf(targets ...string) *TextMatcher {
|
||||||
|
return AnyString().DoesNotContainAnyOf(targets)
|
||||||
|
}
|
||||||
|
|
||||||
func MatchesRegexp(target string) *TextMatcher {
|
func MatchesRegexp(target string) *TextMatcher {
|
||||||
return AnyString().MatchesRegexp(target)
|
return AnyString().MatchesRegexp(target)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package branch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ShowDivergenceFromUpstream = NewIntegrationTest(NewIntegrationTestArgs{
|
||||||
|
Description: "Show divergence from upstream",
|
||||||
|
ExtraCmdArgs: []string{},
|
||||||
|
Skip: false,
|
||||||
|
SetupConfig: func(config *config.AppConfig) {},
|
||||||
|
SetupRepo: func(shell *Shell) {
|
||||||
|
shell.CreateFileAndAdd("file", "content1")
|
||||||
|
shell.Commit("one")
|
||||||
|
shell.UpdateFileAndAdd("file", "content2")
|
||||||
|
shell.Commit("two")
|
||||||
|
shell.CreateFileAndAdd("file3", "content3")
|
||||||
|
shell.Commit("three")
|
||||||
|
|
||||||
|
shell.CloneIntoRemote("origin")
|
||||||
|
|
||||||
|
shell.SetBranchUpstream("master", "origin/master")
|
||||||
|
|
||||||
|
shell.HardReset("HEAD^^")
|
||||||
|
shell.CreateFileAndAdd("file4", "content4")
|
||||||
|
shell.Commit("four")
|
||||||
|
},
|
||||||
|
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||||
|
t.Views().Commits().
|
||||||
|
Lines(
|
||||||
|
Contains("four"),
|
||||||
|
Contains("one"),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Views().Branches().
|
||||||
|
Focus().
|
||||||
|
Lines(Contains("master")).
|
||||||
|
Press(keys.Branches.SetUpstream)
|
||||||
|
|
||||||
|
t.ExpectPopup().Menu().Title(Contains("upstream")).Select(Contains("View divergence from upstream")).Confirm()
|
||||||
|
|
||||||
|
t.Views().SubCommits().
|
||||||
|
IsFocused().
|
||||||
|
Title(Contains("Commits (master <-> origin/master)")).
|
||||||
|
Lines(
|
||||||
|
DoesNotContainAnyOf("↓", "↑").Contains("--- Remote ---"),
|
||||||
|
Contains("↓").Contains("three"),
|
||||||
|
Contains("↓").Contains("two"),
|
||||||
|
DoesNotContainAnyOf("↓", "↑").Contains("--- Local ---"),
|
||||||
|
Contains("↑").Contains("four"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
@ -49,6 +49,7 @@ var tests = []*components.IntegrationTest{
|
|||||||
branch.Reset,
|
branch.Reset,
|
||||||
branch.ResetUpstream,
|
branch.ResetUpstream,
|
||||||
branch.SetUpstream,
|
branch.SetUpstream,
|
||||||
|
branch.ShowDivergenceFromUpstream,
|
||||||
branch.Suggestions,
|
branch.Suggestions,
|
||||||
cherry_pick.CherryPick,
|
cherry_pick.CherryPick,
|
||||||
cherry_pick.CherryPickConflicts,
|
cherry_pick.CherryPickConflicts,
|
||||||
|
Loading…
Reference in New Issue
Block a user