summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIbrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>2018-12-21 10:23:45 +0300
committerIbrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>2018-12-21 10:23:45 +0300
commitb6c946b7bc0aa093ad21ffa93ab5ed7f6ada2570 (patch)
tree3747cb46200821616a89b77a1fc142aaaff7606f
parentMerge remote-tracking branch 'origin/develop' into develop (diff)
parentminor code style imrovements and code reduction (diff)
downloadgitbatch-b6c946b7bc0aa093ad21ffa93ab5ed7f6ada2570.tar.gz
Merge remote-tracking branch 'origin/develop' into develop
-rw-r--r--.gitignore3
-rw-r--r--README.md28
-rw-r--r--main.go2
-rw-r--r--pkg/git/authentication.go10
-rw-r--r--pkg/git/branch.go161
-rw-r--r--pkg/git/cmd-add.go18
-rw-r--r--pkg/git/cmd-commit.go24
-rw-r--r--pkg/git/cmd-config.go28
-rw-r--r--pkg/git/cmd-diff.go89
-rw-r--r--pkg/git/cmd-fetch.go31
-rw-r--r--pkg/git/cmd-merge.go8
-rw-r--r--pkg/git/cmd-pull.go34
-rw-r--r--pkg/git/cmd-reset.go24
-rw-r--r--pkg/git/cmd-rev-list.go4
-rw-r--r--pkg/git/cmd-stash.go20
-rw-r--r--pkg/git/cmd-status.go64
-rw-r--r--pkg/git/cmd.go4
-rw-r--r--pkg/git/commit.go145
-rw-r--r--pkg/git/file.go52
-rw-r--r--pkg/git/job-queue.go32
-rw-r--r--pkg/git/job.go45
-rw-r--r--pkg/git/remote.go56
-rw-r--r--pkg/git/remotebranch.go56
-rw-r--r--pkg/git/repository.go224
-rw-r--r--pkg/gui/authenticationview.go8
-rw-r--r--pkg/gui/commitview.go35
-rw-r--r--pkg/gui/diffview.go18
-rw-r--r--pkg/gui/gui.go7
-rw-r--r--pkg/gui/keybindings.go12
-rw-r--r--pkg/gui/mainview.go104
-rw-r--r--pkg/gui/sideviews.go204
-rw-r--r--pkg/gui/stagedview.go19
-rw-r--r--pkg/gui/stashview.go28
-rw-r--r--pkg/gui/statusview.go41
-rw-r--r--pkg/gui/unstagedview.go12
-rw-r--r--pkg/gui/util-common.go35
-rw-r--r--pkg/gui/util-queuehandler.go58
-rw-r--r--pkg/gui/util-textstyle.go62
38 files changed, 846 insertions, 959 deletions
diff --git a/.gitignore b/.gitignore
index 552c503..9b75aa8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
exec.go.test
+build.sh
test.go
-.vscode \ No newline at end of file
+.vscode
diff --git a/README.md b/README.md
index 7e6f592..12f4cb8 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,32 @@
[![Build Status](https://travis-ci.com/isacikgoz/gitbatch.svg?branch=master)](https://travis-ci.com/isacikgoz/gitbatch) [![MIT License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](/LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/isacikgoz/gitbatch)](https://goreportcard.com/report/github.com/isacikgoz/gitbatch)
## gitbatch
-This tool is beening built to make your local repositories synchronized with remotes easily. Although the focus is batch jobs, you can still do de facto micro management of your git repositories (e.g *add/reset, stash, commit etc.*)
+This tool is being built to make your local repositories synchronized with remotes easily. Although the focus is batch jobs, you can still do de facto micro management of your git repositories (e.g *add/reset, stash, commit etc.*)
Here is the screencast of the app:
[![asciicast](https://asciinema.org/a/QQPVDWVxUR3bvJhIZY3c4PTuG.svg)](https://asciinema.org/a/QQPVDWVxUR3bvJhIZY3c4PTuG)
## Installation
-For now, installation requires golang compiler and minimum golang 1.10 is recommended. (binary distribution will be provided on minimum viable product)
-- If you don't have golang installed refer to [golang.org](https://golang.org/dl/).
-- You should have $GOPATH env variable set and your $PATH should include $GOPATH/bin to run app from anywhere.
-
-To install run the following command;
+To install with go, run the following command;
```bash
go get -u github.com/isacikgoz/gitbatch
```
-I prefer using gopath like this in my zshrc or bashrc;
-```bash
-export GOPATH=$HOME/go
-export PATH=$PATH:$GOPATH/bin
-```
+For other options see [installation page](https://github.com/isacikgoz/gitbatch/wiki/Installation)
## Use
run the `gitbatch` command from the parent of your git repositories. For start-up options simply `gitbatch --help`
-For more information;
-- To see keybindings refer to [Controls page](https://github.com/isacikgoz/gitbatch/wiki/Controls)
-- Learn how to config app at [Configuration page](https://github.com/isacikgoz/gitbatch/wiki/Configuration)
-- Wonder what mode does what? see [Modes page](https://github.com/isacikgoz/gitbatch/wiki/Modes)
-- What are those arrows, colors etc mean? Answer is here at [Display page](https://github.com/isacikgoz/gitbatch/wiki/Display)
+For more information see the [wiki pages](https://github.com/isacikgoz/gitbatch/wiki)
## Further goals
- add testing
-- full src-d/go-git integration (*having some performance issues*)
- - fetch, config, add, reset, commit, status, diff ✔
- - merge, rev-list, stash ✗
- add push
+- full src-d/go-git integration (*having some performance issues in such cases*)
+ - fetch, config, add, reset, commit, status and diff commands are supported but not fully utilized, still using git sometimes
+ - merge, rev-list, stash are not supported yet by go-git
## Known issues
-Please refer to [Known issues page](https://github.com/isacikgoz/gitbatch/wiki/Known-issues)
+Please refer to [Known issues page](https://github.com/isacikgoz/gitbatch/wiki/Known-issues) and feel free to open an issue if you encounter with a problem.
## Credits
- [go-git](https://github.com/src-d/go-git) for git interface (partially)
diff --git a/main.go b/main.go
index 0a8b5b3..02a6b20 100644
--- a/main.go
+++ b/main.go
@@ -15,7 +15,7 @@ var (
)
func main() {
- kingpin.Version("gitbatch version 0.1.1 (alpha)")
+ kingpin.Version("gitbatch version 0.1.2 (pre-release)")
// parse the command line flag and options
kingpin.Parse()
diff --git a/pkg/git/authentication.go b/pkg/git/authentication.go
index def1f99..3e834ec 100644
--- a/pkg/git/authentication.go
+++ b/pkg/git/authentication.go
@@ -7,7 +7,9 @@ import (
// Credentials holds user credentials to authenticate and authorize while
// communicating with remote if required
type Credentials struct {
- User string
+ // User is the user id for authentication
+ User string
+ // Password is the secret information required for authetntication
Password string
}
@@ -17,8 +19,10 @@ var (
authProtocolSSH = "ssh"
)
-func (entity *RepoEntity) authProtocol(remote *Remote) (p string, err error) {
- u, err := url.Parse(remote.URL[0])
+// authentication protocol returns the type of protocol for given remote's URL
+// various auth protocols require different kind of authentication
+func authProtocol(r *Remote) (p string, err error) {
+ u, err := url.Parse(r.URL[0])
if err != nil {
return p, err
}
diff --git a/pkg/git/branch.go b/pkg/git/branch.go
index ace5f79..5be8e16 100644
--- a/pkg/git/branch.go
+++ b/pkg/git/branch.go
@@ -5,7 +5,7 @@ import (
log "github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
- "regexp"
+
"strconv"
"strings"
)
@@ -22,33 +22,22 @@ type Branch struct {
Clean bool
}
-// returns the active branch of the repository entity by simply getting the
-// head reference and searching it from the entities branch slice
-func (entity *RepoEntity) getActiveBranch() (branch *Branch) {
- headRef, _ := entity.Repository.Head()
- for _, lb := range entity.Branches {
- if lb.Name == headRef.Name().Short() {
- return lb
- }
- }
- return nil
-}
-
// search for branches in go-git way. It is useful to do so that checkout and
// checkout error handling can be handled by code rather than struggling with
// git cammand and its output
-func (entity *RepoEntity) loadLocalBranches() error {
+func (e *RepoEntity) loadLocalBranches() error {
lbs := make([]*Branch, 0)
- branches, err := entity.Repository.Branches()
+ bs, err := e.Repository.Branches()
if err != nil {
log.Warn("Cannot load branches " + err.Error())
return err
}
- defer branches.Close()
- branches.ForEach(func(b *plumbing.Reference) error {
+ defer bs.Close()
+ headRef, _ := e.Repository.Head()
+ bs.ForEach(func(b *plumbing.Reference) error {
if b.Type() == plumbing.HashReference {
var push, pull string
- pushables, err := RevList(entity, RevListOptions{
+ pushables, err := RevList(e, RevListOptions{
Ref1: "@{u}",
Ref2: "HEAD",
})
@@ -57,7 +46,7 @@ func (entity *RepoEntity) loadLocalBranches() error {
} else {
push = strconv.Itoa(len(pushables))
}
- pullables, err := RevList(entity, RevListOptions{
+ pullables, err := RevList(e, RevListOptions{
Ref1: "HEAD",
Ref2: "@{u}",
})
@@ -66,53 +55,53 @@ func (entity *RepoEntity) loadLocalBranches() error {
} else {
pull = strconv.Itoa(len(pullables))
}
- clean := entity.isClean()
- branch := &Branch{Name: b.Name().Short(), Reference: b, Pushables: push, Pullables: pull, Clean: clean}
+ clean := e.isClean()
+ branch := &Branch{
+ Name: b.Name().Short(),
+ Reference: b,
+ Pushables: push,
+ Pullables: pull,
+ Clean: clean,
+ }
+ if b.Name() == headRef.Name() {
+ e.Branch = branch
+ }
lbs = append(lbs, branch)
}
return nil
})
- entity.Branches = lbs
+ e.Branches = lbs
return err
}
// NextBranch checkouts the next branch
-func (entity *RepoEntity) NextBranch() *Branch {
- currentBranchIndex := entity.findCurrentBranchIndex()
- if currentBranchIndex == len(entity.Branches)-1 {
- return entity.Branches[0]
- }
- return entity.Branches[currentBranchIndex+1]
+func (e *RepoEntity) NextBranch() *Branch {
+ return e.Branches[(e.currentBranchIndex()+1)%len(e.Branches)]
}
// PreviousBranch checkouts the previous branch
-func (entity *RepoEntity) PreviousBranch() *Branch {
- currentBranchIndex := entity.findCurrentBranchIndex()
- if currentBranchIndex == 0 {
- return entity.Branches[len(entity.Branches)-1]
- }
- return entity.Branches[currentBranchIndex-1]
+func (e *RepoEntity) PreviousBranch() *Branch {
+ return e.Branches[(len(e.Branches)+e.currentBranchIndex()-1)%len(e.Branches)]
}
// returns the active branch index
-func (entity *RepoEntity) findCurrentBranchIndex() int {
- currentBranch := entity.Branch
- currentBranchIndex := 0
- for i, lbs := range entity.Branches {
- if lbs.Name == currentBranch.Name {
- currentBranchIndex = i
+func (e *RepoEntity) currentBranchIndex() int {
+ bix := 0
+ for i, lbs := range e.Branches {
+ if lbs.Name == e.Branch.Name {
+ bix = i
}
}
- return currentBranchIndex
+ return bix
}
// Checkout to given branch. If any errors occur, the method returns it instead
// of returning nil
-func (entity *RepoEntity) Checkout(branch *Branch) error {
- if branch.Name == entity.Branch.Name {
+func (e *RepoEntity) Checkout(branch *Branch) error {
+ if branch.Name == e.Branch.Name {
return nil
}
- w, err := entity.Repository.Worktree()
+ w, err := e.Repository.Worktree()
if err != nil {
log.Warn("Cannot get work tree " + err.Error())
return err
@@ -124,90 +113,26 @@ func (entity *RepoEntity) Checkout(branch *Branch) error {
return err
}
- // after checking out we need to refresh some values such as;
- entity.loadCommits()
- entity.Commit = entity.Commits[0]
- entity.Branch = branch
- entity.RefreshPushPull()
// make this conditional on global scale
- err = entity.Remote.SyncBranches(branch.Name)
- return err
+ err = e.Remote.SyncBranches(branch.Name)
+ return e.Refresh()
}
// checking the branch if it has any changes from its head revision. Initially
// I implemented this with go-git but it was incredibly slow and there is also
// an issue about it: https://github.com/src-d/go-git/issues/844
-func (entity *RepoEntity) isClean() bool {
- status := entity.StatusWithGit()
- status = helpers.TrimTrailingNewline(status)
- if status != "?" {
- verbose := strings.Split(status, "\n")
- lastLine := verbose[len(verbose)-1]
+func (e *RepoEntity) isClean() bool {
+ s := e.StatusWithGit()
+ s = helpers.TrimTrailingNewline(s)
+ if s != "?" {
+ vs := strings.Split(s, "\n")
+ line := vs[len(vs)-1]
// earlier versions of git returns "working directory clean" instead of
//"working tree clean" message
- if strings.Contains(lastLine, "working tree clean") ||
- strings.Contains(lastLine, "working directory clean") {
+ if strings.Contains(line, "working tree clean") ||
+ strings.Contains(line, "working directory clean") {
return true
}
}
return false
}
-
-// RefreshPushPull refreshes the active branchs pushable and pullable count
-func (entity *RepoEntity) RefreshPushPull() {
- pushables, err := RevList(entity, RevListOptions{
- Ref1: "@{u}",
- Ref2: "HEAD",
- })
- if err != nil {
- entity.Branch.Pushables = pushables[0]
- } else {
- entity.Branch.Pushables = strconv.Itoa(len(pushables))
- }
- pullables, err := RevList(entity, RevListOptions{
- Ref1: "HEAD",
- Ref2: "@{u}",
- })
- if err != nil {
- entity.Branch.Pullables = pullables[0]
- } else {
- entity.Branch.Pullables = strconv.Itoa(len(pullables))
- }
-}
-
-// this function creates the commit entities according to active branchs diffs
-// to *its* configured upstream
-func (entity *RepoEntity) pullDiffsToUpstream() ([]*Commit, error) {
- remoteCommits := make([]*Commit, 0)
- pullables, err := RevList(entity, RevListOptions{
- Ref1: "HEAD",
- Ref2: "@{u}",
- })
- if err != nil {
- // possibly found nothing or no upstream set
- } else {
- re := regexp.MustCompile(`\r?\n`)
- for _, s := range pullables {
- commit := &Commit{
- Hash: s,
- Author: GitShowEmail(entity.AbsPath, s),
- Message: re.ReplaceAllString(GitShowBody(entity.AbsPath, s), " "),
- Time: GitShowDate(entity.AbsPath, s),
- CommitType: RemoteCommit,
- }
- remoteCommits = append(remoteCommits, commit)
- }
- }
- return remoteCommits, nil
-}
-
-func (entity *RepoEntity) pushDiffsToUpstream() ([]string, error) {
- pushables, err := RevList(entity, RevListOptions{
- Ref1: "@{u}",
- Ref2: "HEAD",
- })
- if err != nil {
- return make([]string, 0), nil
- }
- return pushables, nil
-}
diff --git a/pkg/git/cmd-add.go b/pkg/git/cmd-add.go
index 649bad2..e7faff2 100644
--- a/pkg/git/cmd-add.go
+++ b/pkg/git/cmd-add.go
@@ -25,31 +25,31 @@ type AddOptions struct {
}
// Add is a wrapper function for "git add" command
-func Add(entity *RepoEntity, file *File, option AddOptions) error {
+func Add(e *RepoEntity, file *File, option AddOptions) error {
addCmdMode = addCmdModeNative
if option.Update || option.Force || option.DryRun {
addCmdMode = addCmdModeLegacy
}
switch addCmdMode {
case addCmdModeLegacy:
- err := addWithGit(entity, file, option)
+ err := addWithGit(e, file, option)
return err
case addCmdModeNative:
- err := addWithGoGit(entity, file)
+ err := addWithGoGit(e, file)
return err
}
return errors.New("Unhandled add operation")
}
// AddAll function is the wrapper of "git add ." command
-func AddAll(entity *RepoEntity, option AddOptions) error {
+func AddAll(e *RepoEntity, option AddOptions) error {
args := make([]string, 0)
args = append(args, addCommand)
if option.DryRun {
args = append(args, "--dry-run")
}
args = append(args, ".")
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while add command")
return errors.New(out + "\n" + err.Error())
@@ -57,7 +57,7 @@ func AddAll(entity *RepoEntity, option AddOptions) error {
return nil
}
-func addWithGit(entity *RepoEntity, file *File, option AddOptions) error {
+func addWithGit(e *RepoEntity, file *File, option AddOptions) error {
args := make([]string, 0)
args = append(args, addCommand)
args = append(args, file.Name)
@@ -70,7 +70,7 @@ func addWithGit(entity *RepoEntity, file *File, option AddOptions) error {
if option.DryRun {
args = append(args, "--dry-run")
}
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while add command")
return errors.New(out + "\n" + err.Error())
@@ -78,8 +78,8 @@ func addWithGit(entity *RepoEntity, file *File, option AddOptions) error {
return nil
}
-func addWithGoGit(entity *RepoEntity, file *File) error {
- w, err := entity.Repository.Worktree()
+func addWithGoGit(e *RepoEntity, file *File) error {
+ w, err := e.Repository.Worktree()
if err != nil {
return err
}
diff --git a/pkg/git/cmd-commit.go b/pkg/git/cmd-commit.go
index 8ea5bfe..6e836ab 100644
--- a/pkg/git/cmd-commit.go
+++ b/pkg/git/cmd-commit.go
@@ -28,24 +28,24 @@ type CommitOptions struct {
}
// CommitCommand
-func CommitCommand(entity *RepoEntity, options CommitOptions) (err error) {
+func CommitCommand(e *RepoEntity, options CommitOptions) (err error) {
// here we configure commit operation
// default mode is go-git (this may be configured)
commitCmdMode = commitCmdModeNative
switch commitCmdMode {
case commitCmdModeLegacy:
- err = commitWithGit(entity, options)
+ err = commitWithGit(e, options)
return err
case commitCmdModeNative:
- err = commitWithGoGit(entity, options)
+ err = commitWithGoGit(e, options)
return err
}
return errors.New("Unhandled commit operation")
}
// commitWithGit is simply a bare git commit -m <msg> command which is flexible
-func commitWithGit(entity *RepoEntity, options CommitOptions) (err error) {
+func commitWithGit(e *RepoEntity, options CommitOptions) (err error) {
args := make([]string, 0)
args = append(args, commitCommand)
args = append(args, "-m")
@@ -53,18 +53,18 @@ func commitWithGit(entity *RepoEntity, options CommitOptions) (err error) {
if len(options.CommitMsg) > 0 {
args = append(args, options.CommitMsg)
}
- if err := GenericGitCommand(entity.AbsPath, args); err != nil {
+ if err := GenericGitCommand(e.AbsPath, args); err != nil {
log.Warn("Error at git command (commit)")
+ e.Refresh()
return err
}
// till this step everything should be ok
- err = entity.Refresh()
- return err
+ return e.Refresh()
}
// commitWithGoGit is the primary commit method
-func commitWithGoGit(entity *RepoEntity, options CommitOptions) (err error) {
- config, err := entity.Repository.Config()
+func commitWithGoGit(e *RepoEntity, options CommitOptions) (err error) {
+ config, err := e.Repository.Config()
if err != nil {
return err
}
@@ -78,16 +78,16 @@ func commitWithGoGit(entity *RepoEntity, options CommitOptions) (err error) {
},
}
- w, err := entity.Repository.Worktree()
+ w, err := e.Repository.Worktree()
if err != nil {
return err
}
_, err = w.Commit(options.CommitMsg, opt)
if err != nil {
+ e.Refresh()
return err
}
// till this step everything should be ok
- err = entity.Refresh()
- return err
+ return e.Refresh()
}
diff --git a/pkg/git/cmd-config.go b/pkg/git/cmd-config.go
index dfbf661..06ba74b 100644
--- a/pkg/git/cmd-config.go
+++ b/pkg/git/cmd-config.go
@@ -34,24 +34,24 @@ const (
)
// Config
-func Config(entity *RepoEntity, options ConfigOptions) (value string, err error) {
+func Config(e *RepoEntity, options ConfigOptions) (value string, err error) {
// here we configure config operation
// default mode is go-git (this may be configured)
configCmdMode = configCmdModeLegacy
switch configCmdMode {
case configCmdModeLegacy:
- value, err = configWithGit(entity, options)
+ value, err = configWithGit(e, options)
return value, err
case configCmdModeNative:
- value, err = configWithGoGit(entity, options)
+ value, err = configWithGoGit(e, options)
return value, err
}
return value, errors.New("Unhandled config operation")
}
// configWithGit is simply a bare git commit -m <msg> command which is flexible
-func configWithGit(entity *RepoEntity, options ConfigOptions) (value string, err error) {
+func configWithGit(e *RepoEntity, options ConfigOptions) (value string, err error) {
args := make([]string, 0)
args = append(args, configCommand)
if len(string(options.Site)) > 0 {
@@ -60,7 +60,7 @@ func configWithGit(entity *RepoEntity, options ConfigOptions) (value string, err
args = append(args, "--get")
args = append(args, options.Section+"."+options.Option)
// parse options to command line arguments
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
return out, err
}
@@ -69,25 +69,23 @@ func configWithGit(entity *RepoEntity, options ConfigOptions) (value string, err
}
// commitWithGoGit is the primary commit method
-func configWithGoGit(entity *RepoEntity, options ConfigOptions) (value string, err error) {
+func configWithGoGit(e *RepoEntity, options ConfigOptions) (value string, err error) {
// TODO: add global search
- config, err := entity.Repository.Config()
+ config, err := e.Repository.Config()
if err != nil {
return value, err
}
- value = config.Raw.Section(options.Section).Option(options.Option)
- return value, nil
+ return config.Raw.Section(options.Section).Option(options.Option), nil
}
// AddConfig
-func AddConfig(entity *RepoEntity, options ConfigOptions, value string) (err error) {
- err = addConfigWithGit(entity, options, value)
- return err
+func AddConfig(e *RepoEntity, options ConfigOptions, value string) (err error) {
+ return addConfigWithGit(e, options, value)
}
// addConfigWithGit is simply a bare git config --add <option> command which is flexible
-func addConfigWithGit(entity *RepoEntity, options ConfigOptions, value string) (err error) {
+func addConfigWithGit(e *RepoEntity, options ConfigOptions, value string) (err error) {
args := make([]string, 0)
args = append(args, configCommand)
if len(string(options.Site)) > 0 {
@@ -98,10 +96,10 @@ func addConfigWithGit(entity *RepoEntity, options ConfigOptions, value string) (
if len(value) > 0 {
args = append(args, value)
}
- if err := GenericGitCommand(entity.AbsPath, args); err != nil {
+ if err := GenericGitCommand(e.AbsPath, args); err != nil {
log.Warn("Error at git command (config)")
return err
}
// till this step everything should be ok
- return entity.Refresh()
+ return e.Refresh()
}
diff --git a/pkg/git/cmd-diff.go b/pkg/git/cmd-diff.go
new file mode 100644
index 0000000..f9fcb3f
--- /dev/null
+++ b/pkg/git/cmd-diff.go
@@ -0,0 +1,89 @@
+package git
+
+import (
+ "errors"
+
+ "gopkg.in/src-d/go-git.v4"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+var (
+ diffCmdMode string
+
+ diffCommand = "diff"
+ diffCmdModeLegacy = "git"
+ diffCmdModeNative = "go-git"
+)
+
+// Diff is a wrapper function for "git diff" command
+// Diff function returns the diff to previous commit detail of the given has
+// of a specific commit
+func Diff(e *RepoEntity, hash string) (diff string, err error) {
+ diffCmdMode = diffCmdModeNative
+
+ switch diffCmdMode {
+ case diffCmdModeLegacy:
+ return diffWithGit(e, hash)
+ case diffCmdModeNative:
+ return diffWithGoGit(e, hash)
+ }
+ return diff, errors.New("Unhandled diff operation")
+}
+
+func diffWithGit(e *RepoEntity, hash string) (diff string, err error) {
+ return diff, nil
+}
+
+func diffWithGoGit(e *RepoEntity, hash string) (diff string, err error) {
+ currentCommitIndex := 0
+ for i, cs := range e.Commits {
+ if cs.Hash == hash {
+ currentCommitIndex = i
+ }
+ }
+ if len(e.Commits)-currentCommitIndex <= 1 {
+ return "there is no diff", nil
+ }
+
+ // maybe we dont need to log the repo again?
+ commits, err := e.Repository.Log(&git.LogOptions{
+ From: plumbing.NewHash(e.Commit.Hash),
+ Order: git.LogOrderCommitterTime,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ currentCommit, err := commits.Next()
+ if err != nil {
+ return "", err
+ }
+ currentTree, err := currentCommit.Tree()
+ if err != nil {
+ return diff, err
+ }
+
+ prevCommit, err := commits.Next()
+ if err != nil {
+ return "", err
+ }
+ prevTree, err := prevCommit.Tree()
+ if err != nil {
+ return diff, err
+ }
+
+ changes, err := prevTree.Diff(currentTree)
+ if err != nil {
+ return "", err
+ }
+
+ // here we collect the actual diff
+ for _, c := range changes {
+ patch, err := c.Patch()
+ if err != nil {
+ break
+ }
+ diff = diff + patch.String() + "\n"
+ }
+ return diff, nil
+}
diff --git a/pkg/git/cmd-fetch.go b/pkg/git/cmd-fetch.go
index d1d6abb..e7a60f7 100644
--- a/pkg/git/cmd-fetch.go
+++ b/pkg/git/cmd-fetch.go
@@ -39,7 +39,7 @@ type FetchOptions struct {
// Fetch branches refs from one or more other repositories, along with the
// objects necessary to complete their histories
-func Fetch(entity *RepoEntity, options FetchOptions) (err error) {
+func Fetch(e *RepoEntity, options FetchOptions) (err error) {
// here we configure fetch operation
// default mode is go-git (this may be configured)
fetchCmdMode = fetchCmdModeNative
@@ -50,18 +50,18 @@ func Fetch(entity *RepoEntity, options FetchOptions) (err error) {
}
switch fetchCmdMode {
case fetchCmdModeLegacy:
- err = fetchWithGit(entity, options)
+ err = fetchWithGit(e, options)
return err
case fetchCmdModeNative:
// this should be the refspec as default, let's give it a try
// TODO: Fix for quick mode, maybe better read config file
var refspec string
- if entity.Branch == nil {
+ if e.Branch == nil {
refspec = "+refs/heads/*:refs/remotes/origin/*"
} else {
- refspec = "+" + "refs/heads/" + entity.Branch.Name + ":" + "/refs/remotes/" + entity.Remote.Branch.Name
+ refspec = "+" + "refs/heads/" + e.Branch.Name + ":" + "/refs/remotes/" + e.Remote.Branch.Name
}
- err = fetchWithGoGit(entity, options, refspec)
+ err = fetchWithGoGit(e, options, refspec)
return err
}
return nil
@@ -70,7 +70,7 @@ func Fetch(entity *RepoEntity, options FetchOptions) (err error) {
// fetchWithGit is simply a bare git fetch <remote> command which is flexible
// for complex operations, but on the other hand, it ties the app to another
// tool. To avoid that, using native implementation is preferred.
-func fetchWithGit(entity *RepoEntity, options FetchOptions) (err error) {
+func fetchWithGit(e *RepoEntity, options FetchOptions) (err error) {
args := make([]string, 0)
args = append(args, fetchCommand)
// parse options to command line arguments
@@ -86,12 +86,13 @@ func fetchWithGit(entity *RepoEntity, options FetchOptions) (err error) {
if options.DryRun {
args = append(args, "--dry-run")
}
- if err := GenericGitCommand(entity.AbsPath, args); err != nil {
+ if err := GenericGitCommand(e.AbsPath, args); err != nil {
log.Warn("Error at git command (fetch)")
return err
}
+ e.SetState(Success)
// till this step everything should be ok
- return entity.Refresh()
+ return e.Refresh()
}
// fetchWithGoGit is the primary fetch method and refspec is the main feature.
@@ -100,7 +101,7 @@ func fetchWithGit(entity *RepoEntity, options FetchOptions) (err error) {
// pattern for references on the remote side and <dst> is where those references
// will be written locally. The + tells Git to update the reference even if it
// isn’t a fast-forward.
-func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (err error) {
+func fetchWithGoGit(e *RepoEntity, options FetchOptions, refspec string) (err error) {
opt := &git.FetchOptions{
RemoteName: options.RemoteName,
RefSpecs: []config.RefSpec{config.RefSpec(refspec)},
@@ -108,7 +109,7 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e
}
// if any credential is given, let's add it to the git.FetchOptions
if len(options.Credentials.User) > 0 {
- protocol, err := entity.authProtocol(entity.Remote)
+ protocol, err := authProtocol(e.Remote)
if err != nil {
return err
}
@@ -122,7 +123,7 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e
}
}
- err = entity.Repository.Fetch(opt)
+ err = e.Repository.Fetch(opt)
if err != nil {
if err == git.NoErrAlreadyUpToDate {
// Already up-to-date
@@ -130,10 +131,10 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e
// TODO: submit a PR for this kind of error, this type of catch is lame
} else if strings.Contains(err.Error(), "couldn't find remote ref") {
// we dont have remote ref, so lets pull other things.. maybe it'd be useful
- rp := entity.Remote.RefSpecs[0]
+ rp := e.Remote.RefSpecs[0]
if fetchTryCount < fetchMaxTry {
fetchTryCount++
- fetchWithGoGit(entity, options, rp)
+ fetchWithGoGit(e, options, rp)
} else {
return err
}
@@ -145,6 +146,8 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e
return err
}
}
+
+ e.SetState(Success)
// till this step everything should be ok
- return entity.Refresh()
+ return e.Refresh()
}
diff --git a/pkg/git/cmd-merge.go b/pkg/git/cmd-merge.go
index 984bc4b..e89761d 100644
--- a/pkg/git/cmd-merge.go
+++ b/pkg/git/cmd-merge.go
@@ -18,7 +18,7 @@ type MergeOptions struct {
// Merge incorporates changes from the named commits or branches into the
// current branch
-func Merge(entity *RepoEntity, options MergeOptions) error {
+func Merge(e *RepoEntity, options MergeOptions) error {
args := make([]string, 0)
args = append(args, mergeCommand)
if len(options.BranchName) > 0 {
@@ -30,10 +30,10 @@ func Merge(entity *RepoEntity, options MergeOptions) error {
if options.NoStat {
args = append(args, "-n")
}
- if err := GenericGitCommand(entity.AbsPath, args); err != nil {
+ if err := GenericGitCommand(e.AbsPath, args); err != nil {
log.Warn("Error while merging")
return err
}
- entity.Refresh()
- return nil
+ e.SetState(Success)
+ return e.Refresh()
}
diff --git a/pkg/git/cmd-pull.go b/pkg/git/cmd-pull.go
index a493b19..2c28d3a 100644
--- a/pkg/git/cmd-pull.go
+++ b/pkg/git/cmd-pull.go
@@ -4,6 +4,7 @@ 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"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
)
@@ -33,7 +34,7 @@ type PullOptions struct {
}
// Pull ncorporates changes from a remote repository into the current branch.
-func Pull(entity *RepoEntity, options PullOptions) (err error) {
+func Pull(e *RepoEntity, options PullOptions) (err error) {
// here we configure pull operation
// default mode is go-git (this may be configured)
pullCmdMode = pullCmdModeNative
@@ -41,16 +42,16 @@ func Pull(entity *RepoEntity, options PullOptions) (err error) {
switch pullCmdMode {
case pullCmdModeLegacy:
- err = pullWithGit(entity, options)
+ err = pullWithGit(e, options)
return err
case pullCmdModeNative:
- err = pullWithGoGit(entity, options)
+ err = pullWithGoGit(e, options)
return err
}
return nil
}
-func pullWithGit(entity *RepoEntity, options PullOptions) (err error) {
+func pullWithGit(e *RepoEntity, options PullOptions) (err error) {
args := make([]string, 0)
args = append(args, pullCommand)
// parse options to command line arguments
@@ -60,14 +61,15 @@ func pullWithGit(entity *RepoEntity, options PullOptions) (err error) {
if options.Force {
args = append(args, "-f")
}
- if err := GenericGitCommand(entity.AbsPath, args); err != nil {
+ if err := GenericGitCommand(e.AbsPath, args); err != nil {
log.Warn("Error at git command (pull)")
return err
}
- return entity.Refresh()
+ e.SetState(Success)
+ return e.Refresh()
}
-func pullWithGoGit(entity *RepoEntity, options PullOptions) (err error) {
+func pullWithGoGit(e *RepoEntity, options PullOptions) (err error) {
opt := &git.PullOptions{
RemoteName: options.RemoteName,
SingleBranch: options.SingleBranch,
@@ -79,7 +81,7 @@ func pullWithGoGit(entity *RepoEntity, options PullOptions) (err error) {
}
// 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)
+ protocol, err := authProtocol(e.Remote)
if err != nil {
return err
}
@@ -92,13 +94,23 @@ func pullWithGoGit(entity *RepoEntity, options PullOptions) (err error) {
return ErrInvalidAuthMethod
}
}
- w, err := entity.Repository.Worktree()
+ w, err := e.Repository.Worktree()
if err != nil {
return err
}
err = w.Pull(opt)
if err != nil {
- return err
+ if err == git.NoErrAlreadyUpToDate {
+ // Already up-to-date
+ log.Warn(err.Error())
+ } else if err == transport.ErrAuthenticationRequired {
+ log.Warn(err.Error())
+ return ErrAuthenticationRequired
+ } else {
+ log.Warn(err.Error())
+ return err
+ }
}
- return entity.Refresh()
+ e.SetState(Success)
+ return e.Refresh()
}
diff --git a/pkg/git/cmd-reset.go b/pkg/git/cmd-reset.go
index 2e6ed91..cc5f0f9 100644
--- a/pkg/git/cmd-reset.go
+++ b/pkg/git/cmd-reset.go
@@ -47,12 +47,12 @@ const (
)
// Reset is the wrapper of "git reset" command
-func Reset(entity *RepoEntity, file *File, option ResetOptions) error {
- resetCmdMode = addCmdModeLegacy
+func Reset(e *RepoEntity, file *File, option ResetOptions) error {
+ resetCmdMode = resetCmdModeLegacy
switch resetCmdMode {
case resetCmdModeLegacy:
- err := resetWithGit(entity, file, option)
+ err := resetWithGit(e, file, option)
return err
case resetCmdModeNative:
@@ -60,7 +60,7 @@ func Reset(entity *RepoEntity, file *File, option ResetOptions) error {
return errors.New("Unhandled reset operation")
}
-func resetWithGit(entity *RepoEntity, file *File, option ResetOptions) error {
+func resetWithGit(e *RepoEntity, file *File, option ResetOptions) error {
args := make([]string, 0)
args = append(args, resetCommand)
@@ -69,7 +69,7 @@ func resetWithGit(entity *RepoEntity, file *File, option ResetOptions) error {
if len(option.Rtype) > 0 {
args = append(args, "--"+string(option.Rtype))
}
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while reset command")
return errors.New(out + "\n" + err.Error())
@@ -78,27 +78,27 @@ func resetWithGit(entity *RepoEntity, file *File, option ResetOptions) error {
}
// ResetAll resets the changes in a repository, should be used wise
-func ResetAll(entity *RepoEntity, option ResetOptions) error {
+func ResetAll(e *RepoEntity, option ResetOptions) error {
resetCmdMode = addCmdModeNative
switch resetCmdMode {
case resetCmdModeLegacy:
- err := resetAllWithGit(entity, option)
+ err := resetAllWithGit(e, option)
return err
case resetCmdModeNative:
- err := resetAllWithGoGit(entity, option)
+ err := resetAllWithGoGit(e, option)
return err
}
return errors.New("Unhandled reset operation")
}
-func resetAllWithGit(entity *RepoEntity, option ResetOptions) error {
+func resetAllWithGit(e *RepoEntity, option ResetOptions) error {
args := make([]string, 0)
args = append(args, resetCommand)
if len(option.Rtype) > 0 {
args = append(args, "--"+string(option.Rtype))
}
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while add command")
return errors.New(out + "\n" + err.Error())
@@ -106,8 +106,8 @@ func resetAllWithGit(entity *RepoEntity, option ResetOptions) error {
return nil
}
-func resetAllWithGoGit(entity *RepoEntity, option ResetOptions) error {
- w, err := entity.Repository.Worktree()
+func resetAllWithGoGit(e *RepoEntity, option ResetOptions) error {
+ w, err := e.Repository.Worktree()
if err != nil {
return err
}
diff --git a/pkg/git/cmd-rev-list.go b/pkg/git/cmd-rev-list.go
index 664d69f..e68e82a 100644
--- a/pkg/git/cmd-rev-list.go
+++ b/pkg/git/cmd-rev-list.go
@@ -19,14 +19,14 @@ type RevListOptions struct {
// RevList returns the commit hashes that are links from the given commit(s).
// The output is given in reverse chronological order by default.
-func RevList(entity *RepoEntity, options RevListOptions) ([]string, error) {
+func RevList(e *RepoEntity, options RevListOptions) ([]string, error) {
args := make([]string, 0)
args = append(args, revlistCommand)
if len(options.Ref1) > 0 && len(options.Ref2) > 0 {
arg1 := options.Ref1 + ".." + options.Ref2
args = append(args, arg1)
}
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while rev-list command")
return []string{out}, err
diff --git a/pkg/git/cmd-stash.go b/pkg/git/cmd-stash.go
index 9d813a6..a786833 100644
--- a/pkg/git/cmd-stash.go
+++ b/pkg/git/cmd-stash.go
@@ -19,11 +19,11 @@ type StashedItem struct {
EntityPath string
}
-func stashGet(entity *RepoEntity, option string) string {
+func stashGet(e *RepoEntity, option string) string {
args := make([]string, 0)
args = append(args, stashCommand)
args = append(args, option)
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while stash command")
return "?"
@@ -31,9 +31,9 @@ func stashGet(entity *RepoEntity, option string) string {
return out
}
-func (entity *RepoEntity) loadStashedItems() error {
- entity.Stasheds = make([]*StashedItem, 0)
- output := stashGet(entity, "list")
+func (e *RepoEntity) loadStashedItems() error {
+ e.Stasheds = make([]*StashedItem, 0)
+ output := stashGet(e, "list")
stashIDRegex := regexp.MustCompile(`stash@{[\d]+}:`)
stashIDRegexInt := regexp.MustCompile(`[\d]+`)
stashBranchRegex := regexp.MustCompile(`[\w]+: `)
@@ -63,24 +63,24 @@ func (entity *RepoEntity) loadStashedItems() error {
// trim hash
desc := stashHashRegex.Split(trimmed, 2)[1][1:]
- entity.Stasheds = append(entity.Stasheds, &StashedItem{
+ e.Stasheds = append(e.Stasheds, &StashedItem{
StashID: i,
BranchName: branchName,
Hash: hash,
Description: desc,
- EntityPath: entity.AbsPath,
+ EntityPath: e.AbsPath,
})
}
return nil
}
// Stash is the wrapper of convetional "git stash" command
-func (entity *RepoEntity) Stash() (output string, err error) {
+func (e *RepoEntity) Stash() (output string, err error) {
args := make([]string, 0)
args = append(args, stashCommand)
- output, err = GenericGitCommandWithErrorOutput(entity.AbsPath, args)
- entity.Refresh()
+ output, err = GenericGitCommandWithErrorOutput(e.AbsPath, args)
+ e.Refresh()
return output, err
}
diff --git a/pkg/git/cmd-status.go b/pkg/git/cmd-status.go
index 508a27c..eec9b57 100644
--- a/pkg/git/cmd-status.go
+++ b/pkg/git/cmd-status.go
@@ -18,44 +18,12 @@ var (
statusCmdModeNative = "go-git"
)
-// File represents the status of a file in an index or work tree
-type File struct {
- Name string
- AbsPath string
- X FileStatus
- Y FileStatus
-}
-
-// FileStatus is the short representation of state of a file
-type FileStatus byte
-
-var (
- // StatusNotupdated says file not updated
- StatusNotupdated FileStatus = ' '
- // StatusModified says file is modifed
- StatusModified FileStatus = 'M'
- // StatusAdded says file is added to index
- StatusAdded FileStatus = 'A'
- // StatusDeleted says file is deleted
- StatusDeleted FileStatus = 'D'
- // StatusRenamed says file is renamed
- StatusRenamed FileStatus = 'R'
- // StatusCopied says file is copied
- StatusCopied FileStatus = 'C'
- // StatusUpdated says file is updated
- StatusUpdated FileStatus = 'U'
- // StatusUntracked says file is untraced
- StatusUntracked FileStatus = '?'
- // StatusIgnored says file is ignored
- StatusIgnored FileStatus = '!'
-)
-
-func shortStatus(entity *RepoEntity, option string) string {
+func shortStatus(e *RepoEntity, option string) string {
args := make([]string, 0)
args = append(args, statusCommand)
args = append(args, option)
args = append(args, "--short")
- out, err := GenericGitCommandWithOutput(entity.AbsPath, args)
+ out, err := GenericGitCommandWithOutput(e.AbsPath, args)
if err != nil {
log.Warn("Error while status command")
return "?"
@@ -63,23 +31,23 @@ func shortStatus(entity *RepoEntity, option string) string {
return out
}
-func Status(entity *RepoEntity) ([]*File, error) {
+func Status(e *RepoEntity) ([]*File, error) {
statusCmdMode = statusCmdModeNative
switch statusCmdMode {
case statusCmdModeLegacy:
- return statusWithGit(entity)
+ return statusWithGit(e)
case statusCmdModeNative:
- return statusWithGoGit(entity)
+ return statusWithGoGit(e)
}
return nil, errors.New("Unhandled status operation")
}
// LoadFiles function simply commands a git status and collects output in a
// structured way
-func statusWithGit(entity *RepoEntity) ([]*File, error) {
+func statusWithGit(e *RepoEntity) ([]*File, error) {
files := make([]*File, 0)
- output := shortStatus(entity, "--untracked-files=all")
+ output := shortStatus(e, "--untracked-files=all")
if len(output) == 0 {
return files, nil
}
@@ -92,7 +60,7 @@ func statusWithGit(entity *RepoEntity) ([]*File, error) {
files = append(files, &File{
Name: path,
- AbsPath: entity.AbsPath + string(os.PathSeparator) + path,
+ AbsPath: e.AbsPath + string(os.PathSeparator) + path,
X: FileStatus(x),
Y: FileStatus(y),
})
@@ -101,9 +69,9 @@ func statusWithGit(entity *RepoEntity) ([]*File, error) {
return files, nil
}
-func statusWithGoGit(entity *RepoEntity) ([]*File, error) {
+func statusWithGoGit(e *RepoEntity) ([]*File, error) {
files := make([]*File, 0)
- w, err := entity.Repository.Worktree()
+ w, err := e.Repository.Worktree()
if err != nil {
return files, err
}
@@ -114,7 +82,7 @@ func statusWithGoGit(entity *RepoEntity) ([]*File, error) {
for k, v := range s {
files = append(files, &File{
Name: k,
- AbsPath: entity.AbsPath + string(os.PathSeparator) + k,
+ AbsPath: e.AbsPath + string(os.PathSeparator) + k,
X: FileStatus(v.Staging),
Y: FileStatus(v.Worktree),
})
@@ -122,13 +90,3 @@ func statusWithGoGit(entity *RepoEntity) ([]*File, error) {
sort.Sort(filesAlphabetical(files))
return files, nil
}
-
-// Diff is a wrapper of "git diff" command for a file to compare with HEAD rev
-func (file *File) Diff() (output string, err error) {
- args := make([]string, 0)
- args = append(args, "diff")
- args = append(args, "HEAD")
- args = append(args, file.Name)
- output, err = GenericGitCommandWithErrorOutput(strings.TrimSuffix(file.AbsPath, file.Name), args)
- return output, err
-}
diff --git a/pkg/git/cmd.go b/pkg/git/cmd.go
index b78b501..a84f489 100644
--- a/pkg/git/cmd.go
+++ b/pkg/git/cmd.go
@@ -72,9 +72,9 @@ func GitShowDate(repoPath, hash string) string {
}
// StatusWithGit returns the plaintext short status of the repo
-func (entity *RepoEntity) StatusWithGit() string {
+func (e *RepoEntity) StatusWithGit() string {
args := []string{"status"}
- status, err := helpers.RunCommandWithOutput(entity.AbsPath, "git", args)
+ status, err := helpers.RunCommandWithOutput(e.AbsPath, "git", args)
if err != nil {
return "?"
}
diff --git a/pkg/git/commit.go b/pkg/git/commit.go
index 8694f1a..716ccba 100644
--- a/pkg/git/commit.go
+++ b/pkg/git/commit.go
@@ -5,7 +5,6 @@ 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/object"
)
@@ -34,49 +33,37 @@ const (
// NextCommit iterates over next commit of a branch
// TODO: the commits entites can tied to branch instead ot the repository
-func (entity *RepoEntity) NextCommit() error {
- currentCommitIndex := entity.findCurrentCommitIndex()
- if currentCommitIndex == len(entity.Commits)-1 {
- entity.Commit = entity.Commits[0]
- return nil
- }
- entity.Commit = entity.Commits[currentCommitIndex+1]
- return nil
+func (e *RepoEntity) NextCommit() {
+ e.Commit = e.Commits[(e.currentCommitIndex()+1)%len(e.Commits)]
}
// PreviousCommit iterates to opposite direction
-func (entity *RepoEntity) PreviousCommit() error {
- currentCommitIndex := entity.findCurrentCommitIndex()
- if currentCommitIndex == 0 {
- entity.Commit = entity.Commits[len(entity.Commits)-1]
- return nil
- }
- entity.Commit = entity.Commits[currentCommitIndex-1]
- return nil
+func (e *RepoEntity) PreviousCommit() {
+ e.Commit = e.Commits[(len(e.Commits)+e.currentCommitIndex()-1)%len(e.Commits)]
}
// returns the active commit index
-func (entity *RepoEntity) findCurrentCommitIndex() int {
- currentCommitIndex := 0
- for i, cs := range entity.Commits {
- if cs.Hash == entity.Commit.Hash {
- currentCommitIndex = i
+func (e *RepoEntity) currentCommitIndex() int {
+ cix := 0
+ for i, c := range e.Commits {
+ if c.Hash == e.Commit.Hash {
+ cix = i
}
}
- return currentCommitIndex
+ return cix
}
// loads the local commits by simply using git log way. ALso, gets the upstream
// diff commits
-func (entity *RepoEntity) loadCommits() error {
- r := entity.Repository
- entity.Commits = make([]*Commit, 0)
+func (e *RepoEntity) loadCommits() error {
+ r := e.Repository
+ e.Commits = make([]*Commit, 0)
ref, err := r.Head()
if err != nil {
log.Trace("Cannot get HEAD " + err.Error())
return err
}
-
+ // git log first
cIter, err := r.Log(&git.LogOptions{
From: ref.Hash(),
Order: git.LogOrderCommitterTime,
@@ -86,19 +73,13 @@ func (entity *RepoEntity) loadCommits() error {
return err
}
defer cIter.Close()
- rmcs, err := entity.pullDiffsToUpstream()
- if err != nil {
- log.Trace("git rev-list failed " + err.Error())
- return err
- }
- for _, rmc := range rmcs {
- entity.Commits = append(entity.Commits, rmc)
- }
- lcs, err := entity.pushDiffsToUpstream()
- if err != nil {
- log.Trace("git rev-list failed " + err.Error())
- return err
- }
+ // find commits that fetched from upstream but not merged commits
+ rmcs, _ := e.pullDiffsToUpstream()
+ e.Commits = append(e.Commits, rmcs...)
+
+ // find commits that not pushed to upstream
+ lcs, _ := e.pushDiffsToUpstream()
+
// ... just iterates over the commits
err = cIter.ForEach(func(c *object.Commit) error {
re := regexp.MustCompile(`\r?\n`)
@@ -115,70 +96,50 @@ func (entity *RepoEntity) loadCommits() error {
Time: c.Author.When.String(),
CommitType: cmType,
}
- entity.Commits = append(entity.Commits, commit)
-
+ e.Commits = append(e.Commits, commit)
return nil
})
if err != nil {
return err
}
- // entity.Commits = commits
return nil
}
-// Diff function returns the diff to previous commit detail of the given has
-// of a specific commit
-func (entity *RepoEntity) Diff(hash string) (diff string, err error) {
-
- currentCommitIndex := 0
- for i, cs := range entity.Commits {
- if cs.Hash == hash {
- currentCommitIndex = i
- }
- }
- if len(entity.Commits)-currentCommitIndex <= 1 {
- return "there is no diff", nil
- }
-
- // maybe we dont need to log the repo again?
- commits, err := entity.Repository.Log(&git.LogOptions{
- From: plumbing.NewHash(entity.Commit.Hash),
- Order: git.LogOrderCommitterTime,
+// this function creates the commit entities according to active branchs diffs
+// to *its* configured upstream
+func (e *RepoEntity) pullDiffsToUpstream() ([]*Commit, error) {
+ remoteCommits := make([]*Commit, 0)
+ pullables, err := RevList(e, RevListOptions{
+ Ref1: "HEAD",
+ Ref2: "@{u}",
})
if err != nil {
- return "", err
- }
-
- currentCommit, err := commits.Next()
- if err != nil {
- return "", err
- }
- currentTree, err := currentCommit.Tree()
- if err != nil {
- return diff, err
- }
-
- prevCommit, err := commits.Next()
- if err != nil {
- return "", err
- }
- prevTree, err := prevCommit.Tree()
- if err != nil {
- return diff, err
+ // possibly found nothing or no upstream set
+ } else {
+ re := regexp.MustCompile(`\r?\n`)
+ for _, s := range pullables {
+ commit := &Commit{
+ Hash: s,
+ Author: GitShowEmail(e.AbsPath, s),
+ Message: re.ReplaceAllString(GitShowBody(e.AbsPath, s), " "),
+ Time: GitShowDate(e.AbsPath, s),
+ CommitType: RemoteCommit,
+ }
+ remoteCommits = append(remoteCommits, commit)
+ }
}
+ return remoteCommits, nil
+}
- changes, err := prevTree.Diff(currentTree)
+// this function returns the hashes of the commits that are not pushed to the
+// upstream of the specific branch
+func (e *RepoEntity) pushDiffsToUpstream() ([]string, error) {
+ pushables, err := RevList(e, RevListOptions{
+ Ref1: "@{u}",
+ Ref2: "HEAD",
+ })
if err != nil {
- return "", err
- }
-
- // here we collect the actual diff
- for _, c := range changes {
- patch, err := c.Patch()
- if err != nil {
- break
- }
- diff = diff + patch.String() + "\n"
+ return make([]string, 0), nil
}
- return diff, nil
+ return pushables, nil
}
diff --git a/pkg/git/file.go b/pkg/git/file.go
new file mode 100644
index 0000000..1e086bc
--- /dev/null
+++ b/pkg/git/file.go
@@ -0,0 +1,52 @@
+package git
+
+import (
+ "strings"
+
+ log "github.com/sirupsen/logrus"
+)
+
+// File represents the status of a file in an index or work tree
+type File struct {
+ Name string
+ AbsPath string
+ X FileStatus
+ Y FileStatus
+}
+
+// FileStatus is the short representation of state of a file
+type FileStatus byte
+
+var (
+ // StatusNotupdated says file not updated
+ StatusNotupdated FileStatus = ' '
+ // StatusModified says file is modifed
+ StatusModified FileStatus = 'M'
+ // StatusAdded says file is added to index
+ StatusAdded FileStatus = 'A'
+ // StatusDeleted says file is deleted
+ StatusDeleted FileStatus = 'D'
+ // StatusRenamed says file is renamed
+ StatusRenamed FileStatus = 'R'
+ // StatusCopied says file is copied
+ StatusCopied FileStatus = 'C'
+ // StatusUpdated says file is updated
+ StatusUpdated FileStatus = 'U'
+ // StatusUntracked says file is untraced
+ StatusUntracked FileStatus = '?'
+ // StatusIgnored says file is ignored
+ StatusIgnored FileStatus = '!'
+)
+
+// Diff is a wrapper of "git diff" command for a file to compare with HEAD rev
+func (f *File) Diff() (output string, err error) {
+ args := make([]string, 0)
+ args = append(args, "diff")
+ args = append(args, "HEAD")
+ args = append(args, f.Name)
+ output, err = GenericGitCommandWithErrorOutput(strings.TrimSuffix(f.AbsPath, f.Name), args)
+ if err != nil {
+ log.Warn(err)
+ }
+ return output, err
+}
diff --git a/pkg/git/job-queue.go b/pkg/git/job-queue.go
index e95250a..3eee605 100644
--- a/pkg/git/job-queue.go
+++ b/pkg/git/job-queue.go
@@ -11,7 +11,7 @@ type JobQueue struct {
// CreateJobQueue creates a jobqueue struct and initialize its slice then return
// its pointer
-func CreateJobQueue() (jobQueue *JobQueue) {
+func CreateJobQueue() (jq *JobQueue) {
s := make([]*Job, 0)
return &JobQueue{
series: s,
@@ -19,28 +19,28 @@ func CreateJobQueue() (jobQueue *JobQueue) {
}
// AddJob adds a job to the queue
-func (jobQueue *JobQueue) AddJob(j *Job) error {
- for _, job := range jobQueue.series {
+func (jq *JobQueue) AddJob(j *Job) error {
+ for _, job := range jq.series {
if job.Entity.RepoID == j.Entity.RepoID && job.JobType == j.JobType {
return errors.New("Same job already is in the queue")
}
}
- jobQueue.series = append(jobQueue.series, nil)
- copy(jobQueue.series[1:], jobQueue.series[0:])
- jobQueue.series[0] = j
+ jq.series = append(jq.series, nil)
+ copy(jq.series[1:], jq.series[0:])
+ jq.series[0] = j
return nil
}
// StartNext starts the next job in the queue
-func (jobQueue *JobQueue) StartNext() (j *Job, finished bool, err error) {
+func (jq *JobQueue) StartNext() (j *Job, finished bool, err error) {
finished = false
- if len(jobQueue.series) < 1 {
+ if len(jq.series) < 1 {
finished = true
return nil, finished, nil
}
- i := len(jobQueue.series) - 1
- lastJob := jobQueue.series[i]
- jobQueue.series = jobQueue.series[:i]
+ i := len(jq.series) - 1
+ lastJob := jq.series[i]
+ jq.series = jq.series[:i]
if err = lastJob.start(); err != nil {
return lastJob, finished, err
}
@@ -49,11 +49,11 @@ func (jobQueue *JobQueue) StartNext() (j *Job, finished bool, err error) {
// RemoveFromQueue deletes the given entity and its job from the queue
// TODO: it is not safe if the job has been started
-func (jobQueue *JobQueue) RemoveFromQueue(entity *RepoEntity) error {
+func (jq *JobQueue) RemoveFromQueue(entity *RepoEntity) error {
removed := false
- for i, job := range jobQueue.series {
+ for i, job := range jq.series {
if job.Entity.RepoID == entity.RepoID {
- jobQueue.series = append(jobQueue.series[:i], jobQueue.series[i+1:]...)
+ jq.series = append(jq.series[:i], jq.series[i+1:]...)
removed = true
}
}
@@ -66,9 +66,9 @@ func (jobQueue *JobQueue) RemoveFromQueue(entity *RepoEntity) error {
// IsInTheQueue function; since the job and entity is not tied with its own
// struct, this function returns true if that entity is in the queue along with
// the jobs type
-func (jobQueue *JobQueue) IsInTheQueue(entity *RepoEntity) (inTheQueue bool, jt JobType) {
+func (jq *JobQueue) IsInTheQueue(entity *RepoEntity) (inTheQueue bool, jt JobType) {
inTheQueue = false
- for _, job := range jobQueue.series {
+ for _, job := range jq.series {
if job.Entity.RepoID == entity.RepoID {
inTheQueue = true
jt = job.JobType
diff --git a/pkg/git/job.go b/pkg/git/job.go
index 7dc4126..c64086e 100644
--- a/pkg/git/job.go
+++ b/pkg/git/job.go
@@ -25,54 +25,47 @@ const (
)
// starts the job
-func (job *Job) start() error {
- job.Entity.State = Working
+func (j *Job) start() error {
+ j.Entity.SetState(Working)
// TODO: Handle errors?
// TOOD: Better implementation required
- switch mode := job.JobType; mode {
+ switch mode := j.JobType; mode {
case FetchJob:
var opts FetchOptions
- if job.Options != nil {
- opts = job.Options.(FetchOptions)
+ if j.Options != nil {
+ opts = j.Options.(FetchOptions)
} else {
opts = FetchOptions{
- RemoteName: job.Entity.Remote.Name,
+ RemoteName: j.Entity.Remote.Name,
}
}
- if err := Fetch(job.Entity, opts); err != nil {
- job.Entity.State = Fail
+ if err := Fetch(j.Entity, opts); err != nil {
+ j.Entity.SetState(Fail)
return err
}
case PullJob:
- var opts FetchOptions
- if job.Options != nil {
- opts = job.Options.(FetchOptions)
+ var opts PullOptions
+ if j.Options != nil {
+ opts = j.Options.(PullOptions)
} else {
- opts = FetchOptions{
- RemoteName: job.Entity.Remote.Name,
+ opts = PullOptions{
+ RemoteName: j.Entity.Remote.Name,
}
}
- if err := Fetch(job.Entity, opts); err != nil {
- job.Entity.State = Fail
+ if err := Pull(j.Entity, opts); err != nil {
+ j.Entity.SetState(Fail)
return err
}
- if err := Merge(job.Entity, MergeOptions{
- BranchName: job.Entity.Remote.Branch.Name,
- }); err != nil {
- job.Entity.State = Fail
- return nil
- }
case MergeJob:
- if err := Merge(job.Entity, MergeOptions{
- BranchName: job.Entity.Remote.Branch.Name,
+ if err := Merge(j.Entity, MergeOptions{
+ BranchName: j.Entity.Remote.Branch.Name,
}); err != nil {
- job.Entity.State = Fail
+ j.Entity.SetState(Fail)
return nil
}
default:
- job.Entity.State = Available
+ j.Entity.SetState(Available)
return nil
}
- job.Entity.State = Success
return nil
}
diff --git a/pkg/git/remote.go b/pkg/git/remote.go
index 27f627b..72c8ddd 100644
--- a/pkg/git/remote.go
+++ b/pkg/git/remote.go
@@ -16,48 +16,38 @@ type Remote struct {
}
// NextRemote iterates over next branch of a remote
-func (entity *RepoEntity) NextRemote() error {
- currentRemoteIndex := entity.findCurrentRemoteIndex()
- if currentRemoteIndex == len(entity.Remotes)-1 {
- entity.Remote = entity.Remotes[0]
- } else {
- entity.Remote = entity.Remotes[currentRemoteIndex+1]
- }
- err := entity.Remote.SyncBranches(entity.Branch.Name)
- return err
+func (e *RepoEntity) NextRemote() error {
+ e.Remote = e.Remotes[(e.currentRemoteIndex()+1)%len(e.Remotes)]
+ e.Remote.SyncBranches(e.Branch.Name)
+ return e.Publish(RepositoryUpdated, nil)
}
// PreviousRemote iterates over previous branch of a remote
-func (entity *RepoEntity) PreviousRemote() error {
- currentRemoteIndex := entity.findCurrentRemoteIndex()
- if currentRemoteIndex == 0 {
- entity.Remote = entity.Remotes[len(entity.Remotes)-1]
- } else {
- entity.Remote = entity.Remotes[currentRemoteIndex-1]
- }
- err := entity.Remote.SyncBranches(entity.Branch.Name)
- return err
+func (e *RepoEntity) PreviousRemote() error {
+ e.Remote = e.Remotes[(len(e.Remotes)+e.currentRemoteIndex()-1)%len(e.Remotes)]
+ e.Remote.SyncBranches(e.Branch.Name)
+ return e.Publish(RepositoryUpdated, nil)
}
// returns the active remote index
-func (entity *RepoEntity) findCurrentRemoteIndex() int {
- currentRemoteIndex := 0
- for i, remote := range entity.Remotes {
- if remote.Name == entity.Remote.Name {
- currentRemoteIndex = i
+func (e *RepoEntity) currentRemoteIndex() int {
+ cix := 0
+ for i, remote := range e.Remotes {
+ if remote.Name == e.Remote.Name {
+ cix = i
}
}
- return currentRemoteIndex
+ return cix
}
// search for remotes in go-git way. It is the short way to get remotes but it
// does not give any insght about remote branches
-func (entity *RepoEntity) loadRemotes() error {
- r := entity.Repository
- entity.Remotes = make([]*Remote, 0)
+func (e *RepoEntity) loadRemotes() error {
+ r := e.Repository
+ e.Remotes = make([]*Remote, 0)
- remotes, err := r.Remotes()
- for _, rm := range remotes {
+ rms, err := r.Remotes()
+ for _, rm := range rms {
rfs := make([]string, 0)
for _, rf := range rm.Config().Fetch {
rfs = append(rfs, string(rf))
@@ -67,11 +57,11 @@ func (entity *RepoEntity) loadRemotes() error {
URL: rm.Config().URLs,
RefSpecs: rfs,
}
- remote.loadRemoteBranches(entity)
+ remote.loadRemoteBranches(e)
if len(remote.Branches) > 0 {
remote.Branch = remote.Branches[0]
}
- entity.Remotes = append(entity.Remotes, remote)
+ e.Remotes = append(e.Remotes, remote)
}
if err != nil {
@@ -82,8 +72,8 @@ func (entity *RepoEntity) loadRemotes() error {
}
// SyncBranches sets the remote branch according to repository's active branch
-func (remote *Remote) SyncBranches(branchName string) error {
- if err := remote.switchRemoteBranch(remote.Name + "/" + branchName); err != nil {
+func (r *Remote) SyncBranches(branchName string) error {
+ if err := r.switchRemoteBranch(r.Name + "/" + branchName); err != nil {
// probably couldn't find, but its ok.
}
return nil
diff --git a/pkg/git/remotebranch.go b/pkg/git/remotebranch.go
index 8ab737f..207f5a9 100644
--- a/pkg/git/remotebranch.go
+++ b/pkg/git/remotebranch.go
@@ -14,66 +14,50 @@ import (
type RemoteBranch struct {
Name string
Reference *plumbing.Reference
- Deleted bool
}
// NextRemoteBranch iterates to the next remote branch
-func (remote *Remote) NextRemoteBranch() error {
- currentRemoteIndex := remote.findCurrentRemoteBranchIndex()
- if currentRemoteIndex == len(remote.Branches)-1 {
- remote.Branch = remote.Branches[0]
- } else {
- remote.Branch = remote.Branches[currentRemoteIndex+1]
- }
- return nil
+func (r *Remote) NextRemoteBranch(e *RepoEntity) error {
+ r.Branch = r.Branches[(r.currentRemoteBranchIndex()+1)%len(r.Branches)]
+ return e.Publish(RepositoryUpdated, nil)
}
// PreviousRemoteBranch iterates to the previous remote branch
-func (remote *Remote) PreviousRemoteBranch() error {
- currentRemoteIndex := remote.findCurrentRemoteBranchIndex()
- if currentRemoteIndex == 0 {
- remote.Branch = remote.Branches[len(remote.Branches)-1]
- } else {
- remote.Branch = remote.Branches[currentRemoteIndex-1]
- }
- return nil
+func (r *Remote) PreviousRemoteBranch(e *RepoEntity) error {
+ r.Branch = r.Branches[(len(r.Branches)+r.currentRemoteBranchIndex()-1)%len(r.Branches)]
+ return e.Publish(RepositoryUpdated, nil)
}
// returns the active remote branch index
-func (remote *Remote) findCurrentRemoteBranchIndex() int {
- currentRemoteIndex := 0
- for i, rb := range remote.Branches {
- if rb.Reference.Hash() == remote.Branch.Reference.Hash() {
- currentRemoteIndex = i
+func (r *Remote) currentRemoteBranchIndex() int {
+ cix := 0
+ for i, rb := range r.Branches {
+ if rb.Reference.Hash() == r.Branch.Reference.Hash() {
+ cix = i
}
}
- return currentRemoteIndex
+ return cix
}
// search for the remote branches of the remote. It takes the go-git's repo
// pointer in order to get storer struct
-func (remote *Remote) loadRemoteBranches(entity *RepoEntity) error {
- remote.Branches = make([]*RemoteBranch, 0)
- bs, err := remoteBranchesIter(entity.Repository.Storer)
+func (r *Remote) loadRemoteBranches(e *RepoEntity) error {
+ r.Branches = make([]*RemoteBranch, 0)
+ bs, err := remoteBranchesIter(e.Repository.Storer)
if err != nil {
log.Warn("Cannot initiate iterator " + err.Error())
return err
}
defer bs.Close()
err = bs.ForEach(func(b *plumbing.Reference) error {
- deleted := false
- if strings.Split(b.Name().Short(), "/")[0] == remote.Name {
- remote.Branches = append(remote.Branches, &RemoteBranch{
+ if strings.Split(b.Name().Short(), "/")[0] == r.Name {
+ r.Branches = append(r.Branches, &RemoteBranch{
Name: b.Name().Short(),
Reference: b,
- Deleted: deleted,
})
}
return nil
})
- if err != nil {
- return err
- }
return err
}
@@ -95,10 +79,10 @@ func remoteBranchesIter(s storer.ReferenceStorer) (storer.ReferenceIter, error)
}
// switches to the given remote branch
-func (remote *Remote) switchRemoteBranch(remoteBranchName string) error {
- for _, rb := range remote.Branches {
+func (r *Remote) switchRemoteBranch(remoteBranchName string) error {
+ for _, rb := range r.Branches {
if rb.Name == remoteBranchName {
- remote.Branch = rb
+ r.Branch = rb
return nil
}
}
diff --git a/pkg/git/repository.go b/pkg/git/repository.go
index f37c813..28c95cd 100644
--- a/pkg/git/repository.go
+++ b/pkg/git/repository.go
@@ -3,6 +3,7 @@ package git
import (
"errors"
"os"
+ "sync"
"time"
"github.com/isacikgoz/gitbatch/pkg/helpers"
@@ -26,126 +27,187 @@ type RepoEntity struct {
Commit *Commit
Commits []*Commit
Stasheds []*StashedItem
- State RepoState
+ state RepoState
+
+ mutex *sync.RWMutex
+ listeners map[string][]RepositoryListener
+}
+
+// RepositoryListener is a type for listeners
+type RepositoryListener func(event *RepositoryEvent) error
+
+// RepositoryEvent is used to transfer event-related data.
+// It is passed to listeners when Emit() is called
+type RepositoryEvent struct {
+ Name string
+ Data interface{}
}
// RepoState is the state of the repository for an operation
-type RepoState uint8
+type RepoState struct {
+ State uint8
+ Ready bool
+}
-const (
+var (
// Available implies repo is ready for the operation
- Available RepoState = 0
+ Available = RepoState{State: 0, Ready: true}
// Queued means repo is queued for a operation
- Queued RepoState = 1
+ Queued = RepoState{State: 1, Ready: false}
// Working means an operation is just started for this repository
- Working RepoState = 2
+ Working = RepoState{State: 2, Ready: false}
// Paused is expected when a user interaction is required
- Paused RepoState = 3
+ Paused = RepoState{State: 3, Ready: true}
// Success is the expected outcome of the operation
- Success RepoState = 4
+ Success = RepoState{State: 4, Ready: true}
// Fail is the unexpected outcome of the operation
- Fail RepoState = 5
+ Fail = RepoState{State: 5, Ready: false}
)
-// InitializeRepo initializes a RepoEntity struct with its belongings.
-func InitializeRepo(directory string) (entity *RepoEntity, err error) {
- entity, err = FastInitializeRepo(directory)
+const (
+ // This is the repository updated topic
+ RepositoryUpdated = "repository.updated"
+)
+
+// FastInitializeRepo initializes a RepoEntity struct without its belongings.
+func FastInitializeRepo(dir string) (e *RepoEntity, err error) {
+ f, err := os.Open(dir)
if err != nil {
return nil, err
}
- // after we intiate the struct we can fill its values
- entity.loadLocalBranches()
- entity.loadCommits()
- // handle if there is no commit, maybe?
- if len(entity.Commits) > 0 {
- // select first commit
- entity.Commit = entity.Commits[0]
- } else {
- return entity, errors.New("There is no commit for this repository: " + directory)
- }
- // lets load remotes this time
- entity.loadRemotes()
- // set the active branch to repositories HEAD
- entity.Branch = entity.getActiveBranch()
- if err = entity.loadStashedItems(); err != nil {
- // TODO: fix here.
+ // get status of the file
+ fstat, _ := f.Stat()
+ r, err := git.PlainOpen(dir)
+ if err != nil {
+ return nil, err
}
- if len(entity.Remotes) > 0 {
- // TODO: tend to take origin/master as default
- entity.Remote = entity.Remotes[0]
- if entity.Branch == nil {
- return nil, errors.New("Unable to find a valid branch")
- }
- if err = entity.Remote.SyncBranches(entity.Branch.Name); err != nil {
- // probably couldn't find, but its ok.
- }
- } else {
- // if there is no remote, this project is totally useless actually
- return entity, errors.New("There is no remote for this repository: " + directory)
+ // initialize RepoEntity with minimum viable fields
+ e = &RepoEntity{RepoID: helpers.RandomString(8),
+ Name: fstat.Name(),
+ AbsPath: dir,
+ ModTime: fstat.ModTime(),
+ Repository: *r,
+ state: Available,
+ mutex: &sync.RWMutex{},
+ listeners: make(map[string][]RepositoryListener),
}
- return entity, nil
+ return e, nil
}
-// FastInitializeRepo initializes a RepoEntity struct without its belongings.
-func FastInitializeRepo(directory string) (entity *RepoEntity, err error) {
- file, err := os.Open(directory)
+// InitializeRepo initializes a RepoEntity struct with its belongings.
+func InitializeRepo(dir string) (e *RepoEntity, err error) {
+ e, err = FastInitializeRepo(dir)
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
+ // need nothing extra but loading additional components
+ return e, e.loadComponents(true)
+}
+
+// loadComponents initializes the fields of a repository such as branches,
+// remotes, commits etc. If reset, reload commit, remote pointers too
+func (e *RepoEntity) loadComponents(reset bool) error {
+ if err := e.loadLocalBranches(); err != nil {
+ return 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
+ if err := e.loadCommits(); err != nil {
+ return err
}
- entity = &RepoEntity{RepoID: helpers.RandomString(8),
- Name: fileInfo.Name(),
- AbsPath: directory,
- ModTime: fileInfo.ModTime(),
- Repository: *r,
- State: Available,
+ if err := e.loadRemotes(); err != nil {
+ return err
+ }
+ if err := e.loadStashedItems(); err != nil {
+ log.Warn("Cannot load stashes")
}
- return entity, nil
+ if reset {
+ // handle if there is no commit, maybe?
+ // set commit pointer for repository
+ if len(e.Commits) > 0 {
+ // select first commit
+ e.Commit = e.Commits[0]
+ }
+ // set remote pointer for repository
+ if len(e.Remotes) > 0 {
+ // TODO: tend to take origin/master as default
+ e.Remote = e.Remotes[0]
+ // if couldn't find, its ok.
+ e.Remote.SyncBranches(e.Branch.Name)
+ } else {
+ // if there is no remote, this project is totally useless actually
+ return errors.New("There is no remote for this repository")
+ }
+ }
+ return nil
}
// Refresh the belongings of a repositoriy, this function is called right after
// fetch/pull/merge operations
-func (entity *RepoEntity) Refresh() error {
+func (e *RepoEntity) Refresh() error {
var err error
// error can be ignored since the file already exists when app is loading
- if entity.Branch == nil {
+ // if the RepoEntity is only fast initialized, no need to refresh because
+ // it won't contain its belongings
+ if e.Branch == nil {
return nil
}
- file, _ := os.Open(entity.AbsPath)
- fileInfo, err := file.Stat()
+ file, _ := os.Open(e.AbsPath)
+ fstat, _ := file.Stat()
+ // re-initialize the go-git repository struct after supposed update
+ r, err := git.PlainOpen(e.AbsPath)
if err != nil {
return err
}
- r, err := git.PlainOpen(entity.AbsPath)
- if err != nil {
+ e.Repository = *r
+ // modification date may be changed
+ e.ModTime = fstat.ModTime()
+ if err := e.loadComponents(false); err != nil {
return err
}
- entity.Repository = *r
- entity.ModTime = fileInfo.ModTime()
- if err := entity.loadLocalBranches(); err != nil {
- return err
+ // we could send an event data but we don't need for this topic
+ return e.Publish(RepositoryUpdated, nil)
+}
+
+// On adds new listener.
+// listener is a callback function that will be called when event emits
+func (e *RepoEntity) On(event string, listener RepositoryListener) {
+ e.mutex.Lock()
+ defer e.mutex.Unlock()
+ // add listener to the specific event topic
+ e.listeners[event] = append(e.listeners[event], listener)
+}
+
+// Emit notifies listeners about the event
+func (e *RepoEntity) Publish(eventName string, data interface{}) error {
+ e.mutex.RLock()
+ defer e.mutex.RUnlock()
+ // let's find listeners for this event topic
+ listeners, ok := e.listeners[eventName]
+ if !ok {
+ return nil
}
- entity.Branch.Clean = entity.isClean()
- entity.RefreshPushPull()
- if err := entity.loadCommits(); err != nil {
- return err
+ // now notify the listeners and channel the data
+ for i := range listeners {
+ event := &RepositoryEvent{
+ Name: eventName,
+ Data: data,
+ }
+ if err := listeners[i](event); err != nil {
+ return err
+ }
}
- if err := entity.loadRemotes(); err != nil {
- return err
+ return nil
+}
+
+// State returns the state of the repository such as queued, failed etc.
+func (e *RepoEntity) State() RepoState {
+ return e.state
+}
+
+// SetState sets the state of repository and sends repository updated event
+func (e *RepoEntity) SetState(state RepoState) {
+ e.state = state
+ // we could send an event data but we don't need for this topic
+ if err := e.Publish(RepositoryUpdated, nil); err != nil {
+ log.Warnf("Cannot publish on %s topic.\n", RepositoryUpdated)
}
- err = entity.loadStashedItems()
- return err
}
diff --git a/pkg/gui/authenticationview.go b/pkg/gui/authenticationview.go
index 9c830cd..b644b6b 100644
--- a/pkg/gui/authenticationview.go
+++ b/pkg/gui/authenticationview.go
@@ -37,7 +37,7 @@ func (gui *Gui) openAuthenticationView(g *gocui.Gui, jobQueue *git.JobQueue, job
return err
}
jobRequiresAuth = job
- if job.Entity.State != git.Fail {
+ if job.Entity.State() != git.Fail {
if err := jobQueue.RemoveFromQueue(job.Entity); err != nil {
log.Fatal(err.Error())
return err
@@ -96,7 +96,7 @@ func (gui *Gui) submitAuthenticationView(g *gocui.Gui, v *gocui.View) error {
}
case git.PullJob:
// we handle pull as fetch&merge so same rule applies
- jobRequiresAuth.Options = git.FetchOptions{
+ jobRequiresAuth.Options = git.PullOptions{
RemoteName: jobRequiresAuth.Entity.Remote.Name,
Credentials: git.Credentials{
User: creduser,
@@ -104,14 +104,12 @@ func (gui *Gui) submitAuthenticationView(g *gocui.Gui, v *gocui.View) error {
},
}
}
- jobRequiresAuth.Entity.State = git.Queued
+ jobRequiresAuth.Entity.SetState(git.Queued)
// add this job to the last of the queue
err = gui.State.Queue.AddJob(jobRequiresAuth)
if err != nil {
return err
}
- // refresh views with the updates
- gui.refreshMain(g)
gui.closeAuthenticationView(g, v)
v_return, err := g.View(authenticationReturnView)
gui.startQueue(g, v_return)
diff --git a/pkg/gui/commitview.go b/pkg/gui/commitview.go
index 94ef574..0810621 100644
--- a/pkg/gui/commitview.go
+++ b/pkg/gui/commitview.go
@@ -52,16 +52,12 @@ func (gui *Gui) openCommitMessageView(g *gocui.Gui, v *gocui.View) error {
if err := gui.openCommitUserEmailView(g); err != nil {
return err
}
- gui.updateKeyBindingsView(g, commitMessageViewFeature.Name)
- if _, err := g.SetCurrentView(commitMessageViewFeature.Name); err != nil {
- return err
- }
- return nil
+ return gui.focusToView(commitMessageViewFeature.Name)
}
// open an error view to inform user with a message and a useful note
func (gui *Gui) openCommitUserNameView(g *gocui.Gui) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
maxX, maxY := g.Size()
// first, create the label for user
vlabel, err := g.SetView(commitUserNameLabelFeature.Name, maxX/2-30, maxY/2, maxX/2-19, maxY/2+2)
@@ -78,7 +74,7 @@ func (gui *Gui) openCommitUserNameView(g *gocui.Gui) error {
if err != gocui.ErrUnknownView {
return err
}
- name, err := git.Config(entity, git.ConfigOptions{
+ name, err := git.Config(e, git.ConfigOptions{
Section: "user",
Option: "name",
})
@@ -94,7 +90,7 @@ func (gui *Gui) openCommitUserNameView(g *gocui.Gui) error {
// open an error view to inform user with a message and a useful note
func (gui *Gui) openCommitUserEmailView(g *gocui.Gui) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
maxX, maxY := g.Size()
// first, create the label for password
vlabel, err := g.SetView(commitUserEmailLabelViewFeature.Name, maxX/2-30, maxY/2+1, maxX/2-19, maxY/2+3)
@@ -111,7 +107,7 @@ func (gui *Gui) openCommitUserEmailView(g *gocui.Gui) error {
if err != gocui.ErrUnknownView {
return err
}
- email, err := git.Config(entity, git.ConfigOptions{
+ email, err := git.Config(e, git.ConfigOptions{
Section: "user",
Option: "email",
})
@@ -127,7 +123,7 @@ func (gui *Gui) openCommitUserEmailView(g *gocui.Gui) error {
// close the opened commite mesage view
func (gui *Gui) submitCommitMessageView(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
// in order to read buffer of the views, first we need to find'em
v_msg, err := g.View(commitMessageViewFeature.Name)
v_name, err := g.View(commitUserUserViewFeature.Name)
@@ -141,7 +137,7 @@ func (gui *Gui) submitCommitMessageView(g *gocui.Gui, v *gocui.View) error {
if len(email) <= 0 {
return errors.New("User email needs to be provided")
}
- err = git.CommitCommand(entity, git.CommitOptions{
+ err = git.CommitCommand(e, git.CommitOptions{
CommitMsg: msg,
User: name,
Email: email,
@@ -149,20 +145,17 @@ func (gui *Gui) submitCommitMessageView(g *gocui.Gui, v *gocui.View) error {
if err != nil {
return err
}
- entity.Refresh()
- err = gui.closeCommitMessageView(g, v)
- return err
+ return gui.closeCommitMessageView(g, v)
}
// focus to next view
func (gui *Gui) nextCommitView(g *gocui.Gui, v *gocui.View) error {
- err := gui.nextViewOfGroup(g, v, commitViews)
- return err
+ return gui.nextViewOfGroup(g, v, commitViews)
}
// close the opened commite mesage view
func (gui *Gui) closeCommitMessageView(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
g.Cursor = false
for _, view := range commitViews {
if err := g.DeleteView(view.Name); err != nil {
@@ -174,13 +167,7 @@ func (gui *Gui) closeCommitMessageView(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- if err := gui.refreshMain(g); err != nil {
- return err
- }
- if err := gui.refreshViews(g, entity); err != nil {
- return err
- }
- if err := refreshAllStatusView(g, entity, true); err != nil {
+ if err := refreshAllStatusView(g, e, true); err != nil {
return err
}
return gui.closeViewCleanup(commitMesageReturnView)
diff --git a/pkg/gui/diffview.go b/pkg/gui/diffview.go
index 55286e9..9a47f95 100644
--- a/pkg/gui/diffview.go
+++ b/pkg/gui/diffview.go
@@ -34,11 +34,11 @@ func (gui *Gui) prepareDiffView(g *gocui.Gui, v *gocui.View, display []string) (
// open diff view for the selcted commit
// called from commitview, so initial view is commitview
func (gui *Gui) openCommitDiffView(g *gocui.Gui, v *gocui.View) (err error) {
- entity := gui.getSelectedRepository()
- commit := entity.Commit
+ e := gui.getSelectedRepository()
+ commit := e.Commit
commitDetail := []string{("Hash: " + cyan.Sprint(commit.Hash) + "\n" + "Author: " + commit.Author +
"\n" + commit.Time + "\n" + "\n" + "\t\t" + commit.Message + "\n")}
- diff, err := entity.Diff(entity.Commit.Hash)
+ diff, err := git.Diff(e, e.Commit.Hash)
if err != nil {
return err
}
@@ -56,15 +56,15 @@ func (gui *Gui) openCommitDiffView(g *gocui.Gui, v *gocui.View) (err error) {
// called from status, so initial view may be stagedview or unstaged view
func (gui *Gui) openFileDiffView(g *gocui.Gui, v *gocui.View) (err error) {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
_, cy := v.Cursor()
_, oy := v.Origin()
var files []*git.File
switch v.Name() {
case unstageViewFeature.Name:
- _, files, err = populateFileLists(entity)
+ _, files, err = populateFileLists(e)
case stageViewFeature.Name:
- files, _, err = populateFileLists(entity)
+ files, _, err = populateFileLists(e)
}
if err != nil {
return err
@@ -95,13 +95,13 @@ func (gui *Gui) openFileDiffView(g *gocui.Gui, v *gocui.View) (err error) {
// called from stashview, so initial view is stashview
func (gui *Gui) showStash(g *gocui.Gui, v *gocui.View) (err error) {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
_, oy := v.Origin()
_, cy := v.Cursor()
- if len(entity.Stasheds) <= 0 {
+ if len(e.Stasheds) <= 0 {
return nil
}
- stashedItem := entity.Stasheds[oy+cy]
+ stashedItem := e.Stasheds[oy+cy]
output, err := stashedItem.Show()
if err != nil {
if err = gui.openErrorView(g, output,
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 1fb94ad..2e1f2b2 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -2,6 +2,7 @@ package gui
import (
"fmt"
+ "sync"
"github.com/isacikgoz/gitbatch/pkg/git"
"github.com/jroimartin/gocui"
@@ -14,6 +15,7 @@ type Gui struct {
g *gocui.Gui
KeyBindings []*KeyBinding
State guiState
+ mutex *sync.Mutex
}
// guiState struct holds the repositories, directiories, mode and queue of the
@@ -81,6 +83,7 @@ func NewGui(mode string, directoies []string) (*Gui, error) {
}
gui := &Gui{
State: initialState,
+ mutex: &sync.Mutex{},
}
for _, m := range modes {
if string(m.ModeID) == mode {
@@ -121,6 +124,10 @@ func (gui *Gui) Run() error {
return
}
g_ui.State.Repositories = rs
+ // add gui's repositoryUpdated func as an observer to repositories
+ for _, repo := range rs {
+ repo.On(git.RepositoryUpdated, gui.repositoryUpdated)
+ }
gui.fillMain(g)
}(gui)
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index eff7952..3c5db5f 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -184,7 +184,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeAuthenticationView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: view.Name,
@@ -214,7 +214,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeCommitMessageView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: view.Name,
@@ -444,7 +444,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeConfirmationView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: confirmationViewFeature.Name,
@@ -462,7 +462,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeCommitDiffView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: diffViewFeature.Name,
@@ -504,7 +504,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeCheatSheetView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: cheatSheetViewFeature.Name,
@@ -546,7 +546,7 @@ func (gui *Gui) generateKeybindings() error {
Modifier: gocui.ModNone,
Handler: gui.closeErrorView,
Display: "esc",
- Description: "close/cancel",
+ Description: "Close/Cancel",
Vital: true,
}, {
View: errorViewFeature.Name,
diff --git a/pkg/gui/mainview.go b/pkg/gui/mainview.go
index 389d66d..e4c0159 100644
--- a/pkg/gui/mainview.go
+++ b/pkg/gui/mainview.go
@@ -6,6 +6,7 @@ import (
"github.com/isacikgoz/gitbatch/pkg/git"
"github.com/jroimartin/gocui"
+ log "github.com/sirupsen/logrus"
)
// this is the initial function for filling the values for the main view. the
@@ -16,9 +17,7 @@ func (gui *Gui) fillMain(g *gocui.Gui) error {
if err != nil {
return err
}
- for _, r := range gui.State.Repositories {
- fmt.Fprintln(v, gui.displayString(r))
- }
+ // if there is still a loading screen we better get rid of it
err = g.DeleteView(loadingViewFeature.Name)
if err != nil {
return err
@@ -26,26 +25,39 @@ func (gui *Gui) fillMain(g *gocui.Gui) error {
if _, err = gui.setCurrentViewOnTop(g, mainViewFeature.Name); err != nil {
return err
}
+ // Sort by name is default behavior as expected, so it handles initial
+ // rendering of the main view
if err = gui.sortByName(g, v); err != nil {
return err
}
- entity := gui.getSelectedRepository()
- gui.refreshViews(g, entity)
return nil
})
return nil
}
// refresh the main view and re-render the repository representations
-func (gui *Gui) refreshMain(g *gocui.Gui) error {
- mainView, err := g.View(mainViewFeature.Name)
+func (gui *Gui) renderMain() error {
+ gui.mutex.Lock()
+ defer gui.mutex.Unlock()
+
+ mainView, err := gui.g.View(mainViewFeature.Name)
if err != nil {
return err
}
mainView.Clear()
for _, r := range gui.State.Repositories {
- fmt.Fprintln(mainView, gui.displayString(r))
+ fmt.Fprintln(mainView, gui.repositoryLabel(r))
}
+ // while refreshing, refresh sideViews for selected entity, something may
+ // be changed?
+ return gui.renderSideViews(gui.getSelectedRepository())
+}
+
+// listens the event -> "repository.updated"
+func (gui *Gui) repositoryUpdated(event *git.RepositoryEvent) error {
+ gui.g.Update(func(g *gocui.Gui) error {
+ return gui.renderMain()
+ })
return nil
}
@@ -62,18 +74,12 @@ func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
return nil
}
if err := v.SetCursor(cx, cy+1); err != nil {
-
if err := v.SetOrigin(ox, oy+1); err != nil {
return err
}
}
- entity := gui.getSelectedRepository()
- if err := gui.refreshMain(g); err != nil {
- return err
- }
- gui.refreshViews(g, entity)
}
- return nil
+ return gui.renderMain()
}
// moves the cursor upwards for the main view
@@ -86,13 +92,8 @@ func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- entity := gui.getSelectedRepository()
- if err := gui.refreshMain(g); err != nil {
- return err
- }
- gui.refreshViews(g, entity)
}
- return nil
+ return gui.renderMain()
}
// returns the entity at cursors position by taking its position in the gui's
@@ -103,9 +104,6 @@ func (gui *Gui) getSelectedRepository() *git.RepoEntity {
v, _ := gui.g.View(mainViewFeature.Name)
_, oy := v.Origin()
_, cy := v.Cursor()
- // if _, err := v.Line(cy); err != nil {
- // return r, err
- // }
return gui.State.Repositories[cy+oy]
}
@@ -129,7 +127,7 @@ func (gui *Gui) addToQueue(entity *git.RepoEntity) error {
if err != nil {
return err
}
- entity.State = git.Queued
+ entity.SetState(git.Queued)
return nil
}
@@ -139,7 +137,36 @@ func (gui *Gui) removeFromQueue(entity *git.RepoEntity) error {
if err != nil {
return err
}
- entity.State = git.Available
+ entity.SetState(git.Available)
+ return nil
+}
+
+// this function starts the queue and updates the gui with the result of an
+// operation
+func (gui *Gui) startQueue(g *gocui.Gui, v *gocui.View) error {
+ go func(gui_go *Gui, g_go *gocui.Gui) {
+ for {
+ job, finished, err := gui_go.State.Queue.StartNext()
+
+ if err != nil {
+ if err == git.ErrAuthenticationRequired {
+ // pause the job, so it will be indicated to being blocking
+ job.Entity.SetState(git.Paused)
+ err := gui_go.openAuthenticationView(g, gui_go.State.Queue, job, v.Name())
+ if err != nil {
+ log.Warn(err.Error())
+ return
+ }
+ }
+ return
+ // with not returning here, we simply ignore and continue
+ }
+ // if queue is finished simply return from this goroutine
+ if finished {
+ return
+ }
+ }
+ }(gui, g)
return nil
}
@@ -148,18 +175,15 @@ func (gui *Gui) removeFromQueue(entity *git.RepoEntity) error {
func (gui *Gui) markRepository(g *gocui.Gui, v *gocui.View) error {
r := gui.getSelectedRepository()
// maybe, failed entities may be added to queue again
- if r.State == git.Available || r.State == git.Success || r.State == git.Paused {
+ if r.State().Ready {
if err := gui.addToQueue(r); err != nil {
return err
}
- } else if r.State == git.Queued {
+ } else if r.State() == git.Queued {
if err := gui.removeFromQueue(r); err != nil {
return err
}
- } else {
- return nil
}
- gui.refreshMain(g)
return nil
}
@@ -167,7 +191,7 @@ func (gui *Gui) markRepository(g *gocui.Gui, v *gocui.View) error {
// current state into account before adding it
func (gui *Gui) markAllRepositories(g *gocui.Gui, v *gocui.View) error {
for _, r := range gui.State.Repositories {
- if r.State == git.Available || r.State == git.Success {
+ if r.State().Ready {
if err := gui.addToQueue(r); err != nil {
return err
}
@@ -175,7 +199,6 @@ func (gui *Gui) markAllRepositories(g *gocui.Gui, v *gocui.View) error {
continue
}
}
- gui.refreshMain(g)
return nil
}
@@ -183,7 +206,7 @@ func (gui *Gui) markAllRepositories(g *gocui.Gui, v *gocui.View) error {
// current state into account before removing it
func (gui *Gui) unmarkAllRepositories(g *gocui.Gui, v *gocui.View) error {
for _, r := range gui.State.Repositories {
- if r.State == git.Queued {
+ if r.State() == git.Queued {
if err := gui.removeFromQueue(r); err != nil {
return err
}
@@ -191,14 +214,13 @@ func (gui *Gui) unmarkAllRepositories(g *gocui.Gui, v *gocui.View) error {
continue
}
}
- gui.refreshMain(g)
return nil
}
// sortByName sorts the repositories by A to Z order
func (gui *Gui) sortByName(g *gocui.Gui, v *gocui.View) error {
sort.Sort(git.Alphabetical(gui.State.Repositories))
- gui.refreshAfterSort(g)
+ gui.renderMain()
return nil
}
@@ -206,14 +228,6 @@ func (gui *Gui) sortByName(g *gocui.Gui, v *gocui.View) error {
// the top element will be the last modified
func (gui *Gui) sortByMod(g *gocui.Gui, v *gocui.View) error {
sort.Sort(git.LastModified(gui.State.Repositories))
- gui.refreshAfterSort(g)
- return nil
-}
-
-// utility function that refreshes main and side views after that
-func (gui *Gui) refreshAfterSort(g *gocui.Gui) error {
- gui.refreshMain(g)
- entity := gui.getSelectedRepository()
- gui.refreshViews(g, entity)
+ gui.renderMain()
return nil
}
diff --git a/pkg/gui/sideviews.go b/pkg/gui/sideviews.go
index 86aa3cb..6316255 100644
--- a/pkg/gui/sideviews.go
+++ b/pkg/gui/sideviews.go
@@ -12,28 +12,43 @@ var (
sideViews = []viewFeature{remoteViewFeature, remoteBranchViewFeature, branchViewFeature, commitViewFeature}
)
+// refreshes the side views of the application for given git.RepoEntity struct
+func (gui *Gui) renderSideViews(e *git.RepoEntity) error {
+ var err error
+ if err = gui.renderRemotes(e); err != nil {
+ return err
+ }
+ if err = gui.renderBranch(e); err != nil {
+ return err
+ }
+ if err = gui.renderRemoteBranches(e); err != nil {
+ return err
+ }
+ if err = gui.renderCommits(e); err != nil {
+ return err
+ }
+ return err
+}
+
// updates the remotesview for given entity
-func (gui *Gui) updateRemotes(g *gocui.Gui, entity *git.RepoEntity) error {
+func (gui *Gui) renderRemotes(e *git.RepoEntity) error {
var err error
- out, err := g.View(remoteViewFeature.Name)
+ out, err := gui.g.View(remoteViewFeature.Name)
if err != nil {
return err
}
out.Clear()
-
currentindex := 0
- totalRemotes := len(entity.Remotes)
+ totalRemotes := len(e.Remotes)
if totalRemotes > 0 {
- for i, r := range entity.Remotes {
- // TODO: maybe the text styling can be moved to textstyle.go file
+ for i, r := range e.Remotes {
_, shortURL := trimRemoteURL(r.URL[0])
- suffix := shortURL
- if r.Name == entity.Remote.Name {
+ if r.Name == e.Remote.Name {
currentindex = i
- fmt.Fprintln(out, selectionIndicator+r.Name+": "+suffix)
+ fmt.Fprintln(out, selectionIndicator+r.Name+": "+shortURL)
continue
}
- fmt.Fprintln(out, tab+r.Name+": "+suffix)
+ fmt.Fprintln(out, tab+r.Name+": "+shortURL)
}
if err = gui.smartAnchorRelativeToLine(out, currentindex, totalRemotes); err != nil {
return err
@@ -43,27 +58,23 @@ func (gui *Gui) updateRemotes(g *gocui.Gui, entity *git.RepoEntity) error {
}
// updates the remotebranchview for given entity
-func (gui *Gui) updateRemoteBranches(g *gocui.Gui, entity *git.RepoEntity) error {
+func (gui *Gui) renderRemoteBranches(e *git.RepoEntity) error {
var err error
- out, err := g.View(remoteBranchViewFeature.Name)
+ out, err := gui.g.View(remoteBranchViewFeature.Name)
if err != nil {
return err
}
out.Clear()
currentindex := 0
- trb := len(entity.Remote.Branches)
+ trb := len(e.Remote.Branches)
if trb > 0 {
- for i, r := range entity.Remote.Branches {
- rName := r.Name
- if r.Deleted {
- rName = rName + ws + dirty
- }
- if r.Name == entity.Remote.Branch.Name {
+ for i, r := range e.Remote.Branches {
+ if r.Name == e.Remote.Branch.Name {
currentindex = i
- fmt.Fprintln(out, selectionIndicator+rName)
+ fmt.Fprintln(out, selectionIndicator+r.Name)
continue
}
- fmt.Fprintln(out, tab+rName)
+ fmt.Fprintln(out, tab+r.Name)
}
if err = gui.smartAnchorRelativeToLine(out, currentindex, trb); err != nil {
return err
@@ -73,18 +84,17 @@ func (gui *Gui) updateRemoteBranches(g *gocui.Gui, entity *git.RepoEntity) error
}
// updates the branchview for given entity
-func (gui *Gui) updateBranch(g *gocui.Gui, entity *git.RepoEntity) error {
+func (gui *Gui) renderBranch(e *git.RepoEntity) error {
var err error
- out, err := g.View(branchViewFeature.Name)
+ out, err := gui.g.View(branchViewFeature.Name)
if err != nil {
return err
}
out.Clear()
-
currentindex := 0
- totalbranches := len(entity.Branches)
- for i, b := range entity.Branches {
- if b.Name == entity.Branch.Name {
+ totalbranches := len(e.Branches)
+ for i, b := range e.Branches {
+ if b.Name == e.Branch.Name {
currentindex = i
fmt.Fprintln(out, selectionIndicator+b.Name)
continue
@@ -96,31 +106,22 @@ func (gui *Gui) updateBranch(g *gocui.Gui, entity *git.RepoEntity) error {
}
// updates the commitsview for given entity
-func (gui *Gui) updateCommits(g *gocui.Gui, entity *git.RepoEntity) error {
+func (gui *Gui) renderCommits(e *git.RepoEntity) error {
var err error
- out, err := g.View(commitViewFeature.Name)
+ out, err := gui.g.View(commitViewFeature.Name)
if err != nil {
return err
}
out.Clear()
-
currentindex := 0
- totalcommits := len(entity.Commits)
- for i, c := range entity.Commits {
- var body string
- if c.CommitType == git.EvenCommit {
- body = cyan.Sprint(c.Hash[:hashLength]) + " " + c.Message
- } else if c.CommitType == git.LocalCommit {
- body = blue.Sprint(c.Hash[:hashLength]) + " " + c.Message
- } else {
- body = yellow.Sprint(c.Hash[:hashLength]) + " " + c.Message
- }
- if c.Hash == entity.Commit.Hash {
+ totalcommits := len(e.Commits)
+ for i, c := range e.Commits {
+ if c.Hash == e.Commit.Hash {
currentindex = i
- fmt.Fprintln(out, selectionIndicator+body)
+ fmt.Fprintln(out, selectionIndicator+commitLabel(c))
continue
}
- fmt.Fprintln(out, tab+body)
+ fmt.Fprintln(out, tab+commitLabel(c))
}
if err = gui.smartAnchorRelativeToLine(out, currentindex, totalcommits); err != nil {
return err
@@ -128,156 +129,103 @@ func (gui *Gui) updateCommits(g *gocui.Gui, entity *git.RepoEntity) error {
return err
}
+// cursor down variant for sideviews
func (gui *Gui) sideViewsNextItem(g *gocui.Gui, v *gocui.View) error {
var err error
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
switch viewName := v.Name(); viewName {
case remoteBranchViewFeature.Name:
- if err = entity.Remote.NextRemoteBranch(); err != nil {
- return err
- }
- err = gui.updateRemoteBranches(g, entity)
+ return e.Remote.NextRemoteBranch(e)
case remoteViewFeature.Name:
- if err = entity.NextRemote(); err != nil {
- return err
- }
- err = gui.remoteChangeFollowUp(g, entity)
+ return e.NextRemote()
case branchViewFeature.Name:
- if err = entity.Checkout(entity.NextBranch()); err != nil {
+ if err = e.Checkout(e.NextBranch()); err != nil {
err = gui.openErrorView(g, err.Error(),
"You should manually resolve this issue",
branchViewFeature.Name)
return err
}
- err = gui.checkoutFollowUp(g, entity)
case commitViewFeature.Name:
- if err = entity.NextCommit(); err != nil {
- return err
- }
- err = gui.updateCommits(g, entity)
+ e.NextCommit()
+ return gui.renderCommits(e)
}
return err
}
+// cursor up variant for sideviews
func (gui *Gui) sideViewsPreviousItem(g *gocui.Gui, v *gocui.View) error {
var err error
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
switch viewName := v.Name(); viewName {
case remoteBranchViewFeature.Name:
- if err = entity.Remote.PreviousRemoteBranch(); err != nil {
- return err
- }
- err = gui.updateRemoteBranches(g, entity)
+ return e.Remote.PreviousRemoteBranch(e)
case remoteViewFeature.Name:
- if err = entity.PreviousRemote(); err != nil {
- return err
- }
- err = gui.remoteChangeFollowUp(g, entity)
+ return e.PreviousRemote()
case branchViewFeature.Name:
- if err = entity.Checkout(entity.PreviousBranch()); err != nil {
+ if err = e.Checkout(e.PreviousBranch()); err != nil {
err = gui.openErrorView(g, err.Error(),
"You should manually resolve this issue",
branchViewFeature.Name)
return err
}
- err = gui.checkoutFollowUp(g, entity)
case commitViewFeature.Name:
- if err = entity.PreviousCommit(); err != nil {
- return err
- }
- err = gui.updateCommits(g, entity)
+ e.PreviousCommit()
+ return gui.renderCommits(e)
}
return err
}
// basically does fetch --prune
func (gui *Gui) syncRemoteBranch(g *gocui.Gui, v *gocui.View) error {
- var err error
- entity := gui.getSelectedRepository()
- if err = git.Fetch(entity, git.FetchOptions{
- RemoteName: entity.Remote.Name,
+ e := gui.getSelectedRepository()
+ return git.Fetch(e, git.FetchOptions{
+ RemoteName: e.Remote.Name,
Prune: true,
- }); err != nil {
- return err
- }
- vr, err := g.View(remoteViewFeature.Name)
- if err != nil {
- return err
- }
- // have no idea why this works..
- // some time need to fix, movement aint bad huh?
- gui.sideViewsNextItem(g, vr)
- gui.sideViewsPreviousItem(g, vr)
- err = gui.updateRemoteBranches(g, entity)
- return err
+ })
}
-// basically does fetch --prune
+// opens a confirmation view for setting default merge branch
func (gui *Gui) setUpstreamToBranch(g *gocui.Gui, v *gocui.View) error {
maxX, maxY := g.Size()
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
v, err := g.SetView(confirmationViewFeature.Name, maxX/2-30, maxY/2-2, maxX/2+30, maxY/2+2)
if err != nil {
if err != gocui.ErrUnknownView {
return err
}
- fmt.Fprintln(v, "branch."+entity.Branch.Name+"."+"remote"+"="+entity.Remote.Name)
- fmt.Fprintln(v, "branch."+entity.Branch.Name+"."+"merge"+"="+entity.Branch.Reference.Name().String())
+ fmt.Fprintln(v, "branch."+e.Branch.Name+"."+"remote"+"="+e.Remote.Name)
+ fmt.Fprintln(v, "branch."+e.Branch.Name+"."+"merge"+"="+e.Branch.Reference.Name().String())
}
return gui.focusToView(confirmationViewFeature.Name)
}
-// basically does fetch --prune
+// add config for upstream merge
func (gui *Gui) confirmSetUpstreamToBranch(g *gocui.Gui, v *gocui.View) error {
var err error
- entity := gui.getSelectedRepository()
- if err = git.AddConfig(entity, git.ConfigOptions{
- Section: "branch." + entity.Branch.Name,
+ e := gui.getSelectedRepository()
+ if err = git.AddConfig(e, git.ConfigOptions{
+ Section: "branch." + e.Branch.Name,
Option: "remote",
Site: git.ConfigSiteLocal,
- }, entity.Remote.Name); err != nil {
+ }, e.Remote.Name); err != nil {
return err
}
- if err = git.AddConfig(entity, git.ConfigOptions{
- Section: "branch." + entity.Branch.Name,
+ if err = git.AddConfig(e, git.ConfigOptions{
+ Section: "branch." + e.Branch.Name,
Option: "merge",
Site: git.ConfigSiteLocal,
- }, entity.Branch.Reference.Name().String()); err != nil {
+ }, e.Branch.Reference.Name().String()); err != nil {
return err
}
- entity.Refresh()
- gui.refreshMain(g)
+ e.Refresh()
return gui.closeConfirmationView(g, v)
}
+// close confirmation view
func (gui *Gui) closeConfirmationView(g *gocui.Gui, v *gocui.View) error {
if err := g.DeleteView(v.Name()); err != nil {
return err
}
return gui.closeViewCleanup(branchViewFeature.Name)
}
-
-// after checkout a remote some refreshments needed
-func (gui *Gui) remoteChangeFollowUp(g *gocui.Gui, entity *git.RepoEntity) (err error) {
- if err = gui.updateRemotes(g, entity); err != nil {
- return err
- }
- err = gui.updateRemoteBranches(g, entity)
- return err
-}
-
-// after checkout a branch some refreshments needed
-func (gui *Gui) checkoutFollowUp(g *gocui.Gui, entity *git.RepoEntity) (err error) {
- if err = gui.updateBranch(g, entity); err != nil {
- return err
- }
- if err = gui.updateCommits(g, entity); err != nil {
- return err
- }
- if err = gui.updateRemoteBranches(g, entity); err != nil {
- return err
- }
- err = gui.refreshMain(g)
- return err
-}
diff --git a/pkg/gui/stagedview.go b/pkg/gui/stagedview.go
index 4a6beb9..a1c9737 100644
--- a/pkg/gui/stagedview.go
+++ b/pkg/gui/stagedview.go
@@ -18,44 +18,43 @@ func (gui *Gui) openStageView(g *gocui.Gui) error {
}
v.Title = stageViewFeature.Title
}
- entity := gui.getSelectedRepository()
- if err := refreshStagedView(g, entity); err != nil {
+ if err := refreshStagedView(g); err != nil {
return err
}
return gui.focusToView(stageViewFeature.Name)
}
func (gui *Gui) resetChanges(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
_, cy := v.Cursor()
_, oy := v.Origin()
if len(stagedFiles) <= 0 || len(stagedFiles) <= cy+oy {
return nil
}
- if err := git.Reset(entity, stagedFiles[cy+oy], git.ResetOptions{}); err != nil {
+ if err := git.Reset(e, stagedFiles[cy+oy], git.ResetOptions{}); err != nil {
return err
}
- return refreshAllStatusView(g, entity, true)
+ return refreshAllStatusView(g, e, true)
}
func (gui *Gui) resetAllChanges(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
- ref, err := entity.Repository.Head()
+ e := gui.getSelectedRepository()
+ ref, err := e.Repository.Head()
if err != nil {
return err
}
- if err := git.ResetAll(entity, git.ResetOptions{
+ if err := git.ResetAll(e, git.ResetOptions{
Hash: ref.Hash().String(),
Rtype: git.ResetMixed,
}); err != nil {
return err
}
- return refreshAllStatusView(g, entity, true)
+ return refreshAllStatusView(g, e, true)
}
// refresh the main view and re-render the repository representations
-func refreshStagedView(g *gocui.Gui, entity *git.RepoEntity) error {
+func refreshStagedView(g *gocui.Gui) error {
stageView, err := g.View(stageViewFeature.Name)
if err != nil {
return err
diff --git a/pkg/gui/stashview.go b/pkg/gui/stashview.go
index 24b4f3c..81de8f0 100644
--- a/pkg/gui/stashview.go
+++ b/pkg/gui/stashview.go
@@ -18,15 +18,15 @@ func (gui *Gui) openStashView(g *gocui.Gui) error {
}
v.Title = stashViewFeature.Title
}
- entity := gui.getSelectedRepository()
- err = refreshStashView(g, entity)
+ e := gui.getSelectedRepository()
+ err = refreshStashView(g, e)
return err
}
//
func (gui *Gui) stashChanges(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
- output, err := entity.Stash()
+ e := gui.getSelectedRepository()
+ output, err := e.Stash()
if err != nil {
if err = gui.openErrorView(g, output,
"You should manually resolve this issue",
@@ -34,19 +34,19 @@ func (gui *Gui) stashChanges(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- err = refreshAllStatusView(g, entity, true)
+ err = refreshAllStatusView(g, e, true)
return err
}
//
func (gui *Gui) popStash(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
_, oy := v.Origin()
_, cy := v.Cursor()
- if len(entity.Stasheds) <= 0 {
+ if len(e.Stasheds) <= 0 {
return nil
}
- stashedItem := entity.Stasheds[oy+cy]
+ stashedItem := e.Stasheds[oy+cy]
output, err := stashedItem.Pop()
if err != nil {
if err = gui.openErrorView(g, output,
@@ -55,15 +55,15 @@ func (gui *Gui) popStash(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- if err := entity.Refresh(); err != nil {
- return err
- }
- err = refreshAllStatusView(g, entity, true)
+ // since the pop is a func of stashed item, we need to refresh entity here
+ e.Refresh()
+
+ err = refreshAllStatusView(g, e, true)
return err
}
// refresh the main view and re-render the repository representations
-func refreshStashView(g *gocui.Gui, entity *git.RepoEntity) error {
+func refreshStashView(g *gocui.Gui, e *git.RepoEntity) error {
stashView, err := g.View(stashViewFeature.Name)
if err != nil {
return err
@@ -71,7 +71,7 @@ func refreshStashView(g *gocui.Gui, entity *git.RepoEntity) error {
stashView.Clear()
_, cy := stashView.Cursor()
_, oy := stashView.Origin()
- stashedItems := entity.Stasheds
+ stashedItems := e.Stasheds
for i, stashedItem := range stashedItems {
var prefix string
if i == cy+oy {
diff --git a/pkg/gui/statusview.go b/pkg/gui/statusview.go
index 82df459..cc6ef51 100644
--- a/pkg/gui/statusview.go
+++ b/pkg/gui/statusview.go
@@ -32,8 +32,8 @@ func (gui *Gui) openStatusView(g *gocui.Gui, v *gocui.View) error {
return nil
}
-func reloadFiles(entity *git.RepoEntity) (err error) {
- stagedFiles, unstagedFiles, err = populateFileLists(entity)
+func reloadFiles(e *git.RepoEntity) (err error) {
+ stagedFiles, unstagedFiles, err = populateFileLists(e)
return err
}
@@ -67,8 +67,8 @@ func (gui *Gui) statusCursorDown(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- entity := gui.getSelectedRepository()
- if err := refreshStatusView(v.Name(), g, entity, false); err != nil {
+ e := gui.getSelectedRepository()
+ if err := refreshStatusView(v.Name(), g, e, false); err != nil {
return err
}
}
@@ -85,8 +85,8 @@ func (gui *Gui) statusCursorUp(g *gocui.Gui, v *gocui.View) error {
return err
}
}
- entity := gui.getSelectedRepository()
- if err := refreshStatusView(v.Name(), g, entity, false); err != nil {
+ e := gui.getSelectedRepository()
+ if err := refreshStatusView(v.Name(), g, e, false); err != nil {
return err
}
}
@@ -96,13 +96,13 @@ func (gui *Gui) statusCursorUp(g *gocui.Gui, v *gocui.View) error {
// header og the status layout
func (gui *Gui) openStatusHeaderView(g *gocui.Gui) error {
maxX, _ := g.Size()
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
v, err := g.SetView(statusHeaderViewFeature.Name, 6, 2, maxX-6, 4)
if err != nil {
if err != gocui.ErrUnknownView {
return err
}
- fmt.Fprintln(v, entity.AbsPath)
+ fmt.Fprintln(v, e.AbsPath)
// v.Frame = false
v.Wrap = true
}
@@ -121,18 +121,13 @@ func (gui *Gui) closeStatusView(g *gocui.Gui, v *gocui.View) error {
}
stagedFiles = make([]*git.File, 0)
unstagedFiles = make([]*git.File, 0)
- entity := gui.getSelectedRepository()
- if err := gui.refreshMain(g); err != nil {
- return err
- }
- if err := gui.refreshViews(g, entity); err != nil {
- return err
- }
+
return gui.closeViewCleanup(mainViewFeature.Name)
}
-func populateFileLists(entity *git.RepoEntity) (staged, unstaged []*git.File, err error) {
- files, err := git.Status(entity)
+// generate file lists by git status command
+func populateFileLists(e *git.RepoEntity) (staged, unstaged []*git.File, err error) {
+ files, err := git.Status(e)
if err != nil {
return nil, nil, err
}
@@ -147,13 +142,13 @@ func populateFileLists(entity *git.RepoEntity) (staged, unstaged []*git.File, er
return staged, unstaged, err
}
-func refreshStatusView(viewName string, g *gocui.Gui, entity *git.RepoEntity, reload bool) error {
+func refreshStatusView(viewName string, g *gocui.Gui, e *git.RepoEntity, reload bool) error {
if reload {
- reloadFiles(entity)
+ reloadFiles(e)
}
switch viewName {
case stageViewFeature.Name:
- if err := refreshStagedView(g, entity); err != nil {
+ if err := refreshStagedView(g); err != nil {
return err
}
case unstageViewFeature.Name:
@@ -161,16 +156,16 @@ func refreshStatusView(viewName string, g *gocui.Gui, entity *git.RepoEntity, re
return err
}
case stashViewFeature.Name:
- if err := refreshStashView(g, entity); err != nil {
+ if err := refreshStashView(g, e); err != nil {
return err
}
}
return nil
}
-func refreshAllStatusView(g *gocui.Gui, entity *git.RepoEntity, reload bool) error {
+func refreshAllStatusView(g *gocui.Gui, e *git.RepoEntity, reload bool) error {
for _, v := range statusViews {
- if err := refreshStatusView(v.Name, g, entity, reload); err != nil {
+ if err := refreshStatusView(v.Name, g, e, reload); err != nil {
return err
}
}
diff --git a/pkg/gui/unstagedview.go b/pkg/gui/unstagedview.go
index 1a33c12..dd2fe6d 100644
--- a/pkg/gui/unstagedview.go
+++ b/pkg/gui/unstagedview.go
@@ -23,26 +23,26 @@ func (gui *Gui) openUnStagedView(g *gocui.Gui) error {
}
func (gui *Gui) addChanges(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
+ e := gui.getSelectedRepository()
_, cy := v.Cursor()
_, oy := v.Origin()
if len(unstagedFiles) <= 0 || len(unstagedFiles) < cy+oy {
return nil
}
- if err := git.Add(entity, unstagedFiles[cy+oy], git.AddOptions{}); err != nil {
+ if err := git.Add(e, unstagedFiles[cy+oy], git.AddOptions{}); err != nil {
return err
}
- err := refreshAllStatusView(g, entity, true)
+ err := refreshAllStatusView(g, e, true)
return err
}
func (gui *Gui) addAllChanges(g *gocui.Gui, v *gocui.View) error {
- entity := gui.getSelectedRepository()
- if err := git.AddAll(entity, git.AddOptions{}); err != nil {
+ e := gui.getSelectedRepository()
+ if err := git.AddAll(e, git.AddOptions{}); err != nil {
return err
}
- err := refreshAllStatusView(g, entity, true)
+ err := refreshAllStatusView(g, e, true)
return err
}
diff --git a/pkg/gui/util-common.go b/pkg/gui/util-common.go
index f203bbf..3f6be1a 100644
--- a/pkg/gui/util-common.go
+++ b/pkg/gui/util-common.go
@@ -1,30 +1,11 @@
package gui
import (
- "github.com/isacikgoz/gitbatch/pkg/git"
"github.com/isacikgoz/gitbatch/pkg/helpers"
"github.com/jroimartin/gocui"
log "github.com/sirupsen/logrus"
)
-// refreshes the side views of the application for given git.RepoEntity struct
-func (gui *Gui) refreshViews(g *gocui.Gui, entity *git.RepoEntity) error {
- var err error
- if err = gui.updateRemotes(g, entity); err != nil {
- return err
- }
- if err = gui.updateBranch(g, entity); err != nil {
- return err
- }
- if err = gui.updateRemoteBranches(g, entity); err != nil {
- return err
- }
- if err = gui.updateCommits(g, entity); err != nil {
- return err
- }
- return err
-}
-
// focus to next view
func (gui *Gui) nextViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFeature) error {
var focusedViewName string
@@ -77,22 +58,6 @@ func (gui *Gui) previousViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFea
return nil
}
-// siwtch the app mode
-func (gui *Gui) switchMode(g *gocui.Gui, v *gocui.View) error {
- for i, mode := range modes {
- if mode == gui.State.Mode {
- if i == len(modes)-1 {
- gui.State.Mode = modes[0]
- break
- }
- gui.State.Mode = modes[i+1]
- break
- }
- }
- gui.updateKeyBindingsView(g, mainViewFeature.Name)
- return nil
-}
-
// siwtch the app's mode to fetch
func (gui *Gui) switchToFetchMode(g *gocui.Gui, v *gocui.View) error {
gui.State.Mode = fetchMode
diff --git a/pkg/gui/util-queuehandler.go b/pkg/gui/util-queuehandler.go
deleted file mode 100644
index a814c47..0000000
--- a/pkg/gui/util-queuehandler.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package gui
-
-import (
- "github.com/isacikgoz/gitbatch/pkg/git"
- "github.com/jroimartin/gocui"
- log "github.com/sirupsen/logrus"
-)
-
-// this function starts the queue and updates the gui with the result of an
-// operation
-func (gui *Gui) startQueue(g *gocui.Gui, v *gocui.View) error {
- go func(gui_go *Gui, g_go *gocui.Gui) {
- for {
- job, finished, err := gui_go.State.Queue.StartNext()
- // for each job execution we better refresh the main
- // it would be nice if we can also refresh side views
- g_go.Update(func(gu *gocui.Gui) error {
- gui_go.refreshMain(gu)
- return nil
- })
-
- if err != nil {
- if err == git.ErrAuthenticationRequired {
- // pause the job, so it will be indicated to being blocking
- job.Entity.State = git.Paused
- err := gui_go.openAuthenticationView(g, gui_go.State.Queue, job, v.Name())
- if err != nil {
- log.Warn(err.Error())
- return
- }
- }
- return
- // with not returning here, we simply ignore and continue
- }
- // if queue is finished simply return from this goroutine
- if finished {
- return
- }
- selectedEntity := gui_go.getSelectedRepository()
- if job.Entity == selectedEntity {
- gui_go.refreshViews(g, job.Entity)
- }
- }
- }(gui, g)
- return nil
-}
-
-// flashes the keybinding view's backgroun with green color to indicate that
-// the queue is started
-func indicateQueueStarted(g *gocui.Gui) error {
- v, err := g.View(keybindingsViewFeature.Name)
- if err != nil {
- return err
- }
- v.BgColor = gocui.ColorGreen
- v.FgColor = gocui.ColorBlack
- return nil
-}
diff --git a/pkg/gui/util-textstyle.go b/pkg/gui/util-textstyle.go
index 7c36263..fbe2b64 100644
--- a/pkg/gui/util-textstyle.go
+++ b/pkg/gui/util-textstyle.go
@@ -24,13 +24,10 @@ var (
maxRepositoryLength = 20
hashLength = 7
- ws = " "
- pushable = string(blue.Sprint("↖"))
- pullable = string(blue.Sprint("↘"))
- confidentArrow = string(magenta.Sprint(""))
- unconfidentArrow = string(yellow.Sprint(""))
- dirty = string(yellow.Sprint("✗"))
- unknown = magenta.Sprint("?")
+ ws = " "
+ pushable = string(blue.Sprint("↖"))
+ pullable = string(blue.Sprint("↘"))
+ dirty = string(yellow.Sprint("✗"))
queuedSymbol = "•"
workingSymbol = "•"
@@ -53,41 +50,41 @@ var (
// this function handles the render and representation of the repository
// TODO: cleanup is required, right now it looks too complicated
-func (gui *Gui) displayString(entity *git.RepoEntity) string {
+func (gui *Gui) repositoryLabel(e *git.RepoEntity) string {
suffix := ""
prefix := ""
repoName := ""
- if entity.Branch.Pushables != "?" {
- prefix = prefix + pushable + ws + entity.Branch.Pushables +
- ws + pullable + ws + entity.Branch.Pullables
+ if e.Branch.Pushables != "?" {
+ prefix = prefix + pushable + ws + e.Branch.Pushables +
+ ws + pullable + ws + e.Branch.Pullables
} else {
- prefix = prefix + pushable + ws + yellow.Sprint(entity.Branch.Pushables) +
- ws + pullable + ws + yellow.Sprint(entity.Branch.Pullables)
+ prefix = prefix + pushable + ws + yellow.Sprint(e.Branch.Pushables) +
+ ws + pullable + ws + yellow.Sprint(e.Branch.Pullables)
}
- selectedEntity := gui.getSelectedRepository()
- if selectedEntity == entity {
+ se := gui.getSelectedRepository()
+ if se == e {
prefix = prefix + selectionIndicator
- repoName = green.Sprint(entity.Name)
+ repoName = green.Sprint(e.Name)
} else {
prefix = prefix + ws
- repoName = entity.Name
+ repoName = e.Name
}
// some branch names can be really long, in that times I hope the first
// characters are important and meaningful
- branch := adjustTextLength(entity.Branch.Name, maxBranchLength)
+ branch := adjustTextLength(e.Branch.Name, maxBranchLength)
prefix = prefix + string(cyan.Sprint(branch))
- if !entity.Branch.Clean {
+ if !e.Branch.Clean {
prefix = prefix + ws + dirty + ws
} else {
prefix = prefix + ws
}
// rendering the satus according to repository's state
- if entity.State == git.Queued {
- if inQueue, ty := gui.State.Queue.IsInTheQueue(entity); inQueue {
+ if e.State() == git.Queued {
+ if inQueue, ty := gui.State.Queue.IsInTheQueue(e); inQueue {
switch mode := ty; mode {
case git.FetchJob:
suffix = blue.Sprint(queuedSymbol)
@@ -100,20 +97,35 @@ func (gui *Gui) displayString(entity *git.RepoEntity) string {
}
}
return prefix + repoName + ws + suffix
- } else if entity.State == git.Working {
+ } else if e.State() == git.Working {
// TODO: maybe the type of the job can be written while its working?
return prefix + repoName + ws + green.Sprint(workingSymbol)
- } else if entity.State == git.Success {
+ } else if e.State() == git.Success {
return prefix + repoName + ws + green.Sprint(successSymbol)
- } else if entity.State == git.Paused {
+ } else if e.State() == git.Paused {
return prefix + repoName + ws + yellow.Sprint(pauseSymbol)
- } else if entity.State == git.Fail {
+ } else if e.State() == git.Fail {
return prefix + repoName + ws + red.Sprint(failSymbol)
} else {
return prefix + repoName
}
}
+func commitLabel(c *git.Commit) string {
+ var body string
+ switch c.CommitType {
+ case git.EvenCommit:
+ body = cyan.Sprint(c.Hash[:hashLength]) + " " + c.Message
+ case git.LocalCommit:
+ body = blue.Sprint(c.Hash[:hashLength]) + " " + c.Message
+ case git.RemoteCommit:
+ body = yellow.Sprint(c.Hash[:hashLength]) + " " + c.Message
+ default:
+ body = c.Hash[:hashLength] + " " + c.Message
+ }
+ return body
+}
+
// limit the text length for visual concerns
func adjustTextLength(text string, maxLength int) (adjusted string) {
if len(text) > maxLength {