summaryrefslogtreecommitdiff
path: root/core/command/pull.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/command/pull.go')
-rw-r--r--core/command/pull.go141
1 files changed, 141 insertions, 0 deletions
diff --git a/core/command/pull.go b/core/command/pull.go
new file mode 100644
index 0000000..5644417
--- /dev/null
+++ b/core/command/pull.go
@@ -0,0 +1,141 @@
+package command
+
+import (
+ "os"
+ "strings"
+
+ gerr "github.com/isacikgoz/gitbatch/core/errors"
+ "github.com/isacikgoz/gitbatch/core/git"
+ log "github.com/sirupsen/logrus"
+ gogit "gopkg.in/src-d/go-git.v4"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
+)
+
+var (
+ pullCmdMode string
+ pullTryCount int
+
+ pullCommand = "pull"
+ pullCmdModeLegacy = "git"
+ pullCmdModeNative = "go-git"
+ pullMaxTry = 1
+)
+
+// PullOptions defines the rules for pull operation
+type PullOptions struct {
+ // Name of the remote to fetch from. Defaults to origin.
+ RemoteName string
+ // ReferenceName Remote branch to clone. If empty, uses HEAD.
+ ReferenceName string
+ // Fetch only ReferenceName if true.
+ SingleBranch bool
+ // Credentials holds the user and pswd information
+ Credentials git.Credentials
+ // Process logs the output to stdout
+ Progress bool
+ // Force allows the pull to update a local branch even when the remote
+ // branch does not descend from it.
+ Force bool
+}
+
+// Pull ncorporates changes from a remote repository into the current branch.
+func Pull(r *git.Repository, options PullOptions) (err error) {
+ // here we configure pull operation
+ // default mode is go-git (this may be configured)
+ pullCmdMode = pullCmdModeNative
+ pullTryCount = 0
+
+ switch pullCmdMode {
+ case pullCmdModeLegacy:
+ err = pullWithGit(r, options)
+ return err
+ case pullCmdModeNative:
+ err = pullWithGoGit(r, options)
+ return err
+ }
+ return nil
+}
+
+func pullWithGit(r *git.Repository, options PullOptions) (err error) {
+ args := make([]string, 0)
+ args = append(args, pullCommand)
+ // parse options to command line arguments
+ if len(options.RemoteName) > 0 {
+ args = append(args, options.RemoteName)
+ }
+ if options.Force {
+ args = append(args, "-f")
+ }
+ if out, err := GenericGitCommandWithOutput(r.AbsPath, args); err != nil {
+ return gerr.ParseGitError(out, err)
+ }
+ r.SetWorkStatus(git.Success)
+ return r.Refresh()
+}
+
+func pullWithGoGit(r *git.Repository, options PullOptions) (err error) {
+ opt := &gogit.PullOptions{
+ RemoteName: options.RemoteName,
+ SingleBranch: options.SingleBranch,
+ Force: options.Force,
+ }
+ if len(options.ReferenceName) > 0 {
+ ref := plumbing.NewRemoteReferenceName(options.RemoteName, options.ReferenceName)
+ opt.ReferenceName = ref
+ }
+ // if any credential is given, let's add it to the git.PullOptions
+ if len(options.Credentials.User) > 0 {
+ protocol, err := git.AuthProtocol(r.State.Remote)
+ if err != nil {
+ return err
+ }
+ if protocol == git.AuthProtocolHTTP || protocol == git.AuthProtocolHTTPS {
+ opt.Auth = &http.BasicAuth{
+ Username: options.Credentials.User,
+ Password: options.Credentials.Password,
+ }
+ } else {
+ return gerr.ErrInvalidAuthMethod
+ }
+ }
+ if options.Progress {
+ opt.Progress = os.Stdout
+ }
+ w, err := r.Repo.Worktree()
+ if err != nil {
+ return err
+ }
+
+ if err = w.Pull(opt); err != nil {
+ if err == gogit.NoErrAlreadyUpToDate {
+ // log.Error("error: " + err.Error())
+ // Already up-to-date
+ log.Warn(err.Error())
+ // TODO: submit a PR for this kind of error, this type of catch is lame
+ } else if err == memory.ErrRefHasChanged && pullTryCount < pullMaxTry {
+ pullTryCount++
+ log.Error("trying to fetch")
+ if err := Fetch(r, FetchOptions{
+ RemoteName: options.RemoteName,
+ }); err != nil {
+ return err
+ }
+ return Pull(r, options)
+ } else if strings.Contains(err.Error(), "SSH_AUTH_SOCK") {
+ // The env variable SSH_AUTH_SOCK is not defined, maybe git can handle this
+ return pullWithGit(r, options)
+ } else if err == transport.ErrAuthenticationRequired {
+ log.Warn(err.Error())
+ return gerr.ErrAuthenticationRequired
+ } else {
+ log.Warn(err.Error())
+ return pullWithGit(r, options)
+ }
+ }
+
+ r.SetWorkStatus(git.Success)
+ return r.Refresh()
+}