diff options
| author | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2018-12-15 01:58:59 +0300 |
|---|---|---|
| committer | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2018-12-15 01:58:59 +0300 |
| commit | 9a54e2957f370fc2515b5263fff271cd842da87d (patch) | |
| tree | d259a1e7debaf9d8c6e0ddfb81ecf378ee525ace | |
| parent | renamed some files (diff) | |
| download | gitbatch-9a54e2957f370fc2515b5263fff271cd842da87d.tar.gz | |
added quick mode
| -rw-r--r-- | main.go | 2 | ||||
| -rw-r--r-- | pkg/app/app.go | 15 | ||||
| -rw-r--r-- | pkg/app/files.go | 2 | ||||
| -rw-r--r-- | pkg/app/quick.go | 46 | ||||
| -rw-r--r-- | pkg/git/cmd-fetch.go | 6 | ||||
| -rw-r--r-- | pkg/git/cmd-pull.go | 104 | ||||
| -rw-r--r-- | pkg/git/repository.go | 60 | ||||
| -rw-r--r-- | pkg/git/util-load.go | 2 |
8 files changed, 205 insertions, 32 deletions
@@ -15,6 +15,7 @@ var ( ignoreConfig = kingpin.Flag("ignore-config", "Ignore config file").Short('i').Bool() recurseDepth = kingpin.Flag("recursive-depth", "Find directories recursively").Default("1").Short('r').Int() logLevel = kingpin.Flag("log-level", "Logging level; trace,debug,info,warn,error").Default("error").Short('l').String() + quick = kingpin.Flag("quick", "runs without gui and fetches/pull remote upstream. modes are fetch or pull").Short('q').String() ) func main() { @@ -28,6 +29,7 @@ func main() { LogLevel: *logLevel, IgnoreConfig: *ignoreConfig, Depth: *recurseDepth, + QuickMode: *quick, }) if err != nil { log.Fatal(err) diff --git a/pkg/app/app.go b/pkg/app/app.go index 21b87a6..9bceee2 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -18,6 +18,7 @@ type SetupConfig struct { LogLevel string IgnoreConfig bool Depth int + QuickMode string } // Setup will handle pre-required operations. It is designed to be a wrapper for @@ -36,9 +37,19 @@ func Setup(setupConfig SetupConfig) (*App, error) { var directories []string if len(app.Config.Directories) <= 0 || setupConfig.IgnoreConfig { - directories = GenerateDirectories(setupConfig.Directories, setupConfig.Depth) + directories = generateDirectories(setupConfig.Directories, setupConfig.Depth) } else { - directories = GenerateDirectories(app.Config.Directories, setupConfig.Depth) + directories = generateDirectories(app.Config.Directories, setupConfig.Depth) + } + + if len(setupConfig.QuickMode) > 0 { + x := setupConfig.QuickMode == "fetch" + y := setupConfig.QuickMode == "pull" + if x == y { + log.Fatal("Unrecognized quick mode: " + setupConfig.QuickMode) + } + quick(directories, setupConfig.Depth, setupConfig.QuickMode) + log.Fatal("Finished") } // create a gui.Gui struct and set it as App's gui diff --git a/pkg/app/files.go b/pkg/app/files.go index 03e4014..25e5074 100644 --- a/pkg/app/files.go +++ b/pkg/app/files.go @@ -11,7 +11,7 @@ import ( // generateDirectories returns poosible git repositories to pipe into git pkg's // load function -func GenerateDirectories(directories []string, depth int) (gitDirectories []string) { +func generateDirectories(directories []string, depth int) (gitDirectories []string) { for i := 0; i <= depth; i++ { nonrepos, repos := walkRecursive(directories, gitDirectories) directories = nonrepos diff --git a/pkg/app/quick.go b/pkg/app/quick.go new file mode 100644 index 0000000..0a845c4 --- /dev/null +++ b/pkg/app/quick.go @@ -0,0 +1,46 @@ +package app + +import ( + "sync" + "time" + + "github.com/isacikgoz/gitbatch/pkg/git" + log "github.com/sirupsen/logrus" +) + +func quick(directories []string, depth int, mode string) { + + var wg sync.WaitGroup + start := time.Now() + for _, dir := range directories { + wg.Add(1) + go func(d string, mode string) { + defer wg.Done() + err := operate(d, mode) + if err != nil { + log.Errorf("%s: %s", d, err.Error()) + } + }(dir, mode) + } + wg.Wait() + elapsed := time.Since(start) + log.Infof("%d repositories finished in: %s\n", len(directories), elapsed) +} + +func operate(directory, mode string) error { + r, err := git.FastInitializeRepo(directory) + if err != nil { + return err + } + switch mode { + case "fetch": + return git.Fetch(r, git.FetchOptions{ + RemoteName: "origin", + }) + case "pull": + return git.Pull(r, git.PullOptions{ + RemoteName: "origin", + }) + } + return nil +} diff --git a/pkg/git/cmd-fetch.go b/pkg/git/cmd-fetch.go index 8a8bf9c..df600b3 100644 --- a/pkg/git/cmd-fetch.go +++ b/pkg/git/cmd-fetch.go @@ -85,8 +85,7 @@ func fetchWithGit(entity *RepoEntity, options FetchOptions) (err error) { return err } // till this step everything should be ok - err = entity.Refresh() - return err + return entity.Refresh() } // fetchWithGoGit is the primary fetch method and refspec is the main feature. @@ -141,6 +140,5 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e } } // till this step everything should be ok - err = entity.Refresh() - return err + return entity.Refresh() } diff --git a/pkg/git/cmd-pull.go b/pkg/git/cmd-pull.go new file mode 100644 index 0000000..a493b19 --- /dev/null +++ b/pkg/git/cmd-pull.go @@ -0,0 +1,104 @@ +package git + +import ( + log "github.com/sirupsen/logrus" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/transport/http" +) + +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 Credentials + // 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(entity *RepoEntity, 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(entity, options) + return err + case pullCmdModeNative: + err = pullWithGoGit(entity, options) + return err + } + return nil +} + +func pullWithGit(entity *RepoEntity, 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 err := GenericGitCommand(entity.AbsPath, args); err != nil { + log.Warn("Error at git command (pull)") + return err + } + return entity.Refresh() +} + +func pullWithGoGit(entity *RepoEntity, options PullOptions) (err error) { + opt := &git.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 := entity.authProtocol(entity.Remote) + if err != nil { + return err + } + if protocol == authProtocolHttp || protocol == authProtocolHttps { + opt.Auth = &http.BasicAuth{ + Username: options.Credentials.User, + Password: options.Credentials.Password, + } + } else { + return ErrInvalidAuthMethod + } + } + w, err := entity.Repository.Worktree() + if err != nil { + return err + } + err = w.Pull(opt) + if err != nil { + return err + } + return entity.Refresh() +} diff --git a/pkg/git/repository.go b/pkg/git/repository.go index ff67611..f37c813 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -47,33 +47,12 @@ const ( Fail RepoState = 5 ) -// InitializeRepository initializes a RepoEntity struct with its belongings. -func InitializeRepository(directory string) (entity *RepoEntity, err error) { - file, err := os.Open(directory) +// InitializeRepo initializes a RepoEntity struct with its belongings. +func InitializeRepo(directory string) (entity *RepoEntity, err error) { + entity, err = FastInitializeRepo(directory) if err != nil { - log.WithFields(log.Fields{ - "directory": directory, - }).Trace("Cannot open as directory") - return nil, err - } - fileInfo, err := file.Stat() - if err != nil { - return nil, err - } - r, err := git.PlainOpen(directory) - if err != nil { - log.WithFields(log.Fields{ - "directory": directory, - }).Trace("Cannot open directory as a git repository") return nil, err } - entity = &RepoEntity{RepoID: helpers.RandomString(8), - Name: fileInfo.Name(), - AbsPath: directory, - ModTime: fileInfo.ModTime(), - Repository: *r, - State: Available, - } // after we intiate the struct we can fill its values entity.loadLocalBranches() entity.loadCommits() @@ -107,11 +86,44 @@ func InitializeRepository(directory string) (entity *RepoEntity, err error) { return entity, nil } +// FastInitializeRepo initializes a RepoEntity struct without its belongings. +func FastInitializeRepo(directory string) (entity *RepoEntity, err error) { + file, err := os.Open(directory) + if err != nil { + log.WithFields(log.Fields{ + "directory": directory, + }).Trace("Cannot open as directory") + return nil, err + } + fileInfo, err := file.Stat() + if err != nil { + return nil, err + } + r, err := git.PlainOpen(directory) + if err != nil { + log.WithFields(log.Fields{ + "directory": directory, + }).Trace("Cannot open directory as a git repository") + return nil, err + } + entity = &RepoEntity{RepoID: helpers.RandomString(8), + Name: fileInfo.Name(), + AbsPath: directory, + ModTime: fileInfo.ModTime(), + Repository: *r, + State: Available, + } + return entity, nil +} + // Refresh the belongings of a repositoriy, this function is called right after // fetch/pull/merge operations func (entity *RepoEntity) Refresh() error { var err error // error can be ignored since the file already exists when app is loading + if entity.Branch == nil { + return nil + } file, _ := os.Open(entity.AbsPath) fileInfo, err := file.Stat() if err != nil { diff --git a/pkg/git/util-load.go b/pkg/git/util-load.go index e35e1c1..bf19de8 100644 --- a/pkg/git/util-load.go +++ b/pkg/git/util-load.go @@ -24,7 +24,7 @@ func LoadRepositoryEntities(directories []string) (entities []*RepoEntity, err e // decrement the wait counter by one, we call it in a defer so it's // called at the end of this goroutine defer wg.Done() - entity, err := InitializeRepository(d) + entity, err := InitializeRepo(d) if err != nil { log.WithFields(log.Fields{ "directory": d, |
