mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-31 22:22:14 +02:00
Visualize local branch heads in commits panel, 2nd approach (#2775)
This commit is contained in:
commit
a6af31a4cb
@ -62,7 +62,6 @@ gui:
|
||||
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
|
||||
showIcons: false # deprecated: use nerdFontsVersion instead
|
||||
|
@ -6,4 +6,5 @@
|
||||
* [Keybindings](./keybindings)
|
||||
* [Undo/Redo](./Undoing.md)
|
||||
* [Searching/Filtering](./Searching.md)
|
||||
* [Stacked Branches](./Stacked_Branches.md)
|
||||
* [Dev docs](./dev)
|
||||
|
18
docs/Stacked_Branches.md
Normal file
18
docs/Stacked_Branches.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Working with stacked branches
|
||||
|
||||
When working on a large branch it can often be useful to break it down into
|
||||
smaller pieces, and it can help to create separate branches for each independent
|
||||
chunk of changes. For example, you could have one branch for preparatory
|
||||
refactorings, one for backend changes, and one for frontend changes. Those
|
||||
branches would then all be stacked onto each other.
|
||||
|
||||
Git has support for rebasing such a stack as a whole; you can enable it by
|
||||
setting the git config `rebase.updateRfs` to true. If you then rebase the
|
||||
topmost branch of the stack, the other ones in the stack will follow. This
|
||||
includes interactive rebases, so for example amending a commit in the first
|
||||
branch of the stack will "just work" in the sense that it keeps the other
|
||||
branches properly stacked onto it.
|
||||
|
||||
Lazygit visualizes the invidual branch heads in the stack by marking them with a
|
||||
cyan asterisk (or a cyan branch symbol if you are using [nerd
|
||||
fonts](Config.md#display-nerd-fonts-icons)).
|
@ -70,6 +70,21 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CurrentBranchName get name of current branch
|
||||
func (self *BranchCommands) CurrentBranchName() (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").
|
||||
Arg("--abbrev-ref").
|
||||
Arg("--verify").
|
||||
Arg("HEAD").
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err == nil {
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Delete delete branch
|
||||
func (self *BranchCommands) Delete(branch string, force bool) error {
|
||||
cmdArgs := NewGitCmd("branch").
|
||||
|
@ -171,7 +171,7 @@ var branchFields = []string{
|
||||
"upstream:short",
|
||||
"upstream:track",
|
||||
"subject",
|
||||
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
|
||||
"objectname",
|
||||
}
|
||||
|
||||
// Obtain branch information from parsed line output of getRawBranches()
|
||||
|
@ -107,3 +107,7 @@ func (self *ConfigCommands) GetCoreCommentChar() byte {
|
||||
|
||||
return '#'
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
|
||||
return self.gitConfig.GetBool("rebase.updateRefs")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -71,3 +72,14 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||
func (self *StatusCommands) IsInMergeState() (bool, error) {
|
||||
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
|
||||
}
|
||||
|
||||
// Full ref (e.g. "refs/heads/mybranch") of the branch that is currently
|
||||
// being rebased, or empty string when we're not in a rebase
|
||||
func (self *StatusCommands) BranchBeingRebased() string {
|
||||
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
||||
if bytesContent, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), dir, "head-name")); err == nil {
|
||||
return strings.TrimSpace(string(bytesContent))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -27,36 +27,35 @@ type RefresherConfig struct {
|
||||
}
|
||||
|
||||
type GuiConfig struct {
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
ShortTimeFormat string `yaml:"shortTimeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
NerdFontsVersion string `yaml:"nerdFontsVersion"`
|
||||
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
@ -436,21 +435,20 @@ func GetDefaultConfig() *UserConfig {
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ExperimentalShowBranchHeads: false,
|
||||
ShowBranchCommitHash: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
NerdFontsVersion: "",
|
||||
ShowBranchCommitHash: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
|
@ -83,3 +83,7 @@ func (self *BranchesContext) GetDiffTerminals() []string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -38,10 +38,14 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
}
|
||||
|
||||
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().Commits,
|
||||
c.Model().Branches,
|
||||
c.Model().CheckedOutBranch,
|
||||
showBranchMarkerForHeadCommit,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
|
@ -86,3 +86,7 @@ func (self *ReflogCommitsContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *ReflogCommitsContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -72,3 +72,7 @@ func (self *RemoteBranchesContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *RemoteBranchesContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -37,6 +37,13 @@ func NewSubCommitsContext(
|
||||
}
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
// This can happen if a sub-commits view is asked to be rerendered while
|
||||
// it is invisble; for example when switching screen modes, which
|
||||
// rerenders all views.
|
||||
if viewModel.GetRef() == nil {
|
||||
return [][]string{}
|
||||
}
|
||||
|
||||
selectedCommitSha := ""
|
||||
if c.CurrentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
|
||||
selectedCommit := viewModel.GetSelected()
|
||||
@ -44,9 +51,17 @@ func NewSubCommitsContext(
|
||||
selectedCommitSha = selectedCommit.Sha
|
||||
}
|
||||
}
|
||||
branches := []*models.Branch{}
|
||||
if viewModel.GetShowBranchHeads() {
|
||||
branches = c.Model().Branches
|
||||
}
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().SubCommits,
|
||||
branches,
|
||||
viewModel.GetRef().RefName(),
|
||||
showBranchMarkerForHeadCommit,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
@ -97,7 +112,8 @@ type SubCommitsViewModel struct {
|
||||
ref types.Ref
|
||||
*ListViewModel[*models.Commit]
|
||||
|
||||
limitCommits bool
|
||||
limitCommits bool
|
||||
showBranchHeads bool
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) SetRef(ref types.Ref) {
|
||||
@ -108,6 +124,14 @@ func (self *SubCommitsViewModel) GetRef() types.Ref {
|
||||
return self.ref
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
|
||||
self.showBranchHeads = value
|
||||
}
|
||||
|
||||
func (self *SubCommitsViewModel) GetShowBranchHeads() bool {
|
||||
return self.showBranchHeads
|
||||
}
|
||||
|
||||
func (self *SubCommitsContext) GetSelectedItemId() string {
|
||||
item := self.GetSelected()
|
||||
if item == nil {
|
||||
|
@ -69,3 +69,7 @@ func (self *TagsContext) GetDiffTerminals() []string {
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *TagsContext) ShowBranchHeadsInSubCommits() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -272,6 +272,32 @@ func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) determineCheckedOutBranchName() string {
|
||||
if rebasedBranch := self.c.Git().Status.BranchBeingRebased(); rebasedBranch != "" {
|
||||
// During a rebase we're on a detached head, so cannot determine the
|
||||
// branch name in the usual way. We need to read it from the
|
||||
// ".git/rebase-merge/head-name" file instead.
|
||||
return strings.TrimPrefix(rebasedBranch, "refs/heads/")
|
||||
}
|
||||
|
||||
if bisectInfo := self.c.Git().Bisect.GetInfo(); bisectInfo.Bisecting() && bisectInfo.GetStartSha() != "" {
|
||||
// Likewise, when we're bisecting we're on a detached head as well. In
|
||||
// this case we read the branch name from the ".git/BISECT_START" file.
|
||||
return bisectInfo.GetStartSha()
|
||||
}
|
||||
|
||||
// In all other cases, get the branch name by asking git what branch is
|
||||
// checked out. Note that if we're on a detached head (for reasons other
|
||||
// than rebasing or bisecting, i.e. it was explicitly checked out), then
|
||||
// this will return its sha.
|
||||
if branchName, err := self.c.Git().Branch.CurrentBranchName(); err == nil {
|
||||
return branchName
|
||||
}
|
||||
|
||||
// Should never get here unless the working copy is corrupt
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
|
||||
@ -291,6 +317,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
self.c.Model().Commits = commits
|
||||
self.RefreshAuthors(commits)
|
||||
self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
|
||||
self.c.Model().CheckedOutBranch = self.determineCheckedOutBranchName()
|
||||
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
@ -412,6 +439,12 @@ func (self *RefreshHelper) refreshBranches() {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
// Need to re-render the commits view because the visualization of local
|
||||
// branch heads might have changed
|
||||
if err := self.c.Contexts().LocalCommits.HandleRender(); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
self.refreshStatus()
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ var _ types.IController = &SwitchToSubCommitsController{}
|
||||
type CanSwitchToSubCommits interface {
|
||||
types.Context
|
||||
GetSelectedRef() types.Ref
|
||||
ShowBranchHeadsInSubCommits() bool
|
||||
}
|
||||
|
||||
type SwitchToSubCommitsController struct {
|
||||
@ -79,6 +80,7 @@ func (self *SwitchToSubCommitsController) viewCommits() error {
|
||||
subCommitsContext.SetTitleRef(ref.Description())
|
||||
subCommitsContext.SetRef(ref)
|
||||
subCommitsContext.SetLimitCommits(true)
|
||||
subCommitsContext.SetShowBranchHeads(self.context.ShowBranchHeadsInSubCommits())
|
||||
subCommitsContext.ClearSearchString()
|
||||
subCommitsContext.GetView().ClearSearch()
|
||||
|
||||
|
@ -71,7 +71,7 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
|
||||
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
|
||||
res = append(res, b.CommitHash)
|
||||
res = append(res, utils.ShortSha(b.CommitHash))
|
||||
}
|
||||
|
||||
res = append(res, coloredName)
|
||||
|
@ -39,6 +39,9 @@ type bisectBounds struct {
|
||||
func GetCommitListDisplayStrings(
|
||||
common *common.Common,
|
||||
commits []*models.Commit,
|
||||
branches []*models.Branch,
|
||||
currentBranchName string,
|
||||
showBranchMarkerForHeadCommit bool,
|
||||
fullDescription bool,
|
||||
cherryPickedCommitShaSet *set.Set[string],
|
||||
diffName string,
|
||||
@ -99,6 +102,30 @@ func GetCommitListDisplayStrings(
|
||||
getGraphLine = func(idx int) string { return "" }
|
||||
}
|
||||
|
||||
// Determine the hashes of the local branches for which we want to show a
|
||||
// branch marker in the commits list. We only want to do this for branches
|
||||
// that are not the current branch, and not any of the main branches. The
|
||||
// goal is to visualize stacks of local branches, so anything that doesn't
|
||||
// contribute to a branch stack shouldn't show a marker.
|
||||
//
|
||||
// If there are other branches pointing to the current head commit, we only
|
||||
// want to show the marker if the rebase.updateRefs config is on.
|
||||
branchHeadsToVisualize := set.NewFromSlice(lo.FilterMap(branches,
|
||||
func(b *models.Branch, index int) (string, bool) {
|
||||
return b.CommitHash,
|
||||
// Don't consider branches that don't have a commit hash. As far
|
||||
// as I can see, this happens for a detached head, so filter
|
||||
// these out
|
||||
b.CommitHash != "" &&
|
||||
// Don't show a marker for the current branch
|
||||
b.Name != currentBranchName &&
|
||||
// Don't show a marker for main branches
|
||||
!lo.Contains(common.UserConfig.Git.MainBranches, b.Name) &&
|
||||
// Don't show a marker for the head commit unless the
|
||||
// rebase.updateRefs config is on
|
||||
(showBranchMarkerForHeadCommit || b.CommitHash != commits[0].Sha)
|
||||
}))
|
||||
|
||||
lines := make([][]string, 0, len(filteredCommits))
|
||||
var bisectStatus BisectStatus
|
||||
for i, commit := range filteredCommits {
|
||||
@ -112,6 +139,7 @@ func GetCommitListDisplayStrings(
|
||||
lines = append(lines, displayCommit(
|
||||
common,
|
||||
commit,
|
||||
branchHeadsToVisualize,
|
||||
cherryPickedCommitShaSet,
|
||||
diffName,
|
||||
timeFormat,
|
||||
@ -260,6 +288,7 @@ func getBisectStatusText(bisectStatus BisectStatus, bisectInfo *git_commands.Bis
|
||||
func displayCommit(
|
||||
common *common.Common,
|
||||
commit *models.Commit,
|
||||
branchHeadsToVisualize *set.Set[string],
|
||||
cherryPickedCommitShaSet *set.Set[string],
|
||||
diffName string,
|
||||
timeFormat string,
|
||||
@ -289,8 +318,11 @@ func displayCommit(
|
||||
} else {
|
||||
if len(commit.Tags) > 0 {
|
||||
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(commit.Tags, " ")) + " "
|
||||
} else if common.UserConfig.Gui.ExperimentalShowBranchHeads && commit.ExtraInfo != "" {
|
||||
tagString = style.FgMagenta.SetBold().Sprint("(*)") + " "
|
||||
}
|
||||
|
||||
if branchHeadsToVisualize.Includes(commit.Sha) && commit.Status != models.StatusMerged {
|
||||
tagString = style.FgCyan.SetBold().Sprint(
|
||||
lo.Ternary(icons.IsIconEnabled(), icons.BRANCH_ICON, "*") + " " + tagString)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,9 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
commits []*models.Commit
|
||||
branches []*models.Branch
|
||||
currentBranchName string
|
||||
hasUpdateRefConfig bool
|
||||
fullDescription bool
|
||||
cherryPickedCommitShaSet *set.Set[string]
|
||||
diffName string
|
||||
@ -72,6 +75,120 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
||||
sha2 commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "commit with tags",
|
||||
commits: []*models.Commit{
|
||||
{Name: "commit1", Sha: "sha1", Tags: []string{"tag1", "tag2"}},
|
||||
{Name: "commit2", Sha: "sha2"},
|
||||
},
|
||||
startIdx: 0,
|
||||
length: 2,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitShaSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
sha1 tag1 tag2 commit1
|
||||
sha2 commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "show local branch head, except the current branch, main branches, or merged branches",
|
||||
commits: []*models.Commit{
|
||||
{Name: "commit1", Sha: "sha1"},
|
||||
{Name: "commit2", Sha: "sha2"},
|
||||
{Name: "commit3", Sha: "sha3"},
|
||||
{Name: "commit4", Sha: "sha4", Status: models.StatusMerged},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{Name: "current-branch", CommitHash: "sha1", Head: true},
|
||||
{Name: "other-branch", CommitHash: "sha2", Head: false},
|
||||
{Name: "master", CommitHash: "sha3", Head: false},
|
||||
{Name: "old-branch", CommitHash: "sha4", Head: false},
|
||||
},
|
||||
currentBranchName: "current-branch",
|
||||
hasUpdateRefConfig: true,
|
||||
startIdx: 0,
|
||||
length: 4,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitShaSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
sha1 commit1
|
||||
sha2 * commit2
|
||||
sha3 commit3
|
||||
sha4 commit4
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "show local branch head for head commit if updateRefs is on",
|
||||
commits: []*models.Commit{
|
||||
{Name: "commit1", Sha: "sha1"},
|
||||
{Name: "commit2", Sha: "sha2"},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{Name: "current-branch", CommitHash: "sha1", Head: true},
|
||||
{Name: "other-branch", CommitHash: "sha1", Head: false},
|
||||
},
|
||||
currentBranchName: "current-branch",
|
||||
hasUpdateRefConfig: true,
|
||||
startIdx: 0,
|
||||
length: 2,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitShaSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
sha1 * commit1
|
||||
sha2 commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "don't show local branch head for head commit if updateRefs is off",
|
||||
commits: []*models.Commit{
|
||||
{Name: "commit1", Sha: "sha1"},
|
||||
{Name: "commit2", Sha: "sha2"},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{Name: "current-branch", CommitHash: "sha1", Head: true},
|
||||
{Name: "other-branch", CommitHash: "sha1", Head: false},
|
||||
},
|
||||
currentBranchName: "current-branch",
|
||||
hasUpdateRefConfig: false,
|
||||
startIdx: 0,
|
||||
length: 2,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitShaSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
sha1 commit1
|
||||
sha2 commit2
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "show local branch head and tag if both exist",
|
||||
commits: []*models.Commit{
|
||||
{Name: "commit1", Sha: "sha1"},
|
||||
{Name: "commit2", Sha: "sha2", Tags: []string{"some-tag"}},
|
||||
{Name: "commit3", Sha: "sha3"},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{Name: "some-branch", CommitHash: "sha2"},
|
||||
},
|
||||
startIdx: 0,
|
||||
length: 3,
|
||||
showGraph: false,
|
||||
bisectInfo: git_commands.NewNullBisectInfo(),
|
||||
cherryPickedCommitShaSet: set.New[string](),
|
||||
now: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: formatExpected(`
|
||||
sha1 commit1
|
||||
sha2 * some-tag commit2
|
||||
sha3 commit3
|
||||
`),
|
||||
},
|
||||
{
|
||||
testName: "showing graph",
|
||||
commits: []*models.Commit{
|
||||
@ -285,6 +402,9 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
||||
result := GetCommitListDisplayStrings(
|
||||
common,
|
||||
s.commits,
|
||||
s.branches,
|
||||
s.currentBranchName,
|
||||
s.hasUpdateRefConfig,
|
||||
s.fullDescription,
|
||||
s.cherryPickedCommitShaSet,
|
||||
s.diffName,
|
||||
|
@ -217,6 +217,10 @@ type Model struct {
|
||||
RemoteBranches []*models.RemoteBranch
|
||||
Tags []*models.Tag
|
||||
|
||||
// Name of the currently checked out branch. This will be set even when
|
||||
// we're on a detached head because we're rebasing or bisecting.
|
||||
CheckedOutBranch string
|
||||
|
||||
// for displaying suggestions while typing in a file name
|
||||
FilesTrie *patricia.Trie
|
||||
|
||||
|
@ -11,6 +11,7 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Skip: false,
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
NewBranch("mybranch").
|
||||
CreateNCommits(10)
|
||||
},
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
@ -31,20 +32,21 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
SelectedLine(Contains("commit 10")).
|
||||
NavigateToLine(Contains("commit 09")).
|
||||
SelectedLine(Contains("CI commit 10")).
|
||||
NavigateToLine(Contains("CI commit 09")).
|
||||
Tap(func() {
|
||||
markCommitAsBad()
|
||||
|
||||
t.Views().Information().Content(Contains("Bisecting"))
|
||||
}).
|
||||
SelectedLine(Contains("<-- bad")).
|
||||
NavigateToLine(Contains("commit 02")).
|
||||
NavigateToLine(Contains("CI commit 02")).
|
||||
Tap(markCommitAsGood).
|
||||
TopLines(Contains("CI commit 10")).
|
||||
// lazygit will land us in the commit between our good and bad commits.
|
||||
SelectedLine(Contains("commit 05").Contains("<-- current")).
|
||||
SelectedLine(Contains("CI commit 05").Contains("<-- current")).
|
||||
Tap(markCommitAsBad).
|
||||
SelectedLine(Contains("commit 04").Contains("<-- current")).
|
||||
SelectedLine(Contains("CI commit 04").Contains("<-- current")).
|
||||
Tap(func() {
|
||||
markCommitAsGood()
|
||||
|
||||
@ -52,7 +54,7 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.ExpectPopup().Alert().Title(Equals("Bisect complete")).Content(MatchesRegexp("(?s)commit 05.*Do you want to reset")).Confirm()
|
||||
}).
|
||||
IsFocused().
|
||||
Content(Contains("commit 04"))
|
||||
Content(Contains("CI commit 04"))
|
||||
|
||||
t.Views().Information().Content(DoesNotContain("Bisecting"))
|
||||
},
|
||||
|
@ -10,12 +10,16 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.MainBranches = []string{"master"}
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
CreateNCommits(3).
|
||||
NewBranch("mybranch").
|
||||
CreateNCommitsStartingAt(3, 4)
|
||||
CreateNCommits(1).
|
||||
NewBranch("branch1").
|
||||
CreateNCommitsStartingAt(3, 2).
|
||||
NewBranch("branch2").
|
||||
CreateNCommitsStartingAt(3, 5)
|
||||
|
||||
shell.SetConfig("rebase.updateRefs", "true")
|
||||
},
|
||||
@ -23,26 +27,28 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("commit 06").IsSelected(),
|
||||
Contains("commit 05"),
|
||||
Contains("commit 04"),
|
||||
Contains("commit 03"),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
Contains("CI commit 07").IsSelected(),
|
||||
Contains("CI commit 06"),
|
||||
Contains("CI commit 05"),
|
||||
Contains("CI * commit 04"),
|
||||
Contains("CI commit 03"),
|
||||
Contains("CI commit 02"),
|
||||
Contains("CI commit 01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 02")).
|
||||
Press(keys.Universal.Edit).
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("pick").Contains("commit 06"),
|
||||
Contains("pick").Contains("commit 05"),
|
||||
Contains("pick").Contains("commit 04"),
|
||||
Contains("update-ref").Contains("master"),
|
||||
Contains("pick").Contains("commit 03"),
|
||||
Contains("pick").Contains("commit 02"),
|
||||
Contains("<-- YOU ARE HERE --- commit 01"),
|
||||
Contains("pick").Contains("CI commit 07"),
|
||||
Contains("pick").Contains("CI commit 06"),
|
||||
Contains("pick").Contains("CI commit 05"),
|
||||
Contains("update-ref").Contains("branch1").DoesNotContain("*"),
|
||||
Contains("pick").Contains("CI * commit 04"),
|
||||
Contains("pick").Contains("CI commit 03"),
|
||||
Contains("<-- YOU ARE HERE --- commit 02"),
|
||||
Contains("CI commit 01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit 05")).
|
||||
NavigateToLine(Contains("commit 06")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.Common().ContinueRebase()
|
||||
@ -50,11 +56,12 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("commit 06"),
|
||||
Contains("commit 04"),
|
||||
Contains("commit 03"),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
Contains("CI commit 07"),
|
||||
Contains("CI commit 05"),
|
||||
Contains("CI * commit 04"),
|
||||
Contains("CI commit 03"),
|
||||
Contains("CI commit 02"),
|
||||
Contains("CI commit 01"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
@ -1,62 +0,0 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DropTodoCommitWithUpdateRefShowBranchHeads = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file (with experimentalShowBranchHeads on)",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Gui.ExperimentalShowBranchHeads = true
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
CreateNCommits(3).
|
||||
NewBranch("mybranch").
|
||||
CreateNCommitsStartingAt(3, 4)
|
||||
|
||||
shell.SetConfig("rebase.updateRefs", "true")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("(*) commit 06").IsSelected(),
|
||||
Contains("commit 05"),
|
||||
Contains("commit 04"),
|
||||
Contains("(*) commit 03"),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Universal.Edit).
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("pick").Contains("(*) commit 06"),
|
||||
Contains("pick").Contains("commit 05"),
|
||||
Contains("pick").Contains("commit 04"),
|
||||
Contains("update-ref").Contains("master"),
|
||||
Contains("pick").Contains("(*) commit 03"),
|
||||
Contains("pick").Contains("commit 02"),
|
||||
Contains("<-- YOU ARE HERE --- commit 01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit 05")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.Common().ContinueRebase()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("(*) commit 06"),
|
||||
Contains("commit 04"),
|
||||
Contains("(*) commit 03"),
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
)
|
||||
},
|
||||
})
|
@ -0,0 +1,71 @@
|
||||
package reflog
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DoNotShowBranchMarkersInReflogSubcommits = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Verify that no branch heads are shown in the subcommits view of a reflog entry",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.NewBranch("branch1")
|
||||
shell.EmptyCommit("one")
|
||||
shell.EmptyCommit("two")
|
||||
shell.NewBranch("branch2")
|
||||
shell.EmptyCommit("three")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
// Check that the local commits view does show a branch marker for branch1
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("CI three"),
|
||||
Contains("CI * two"),
|
||||
Contains("CI one"),
|
||||
)
|
||||
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
// Check out branch1
|
||||
NavigateToLine(Contains("branch1")).
|
||||
PressPrimaryAction().
|
||||
// Look at the subcommits of branch2
|
||||
NavigateToLine(Contains("branch2")).
|
||||
PressEnter().
|
||||
// Check that we see a marker for branch1 here (but not for
|
||||
// branch2), even though branch1 is checked out
|
||||
Tap(func() {
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("CI three"),
|
||||
Contains("CI * two"),
|
||||
Contains("CI one"),
|
||||
).
|
||||
PressEscape()
|
||||
}).
|
||||
// Check out branch2 again
|
||||
NavigateToLine(Contains("branch2")).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Views().ReflogCommits().
|
||||
Focus().
|
||||
TopLines(
|
||||
Contains("checkout: moving from branch1 to branch2").IsSelected(),
|
||||
).
|
||||
PressEnter().
|
||||
// Check that the subcommits view for a reflog entry doesn't show
|
||||
// any branch markers
|
||||
Tap(func() {
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("CI three"),
|
||||
Contains("CI two"),
|
||||
Contains("CI one"),
|
||||
)
|
||||
})
|
||||
},
|
||||
})
|
@ -117,7 +117,6 @@ var tests = []*components.IntegrationTest{
|
||||
interactive_rebase.AmendMerge,
|
||||
interactive_rebase.AmendNonHeadCommitDuringRebase,
|
||||
interactive_rebase.DropTodoCommitWithUpdateRef,
|
||||
interactive_rebase.DropTodoCommitWithUpdateRefShowBranchHeads,
|
||||
interactive_rebase.DropWithCustomCommentChar,
|
||||
interactive_rebase.EditFirstCommit,
|
||||
interactive_rebase.EditNonTodoCommitDuringRebase,
|
||||
@ -164,6 +163,7 @@ var tests = []*components.IntegrationTest{
|
||||
patch_building.StartNewPatch,
|
||||
reflog.Checkout,
|
||||
reflog.CherryPick,
|
||||
reflog.DoNotShowBranchMarkersInReflogSubcommits,
|
||||
reflog.Patch,
|
||||
reflog.Reset,
|
||||
staging.DiffContextChange,
|
||||
|
Loading…
x
Reference in New Issue
Block a user