summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIbrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>2018-12-19 00:05:39 +0300
committerGitHub <noreply@github.com>2018-12-19 00:05:39 +0300
commit68bcd82ca27198c31bfe06858e3ae85e48648dab (patch)
tree3747cb46200821616a89b77a1fc142aaaff7606f
parentMerge pull request #35 from isacikgoz/develop (diff)
parentminor code style imrovements and code reduction (diff)
downloadgitbatch-68bcd82ca27198c31bfe06858e3ae85e48648dab.tar.gz
Merge pull request #36 from isacikgoz/develop
Develop
-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
36 files changed, 836 insertions, 938 deletions
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 {