Sort local branches with the same committer date in stack order

In date order, the branches panel lists a stack of branches
alphabetically rather than from the top of the stack down. Rebasing a
stack creates all of its commits within the same second, so all of its
branch tips end up carrying the same committer date, and git sorts refs
with equal committer dates by name.

Sort each group of branches that share a committer date by ancestry
instead, so that a branch comes before the branches it is based on.
Branches that are not descended from one another keep the alphabetical
order git gave them. Determining the ancestry takes one more
for-each-ref call, and it only runs when there is a group to sort.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-09-19 18:10:59 +02:00
co-authored by Claude Opus 5
parent 1441359ccc
commit effe661e09
6 changed files with 633 additions and 8 deletions
+24 -7
View File
@@ -71,9 +71,17 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit,
onWorker func(func() error),
renderFunc func(),
) ([]*models.Branch, error) {
branches := self.obtainBranches()
branches, tips := self.obtainBranches()
if self.UserConfig().Git.LocalBranchSortOrder == "recency" {
switch self.UserConfig().Git.LocalBranchSortOrder {
case "date":
if err := sortRefsWithEqualDatesByAncestry(
self.cmd, self.version, branches, (*models.Branch).FullRefName, tips,
); err != nil {
self.Log.Errorf("Failed to sort branches by ancestry: %v", err)
}
case "recency":
reflogBranches := self.obtainReflogBranches(reflogCommits)
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*models.Branch, 0)
@@ -274,7 +282,9 @@ func (self *BranchLoader) GetBaseBranch(branch *models.Branch, mainBranches *Mai
return split[0], nil
}
func (self *BranchLoader) obtainBranches() []*models.Branch {
// Returns the branches, along with the tip of each of them, keyed by full ref
// name
func (self *BranchLoader) obtainBranches() ([]*models.Branch, map[string]refTip) {
output, err := self.getRawBranches()
if err != nil {
panic(err)
@@ -283,7 +293,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
return lo.FilterMap(outputLines, func(line string, _ int) (*models.Branch, bool) {
tips := make(map[string]refTip, len(outputLines))
branches := lo.FilterMap(outputLines, func(line string, _ int) (*models.Branch, bool) {
if line == "" {
return nil, false
}
@@ -297,8 +308,12 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
}
storeCommitDateAsRecency := self.UserConfig().Git.LocalBranchSortOrder != "recency"
return obtainBranch(split, storeCommitDateAsRecency), true
branch, tip := obtainBranch(split, storeCommitDateAsRecency)
tips[branch.FullRefName()] = tip
return branch, true
})
return branches, tips
}
func (self *BranchLoader) getRawBranches() (string, error) {
@@ -340,7 +355,7 @@ var branchFields = []string{
}
// Obtain branch information from parsed line output of getRawBranches()
func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch {
func obtainBranch(split []string, storeCommitDateAsRecency bool) (*models.Branch, refTip) {
headMarker := split[0]
fullName := split[1]
upstreamName := split[2]
@@ -361,7 +376,7 @@ func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch
}
}
return &models.Branch{
branch := &models.Branch{
Name: name,
Recency: recency,
AheadForPull: aheadForPull,
@@ -373,6 +388,8 @@ func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch
Subject: subject,
CommitHash: commitHash,
}
return branch, refTip{hash: commitHash, committerDate: commitDate}
}
func parseUpstreamInfo(upstreamName string, track string) (string, string, bool) {
@@ -120,8 +120,9 @@ func TestObtainBranch(t *testing.T) {
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
branch := obtainBranch(s.input, s.storeCommitDateAsRecency)
branch, tip := obtainBranch(s.input, s.storeCommitDateAsRecency)
assert.EqualValues(t, s.expectedBranch, branch)
assert.Equal(t, refTip{hash: "123", committerDate: timeStamp}, tip)
})
}
}
+223
View File
@@ -0,0 +1,223 @@
package git_commands
import (
"slices"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/samber/lo"
)
// The tip commit of a ref, as far as sorting refs by ancestry needs it. The
// date is the raw %(committerdate:unix) field, and is only ever compared for
// equality.
type refTip struct {
hash string
committerDate string
}
// Determining ancestry costs one %(ahead-behind:<tip>) field per tip, and git
// evaluates each of those for every tip we ask about, so the work grows with
// the square of the number of tips. Branches whose tips came out of one rebase
// are nowhere near this many, so leave the refs in the order git returned them
// once the tips add up to more than this.
const maxTipsForAncestrySorting = 100
// Sorts each group of refs that share a committer date so that a ref comes
// before the refs it is descended from. Refs that are not descended from one
// another keep the order they came in.
//
// git sorts refs with equal committer dates by name, and a stack of branches
// that gets rebased in one go ends up with the same committer date on all of
// its tips. Without this, such a stack appears in alphabetical order.
//
// refs must be sorted by committer date already, and tips must have an entry
// for each of them.
func sortRefsWithEqualDatesByAncestry[T any](
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
refs []T,
fullRefName func(T) string,
tips map[string]refTip,
) error {
// %(ahead-behind:...) was added in git 2.41
if !version.IsAtLeast(2, 41, 0) {
return nil
}
groups := lo.Filter(groupRefsWithEqualDates(refs, fullRefName, tips),
func(group []T, _ int) bool {
return pointAtMoreThanOneCommit(group, fullRefName, tips)
})
if len(groups) == 0 {
return nil
}
// Ancestry is a property of the tip commits, so ask git about each of them
// once, however many refs point at it. A repository with several remotes
// has the same branches under each of them, and they all share a date.
tipHashes := []string{}
refNames := []string{}
seenTips := set.New[string]()
for _, ref := range lo.Flatten(groups) {
refName := fullRefName(ref)
if hash := tips[refName].hash; !seenTips.Includes(hash) {
seenTips.Add(hash)
tipHashes = append(tipHashes, hash)
refNames = append(refNames, refName)
}
}
if len(tipHashes) > maxTipsForAncestrySorting {
return nil
}
containedTips, err := loadContainedTips(cmd, tipHashes, refNames)
if err != nil {
return err
}
for _, group := range groups {
sortGroupByAncestry(group, fullRefName, tips, containedTips)
}
return nil
}
// Refs that all point at the same commit have no order to be put in, so
// there is nothing to ask git about them
func pointAtMoreThanOneCommit[T any](
refs []T,
fullRefName func(T) string,
tips map[string]refTip,
) bool {
firstHash := tips[fullRefName(refs[0])].hash
return lo.SomeBy(refs, func(ref T) bool {
return tips[fullRefName(ref)].hash != firstHash
})
}
// Returns the runs of consecutive refs that share a committer date, for runs of
// more than one ref. The returned slices share their backing array with refs,
// so sorting a run sorts that part of refs.
func groupRefsWithEqualDates[T any](
refs []T,
fullRefName func(T) string,
tips map[string]refTip,
) [][]T {
committerDate := func(ref T) string {
return tips[fullRefName(ref)].committerDate
}
groups := [][]T{}
start := 0
for i := 1; i <= len(refs); i++ {
if i < len(refs) && committerDate(refs[i]) == committerDate(refs[start]) {
continue
}
if i-start > 1 {
groups = append(groups, refs[start:i])
}
start = i
}
return groups
}
// For each of the given tips, which of the tips its history contains. Keyed by
// tip hash, with an entry for every tip passed in. refNames names, for each
// tip, a ref that points at it; git reports the values by ref name.
func loadContainedTips(
cmd oscommands.ICmdObjBuilder,
tipHashes []string,
refNames []string,
) (map[string]*set.Set[string], error) {
output, err := cmd.New(
buildAheadBehindForEachRefArgs(tipHashes, refNames),
).DontLog().RunWithOutput()
if err != nil {
return nil, err
}
containedTips := make(map[string]*set.Set[string], len(tipHashes))
for _, hash := range tipHashes {
containedTips[hash] = set.New[string]()
}
tipByRefName := make(map[string]string, len(refNames))
for i, refName := range refNames {
tipByRefName[refName] = tipHashes[i]
}
for _, entry := range parseAheadBehindForEachRefOutput(output, len(tipHashes)) {
hash, ok := tipByRefName[entry.refName]
if !ok {
continue
}
contained := containedTips[hash]
for i, ab := range entry.aheadBehinds {
// The tip's history contains the other tip if it has commits the
// other one doesn't have, and the other one has none it doesn't
// have.
if ab.valid && ab.ahead > 0 && ab.behind == 0 {
contained.Add(tipHashes[i])
}
}
}
return containedTips, nil
}
func sortGroupByAncestry[T any](
group []T,
fullRefName func(T) string,
tips map[string]refTip,
containedTips map[string]*set.Set[string],
) {
hashes := lo.Map(group, func(ref T, _ int) string {
return tips[fullRefName(ref)].hash
})
contained := lo.Map(hashes, func(hash string, _ int) *set.Set[string] {
if contained, ok := containedTips[hash]; ok {
return contained
}
return set.New[string]()
})
isDescendedFrom := func(descendant int, ancestor int) bool {
return contained[descendant].Includes(hashes[ancestor])
}
// Indices into group, holding the refs we haven't placed yet
remaining := lo.Range(len(group))
// A ref can only go in once every ref that is descended from it is in
canBePlaced := func(i int) bool {
return !lo.SomeBy(remaining, func(j int) bool {
return isDescendedFrom(j, i)
})
}
sorted := make([]T, 0, len(group))
lastPlaced := -1
for len(remaining) > 0 {
next := -1
if lastPlaced != -1 {
// Walk from the ref we placed last down to the refs it is based on,
// so that the branches of a stack come out as one run even when an
// unrelated branch sorts into the middle of them by name
next = slices.IndexFunc(remaining, func(i int) bool {
return isDescendedFrom(lastPlaced, i) && canBePlaced(i)
})
}
if next == -1 {
next = slices.IndexFunc(remaining, canBePlaced)
}
if next == -1 {
// Commits can't descend from each other in a circle; only take the
// first ref so that we don't spin if the values ever say otherwise.
next = 0
}
lastPlaced = remaining[next]
sorted = append(sorted, group[lastPlaced])
remaining = slices.Delete(remaining, next, next+1)
}
copy(group, sorted)
}
@@ -0,0 +1,331 @@
package git_commands
import (
"errors"
"fmt"
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
// A branch as the tests below describe it: a name, the hash of its tip, and the
// committer date of its tip
type testRef struct {
name string
hash string
date string
}
func buildTestRefs(refs []testRef) ([]*models.Branch, map[string]refTip) {
branches := lo.Map(refs, func(ref testRef, _ int) *models.Branch {
return &models.Branch{Name: ref.name, CommitHash: ref.hash}
})
tips := make(map[string]refTip, len(refs))
for _, ref := range refs {
tips["refs/heads/"+ref.name] = refTip{hash: ref.hash, committerDate: ref.date}
}
return branches, tips
}
func branchNames(branches []*models.Branch) []string {
return lo.Map(branches, func(branch *models.Branch, _ int) string {
return branch.Name
})
}
func TestGroupRefsWithEqualDates(t *testing.T) {
scenarios := []struct {
testName string
refs []testRef
expected [][]string
}{
{
testName: "no refs",
refs: []testRef{},
expected: [][]string{},
},
{
testName: "all dates distinct",
refs: []testRef{
{name: "a", hash: "1", date: "300"},
{name: "b", hash: "2", date: "200"},
{name: "c", hash: "3", date: "100"},
},
expected: [][]string{},
},
{
testName: "all dates equal",
refs: []testRef{
{name: "a", hash: "1", date: "100"},
{name: "b", hash: "2", date: "100"},
{name: "c", hash: "3", date: "100"},
},
expected: [][]string{{"a", "b", "c"}},
},
{
testName: "two groups, with a lone ref between and after them",
refs: []testRef{
{name: "a", hash: "1", date: "300"},
{name: "b", hash: "2", date: "300"},
{name: "c", hash: "3", date: "200"},
{name: "d", hash: "4", date: "100"},
{name: "e", hash: "5", date: "100"},
{name: "f", hash: "6", date: "50"},
},
expected: [][]string{{"a", "b"}, {"d", "e"}},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
branches, tips := buildTestRefs(s.refs)
groups := groupRefsWithEqualDates(branches, (*models.Branch).FullRefName, tips)
assert.Equal(t, s.expected, lo.Map(groups,
func(group []*models.Branch, _ int) []string {
return branchNames(group)
}))
})
}
}
func TestSortRefsWithEqualDatesByAncestry(t *testing.T) {
// A stack of three branches, all committed within the same second, as git
// returns them: sorted by name. bottom is the base of middle, which is the
// base of top.
stack := []testRef{
{name: "middle", hash: "m", date: "100"},
{name: "top", hash: "t", date: "100"},
{name: "bottom", hash: "b", date: "100"},
}
// One %(ahead-behind:<hash>) field per tip, in the order the tips appear in
// the branch list
stackOutput := "refs/heads/middle\x000 0\x000 1\x001 0\n" +
"refs/heads/top\x001 0\x000 0\x002 0\n" +
"refs/heads/bottom\x000 1\x000 2\x000 0\n"
stackArgs := []string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:m)%00%(ahead-behind:t)%00%(ahead-behind:b)",
"refs/heads/middle", "refs/heads/top", "refs/heads/bottom",
}
scenarios := []struct {
testName string
refs []testRef
gitVersion *GitVersion
expectedArgs []string
output string
outputErr error
expectedOrder []string
expectedErr string
}{
{
testName: "a stack of branches is sorted from the top down",
refs: stack,
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: stackArgs,
output: stackOutput,
expectedOrder: []string{"top", "middle", "bottom"},
},
{
testName: "git too old for %(ahead-behind:...), so nothing to do",
refs: stack,
gitVersion: &GitVersion{2, 40, 0, ""},
expectedOrder: []string{"middle", "top", "bottom"},
},
{
testName: "no two refs share a date, so nothing to do",
refs: []testRef{
{name: "middle", hash: "m", date: "300"},
{name: "top", hash: "t", date: "200"},
{name: "bottom", hash: "b", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedOrder: []string{"middle", "top", "bottom"},
},
{
testName: "the command fails",
refs: stack,
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: stackArgs,
outputErr: errors.New("fatal: failed to find 'm'"),
expectedOrder: []string{"middle", "top", "bottom"},
expectedErr: "fatal: failed to find 'm'",
},
{
testName: "a branch unrelated to the stack is placed by name",
refs: []testRef{
{name: "middle", hash: "m", date: "100"},
{name: "other", hash: "o", date: "100"},
{name: "top", hash: "t", date: "100"},
{name: "bottom", hash: "b", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: []string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:m)%00%(ahead-behind:o)%00%(ahead-behind:t)%00%(ahead-behind:b)",
"refs/heads/middle", "refs/heads/other", "refs/heads/top", "refs/heads/bottom",
},
output: "refs/heads/middle\x000 0\x002 1\x000 1\x001 0\n" +
"refs/heads/other\x001 2\x000 0\x001 3\x001 1\n" +
"refs/heads/top\x001 0\x003 1\x000 0\x002 0\n" +
"refs/heads/bottom\x000 1\x001 1\x000 2\x000 0\n",
expectedOrder: []string{"other", "top", "middle", "bottom"},
},
{
testName: "a stack stays together when a branch sorts into the middle of it",
refs: []testRef{
{name: "add-tests", hash: "t", date: "100"},
{name: "cleanup", hash: "c", date: "100"},
{name: "fix-parser", hash: "p", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: []string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:t)%00%(ahead-behind:c)%00%(ahead-behind:p)",
"refs/heads/add-tests", "refs/heads/cleanup", "refs/heads/fix-parser",
},
// add-tests is based on fix-parser, and cleanup is on a line of its
// own, but its name sorts between the two
output: "refs/heads/add-tests\x000 0\x002 1\x001 0\n" +
"refs/heads/cleanup\x001 2\x000 0\x001 1\n" +
"refs/heads/fix-parser\x000 1\x001 1\x000 0\n",
expectedOrder: []string{"add-tests", "fix-parser", "cleanup"},
},
{
testName: "each group is sorted on its own",
refs: []testRef{
{name: "middle", hash: "m", date: "200"},
{name: "top", hash: "t", date: "200"},
{name: "lone", hash: "l", date: "150"},
{name: "base", hash: "a", date: "100"},
{name: "derived", hash: "d", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: []string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:m)%00%(ahead-behind:t)%00%(ahead-behind:a)%00%(ahead-behind:d)",
"refs/heads/middle", "refs/heads/top", "refs/heads/base", "refs/heads/derived",
},
output: "refs/heads/middle\x000 0\x000 1\x002 0\x001 0\n" +
"refs/heads/top\x001 0\x000 0\x003 0\x002 0\n" +
"refs/heads/base\x000 2\x000 3\x000 0\x000 1\n" +
"refs/heads/derived\x000 1\x000 2\x001 0\x000 0\n",
expectedOrder: []string{"top", "middle", "lone", "derived", "base"},
},
{
testName: "a group whose branches are all on one commit is left alone",
refs: []testRef{
{name: "a", hash: "x", date: "100"},
{name: "b", hash: "x", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedOrder: []string{"a", "b"},
},
{
testName: "branches on the same commit are asked about once",
refs: []testRef{
{name: "mirror-1", hash: "s", date: "100"},
{name: "mirror-2", hash: "s", date: "100"},
{name: "stack-bottom", hash: "u", date: "100"},
{name: "stack-top", hash: "t", date: "100"},
},
gitVersion: &GitVersion{2, 41, 0, ""},
expectedArgs: []string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:s)%00%(ahead-behind:u)%00%(ahead-behind:t)",
"refs/heads/mirror-1", "refs/heads/stack-bottom", "refs/heads/stack-top",
},
output: "refs/heads/mirror-1\x000 0\x001 1\x001 2\n" +
"refs/heads/stack-bottom\x001 1\x000 0\x000 1\n" +
"refs/heads/stack-top\x002 1\x001 0\x000 0\n",
expectedOrder: []string{"mirror-1", "mirror-2", "stack-top", "stack-bottom"},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t)
if s.expectedArgs != nil {
runner.ExpectGitArgs(s.expectedArgs, s.output, s.outputErr)
}
gitCommon := buildGitCommon(commonDeps{runner: runner, gitVersion: s.gitVersion})
branches, tips := buildTestRefs(s.refs)
err := sortRefsWithEqualDatesByAncestry(gitCommon.cmd, gitCommon.version,
branches, (*models.Branch).FullRefName, tips)
if s.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.ErrorContains(t, err, s.expectedErr)
}
assert.Equal(t, s.expectedOrder, branchNames(branches))
runner.CheckForMissingCalls()
})
}
}
func TestSortRefsWithEqualDatesByAncestry_TooManyTips(t *testing.T) {
refs := lo.Map(lo.Range(maxTipsForAncestrySorting+1), func(i int, _ int) testRef {
return testRef{
name: fmt.Sprintf("branch-%03d", i),
hash: fmt.Sprintf("hash-%03d", i),
date: "100",
}
})
// The runner fails the test if the command runs at all
runner := oscommands.NewFakeRunner(t)
gitCommon := buildGitCommon(commonDeps{runner: runner, gitVersion: &GitVersion{2, 41, 0, ""}})
branches, tips := buildTestRefs(refs)
err := sortRefsWithEqualDatesByAncestry(gitCommon.cmd, gitCommon.version,
branches, (*models.Branch).FullRefName, tips)
assert.NoError(t, err)
assert.Equal(t, lo.Map(refs, func(ref testRef, _ int) string { return ref.name }),
branchNames(branches))
runner.CheckForMissingCalls()
}
// A repository with many remotes has the same branches under each of them, so
// a group can hold far more refs than the commits they point at
func TestSortRefsWithEqualDatesByAncestry_ManyRefsOnTwoTips(t *testing.T) {
refs := lo.Map(lo.Range(4*maxTipsForAncestrySorting), func(i int, _ int) testRef {
return testRef{
name: fmt.Sprintf("branch-%03d", i),
hash: lo.Ternary(i%2 == 0, "bottom", "top"),
date: "100",
}
})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs([]string{
"for-each-ref",
"--format=%(refname)%00%(ahead-behind:bottom)%00%(ahead-behind:top)",
"refs/heads/branch-000", "refs/heads/branch-001",
},
"refs/heads/branch-000\x000 0\x000 1\n"+
"refs/heads/branch-001\x001 0\x000 0\n", nil)
gitCommon := buildGitCommon(commonDeps{runner: runner, gitVersion: &GitVersion{2, 41, 0, ""}})
branches, tips := buildTestRefs(refs)
err := sortRefsWithEqualDatesByAncestry(gitCommon.cmd, gitCommon.version,
branches, (*models.Branch).FullRefName, tips)
assert.NoError(t, err)
// the branches on the top commit first, each half still ordered by name
expected := append(
lo.FilterMap(refs, func(ref testRef, _ int) (string, bool) {
return ref.name, ref.hash == "top"
}),
lo.FilterMap(refs, func(ref testRef, _ int) (string, bool) {
return ref.name, ref.hash == "bottom"
})...)
assert.Equal(t, expected, branchNames(branches))
runner.CheckForMissingCalls()
}
@@ -0,0 +1,52 @@
package branch
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var SortLocalBranchesInStackOrder = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Sort each group of branches that share a committer date so that a branch comes before the ones it is based on",
ExtraCmdArgs: []string{},
Skip: false,
GitVersion: AtLeast("2.41.0"),
SetupConfig: func(config *config.AppConfig) {
config.GetUserConfig().Git.LocalBranchSortOrder = "date"
},
SetupRepo: func(shell *Shell) {
// Rebasing a stack of branches gives every commit it creates the same
// committer date; give each of these two stacks one date of its own for
// the same effect.
dateOfFirstStack := "2024-01-01 10:00:00"
dateOfSecondStack := "2024-01-01 11:00:00"
shell.
EmptyCommitWithDate("base", dateOfFirstStack).
NewBranch("one-bottom").
EmptyCommitWithDate("one-bottom", dateOfFirstStack).
NewBranch("one-middle").
EmptyCommitWithDate("one-middle", dateOfFirstStack).
NewBranch("one-top").
EmptyCommitWithDate("one-top", dateOfFirstStack).
NewBranchFrom("unrelated", "master").
EmptyCommitWithDate("unrelated", dateOfFirstStack).
NewBranchFrom("two-bottom", "master").
EmptyCommitWithDate("two-bottom", dateOfSecondStack).
NewBranch("two-top").
EmptyCommitWithDate("two-top", dateOfSecondStack).
Checkout("master")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Focus().
Lines(
Contains("master").IsSelected(),
Contains("two-top"),
Contains("two-bottom"),
Contains("one-top"),
Contains("one-middle"),
Contains("one-bottom"),
Contains("unrelated"),
)
},
})
+1
View File
@@ -86,6 +86,7 @@ var tests = []*components.IntegrationTest{
branch.ShowDivergenceFromUpstream,
branch.ShowDivergenceFromUpstreamNoDivergence,
branch.SortLocalBranches,
branch.SortLocalBranchesInStackOrder,
branch.SortRemoteBranches,
branch.SquashMerge,
branch.Suggestions,