diff options
| author | İbrahim Serdar Açıkgöz <serdaracikgoz86@gmail.com> | 2019-01-03 18:04:25 +0300 |
|---|---|---|
| committer | İbrahim Serdar Açıkgöz <serdaracikgoz86@gmail.com> | 2019-01-03 18:04:25 +0300 |
| commit | f2f2c1bb18b2f7236f5ede917b1eacdd422cbd5b (patch) | |
| tree | 69a14914ca83dc08bb02169e2988960e361525f0 | |
| parent | superfast job handling and failover for auth added (diff) | |
| download | gitbatch-f2f2c1bb18b2f7236f5ede917b1eacdd422cbd5b.tar.gz | |
error messages and semaphores added
| -rw-r--r-- | pkg/git/cmd-pull.go | 5 | ||||
| -rw-r--r-- | pkg/git/cmd-rev-list.go | 2 | ||||
| -rw-r--r-- | pkg/git/cmd.go | 4 | ||||
| -rw-r--r-- | pkg/git/job-queue.go | 36 | ||||
| -rw-r--r-- | pkg/git/job.go | 3 | ||||
| -rw-r--r-- | pkg/git/repository.go | 10 | ||||
| -rw-r--r-- | pkg/git/util-errors.go | 45 | ||||
| -rw-r--r-- | pkg/gui/util-textstyle.go | 4 |
8 files changed, 91 insertions, 18 deletions
diff --git a/pkg/git/cmd-pull.go b/pkg/git/cmd-pull.go index 0c3954e..769d6b5 100644 --- a/pkg/git/cmd-pull.go +++ b/pkg/git/cmd-pull.go @@ -67,9 +67,8 @@ func pullWithGit(e *RepoEntity, options PullOptions) (err error) { if options.Force { args = append(args, "-f") } - if err := GenericGitCommand(e.AbsPath, args); err != nil { - log.Warn("Error at git command (pull)") - return err + if out, err := GenericGitCommandWithOutput(e.AbsPath, args); err != nil { + return parseGitError(out, err) } e.SetState(Success) return e.Refresh() diff --git a/pkg/git/cmd-rev-list.go b/pkg/git/cmd-rev-list.go index e68e82a..624e290 100644 --- a/pkg/git/cmd-rev-list.go +++ b/pkg/git/cmd-rev-list.go @@ -29,7 +29,7 @@ func RevList(e *RepoEntity, options RevListOptions) ([]string, error) { out, err := GenericGitCommandWithOutput(e.AbsPath, args) if err != nil { log.Warn("Error while rev-list command") - return []string{out}, err + return []string{"?"}, err } hashes := strings.Split(out, "\n") for _, hash := range hashes { diff --git a/pkg/git/cmd.go b/pkg/git/cmd.go index 05d030c..b177b88 100644 --- a/pkg/git/cmd.go +++ b/pkg/git/cmd.go @@ -15,7 +15,7 @@ func RunCommandWithOutput(dir string, command string, args []string) (string, er if dir != "" { cmd.Dir = dir } - output, err := cmd.Output() + output, err := cmd.CombinedOutput() return string(output), err } @@ -73,7 +73,7 @@ func GenericGitCommand(repoPath string, args []string) error { func GenericGitCommandWithOutput(repoPath string, args []string) (string, error) { out, err := RunCommandWithOutput(repoPath, "git", args) if err != nil { - return "?", err + return out, err } return TrimTrailingNewline(out), nil } diff --git a/pkg/git/job-queue.go b/pkg/git/job-queue.go index e6e6f0c..975b8af 100644 --- a/pkg/git/job-queue.go +++ b/pkg/git/job-queue.go @@ -1,8 +1,13 @@ package git import ( + "context" "errors" + "runtime" "sync" + + log "github.com/sirupsen/logrus" + "golang.org/x/sync/semaphore" ) // JobQueue holds the slice of Jobs @@ -80,13 +85,26 @@ func (jq *JobQueue) IsInTheQueue(entity *RepoEntity) (inTheQueue bool, j *Job) { // StartJobsAsync start he jobs in the queue asynchronously func (jq *JobQueue) StartJobsAsync() map[*Job]error { - fails := make(map[*Job]error) - var wg sync.WaitGroup + + ctx := context.TODO() + + var ( + maxWorkers = runtime.GOMAXPROCS(0) + sem = semaphore.NewWeighted(int64(maxWorkers)) + fails = make(map[*Job]error) + ) + var mx sync.Mutex for range jq.series { - wg.Add(1) + + if err := sem.Acquire(ctx, 1); err != nil { + log.Errorf("Failed to acquire semaphore: %v", err) + break + } + go func() { - defer wg.Done() + + defer sem.Release(1) j, _, err := jq.StartNext() if err != nil { mx.Lock() @@ -95,6 +113,14 @@ func (jq *JobQueue) StartJobsAsync() map[*Job]error { } }() } - wg.Wait() + + // Acquire all of the tokens to wait for any remaining workers to finish. + // + // If you are already waiting for the workers by some other means (such as an + // errgroup.Group), you can omit this final Acquire call. + if err := sem.Acquire(ctx, int64(maxWorkers)); err != nil { + log.Errorf("Failed to acquire semaphore: %v", err) + } + return fails } diff --git a/pkg/git/job.go b/pkg/git/job.go index 85c28f3..82a14f6 100644 --- a/pkg/git/job.go +++ b/pkg/git/job.go @@ -41,6 +41,7 @@ func (j *Job) start() error { } if err := Fetch(j.Entity, opts); err != nil { j.Entity.SetState(Fail) + j.Entity.SetStateMessage(err.Error()) return err } case PullJob: @@ -54,6 +55,7 @@ func (j *Job) start() error { } if err := Pull(j.Entity, opts); err != nil { j.Entity.SetState(Fail) + j.Entity.SetStateMessage(err.Error()) return err } case MergeJob: @@ -61,6 +63,7 @@ func (j *Job) start() error { BranchName: j.Entity.Remote.Branch.Name, }); err != nil { j.Entity.SetState(Fail) + j.Entity.SetStateMessage(err.Error()) return nil } default: diff --git a/pkg/git/repository.go b/pkg/git/repository.go index 0c02f26..31b926d 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -28,6 +28,9 @@ type RepoEntity struct { Stasheds []*StashedItem state RepoState + // TODO: move this into state + Message string + mutex *sync.RWMutex listeners map[string][]RepositoryListener } @@ -210,3 +213,10 @@ func (e *RepoEntity) SetState(state RepoState) { log.Warnf("Cannot publish on %s topic.\n", RepositoryUpdated) } } + +// SetMessage sets the message of status, it is used if state is Fail +func (e *RepoEntity) SetStateMessage(msg string) { + if e.State() == Fail { + e.Message = msg + } +} diff --git a/pkg/git/util-errors.go b/pkg/git/util-errors.go index 8621cbc..33eb15f 100644 --- a/pkg/git/util-errors.go +++ b/pkg/git/util-errors.go @@ -2,23 +2,58 @@ package git import ( "errors" + "strings" ) var ( // ErrGitCommand is thrown when git command returned an error code - ErrGitCommand = errors.New("Git command returned error code") + ErrGitCommand = errors.New("git command returned error code") // ErrAuthenticationRequired is thrown when an authentication required on // a remote operation - ErrAuthenticationRequired = errors.New("Authentication required") + ErrAuthenticationRequired = errors.New("authentication required") // ErrAuthorizationFailed is thrown when authorization failed while trying // to authenticate with remote - ErrAuthorizationFailed = errors.New("Authorization failed") + ErrAuthorizationFailed = errors.New("authorization failed") // ErrInvalidAuthMethod is thrown when invalid auth method is invoked ErrInvalidAuthMethod = errors.New("invalid auth method") // ErrAlreadyUpToDate is thrown when a repository is already up to date // with its src on merge/fetch/pull - ErrAlreadyUpToDate = errors.New("Already up to date") + ErrAlreadyUpToDate = errors.New("already up to date") // ErrCouldNotFindRemoteRef is thrown when trying to fetch/pull cannot // find suitable remote reference - ErrCouldNotFindRemoteRef = errors.New("Could not find remote ref") + ErrCouldNotFindRemoteRef = errors.New("could not find remote ref") + // ErrPullAbortedTryCommit indicates that the repositort is not clean and + // some changes may conflict with the merge + ErrMergeAbortedTryCommit = errors.New("stash/commit changes. aborted") + // ErrRemoteBranchNotSpecified means that default remote branch is not set + // for the current branch. can be setted with "git config --local --add + // branch.<your branch name>.remote=<your remote name> " + ErrRemoteBranchNotSpecified = errors.New("upstream not set") + // ErrRemoteNotFound is thrown when the remote is not reachable. It may be + // caused by the deletion of the remote or coneectivty problems + ErrRemoteNotFound = errors.New("remote not found") + // ErrConflictAfterMerge is thrown when a conflict occurs at merging two + // references + ErrConflictAfterMerge = errors.New("conflict while merging") + // ErrUnmergedFiles possibly occurs after a conflict + ErrUnmergedFiles = errors.New("unmerged files detected") + // ErrUnclassified is unconsidered error type + ErrUnclassified = errors.New("unclassified error") ) + +// parseGitError takes git output as an input and tries to find some meaningful +// errors can be used by the app +func parseGitError(out string, err error) error { + if strings.Contains(out, "error: Your local changes to the following files would be overwritten by merge") { + return ErrMergeAbortedTryCommit + } else if strings.Contains(out, "ERROR: Repository not found") { + return ErrRemoteNotFound + } else if strings.Contains(out, "for your current branch, you must specify a branch on the command line") { + return ErrRemoteBranchNotSpecified + } else if strings.Contains(out, "Automatic merge failed; fix conflicts and then commit the result") { + return ErrConflictAfterMerge + } else if strings.Contains(out, "error: Pulling is not possible because you have unmerged files.") { + return ErrUnmergedFiles + } + return ErrUnclassified +} diff --git a/pkg/gui/util-textstyle.go b/pkg/gui/util-textstyle.go index 1bb3635..c42857b 100644 --- a/pkg/gui/util-textstyle.go +++ b/pkg/gui/util-textstyle.go @@ -103,9 +103,9 @@ func (gui *Gui) repositoryLabel(e *git.RepoEntity) string { } else if e.State() == git.Success { return prefix + repoName + ws + green.Sprint(successSymbol) } else if e.State() == git.Paused { - return prefix + repoName + ws + yellow.Sprint("auth required (u)") + return prefix + repoName + ws + yellow.Sprint("authentication required (u)") } else if e.State() == git.Fail { - return prefix + repoName + ws + red.Sprint(failSymbol) + return prefix + repoName + ws + red.Sprint(failSymbol) + ws + red.Sprint(e.Message) } return prefix + repoName } |
