summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commitf2f2c1bb18b2f7236f5ede917b1eacdd422cbd5b (patch)
tree69a14914ca83dc08bb02169e2988960e361525f0
parentsuperfast job handling and failover for auth added (diff)
downloadgitbatch-f2f2c1bb18b2f7236f5ede917b1eacdd422cbd5b.tar.gz
error messages and semaphores added
-rw-r--r--pkg/git/cmd-pull.go5
-rw-r--r--pkg/git/cmd-rev-list.go2
-rw-r--r--pkg/git/cmd.go4
-rw-r--r--pkg/git/job-queue.go36
-rw-r--r--pkg/git/job.go3
-rw-r--r--pkg/git/repository.go10
-rw-r--r--pkg/git/util-errors.go45
-rw-r--r--pkg/gui/util-textstyle.go4
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
}