summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go2
-rw-r--r--pkg/app/app.go15
-rw-r--r--pkg/app/files.go2
-rw-r--r--pkg/app/quick.go46
-rw-r--r--pkg/git/cmd-fetch.go6
-rw-r--r--pkg/git/cmd-pull.go104
-rw-r--r--pkg/git/repository.go60
-rw-r--r--pkg/git/util-load.go2
8 files changed, 205 insertions, 32 deletions
diff --git a/main.go b/main.go
index cb8a9eb..788d72c 100644
--- a/main.go
+++ b/main.go
@@ -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,