package commands import ( "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "gitlab.com/revalus/grm/config" ) type StatusChecker struct { workspace string } func NewStatusChecker(workspace string) StatusChecker { return StatusChecker{ workspace: workspace, } } func findNumberOfCommitDiffs(srcCommit *object.Commit, dstCommit *object.Commit) int { getFiveElementsFromHashes := func(commit *object.Commit, hashedSlice *[]string) *object.Commit { var err error for iter := 0; iter <= 5; iter++ { *hashedSlice = append(*hashedSlice, commit.Hash.String()) commit, err = commit.Parents().Next() if err != nil { return nil } } return commit } getRangeDiff := func(listFist, listSecond []string) (int, bool) { diffRange := 0 for _, itemFirst := range listFist { for _, itemSecond := range listSecond { if itemFirst == itemSecond { return diffRange, true } } diffRange++ } return diffRange, false } baseCommitHashes := []string{} destCommitHashes := []string{} for { if srcCommit != nil { srcCommit = getFiveElementsFromHashes(srcCommit, &baseCommitHashes) } if dstCommit != nil { dstCommit = getFiveElementsFromHashes(dstCommit, &destCommitHashes) } diff, finished := getRangeDiff(baseCommitHashes, destCommitHashes) if finished { return diff } } } func (sc StatusChecker) Command(repoCfg config.RepositoryConfig) CommandStatus { cmdStatus := CommandStatus{ Name: repoCfg.Name, Changed: false, Message: "", Error: false, } destPath := fmt.Sprintf("%v/%v", sc.workspace, repoCfg.Dest) repo, err := git.PlainOpen(destPath) if err != nil { cmdStatus.Error = true cmdStatus.Message = err.Error() return cmdStatus } headReference, err := repo.Head() if err != nil { cmdStatus.Error = true cmdStatus.Message = err.Error() return cmdStatus } remotes, err := repo.Remotes() if err != nil || len(remotes) == 0 { cmdStatus.Error = true cmdStatus.Message = "cannot find remote branches" return cmdStatus } currentBranchCommit, err := repo.CommitObject(headReference.Hash()) if err != nil { cmdStatus.Error = true cmdStatus.Message = err.Error() return cmdStatus } type remoteStatus struct { ahead int behind int err error } remotesStatus := make(map[string]remoteStatus) for _, remote := range remotes { remoteName := remote.Config().Name remoteRevision, err := repo.ResolveRevision(plumbing.Revision(fmt.Sprintf("%v/%v", remoteName, headReference.Name().Short()))) if err != nil { remotesStatus[remoteName] = remoteStatus{ err: err, } continue } remoteBranchCommit, err := repo.CommitObject(*remoteRevision) if err != nil { remotesStatus[remoteName] = remoteStatus{ err: err, } continue } status := remoteStatus{ ahead: findNumberOfCommitDiffs(currentBranchCommit, remoteBranchCommit), behind: findNumberOfCommitDiffs(remoteBranchCommit, currentBranchCommit), } if status.ahead > 0 || status.behind > 0 { cmdStatus.Changed = true } remotesStatus[remoteName] = status } cmdStatus.Message = fmt.Sprintf("branch %v", headReference.Name().Short()) for remoteName, status := range remotesStatus { if status.err != nil { cmdStatus.Message = fmt.Sprintf("%v - ( | %v | problem: %v )", cmdStatus.Message, remoteName, status.err.Error()) continue } cmdStatus.Message = fmt.Sprintf("%v - ( | %v | \u2191%v \u2193%v )", cmdStatus.Message, remoteName, status.ahead, status.behind) } return cmdStatus }