From 96c2887fd0c1ca95e6b3d55756be8d424f8d905a Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 30 Dec 2021 13:44:41 +1100 Subject: WIP --- pkg/commands/loader_adapters.go | 28 ------ pkg/commands/loaders/branches.go | 172 ++++++++++++++++++++++++++++++++++++ pkg/commands/loaders/commits.go | 27 +++--- pkg/commands/loading_branches.go | 174 ------------------------------------- pkg/commands/oscommands/os_test.go | 2 +- 5 files changed, 186 insertions(+), 217 deletions(-) delete mode 100644 pkg/commands/loader_adapters.go create mode 100644 pkg/commands/loaders/branches.go delete mode 100644 pkg/commands/loading_branches.go (limited to 'pkg/commands') diff --git a/pkg/commands/loader_adapters.go b/pkg/commands/loader_adapters.go deleted file mode 100644 index 166bf012d..000000000 --- a/pkg/commands/loader_adapters.go +++ /dev/null @@ -1,28 +0,0 @@ -package commands - -import ( - "io/ioutil" - "path/filepath" - - "github.com/jesseduffield/lazygit/pkg/commands/loaders" - "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/common" -) - -// this file defines constructors for loaders, passing in all the dependencies required based on a smaller set of arguments passed in by the client. - -func NewCommitLoader( - cmn *common.Common, - gitCommand *GitCommand, - osCommand *oscommands.OSCommand, -) *loaders.CommitLoader { - return loaders.NewCommitLoader( - cmn, - gitCommand.Cmd, - gitCommand.CurrentBranchName, - gitCommand.RebaseMode, - ioutil.ReadFile, - filepath.Walk, - gitCommand.DotGitDir, - ) -} diff --git a/pkg/commands/loaders/branches.go b/pkg/commands/loaders/branches.go new file mode 100644 index 000000000..fc3c15fc9 --- /dev/null +++ b/pkg/commands/loaders/branches.go @@ -0,0 +1,172 @@ +package loaders + +import ( + "regexp" + "strings" + + "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/common" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +// 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 + +// BranchLoader returns a list of Branch objects for the current repo +type BranchLoader struct { + *common.Common + getRawBranches func() (string, error) + getCurrentBranchName func() (string, string, error) + reflogCommits []*models.Commit +} + +func NewBranchLoader( + cmn *common.Common, + gitCommand *commands.GitCommand, + reflogCommits []*models.Commit, +) *BranchLoader { + return &BranchLoader{ + Common: cmn, + getRawBranches: gitCommand.GetRawBranches, + getCurrentBranchName: gitCommand.CurrentBranchName, + reflogCommits: reflogCommits, + } +} + +// Load the list of branches for the current repo +func (b *BranchLoader) Load() []*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.getCurrentBranchName() + if err != nil { + panic(err) + } + branches = append([]*models.Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...) + } + return branches +} + +func (b *BranchLoader) obtainBranches() []*models.Branch { + output, err := b.getRawBranches() + 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) + if len(split) != 4 { + // Ignore line if it isn't separated into 4 parts + // This is probably a warning message, for more info see: + // https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439 + continue + } + + 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 +} + +// 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 *BranchLoader) 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 +} diff --git a/pkg/commands/loaders/commits.go b/pkg/commands/loaders/commits.go index 5fc2caa98..bbd520c81 100644 --- a/pkg/commands/loaders/commits.go +++ b/pkg/commands/loaders/commits.go @@ -2,12 +2,14 @@ package loaders import ( "fmt" + "io/ioutil" "os" "path/filepath" "regexp" "strconv" "strings" + "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" @@ -35,23 +37,20 @@ type CommitLoader struct { dotGitDir string } +// making our dependencies explicit for the sake of easier testing func NewCommitLoader( - common *common.Common, - cmd oscommands.ICmdObjBuilder, - getCurrentBranchName func() (string, string, error), - getRebaseMode func() (enums.RebaseMode, error), - readFile func(filename string) ([]byte, error), - walkFiles func(root string, fn filepath.WalkFunc) error, - dotGitDir string, + cmn *common.Common, + gitCommand *commands.GitCommand, + osCommand *oscommands.OSCommand, ) *CommitLoader { return &CommitLoader{ - Common: common, - cmd: cmd, - getCurrentBranchName: getCurrentBranchName, - getRebaseMode: getRebaseMode, - readFile: readFile, - walkFiles: walkFiles, - dotGitDir: dotGitDir, + Common: cmn, + cmd: gitCommand.Cmd, + getCurrentBranchName: gitCommand.CurrentBranchName, + getRebaseMode: gitCommand.RebaseMode, + readFile: ioutil.ReadFile, + walkFiles: filepath.Walk, + dotGitDir: gitCommand.DotGitDir, } } diff --git a/pkg/commands/loading_branches.go b/pkg/commands/loading_branches.go deleted file mode 100644 index 62f758bcd..000000000 --- a/pkg/commands/loading_branches.go +++ /dev/null @@ -1,174 +0,0 @@ -package commands - -import ( - "regexp" - "strings" - - "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/common" - "github.com/jesseduffield/lazygit/pkg/utils" -) - -// 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 - -const SEPARATION_CHAR = "|" - -// BranchListBuilder returns a list of Branch objects for the current repo -type BranchListBuilder struct { - *common.Common - getRawBranches func() (string, error) - getCurrentBranchName func() (string, string, error) - reflogCommits []*models.Commit -} - -func NewBranchListBuilder( - cmn *common.Common, - getRawBranches func() (string, error), - getCurrentBranchName func() (string, string, error), - reflogCommits []*models.Commit, -) *BranchListBuilder { - return &BranchListBuilder{ - Common: cmn, - getRawBranches: getRawBranches, - getCurrentBranchName: getCurrentBranchName, - reflogCommits: reflogCommits, - } -} - -// 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.getCurrentBranchName() - if err != nil { - panic(err) - } - branches = append([]*models.Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...) - } - return branches -} - -func (b *BranchListBuilder) obtainBranches() []*models.Branch { - output, err := b.getRawBranches() - 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) - if len(split) != 4 { - // Ignore line if it isn't separated into 4 parts - // This is probably a warning message, for more info see: - // https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439 - continue - } - - 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 -} - -// 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 -} diff --git a/pkg/commands/oscommands/os_test.go b/pkg/commands/oscommands/os_test.go index 424aa72b3..73c846fb0 100644 --- a/pkg/commands/oscommands/os_test.go +++ b/pkg/commands/oscommands/os_test.go @@ -55,7 +55,7 @@ func TestOSCommandRun(t *testing.T) { for _, s := range scenarios { c := NewDummyOSCommand() - s.test(c.Cmd.New(s.command)).Run() + s.test(c.Cmd.New(s.command).Run()) } } -- cgit v1.2.3