1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-12 11:15:00 +02:00
lazygit/pkg/commands/branch_list_builder.go

168 lines
4.6 KiB
Go
Raw Normal View History

package commands
import (
"regexp"
"strconv"
"strings"
2018-08-13 12:26:02 +02:00
"github.com/jesseduffield/lazygit/pkg/utils"
2018-08-23 14:22:03 +02:00
"github.com/sirupsen/logrus"
)
// context:
// we want to only show 'safe' branches (ones that haven't e.g. been deleted)
// which `git branch -a` gives us, but we also want the recency data that
// git reflog gives us.
// So we get the HEAD, then append get the reflog branches that intersect with
// our safe branches, then add the remaining safe branches, ensuring uniqueness
// along the way
// if we find out we need to use one of these functions in the git.go file, we
// can just pull them out of here and put them there and then call them from in here
2018-08-13 12:26:02 +02:00
// BranchListBuilder returns a list of Branch objects for the current repo
2018-08-12 13:04:47 +02:00
type BranchListBuilder struct {
Log *logrus.Entry
GitCommand *GitCommand
ReflogCommits []*Commit
2018-08-12 13:04:47 +02:00
}
2018-08-13 12:26:02 +02:00
// NewBranchListBuilder builds a new branch list builder
func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand, reflogCommits []*Commit) (*BranchListBuilder, error) {
2018-08-13 12:26:02 +02:00
return &BranchListBuilder{
Log: log,
GitCommand: gitCommand,
ReflogCommits: reflogCommits,
2018-08-13 12:26:02 +02:00
}, nil
}
func (b *BranchListBuilder) obtainBranches() []*Branch {
2020-03-23 12:26:24 +02:00
cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads`
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
if err != nil {
panic(err)
}
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
2020-03-23 12:26:24 +02:00
branches := make([]*Branch, 0, len(outputLines))
for _, line := range outputLines {
if line == "" {
continue
}
split := strings.Split(line, SEPARATION_CHAR)
2020-03-19 03:04:17 +02:00
name := split[1]
2020-03-23 12:26:24 +02:00
branch := &Branch{
Name: name,
Pullables: "?",
Pushables: "?",
2020-03-19 03:04:17 +02:00
Head: split[0] == "*",
}
2020-03-23 12:26:24 +02:00
2020-03-19 03:04:17 +02:00
upstreamName := split[2]
if upstreamName == "" {
2020-03-23 12:26:24 +02:00
branches = append(branches, branch)
continue
}
2020-03-23 12:26:24 +02:00
branch.UpstreamName = upstreamName
2020-03-19 03:04:17 +02:00
track := split[3]
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
2020-03-23 12:26:24 +02:00
branch.Pushables = match[1]
} else {
2020-03-23 12:26:24 +02:00
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
2020-03-23 12:26:24 +02:00
branch.Pullables = match[1]
} else {
2020-03-23 12:26:24 +02:00
branch.Pullables = "0"
2018-08-11 08:11:17 +02:00
}
2020-03-23 12:26:24 +02:00
branches = append(branches, branch)
2018-08-11 08:11:17 +02:00
}
return branches
2018-08-11 08:11:17 +02:00
}
2018-08-13 12:26:02 +02:00
// Build the list of branches for the current repo
func (b *BranchListBuilder) Build() []*Branch {
branches := b.obtainBranches()
2020-03-19 03:04:17 +02:00
reflogBranches := b.obtainReflogBranches()
// 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([]*Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
2020-03-19 03:04:17 +02:00
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = append(branches[0:j], branches[j+1:]...)
continue outer
}
}
}
branches = append(branchesWithRecency, branches...)
2020-03-23 12:26:24 +02:00
foundHead := false
for i, branch := range branches {
2020-03-19 03:04:17 +02:00
if branch.Head {
2020-03-23 12:26:24 +02:00
foundHead = true
2020-03-19 03:04:17 +02:00
branch.Recency = " *"
branches = append(branches[0:i], branches[i+1:]...)
branches = append([]*Branch{branch}, branches...)
break
2018-08-13 12:26:02 +02:00
}
}
2020-03-23 12:26:24 +02:00
if !foundHead {
2020-03-26 11:29:35 +02:00
currentBranchName, currentBranchDisplayName, err := b.GitCommand.CurrentBranchName()
if err != nil {
panic(err)
}
branches = append([]*Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...)
2020-03-23 12:26:24 +02:00
}
return branches
}
// TODO: only look at the new reflog commits, and otherwise store the recencies in
// int form against the branch to recalculate the time ago
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
foundBranchesMap := map[string]bool{}
re := regexp.MustCompile(`checkout: moving from ([\S]+) to ([\S]+)`)
reflogBranches := make([]*Branch, 0, len(b.ReflogCommits))
for _, commit := range b.ReflogCommits {
if match := re.FindStringSubmatch(commit.Name); len(match) == 3 {
timestamp, err := strconv.Atoi(commit.Date)
if err != nil {
b.Log.Errorf("couldn't parse reflog date: %s", commit.Date)
continue
}
recency := utils.UnixToTimeAgo(timestamp)
for _, branchName := range match[1:] {
if !foundBranchesMap[branchName] {
foundBranchesMap[branchName] = true
reflogBranches = append(reflogBranches, &Branch{
Recency: recency,
Name: branchName,
})
}
}
}
}
return reflogBranches
}