summaryrefslogtreecommitdiffstats
path: root/pkg/commands/loading_branches.go
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2020-09-29 20:03:39 +1000
committerJesse Duffield <jessedduffield@gmail.com>2020-09-29 20:48:49 +1000
commit72af7e41778bca93d82fa668641f515fba1d92bc (patch)
tree7e755e857be72205ee99641d5eb5d4556151ad8f /pkg/commands/loading_branches.go
parent1767f91047a35318f6b1e469199c8a7f547f2afc (diff)
factor out code from git.go
Diffstat (limited to 'pkg/commands/loading_branches.go')
-rw-r--r--pkg/commands/loading_branches.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/pkg/commands/loading_branches.go b/pkg/commands/loading_branches.go
new file mode 100644
index 000000000..99c60d9c9
--- /dev/null
+++ b/pkg/commands/loading_branches.go
@@ -0,0 +1,161 @@
+package commands
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/jesseduffield/lazygit/pkg/models"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+ "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
+
+// BranchListBuilder returns a list of Branch objects for the current repo
+type BranchListBuilder struct {
+ Log *logrus.Entry
+ GitCommand *GitCommand
+ ReflogCommits []*models.Commit
+}
+
+// NewBranchListBuilder builds a new branch list builder
+func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand, reflogCommits []*models.Commit) (*BranchListBuilder, error) {
+ return &BranchListBuilder{
+ Log: log,
+ GitCommand: gitCommand,
+ ReflogCommits: reflogCommits,
+ }, nil
+}
+
+func (b *BranchListBuilder) obtainBranches() []*models.Branch {
+ 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")
+ branches := make([]*models.Branch, 0, len(outputLines))
+ for _, line := range outputLines {
+ if line == "" {
+ continue
+ }
+
+ split := strings.Split(line, SEPARATION_CHAR)
+
+ name := strings.TrimPrefix(split[1], "heads/")
+ branch := &models.Branch{
+ Name: name,
+ Pullables: "?",
+ Pushables: "?",
+ Head: split[0] == "*",
+ }
+
+ upstreamName := split[2]
+ if upstreamName == "" {
+ branches = append(branches, branch)
+ continue
+ }
+
+ branch.UpstreamName = upstreamName
+
+ track := split[3]
+ re := regexp.MustCompile(`ahead (\d+)`)
+ match := re.FindStringSubmatch(track)
+ if len(match) > 1 {
+ branch.Pushables = match[1]
+ } else {
+ branch.Pushables = "0"
+ }
+
+ re = regexp.MustCompile(`behind (\d+)`)
+ match = re.FindStringSubmatch(track)
+ if len(match) > 1 {
+ branch.Pullables = match[1]
+ } else {
+ branch.Pullables = "0"
+ }
+
+ branches = append(branches, branch)
+ }
+
+ return branches
+}
+
+// Build the list of branches for the current repo
+func (b *BranchListBuilder) Build() []*models.Branch {
+ branches := b.obtainBranches()
+
+ 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([]*models.Branch, 0)
+outer:
+ for _, reflogBranch := range reflogBranches {
+ for j, branch := range branches {
+ 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...)
+
+ foundHead := false
+ for i, branch := range branches {
+ if branch.Head {
+ foundHead = true
+ branch.Recency = " *"
+ branches = append(branches[0:i], branches[i+1:]...)
+ branches = append([]*models.Branch{branch}, branches...)
+ break
+ }
+ }
+ if !foundHead {
+ currentBranchName, currentBranchDisplayName, err := b.GitCommand.CurrentBranchName()
+ if err != nil {
+ panic(err)
+ }
+ branches = append([]*models.Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...)
+ }
+ 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() []*models.Branch {
+ foundBranchesMap := map[string]bool{}
+ re := regexp.MustCompile(`checkout: moving from ([\S]+) to ([\S]+)`)
+ reflogBranches := make([]*models.Branch, 0, len(b.ReflogCommits))
+ for _, commit := range b.ReflogCommits {
+ if match := re.FindStringSubmatch(commit.Name); len(match) == 3 {
+ recency := utils.UnixToTimeAgo(commit.UnixTimestamp)
+ for _, branchName := range match[1:] {
+ if !foundBranchesMap[branchName] {
+ foundBranchesMap[branchName] = true
+ reflogBranches = append(reflogBranches, &models.Branch{
+ Recency: recency,
+ Name: branchName,
+ })
+ }
+ }
+ }
+ }
+ return reflogBranches
+}