mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-28 02:07:09 -04:00
Add a function to find the branches stacked below a branch
lazygit will offer to push these along with the current branch, so that a rebased stack can be pushed in one go. The commits panel already marks the tips of stacked branches, and this uses the same criterion. A branch is below the given one if its tip is one of the loaded commits of that branch that isn't merged into a main branch. This needs no git command. In whole-graph mode the commit list also contains commits of other branches, but those are marked as merged, so they don't count. Not used yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
6378618544
commit
c3b09cebad
@@ -0,0 +1,39 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// Returns the branches stacked below the given branch: those whose tip is one
|
||||
// of the given commits of the branch that hasn't been merged into a main
|
||||
// branch yet. The branch itself and the main branches are left out. The
|
||||
// result is in the order in which the tips appear in commits, so the branch
|
||||
// closest to the given one comes first.
|
||||
//
|
||||
// Only the commits passed in are looked at, so a branch whose tip is further
|
||||
// down the history than the loaded commits is not found.
|
||||
func BranchesBelowInStack(
|
||||
commits []*models.Commit,
|
||||
branches []*models.Branch,
|
||||
branch *models.Branch,
|
||||
mainBranches []string,
|
||||
) []*models.Branch {
|
||||
branchesByTip := map[string][]*models.Branch{}
|
||||
for _, other := range branches {
|
||||
if other.Name == branch.Name || lo.Contains(mainBranches, other.Name) {
|
||||
continue
|
||||
}
|
||||
branchesByTip[other.CommitHash] = append(branchesByTip[other.CommitHash], other)
|
||||
}
|
||||
|
||||
result := []*models.Branch{}
|
||||
for _, commit := range commits {
|
||||
if commit.IsTODO() || commit.Status == models.StatusMerged {
|
||||
continue
|
||||
}
|
||||
result = append(result, branchesByTip[commit.Hash()]...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stefanhaller/git-todo-parser/todo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBranchesBelowInStack(t *testing.T) {
|
||||
hashPool := &utils.StringPool{}
|
||||
commit := func(hash string, status models.CommitStatus) *models.Commit {
|
||||
return models.NewCommit(hashPool, models.NewCommitOpts{Hash: hash, Status: status})
|
||||
}
|
||||
todoCommit := func(hash string) *models.Commit {
|
||||
return models.NewCommit(hashPool, models.NewCommitOpts{Hash: hash, Action: todo.Pick, Status: models.StatusRebasing})
|
||||
}
|
||||
branch := func(name string, tip string) *models.Branch {
|
||||
return &models.Branch{Name: name, CommitHash: tip}
|
||||
}
|
||||
|
||||
current := branch("current", "c3")
|
||||
mainBranches := []string{"master", "main"}
|
||||
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
commits []*models.Commit
|
||||
branches []*models.Branch
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
testName: "stack of branches, closest to the current one first",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
commit("c2", models.StatusPushed),
|
||||
commit("c1", models.StatusUnpushed),
|
||||
commit("m1", models.StatusMerged),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("first", "c1"), branch("second", "c2"), branch("master", "m1")},
|
||||
expected: []string{"second", "first"},
|
||||
},
|
||||
{
|
||||
testName: "branch pointing at the same commit as the current one",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
commit("m1", models.StatusMerged),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("twin", "c3")},
|
||||
expected: []string{"twin"},
|
||||
},
|
||||
{
|
||||
testName: "several branches pointing at one commit keep their order",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
commit("c1", models.StatusUnpushed),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("one", "c1"), branch("two", "c1")},
|
||||
expected: []string{"one", "two"},
|
||||
},
|
||||
{
|
||||
testName: "main branches are left out even if their tip isn't merged",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
commit("c1", models.StatusUnpushed),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("main", "c1")},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
testName: "branches whose tip is merged are left out",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
commit("m1", models.StatusMerged),
|
||||
commit("m0", models.StatusMerged),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("old", "m0"), branch("master", "m1")},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
testName: "branches whose tip is not among the commits are left out",
|
||||
commits: []*models.Commit{
|
||||
commit("c3", models.StatusUnpushed),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("sibling", "s1")},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
testName: "todo commits of a rebase don't count",
|
||||
commits: []*models.Commit{
|
||||
todoCommit("t1"),
|
||||
commit("c3", models.StatusUnpushed),
|
||||
},
|
||||
branches: []*models.Branch{current, branch("other", "t1")},
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
result := BranchesBelowInStack(s.commits, s.branches, current, mainBranches)
|
||||
names := lo.Map(result, func(b *models.Branch, _ int) string { return b.Name })
|
||||
assert.Equal(t, s.expected, names)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user