From eb4260349098014f450b992e9c2659338a504a20 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Sat, 15 Dec 2018 00:16:34 +0300 Subject: minor code reduction --- pkg/app/app.go | 4 +- pkg/app/files.go | 2 +- pkg/gui/branchview.go | 74 -------------- pkg/gui/commitsview.go | 67 ------------- pkg/gui/keybindings.go | 174 +++++++------------------------- pkg/gui/remotebranchview.go | 78 --------------- pkg/gui/remotesview.go | 73 -------------- pkg/gui/sideviews.go | 236 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 277 insertions(+), 431 deletions(-) delete mode 100644 pkg/gui/branchview.go delete mode 100644 pkg/gui/commitsview.go delete mode 100644 pkg/gui/remotebranchview.go delete mode 100644 pkg/gui/remotesview.go create mode 100644 pkg/gui/sideviews.go diff --git a/pkg/app/app.go b/pkg/app/app.go index 740e3ff..21b87a6 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -36,9 +36,9 @@ func Setup(setupConfig SetupConfig) (*App, error) { var directories []string if len(app.Config.Directories) <= 0 || setupConfig.IgnoreConfig { - directories = generateDirectories(setupConfig.Directories, setupConfig.Depth) + directories = GenerateDirectories(setupConfig.Directories, setupConfig.Depth) } else { - directories = generateDirectories(app.Config.Directories, setupConfig.Depth) + directories = GenerateDirectories(app.Config.Directories, setupConfig.Depth) } // create a gui.Gui struct and set it as App's gui diff --git a/pkg/app/files.go b/pkg/app/files.go index 25e5074..03e4014 100644 --- a/pkg/app/files.go +++ b/pkg/app/files.go @@ -11,7 +11,7 @@ import ( // generateDirectories returns poosible git repositories to pipe into git pkg's // load function -func generateDirectories(directories []string, depth int) (gitDirectories []string) { +func GenerateDirectories(directories []string, depth int) (gitDirectories []string) { for i := 0; i <= depth; i++ { nonrepos, repos := walkRecursive(directories, gitDirectories) directories = nonrepos diff --git a/pkg/gui/branchview.go b/pkg/gui/branchview.go deleted file mode 100644 index f69a212..0000000 --- a/pkg/gui/branchview.go +++ /dev/null @@ -1,74 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/isacikgoz/gitbatch/pkg/git" - "github.com/jroimartin/gocui" -) - -// updates the branchview for given entity -func (gui *Gui) updateBranch(g *gocui.Gui, entity *git.RepoEntity) error { - var err error - out, err := 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 { - currentindex = i - fmt.Fprintln(out, selectionIndicator+b.Name) - continue - } - fmt.Fprintln(out, tab+b.Name) - } - err = gui.smartAnchorRelativeToLine(out, currentindex, totalbranches) - return err -} - -// iteration handler for the branchview -func (gui *Gui) nextBranch(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.Checkout(entity.NextBranch()); err != nil { - err = gui.openErrorView(g, err.Error(), - "You should manually resolve this issue", - branchViewFeature.Name) - return err - } - err = gui.checkoutFollowUp(g, entity) - return err -} - -// iteration handler for the branchview -func (gui *Gui) previousBranch(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.Checkout(entity.PreviousBranch()); err != nil { - err = gui.openErrorView(g, err.Error(), - "You should manually resolve this issue", - branchViewFeature.Name) - return err - } - err = gui.checkoutFollowUp(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/commitsview.go b/pkg/gui/commitsview.go deleted file mode 100644 index 6d1325c..0000000 --- a/pkg/gui/commitsview.go +++ /dev/null @@ -1,67 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/isacikgoz/gitbatch/pkg/git" - "github.com/jroimartin/gocui" -) - -// updates the commitsview for given entity -func (gui *Gui) updateCommits(g *gocui.Gui, entity *git.RepoEntity) error { - var err error - out, err := 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 { - currentindex = i - fmt.Fprintln(out, selectionIndicator+body) - continue - } - fmt.Fprintln(out, tab+body) - } - if err = gui.smartAnchorRelativeToLine(out, currentindex, totalcommits); err != nil { - return err - } - return err -} - -// iteration handler for the commitsview -func (gui *Gui) nextCommit(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.NextCommit(); err != nil { - return err - } - if err = gui.updateCommits(g, entity); err != nil { - return err - } - return err -} - -// reverse iteration handler for the commitsview -func (gui *Gui) prevCommit(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.PreviousCommit(); err != nil { - return err - } - if err = gui.updateCommits(g, entity); err != nil { - return err - } - return err -} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 936c7c6..6f21fab 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -67,6 +67,44 @@ func (gui *Gui) generateKeybindings() error { } gui.KeyBindings = append(gui.KeyBindings, mainKeybindings...) } + for _, view := range sideViews { + sideViewKeybindings := []*KeyBinding{ + { + View: view.Name, + Key: gocui.KeyArrowDown, + Modifier: gocui.ModNone, + Handler: gui.sideViewsNextItem, + Display: "↓", + Description: "Iterate over branches", + Vital: false, + }, { + View: view.Name, + Key: gocui.KeyArrowUp, + Modifier: gocui.ModNone, + Handler: gui.sideViewsPreviousItem, + Display: "↑", + Description: "Iterate over branches", + Vital: false, + }, { + View: view.Name, + Key: 'j', + Modifier: gocui.ModNone, + Handler: gui.sideViewsNextItem, + Display: "j", + Description: "Down", + Vital: false, + }, { + View: view.Name, + Key: 'k', + Modifier: gocui.ModNone, + Handler: gui.sideViewsPreviousItem, + Display: "k", + Description: "Up", + Vital: false, + }, + } + gui.KeyBindings = append(gui.KeyBindings, sideViewKeybindings...) + } // Statusviews common keybindings for _, view := range statusViews { statusKeybindings := []*KeyBinding{ @@ -374,108 +412,6 @@ func (gui *Gui) generateKeybindings() error { Display: "ctrl + c", Description: "Force application to quit", Vital: false, - }, - // Branch View Controls - { - View: branchViewFeature.Name, - Key: gocui.KeyArrowDown, - Modifier: gocui.ModNone, - Handler: gui.nextBranch, - Display: "↓", - Description: "Iterate over branches", - Vital: false, - }, { - View: branchViewFeature.Name, - Key: gocui.KeyArrowUp, - Modifier: gocui.ModNone, - Handler: gui.previousBranch, - Display: "↑", - Description: "Iterate over branches", - Vital: false, - }, { - View: branchViewFeature.Name, - Key: 'j', - Modifier: gocui.ModNone, - Handler: gui.nextBranch, - Display: "j", - Description: "Down", - Vital: false, - }, { - View: branchViewFeature.Name, - Key: 'k', - Modifier: gocui.ModNone, - Handler: gui.previousBranch, - Display: "k", - Description: "Up", - Vital: false, - }, - // Remote View Controls - { - View: remoteViewFeature.Name, - Key: gocui.KeyArrowDown, - Modifier: gocui.ModNone, - Handler: gui.nextRemote, - Display: "↓", - Description: "Iterate over remotes", - Vital: false, - }, { - View: remoteViewFeature.Name, - Key: gocui.KeyArrowUp, - Modifier: gocui.ModNone, - Handler: gui.previousRemote, - Display: "↑", - Description: "Iterate over remotes", - Vital: false, - }, { - View: remoteViewFeature.Name, - Key: 'j', - Modifier: gocui.ModNone, - Handler: gui.nextRemote, - Display: "j", - Description: "Down", - Vital: false, - }, { - View: remoteViewFeature.Name, - Key: 'k', - Modifier: gocui.ModNone, - Handler: gui.previousRemote, - Display: "k", - Description: "Up", - Vital: false, - }, - // Remote Branch View Controls - { - View: remoteBranchViewFeature.Name, - Key: gocui.KeyArrowDown, - Modifier: gocui.ModNone, - Handler: gui.nextRemoteBranch, - Display: "↓", - Description: "Iterate over remote branches", - Vital: false, - }, { - View: remoteBranchViewFeature.Name, - Key: gocui.KeyArrowUp, - Modifier: gocui.ModNone, - Handler: gui.previousRemoteBranch, - Display: "↑", - Description: "Iterate over remote branches", - Vital: false, - }, { - View: remoteBranchViewFeature.Name, - Key: 'j', - Modifier: gocui.ModNone, - Handler: gui.nextRemoteBranch, - Display: "j", - Description: "Down", - Vital: false, - }, { - View: remoteBranchViewFeature.Name, - Key: 'k', - Modifier: gocui.ModNone, - Handler: gui.previousRemoteBranch, - Display: "k", - Description: "Up", - Vital: false, }, { View: remoteBranchViewFeature.Name, Key: 's', @@ -484,40 +420,6 @@ func (gui *Gui) generateKeybindings() error { Display: "s", Description: "Synch with Remote", Vital: true, - }, - // Commit View Controls - { - View: commitViewFeature.Name, - Key: gocui.KeyArrowDown, - Modifier: gocui.ModNone, - Handler: gui.nextCommit, - Display: "↓", - Description: "Iterate over commits", - Vital: false, - }, { - View: commitViewFeature.Name, - Key: gocui.KeyArrowUp, - Modifier: gocui.ModNone, - Handler: gui.prevCommit, - Display: "↑", - Description: "Iterate over commits", - Vital: false, - }, { - View: commitViewFeature.Name, - Key: 'j', - Modifier: gocui.ModNone, - Handler: gui.nextCommit, - Display: "j", - Description: "Down", - Vital: false, - }, { - View: commitViewFeature.Name, - Key: 'k', - Modifier: gocui.ModNone, - Handler: gui.prevCommit, - Display: "k", - Description: "Up", - Vital: false, }, { View: commitViewFeature.Name, Key: 'd', diff --git a/pkg/gui/remotebranchview.go b/pkg/gui/remotebranchview.go deleted file mode 100644 index 4974808..0000000 --- a/pkg/gui/remotebranchview.go +++ /dev/null @@ -1,78 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/isacikgoz/gitbatch/pkg/git" - "github.com/jroimartin/gocui" -) - -// updates the remotebranchview for given entity -func (gui *Gui) updateRemoteBranches(g *gocui.Gui, entity *git.RepoEntity) error { - var err error - out, err := g.View(remoteBranchViewFeature.Name) - if err != nil { - return err - } - out.Clear() - currentindex := 0 - trb := len(entity.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 { - currentindex = i - fmt.Fprintln(out, selectionIndicator+rName) - continue - } - fmt.Fprintln(out, tab+rName) - } - if err = gui.smartAnchorRelativeToLine(out, currentindex, trb); err != nil { - return err - } - } - return nil -} - -// iteration handler for the remotebranchview -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, - Prune: true, - }); err != nil { - return err - } - // have no idea why this works.. - // some time need to fix, movement aint bad huh? - gui.nextRemote(g, v) - gui.previousRemote(g, v) - err = gui.updateRemoteBranches(g, entity) - return err -} - -// iteration handler for the remotebranchview -func (gui *Gui) nextRemoteBranch(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.Remote.NextRemoteBranch(); err != nil { - return err - } - err = gui.updateRemoteBranches(g, entity) - return err -} - -// iteration handler for the remotebranchview -func (gui *Gui) previousRemoteBranch(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.Remote.PreviousRemoteBranch(); err != nil { - return err - } - err = gui.updateRemoteBranches(g, entity) - return err -} diff --git a/pkg/gui/remotesview.go b/pkg/gui/remotesview.go deleted file mode 100644 index 51323dc..0000000 --- a/pkg/gui/remotesview.go +++ /dev/null @@ -1,73 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/isacikgoz/gitbatch/pkg/git" - "github.com/jroimartin/gocui" -) - -// updates the remotesview for given entity -func (gui *Gui) updateRemotes(g *gocui.Gui, entity *git.RepoEntity) error { - var err error - out, err := g.View(remoteViewFeature.Name) - if err != nil { - return err - } - out.Clear() - - currentindex := 0 - totalRemotes := len(entity.Remotes) - if totalRemotes > 0 { - for i, r := range entity.Remotes { - // TODO: maybe the text styling can be moved to textstyle.go file - _, shortURL := trimRemoteURL(r.URL[0]) - suffix := shortURL - if r.Name == entity.Remote.Name { - currentindex = i - fmt.Fprintln(out, selectionIndicator+r.Name+": "+suffix) - continue - } - fmt.Fprintln(out, tab+r.Name+": "+suffix) - } - if err = gui.smartAnchorRelativeToLine(out, currentindex, totalRemotes); err != nil { - return err - } - } - return nil -} - -// iteration handler for the remotesview -func (gui *Gui) nextRemote(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.NextRemote(); err != nil { - return err - } - if err = gui.remoteChangeFollowUp(g, entity); err != nil { - return err - } - return err -} - -// iteration handler for the remotesview -func (gui *Gui) previousRemote(g *gocui.Gui, v *gocui.View) error { - var err error - entity := gui.getSelectedRepository() - if err = entity.PreviousRemote(); err != nil { - return err - } - if err = gui.remoteChangeFollowUp(g, entity); err != nil { - return err - } - return err -} - -// 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 -} diff --git a/pkg/gui/sideviews.go b/pkg/gui/sideviews.go new file mode 100644 index 0000000..57518f3 --- /dev/null +++ b/pkg/gui/sideviews.go @@ -0,0 +1,236 @@ +package gui + +import ( + "fmt" + + "github.com/isacikgoz/gitbatch/pkg/git" + "github.com/jroimartin/gocui" +) + +var ( + sideViews = []viewFeature{remoteViewFeature, remoteBranchViewFeature, branchViewFeature, commitViewFeature} +) + +// updates the remotesview for given entity +func (gui *Gui) updateRemotes(g *gocui.Gui, entity *git.RepoEntity) error { + var err error + out, err := g.View(remoteViewFeature.Name) + if err != nil { + return err + } + out.Clear() + + currentindex := 0 + totalRemotes := len(entity.Remotes) + if totalRemotes > 0 { + for i, r := range entity.Remotes { + // TODO: maybe the text styling can be moved to textstyle.go file + _, shortURL := trimRemoteURL(r.URL[0]) + suffix := shortURL + if r.Name == entity.Remote.Name { + currentindex = i + fmt.Fprintln(out, selectionIndicator+r.Name+": "+suffix) + continue + } + fmt.Fprintln(out, tab+r.Name+": "+suffix) + } + if err = gui.smartAnchorRelativeToLine(out, currentindex, totalRemotes); err != nil { + return err + } + } + return nil +} + +// updates the remotebranchview for given entity +func (gui *Gui) updateRemoteBranches(g *gocui.Gui, entity *git.RepoEntity) error { + var err error + out, err := g.View(remoteBranchViewFeature.Name) + if err != nil { + return err + } + out.Clear() + currentindex := 0 + trb := len(entity.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 { + currentindex = i + fmt.Fprintln(out, selectionIndicator+rName) + continue + } + fmt.Fprintln(out, tab+rName) + } + if err = gui.smartAnchorRelativeToLine(out, currentindex, trb); err != nil { + return err + } + } + return nil +} + +// updates the branchview for given entity +func (gui *Gui) updateBranch(g *gocui.Gui, entity *git.RepoEntity) error { + var err error + out, err := 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 { + currentindex = i + fmt.Fprintln(out, selectionIndicator+b.Name) + continue + } + fmt.Fprintln(out, tab+b.Name) + } + err = gui.smartAnchorRelativeToLine(out, currentindex, totalbranches) + return err +} + +// updates the commitsview for given entity +func (gui *Gui) updateCommits(g *gocui.Gui, entity *git.RepoEntity) error { + var err error + out, err := 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 { + currentindex = i + fmt.Fprintln(out, selectionIndicator+body) + continue + } + fmt.Fprintln(out, tab+body) + } + if err = gui.smartAnchorRelativeToLine(out, currentindex, totalcommits); err != nil { + return err + } + return err +} + +func (gui *Gui) sideViewsNextItem(g *gocui.Gui, v *gocui.View) error { + var err error + entity := gui.getSelectedRepository() + switch viewName := v.Name(); viewName { + case remoteBranchViewFeature.Name: + if err = entity.Remote.NextRemoteBranch(); err != nil { + return err + } + err = gui.updateRemoteBranches(g, entity) + case remoteViewFeature.Name: + if err = entity.NextRemote(); err != nil { + return err + } + err = gui.remoteChangeFollowUp(g, entity) + case branchViewFeature.Name: + if err = entity.Checkout(entity.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) + } + return err +} + +func (gui *Gui) sideViewsPreviousItem(g *gocui.Gui, v *gocui.View) error { + var err error + entity := gui.getSelectedRepository() + switch viewName := v.Name(); viewName { + case remoteBranchViewFeature.Name: + if err = entity.Remote.PreviousRemoteBranch(); err != nil { + return err + } + err = gui.updateRemoteBranches(g, entity) + case remoteViewFeature.Name: + if err = entity.PreviousRemote(); err != nil { + return err + } + err = gui.remoteChangeFollowUp(g, entity) + case branchViewFeature.Name: + if err = entity.Checkout(entity.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) + } + 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, + 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 +} + +// 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 +} -- cgit v1.2.3 From f89587ee582360238e58865f4b6ad556d6418484 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Sat, 15 Dec 2018 00:24:20 +0300 Subject: renamed some files --- pkg/gui/cheatsheet.go | 36 ------- pkg/gui/controlsview.go | 36 +++++++ pkg/gui/gui-util.go | 229 ------------------------------------------- pkg/gui/queuehandler.go | 58 ----------- pkg/gui/textstyle.go | 175 --------------------------------- pkg/gui/util-common.go | 229 +++++++++++++++++++++++++++++++++++++++++++ pkg/gui/util-queuehandler.go | 58 +++++++++++ pkg/gui/util-textstyle.go | 175 +++++++++++++++++++++++++++++++++ 8 files changed, 498 insertions(+), 498 deletions(-) delete mode 100644 pkg/gui/cheatsheet.go create mode 100644 pkg/gui/controlsview.go delete mode 100644 pkg/gui/gui-util.go delete mode 100644 pkg/gui/queuehandler.go delete mode 100644 pkg/gui/textstyle.go create mode 100644 pkg/gui/util-common.go create mode 100644 pkg/gui/util-queuehandler.go create mode 100644 pkg/gui/util-textstyle.go diff --git a/pkg/gui/cheatsheet.go b/pkg/gui/cheatsheet.go deleted file mode 100644 index 4b2f97e..0000000 --- a/pkg/gui/cheatsheet.go +++ /dev/null @@ -1,36 +0,0 @@ -package gui - -import ( - "fmt" - - "github.com/jroimartin/gocui" -) - -// open the application controls -// TODO: view size can handled better for such situations like too small -// application area -func (gui *Gui) openCheatSheetView(g *gocui.Gui, v *gocui.View) error { - maxX, maxY := g.Size() - v, err := g.SetView(cheatSheetViewFeature.Name, maxX/2-25, maxY/2-10, maxX/2+25, maxY/2+10) - if err != nil { - if err != gocui.ErrUnknownView { - return err - } - v.Title = cheatSheetViewFeature.Title - for _, k := range gui.KeyBindings { - if k.View == mainViewFeature.Name || k.View == "" { - binding := " " + cyan.Sprint(k.Display) + ": " + k.Description - fmt.Fprintln(v, binding) - } - } - } - return gui.focusToView(cheatSheetViewFeature.Name) -} - -// close the application controls and do the clean job -func (gui *Gui) closeCheatSheetView(g *gocui.Gui, v *gocui.View) error { - if err := g.DeleteView(v.Name()); err != nil { - return nil - } - return gui.closeViewCleanup(mainViewFeature.Name) -} diff --git a/pkg/gui/controlsview.go b/pkg/gui/controlsview.go new file mode 100644 index 0000000..4b2f97e --- /dev/null +++ b/pkg/gui/controlsview.go @@ -0,0 +1,36 @@ +package gui + +import ( + "fmt" + + "github.com/jroimartin/gocui" +) + +// open the application controls +// TODO: view size can handled better for such situations like too small +// application area +func (gui *Gui) openCheatSheetView(g *gocui.Gui, v *gocui.View) error { + maxX, maxY := g.Size() + v, err := g.SetView(cheatSheetViewFeature.Name, maxX/2-25, maxY/2-10, maxX/2+25, maxY/2+10) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + v.Title = cheatSheetViewFeature.Title + for _, k := range gui.KeyBindings { + if k.View == mainViewFeature.Name || k.View == "" { + binding := " " + cyan.Sprint(k.Display) + ": " + k.Description + fmt.Fprintln(v, binding) + } + } + } + return gui.focusToView(cheatSheetViewFeature.Name) +} + +// close the application controls and do the clean job +func (gui *Gui) closeCheatSheetView(g *gocui.Gui, v *gocui.View) error { + if err := g.DeleteView(v.Name()); err != nil { + return nil + } + return gui.closeViewCleanup(mainViewFeature.Name) +} diff --git a/pkg/gui/gui-util.go b/pkg/gui/gui-util.go deleted file mode 100644 index f203bbf..0000000 --- a/pkg/gui/gui-util.go +++ /dev/null @@ -1,229 +0,0 @@ -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 - if v == nil || v.Name() == group[len(group)-1].Name { - focusedViewName = group[0].Name - } else { - for i := range group { - if v.Name() == group[i].Name { - focusedViewName = group[i+1].Name - break - } - if i == len(group)-1 { - return nil - } - } - } - if _, err := g.SetCurrentView(focusedViewName); err != nil { - log.WithFields(log.Fields{ - "view": focusedViewName, - }).Warn("View cannot be focused.") - return nil - } - gui.updateKeyBindingsView(g, focusedViewName) - return nil -} - -// focus to previous view -func (gui *Gui) previousViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFeature) error { - var focusedViewName string - if v == nil || v.Name() == group[0].Name { - focusedViewName = group[len(group)-1].Name - } else { - for i := range group { - if v.Name() == group[i].Name { - focusedViewName = group[i-1].Name - break - } - if i == len(group)-1 { - return nil - } - } - } - if _, err := g.SetCurrentView(focusedViewName); err != nil { - log.WithFields(log.Fields{ - "view": focusedViewName, - }).Warn("View cannot be focused.") - return nil - } - gui.updateKeyBindingsView(g, focusedViewName) - 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 - gui.updateKeyBindingsView(g, mainViewFeature.Name) - return nil -} - -// siwtch the app's mode to pull -func (gui *Gui) switchToPullMode(g *gocui.Gui, v *gocui.View) error { - gui.State.Mode = pullMode - gui.updateKeyBindingsView(g, mainViewFeature.Name) - return nil -} - -// siwtch the app's mode to merge -func (gui *Gui) switchToMergeMode(g *gocui.Gui, v *gocui.View) error { - gui.State.Mode = mergeMode - gui.updateKeyBindingsView(g, mainViewFeature.Name) - return nil -} - -// bring the view on the top by its name -func (gui *Gui) setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) { - if _, err := g.SetCurrentView(name); err != nil { - return nil, err - } - return g.SetViewOnTop(name) -} - -// if the cursor down past the last item, move it to the last line -func (gui *Gui) correctCursor(v *gocui.View) error { - cx, cy := v.Cursor() - ox, oy := v.Origin() - width, height := v.Size() - maxY := height - 1 - ly := width - 1 - if oy+cy <= ly { - return nil - } - newCy := helpers.Min(ly, maxY) - if err := v.SetCursor(cx, newCy); err != nil { - return err - } - err := v.SetOrigin(ox, ly-newCy) - return err -} - -// this function handles the iteration of a side view and set its origin point -// so that the selected line can be in the middle of the view -func (gui *Gui) smartAnchorRelativeToLine(v *gocui.View, currentindex, totallines int) error { - _, y := v.Size() - if currentindex >= int(0.5*float32(y)) && totallines-currentindex+int(0.5*float32(y)) >= y { - if err := v.SetOrigin(0, currentindex-int(0.5*float32(y))); err != nil { - return err - } - } else if totallines-currentindex < y && totallines > y { - if err := v.SetOrigin(0, totallines-y); err != nil { - return err - } - } else if totallines-currentindex <= int(0.5*float32(y)) && totallines > y-1 && currentindex > y { - if err := v.SetOrigin(0, currentindex-int(0.5*float32(y))); err != nil { - return err - } - } else { - if err := v.SetOrigin(0, 0); err != nil { - return err - } - } - return nil -} - -// this function writes the given text to rgiht hand side of the view -// cx and cy values are important to get the cursor to its old position -func writeRightHandSide(v *gocui.View, text string, cx, cy int) error { - runes := []rune(text) - tl := len(runes) - lx, _ := v.Size() - v.MoveCursor(lx-tl, cy-1, true) - for i := tl - 1; i >= 0; i-- { - v.EditDelete(true) - v.EditWrite(runes[i]) - } - v.SetCursor(cx, cy) - return nil -} - -// cursor down acts like half-page down for faster scrolling -func (gui *Gui) fastCursorDown(g *gocui.Gui, v *gocui.View) error { - if v != nil { - ox, oy := v.Origin() - _, vy := v.Size() - if len(v.BufferLines())+len(v.ViewBufferLines()) <= vy+oy || len(v.ViewBufferLines()) < vy { - return nil - } - // TODO: do something when it hits bottom - if err := v.SetOrigin(ox, oy+vy/2); err != nil { - return err - } - } - return nil -} - -// cursor up acts like half-page up for faster scrolling -func (gui *Gui) fastCursorUp(g *gocui.Gui, v *gocui.View) error { - if v != nil { - ox, oy := v.Origin() - _, vy := v.Size() - - if oy-vy/2 > 0 { - if err := v.SetOrigin(ox, oy-vy/2); err != nil { - return err - } - } else if oy-vy/2 <= 0 { - if err := v.SetOrigin(0, 0); err != nil { - return err - } - } - } - return nil -} - -// closeViewCleanup both updates the keybidings view and focuses to returning view -func (gui *Gui) closeViewCleanup(returningViewName string) (err error) { - if _, err = gui.g.SetCurrentView(returningViewName); err != nil { - return err - } - err = gui.updateKeyBindingsView(gui.g, returningViewName) - return err -} - -// focus to view same as closeViewCleanup but its just a wrapper for easy reading -func (gui *Gui) focusToView(viewName string) (err error) { - return gui.closeViewCleanup(viewName) -} diff --git a/pkg/gui/queuehandler.go b/pkg/gui/queuehandler.go deleted file mode 100644 index a814c47..0000000 --- a/pkg/gui/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/textstyle.go b/pkg/gui/textstyle.go deleted file mode 100644 index 7c36263..0000000 --- a/pkg/gui/textstyle.go +++ /dev/null @@ -1,175 +0,0 @@ -package gui - -import ( - "regexp" - "strings" - - "github.com/fatih/color" - "github.com/isacikgoz/gitbatch/pkg/git" -) - -var ( - black = color.New(color.FgBlack) - blue = color.New(color.FgBlue) - green = color.New(color.FgGreen) - red = color.New(color.FgRed) - cyan = color.New(color.FgCyan) - yellow = color.New(color.FgYellow) - white = color.New(color.FgWhite) - magenta = color.New(color.FgMagenta) - - bold = color.New(color.Bold) - - maxBranchLength = 15 - 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("?") - - queuedSymbol = "•" - workingSymbol = "•" - successSymbol = "✔" - pauseSymbol = "॥" - failSymbol = "✗" - - fetchSymbol = "↓" - pullSymbol = "↓↳" - mergeSymbol = "↳" - - keySymbol = ws + yellow.Sprint("🔑") + ws - - modeSeperator = "" - keyBindingSeperator = "░" - - selectionIndicator = ws + string(green.Sprint("→")) + ws - tab = ws -) - -// 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 { - suffix := "" - prefix := "" - repoName := "" - - if entity.Branch.Pushables != "?" { - prefix = prefix + pushable + ws + entity.Branch.Pushables + - ws + pullable + ws + entity.Branch.Pullables - } else { - prefix = prefix + pushable + ws + yellow.Sprint(entity.Branch.Pushables) + - ws + pullable + ws + yellow.Sprint(entity.Branch.Pullables) - } - - selectedEntity := gui.getSelectedRepository() - if selectedEntity == entity { - prefix = prefix + selectionIndicator - repoName = green.Sprint(entity.Name) - } else { - prefix = prefix + ws - repoName = entity.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) - prefix = prefix + string(cyan.Sprint(branch)) - - if !entity.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 { - switch mode := ty; mode { - case git.FetchJob: - suffix = blue.Sprint(queuedSymbol) - case git.PullJob: - suffix = magenta.Sprint(queuedSymbol) - case git.MergeJob: - suffix = cyan.Sprint(queuedSymbol) - default: - suffix = green.Sprint(queuedSymbol) - } - } - return prefix + repoName + ws + suffix - } else if entity.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 { - return prefix + repoName + ws + green.Sprint(successSymbol) - } else if entity.State == git.Paused { - return prefix + repoName + ws + yellow.Sprint(pauseSymbol) - } else if entity.State == git.Fail { - return prefix + repoName + ws + red.Sprint(failSymbol) - } else { - return prefix + repoName - } -} - -// limit the text length for visual concerns -func adjustTextLength(text string, maxLength int) (adjusted string) { - if len(text) > maxLength { - adjusted := text[:maxLength-2] + ".." - return adjusted - } - return text -} - -// colorize the plain diff text collected from system output -// the style is near to original diff command -func colorizeDiff(original string) (colorized []string) { - colorized = strings.Split(original, "\n") - re := regexp.MustCompile(`@@ .+ @@`) - for i, line := range colorized { - if len(line) > 0 { - if line[0] == '-' { - colorized[i] = red.Sprint(line) - } else if line[0] == '+' { - colorized[i] = green.Sprint(line) - } else if re.MatchString(line) { - s := re.FindString(line) - colorized[i] = cyan.Sprint(s) + line[len(s):] - } else { - continue - } - } else { - continue - } - } - return colorized -} - -// the remote link can be too verbose sometimes, so it is good to trim it -func trimRemoteURL(url string) (urltype string, shorturl string) { - // lets trim the unnecessary .git extension of the url - regit := regexp.MustCompile(`.git`) - if regit.MatchString(url[len(url)-4:]) { - url = url[:len(url)-4] - } - - // find out the protocol - ressh := regexp.MustCompile(`git@`) - rehttp := regexp.MustCompile(`http://`) - rehttps := regexp.MustCompile(`https://`) - - // separate the protocol and remote link - if ressh.MatchString(url) { - shorturl = ressh.Split(url, 5)[1] - urltype = "ssh" - } else if rehttp.MatchString(url) { - shorturl = rehttp.Split(url, 5)[1] - urltype = "http" - } else if rehttps.MatchString(url) { - shorturl = rehttps.Split(url, 5)[1] - urltype = "https" - } - return urltype, shorturl -} diff --git a/pkg/gui/util-common.go b/pkg/gui/util-common.go new file mode 100644 index 0000000..f203bbf --- /dev/null +++ b/pkg/gui/util-common.go @@ -0,0 +1,229 @@ +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 + if v == nil || v.Name() == group[len(group)-1].Name { + focusedViewName = group[0].Name + } else { + for i := range group { + if v.Name() == group[i].Name { + focusedViewName = group[i+1].Name + break + } + if i == len(group)-1 { + return nil + } + } + } + if _, err := g.SetCurrentView(focusedViewName); err != nil { + log.WithFields(log.Fields{ + "view": focusedViewName, + }).Warn("View cannot be focused.") + return nil + } + gui.updateKeyBindingsView(g, focusedViewName) + return nil +} + +// focus to previous view +func (gui *Gui) previousViewOfGroup(g *gocui.Gui, v *gocui.View, group []viewFeature) error { + var focusedViewName string + if v == nil || v.Name() == group[0].Name { + focusedViewName = group[len(group)-1].Name + } else { + for i := range group { + if v.Name() == group[i].Name { + focusedViewName = group[i-1].Name + break + } + if i == len(group)-1 { + return nil + } + } + } + if _, err := g.SetCurrentView(focusedViewName); err != nil { + log.WithFields(log.Fields{ + "view": focusedViewName, + }).Warn("View cannot be focused.") + return nil + } + gui.updateKeyBindingsView(g, focusedViewName) + 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 + gui.updateKeyBindingsView(g, mainViewFeature.Name) + return nil +} + +// siwtch the app's mode to pull +func (gui *Gui) switchToPullMode(g *gocui.Gui, v *gocui.View) error { + gui.State.Mode = pullMode + gui.updateKeyBindingsView(g, mainViewFeature.Name) + return nil +} + +// siwtch the app's mode to merge +func (gui *Gui) switchToMergeMode(g *gocui.Gui, v *gocui.View) error { + gui.State.Mode = mergeMode + gui.updateKeyBindingsView(g, mainViewFeature.Name) + return nil +} + +// bring the view on the top by its name +func (gui *Gui) setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) { + if _, err := g.SetCurrentView(name); err != nil { + return nil, err + } + return g.SetViewOnTop(name) +} + +// if the cursor down past the last item, move it to the last line +func (gui *Gui) correctCursor(v *gocui.View) error { + cx, cy := v.Cursor() + ox, oy := v.Origin() + width, height := v.Size() + maxY := height - 1 + ly := width - 1 + if oy+cy <= ly { + return nil + } + newCy := helpers.Min(ly, maxY) + if err := v.SetCursor(cx, newCy); err != nil { + return err + } + err := v.SetOrigin(ox, ly-newCy) + return err +} + +// this function handles the iteration of a side view and set its origin point +// so that the selected line can be in the middle of the view +func (gui *Gui) smartAnchorRelativeToLine(v *gocui.View, currentindex, totallines int) error { + _, y := v.Size() + if currentindex >= int(0.5*float32(y)) && totallines-currentindex+int(0.5*float32(y)) >= y { + if err := v.SetOrigin(0, currentindex-int(0.5*float32(y))); err != nil { + return err + } + } else if totallines-currentindex < y && totallines > y { + if err := v.SetOrigin(0, totallines-y); err != nil { + return err + } + } else if totallines-currentindex <= int(0.5*float32(y)) && totallines > y-1 && currentindex > y { + if err := v.SetOrigin(0, currentindex-int(0.5*float32(y))); err != nil { + return err + } + } else { + if err := v.SetOrigin(0, 0); err != nil { + return err + } + } + return nil +} + +// this function writes the given text to rgiht hand side of the view +// cx and cy values are important to get the cursor to its old position +func writeRightHandSide(v *gocui.View, text string, cx, cy int) error { + runes := []rune(text) + tl := len(runes) + lx, _ := v.Size() + v.MoveCursor(lx-tl, cy-1, true) + for i := tl - 1; i >= 0; i-- { + v.EditDelete(true) + v.EditWrite(runes[i]) + } + v.SetCursor(cx, cy) + return nil +} + +// cursor down acts like half-page down for faster scrolling +func (gui *Gui) fastCursorDown(g *gocui.Gui, v *gocui.View) error { + if v != nil { + ox, oy := v.Origin() + _, vy := v.Size() + if len(v.BufferLines())+len(v.ViewBufferLines()) <= vy+oy || len(v.ViewBufferLines()) < vy { + return nil + } + // TODO: do something when it hits bottom + if err := v.SetOrigin(ox, oy+vy/2); err != nil { + return err + } + } + return nil +} + +// cursor up acts like half-page up for faster scrolling +func (gui *Gui) fastCursorUp(g *gocui.Gui, v *gocui.View) error { + if v != nil { + ox, oy := v.Origin() + _, vy := v.Size() + + if oy-vy/2 > 0 { + if err := v.SetOrigin(ox, oy-vy/2); err != nil { + return err + } + } else if oy-vy/2 <= 0 { + if err := v.SetOrigin(0, 0); err != nil { + return err + } + } + } + return nil +} + +// closeViewCleanup both updates the keybidings view and focuses to returning view +func (gui *Gui) closeViewCleanup(returningViewName string) (err error) { + if _, err = gui.g.SetCurrentView(returningViewName); err != nil { + return err + } + err = gui.updateKeyBindingsView(gui.g, returningViewName) + return err +} + +// focus to view same as closeViewCleanup but its just a wrapper for easy reading +func (gui *Gui) focusToView(viewName string) (err error) { + return gui.closeViewCleanup(viewName) +} diff --git a/pkg/gui/util-queuehandler.go b/pkg/gui/util-queuehandler.go new file mode 100644 index 0000000..a814c47 --- /dev/null +++ b/pkg/gui/util-queuehandler.go @@ -0,0 +1,58 @@ +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 new file mode 100644 index 0000000..7c36263 --- /dev/null +++ b/pkg/gui/util-textstyle.go @@ -0,0 +1,175 @@ +package gui + +import ( + "regexp" + "strings" + + "github.com/fatih/color" + "github.com/isacikgoz/gitbatch/pkg/git" +) + +var ( + black = color.New(color.FgBlack) + blue = color.New(color.FgBlue) + green = color.New(color.FgGreen) + red = color.New(color.FgRed) + cyan = color.New(color.FgCyan) + yellow = color.New(color.FgYellow) + white = color.New(color.FgWhite) + magenta = color.New(color.FgMagenta) + + bold = color.New(color.Bold) + + maxBranchLength = 15 + 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("?") + + queuedSymbol = "•" + workingSymbol = "•" + successSymbol = "✔" + pauseSymbol = "॥" + failSymbol = "✗" + + fetchSymbol = "↓" + pullSymbol = "↓↳" + mergeSymbol = "↳" + + keySymbol = ws + yellow.Sprint("🔑") + ws + + modeSeperator = "" + keyBindingSeperator = "░" + + selectionIndicator = ws + string(green.Sprint("→")) + ws + tab = ws +) + +// 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 { + suffix := "" + prefix := "" + repoName := "" + + if entity.Branch.Pushables != "?" { + prefix = prefix + pushable + ws + entity.Branch.Pushables + + ws + pullable + ws + entity.Branch.Pullables + } else { + prefix = prefix + pushable + ws + yellow.Sprint(entity.Branch.Pushables) + + ws + pullable + ws + yellow.Sprint(entity.Branch.Pullables) + } + + selectedEntity := gui.getSelectedRepository() + if selectedEntity == entity { + prefix = prefix + selectionIndicator + repoName = green.Sprint(entity.Name) + } else { + prefix = prefix + ws + repoName = entity.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) + prefix = prefix + string(cyan.Sprint(branch)) + + if !entity.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 { + switch mode := ty; mode { + case git.FetchJob: + suffix = blue.Sprint(queuedSymbol) + case git.PullJob: + suffix = magenta.Sprint(queuedSymbol) + case git.MergeJob: + suffix = cyan.Sprint(queuedSymbol) + default: + suffix = green.Sprint(queuedSymbol) + } + } + return prefix + repoName + ws + suffix + } else if entity.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 { + return prefix + repoName + ws + green.Sprint(successSymbol) + } else if entity.State == git.Paused { + return prefix + repoName + ws + yellow.Sprint(pauseSymbol) + } else if entity.State == git.Fail { + return prefix + repoName + ws + red.Sprint(failSymbol) + } else { + return prefix + repoName + } +} + +// limit the text length for visual concerns +func adjustTextLength(text string, maxLength int) (adjusted string) { + if len(text) > maxLength { + adjusted := text[:maxLength-2] + ".." + return adjusted + } + return text +} + +// colorize the plain diff text collected from system output +// the style is near to original diff command +func colorizeDiff(original string) (colorized []string) { + colorized = strings.Split(original, "\n") + re := regexp.MustCompile(`@@ .+ @@`) + for i, line := range colorized { + if len(line) > 0 { + if line[0] == '-' { + colorized[i] = red.Sprint(line) + } else if line[0] == '+' { + colorized[i] = green.Sprint(line) + } else if re.MatchString(line) { + s := re.FindString(line) + colorized[i] = cyan.Sprint(s) + line[len(s):] + } else { + continue + } + } else { + continue + } + } + return colorized +} + +// the remote link can be too verbose sometimes, so it is good to trim it +func trimRemoteURL(url string) (urltype string, shorturl string) { + // lets trim the unnecessary .git extension of the url + regit := regexp.MustCompile(`.git`) + if regit.MatchString(url[len(url)-4:]) { + url = url[:len(url)-4] + } + + // find out the protocol + ressh := regexp.MustCompile(`git@`) + rehttp := regexp.MustCompile(`http://`) + rehttps := regexp.MustCompile(`https://`) + + // separate the protocol and remote link + if ressh.MatchString(url) { + shorturl = ressh.Split(url, 5)[1] + urltype = "ssh" + } else if rehttp.MatchString(url) { + shorturl = rehttp.Split(url, 5)[1] + urltype = "http" + } else if rehttps.MatchString(url) { + shorturl = rehttps.Split(url, 5)[1] + urltype = "https" + } + return urltype, shorturl +} -- cgit v1.2.3 From 9a54e2957f370fc2515b5263fff271cd842da87d Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Sat, 15 Dec 2018 01:58:59 +0300 Subject: added quick mode --- main.go | 2 + pkg/app/app.go | 15 +++++++- pkg/app/files.go | 2 +- pkg/app/quick.go | 46 ++++++++++++++++++++++ pkg/git/cmd-fetch.go | 6 +-- pkg/git/cmd-pull.go | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ pkg/git/repository.go | 60 +++++++++++++++++------------ pkg/git/util-load.go | 2 +- 8 files changed, 205 insertions(+), 32 deletions(-) create mode 100644 pkg/app/quick.go create mode 100644 pkg/git/cmd-pull.go diff --git a/main.go b/main.go index cb8a9eb..788d72c 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ var ( ignoreConfig = kingpin.Flag("ignore-config", "Ignore config file").Short('i').Bool() recurseDepth = kingpin.Flag("recursive-depth", "Find directories recursively").Default("1").Short('r').Int() logLevel = kingpin.Flag("log-level", "Logging level; trace,debug,info,warn,error").Default("error").Short('l').String() + quick = kingpin.Flag("quick", "runs without gui and fetches/pull remote upstream. modes are fetch or pull").Short('q').String() ) func main() { @@ -28,6 +29,7 @@ func main() { LogLevel: *logLevel, IgnoreConfig: *ignoreConfig, Depth: *recurseDepth, + QuickMode: *quick, }) if err != nil { log.Fatal(err) diff --git a/pkg/app/app.go b/pkg/app/app.go index 21b87a6..9bceee2 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -18,6 +18,7 @@ type SetupConfig struct { LogLevel string IgnoreConfig bool Depth int + QuickMode string } // Setup will handle pre-required operations. It is designed to be a wrapper for @@ -36,9 +37,19 @@ func Setup(setupConfig SetupConfig) (*App, error) { var directories []string if len(app.Config.Directories) <= 0 || setupConfig.IgnoreConfig { - directories = GenerateDirectories(setupConfig.Directories, setupConfig.Depth) + directories = generateDirectories(setupConfig.Directories, setupConfig.Depth) } else { - directories = GenerateDirectories(app.Config.Directories, setupConfig.Depth) + directories = generateDirectories(app.Config.Directories, setupConfig.Depth) + } + + if len(setupConfig.QuickMode) > 0 { + x := setupConfig.QuickMode == "fetch" + y := setupConfig.QuickMode == "pull" + if x == y { + log.Fatal("Unrecognized quick mode: " + setupConfig.QuickMode) + } + quick(directories, setupConfig.Depth, setupConfig.QuickMode) + log.Fatal("Finished") } // create a gui.Gui struct and set it as App's gui diff --git a/pkg/app/files.go b/pkg/app/files.go index 03e4014..25e5074 100644 --- a/pkg/app/files.go +++ b/pkg/app/files.go @@ -11,7 +11,7 @@ import ( // generateDirectories returns poosible git repositories to pipe into git pkg's // load function -func GenerateDirectories(directories []string, depth int) (gitDirectories []string) { +func generateDirectories(directories []string, depth int) (gitDirectories []string) { for i := 0; i <= depth; i++ { nonrepos, repos := walkRecursive(directories, gitDirectories) directories = nonrepos diff --git a/pkg/app/quick.go b/pkg/app/quick.go new file mode 100644 index 0000000..0a845c4 --- /dev/null +++ b/pkg/app/quick.go @@ -0,0 +1,46 @@ +package app + +import ( + "sync" + "time" + + "github.com/isacikgoz/gitbatch/pkg/git" + log "github.com/sirupsen/logrus" +) + +func quick(directories []string, depth int, mode string) { + + var wg sync.WaitGroup + start := time.Now() + for _, dir := range directories { + wg.Add(1) + go func(d string, mode string) { + defer wg.Done() + err := operate(d, mode) + if err != nil { + log.Errorf("%s: %s", d, err.Error()) + } + }(dir, mode) + } + wg.Wait() + elapsed := time.Since(start) + log.Infof("%d repositories finished in: %s\n", len(directories), elapsed) +} + +func operate(directory, mode string) error { + r, err := git.FastInitializeRepo(directory) + if err != nil { + return err + } + switch mode { + case "fetch": + return git.Fetch(r, git.FetchOptions{ + RemoteName: "origin", + }) + case "pull": + return git.Pull(r, git.PullOptions{ + RemoteName: "origin", + }) + } + return nil +} diff --git a/pkg/git/cmd-fetch.go b/pkg/git/cmd-fetch.go index 8a8bf9c..df600b3 100644 --- a/pkg/git/cmd-fetch.go +++ b/pkg/git/cmd-fetch.go @@ -85,8 +85,7 @@ func fetchWithGit(entity *RepoEntity, options FetchOptions) (err error) { return err } // till this step everything should be ok - err = entity.Refresh() - return err + return entity.Refresh() } // fetchWithGoGit is the primary fetch method and refspec is the main feature. @@ -141,6 +140,5 @@ func fetchWithGoGit(entity *RepoEntity, options FetchOptions, refspec string) (e } } // till this step everything should be ok - err = entity.Refresh() - return err + return entity.Refresh() } diff --git a/pkg/git/cmd-pull.go b/pkg/git/cmd-pull.go new file mode 100644 index 0000000..a493b19 --- /dev/null +++ b/pkg/git/cmd-pull.go @@ -0,0 +1,104 @@ +package git + +import ( + log "github.com/sirupsen/logrus" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/transport/http" +) + +var ( + pullCmdMode string + pullTryCount int + + pullCommand = "pull" + pullCmdModeLegacy = "git" + pullCmdModeNative = "go-git" + pullMaxTry = 1 +) + +// PullOptions defines the rules for pull operation +type PullOptions struct { + // Name of the remote to fetch from. Defaults to origin. + RemoteName string + // ReferenceName Remote branch to clone. If empty, uses HEAD. + ReferenceName string + // Fetch only ReferenceName if true. + SingleBranch bool + // Credentials holds the user and pswd information + Credentials Credentials + // Force allows the pull to update a local branch even when the remote + // branch does not descend from it. + Force bool +} + +// Pull ncorporates changes from a remote repository into the current branch. +func Pull(entity *RepoEntity, options PullOptions) (err error) { + // here we configure pull operation + // default mode is go-git (this may be configured) + pullCmdMode = pullCmdModeNative + pullTryCount = 0 + + switch pullCmdMode { + case pullCmdModeLegacy: + err = pullWithGit(entity, options) + return err + case pullCmdModeNative: + err = pullWithGoGit(entity, options) + return err + } + return nil +} + +func pullWithGit(entity *RepoEntity, options PullOptions) (err error) { + args := make([]string, 0) + args = append(args, pullCommand) + // parse options to command line arguments + if len(options.RemoteName) > 0 { + args = append(args, options.RemoteName) + } + if options.Force { + args = append(args, "-f") + } + if err := GenericGitCommand(entity.AbsPath, args); err != nil { + log.Warn("Error at git command (pull)") + return err + } + return entity.Refresh() +} + +func pullWithGoGit(entity *RepoEntity, options PullOptions) (err error) { + opt := &git.PullOptions{ + RemoteName: options.RemoteName, + SingleBranch: options.SingleBranch, + Force: options.Force, + } + if len(options.ReferenceName) > 0 { + ref := plumbing.NewRemoteReferenceName(options.RemoteName, options.ReferenceName) + opt.ReferenceName = ref + } + // if any credential is given, let's add it to the git.PullOptions + if len(options.Credentials.User) > 0 { + protocol, err := entity.authProtocol(entity.Remote) + if err != nil { + return err + } + if protocol == authProtocolHttp || protocol == authProtocolHttps { + opt.Auth = &http.BasicAuth{ + Username: options.Credentials.User, + Password: options.Credentials.Password, + } + } else { + return ErrInvalidAuthMethod + } + } + w, err := entity.Repository.Worktree() + if err != nil { + return err + } + err = w.Pull(opt) + if err != nil { + return err + } + return entity.Refresh() +} diff --git a/pkg/git/repository.go b/pkg/git/repository.go index ff67611..f37c813 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -47,33 +47,12 @@ const ( Fail RepoState = 5 ) -// InitializeRepository initializes a RepoEntity struct with its belongings. -func InitializeRepository(directory string) (entity *RepoEntity, err error) { - file, err := os.Open(directory) +// InitializeRepo initializes a RepoEntity struct with its belongings. +func InitializeRepo(directory string) (entity *RepoEntity, err error) { + entity, err = FastInitializeRepo(directory) if err != nil { - log.WithFields(log.Fields{ - "directory": directory, - }).Trace("Cannot open as directory") - return nil, err - } - fileInfo, err := file.Stat() - if err != nil { - return nil, err - } - r, err := git.PlainOpen(directory) - if err != nil { - log.WithFields(log.Fields{ - "directory": directory, - }).Trace("Cannot open directory as a git repository") return nil, err } - entity = &RepoEntity{RepoID: helpers.RandomString(8), - Name: fileInfo.Name(), - AbsPath: directory, - ModTime: fileInfo.ModTime(), - Repository: *r, - State: Available, - } // after we intiate the struct we can fill its values entity.loadLocalBranches() entity.loadCommits() @@ -107,11 +86,44 @@ func InitializeRepository(directory string) (entity *RepoEntity, err error) { return entity, nil } +// FastInitializeRepo initializes a RepoEntity struct without its belongings. +func FastInitializeRepo(directory string) (entity *RepoEntity, err error) { + file, err := os.Open(directory) + if err != nil { + log.WithFields(log.Fields{ + "directory": directory, + }).Trace("Cannot open as directory") + return nil, err + } + fileInfo, err := file.Stat() + if err != nil { + return nil, err + } + r, err := git.PlainOpen(directory) + if err != nil { + log.WithFields(log.Fields{ + "directory": directory, + }).Trace("Cannot open directory as a git repository") + return nil, err + } + entity = &RepoEntity{RepoID: helpers.RandomString(8), + Name: fileInfo.Name(), + AbsPath: directory, + ModTime: fileInfo.ModTime(), + Repository: *r, + State: Available, + } + return entity, nil +} + // Refresh the belongings of a repositoriy, this function is called right after // fetch/pull/merge operations func (entity *RepoEntity) Refresh() error { var err error // error can be ignored since the file already exists when app is loading + if entity.Branch == nil { + return nil + } file, _ := os.Open(entity.AbsPath) fileInfo, err := file.Stat() if err != nil { diff --git a/pkg/git/util-load.go b/pkg/git/util-load.go index e35e1c1..bf19de8 100644 --- a/pkg/git/util-load.go +++ b/pkg/git/util-load.go @@ -24,7 +24,7 @@ func LoadRepositoryEntities(directories []string) (entities []*RepoEntity, err e // decrement the wait counter by one, we call it in a defer so it's // called at the end of this goroutine defer wg.Done() - entity, err := InitializeRepository(d) + entity, err := InitializeRepo(d) if err != nil { log.WithFields(log.Fields{ "directory": d, -- cgit v1.2.3 From e38a04fbca4936cfbe591fd12936361427992714 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Sat, 15 Dec 2018 16:36:48 +0300 Subject: app args and configuration handle revised --- main.go | 28 ++++++++++------------ pkg/app/app.go | 69 +++++++++++++++++++++++++++++++++++-------------------- pkg/app/config.go | 39 ++++++++++++++++++++----------- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/main.go b/main.go index 788d72c..0a8b5b3 100644 --- a/main.go +++ b/main.go @@ -1,35 +1,31 @@ package main import ( - "os" - "github.com/isacikgoz/gitbatch/pkg/app" log "github.com/sirupsen/logrus" "gopkg.in/alecthomas/kingpin.v2" ) var ( - // take this as default directory if user does not start app with -d flag - currentDir, err = os.Getwd() - dirs = kingpin.Flag("directory", "Directory to roam for git repositories").Default(currentDir).Short('d').Strings() - ignoreConfig = kingpin.Flag("ignore-config", "Ignore config file").Short('i').Bool() - recurseDepth = kingpin.Flag("recursive-depth", "Find directories recursively").Default("1").Short('r').Int() - logLevel = kingpin.Flag("log-level", "Logging level; trace,debug,info,warn,error").Default("error").Short('l').String() - quick = kingpin.Flag("quick", "runs without gui and fetches/pull remote upstream. modes are fetch or pull").Short('q').String() + dirs = kingpin.Flag("directory", "Directory(s) to roam for git repositories.").Short('d').Strings() + mode = kingpin.Flag("mode", "Application start mode, more sensible with quick run.").Short('m').String() + recurseDepth = kingpin.Flag("recursive-depth", "Find directories recursively.").Default("0").Short('r').Int() + logLevel = kingpin.Flag("log-level", "Logging level; trace,debug,info,warn,error").Default("error").Short('l').String() + quick = kingpin.Flag("quick", "runs without gui and fetches/pull remote upstream.").Short('q').Bool() ) func main() { - kingpin.Version("gitbatch version 0.1.0 (alpha)") + kingpin.Version("gitbatch version 0.1.1 (alpha)") // parse the command line flag and options kingpin.Parse() // set the app - app, err := app.Setup(app.SetupConfig{ - Directories: *dirs, - LogLevel: *logLevel, - IgnoreConfig: *ignoreConfig, - Depth: *recurseDepth, - QuickMode: *quick, + app, err := app.Setup(&app.SetupConfig{ + Directories: *dirs, + LogLevel: *logLevel, + Depth: *recurseDepth, + QuickMode: *quick, + Mode: *mode, }) if err != nil { log.Fatal(err) diff --git a/pkg/app/app.go b/pkg/app/app.go index 9bceee2..9c9e9a7 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1,6 +1,8 @@ package app import ( + "os" + "github.com/isacikgoz/gitbatch/pkg/gui" log "github.com/sirupsen/logrus" ) @@ -9,51 +11,48 @@ import ( // it has only the gui.Gui pointer for interface entity. type App struct { Gui *gui.Gui - Config *Config + Config *SetupConfig } // SetupConfig is an assembler data to initiate a setup type SetupConfig struct { - Directories []string - LogLevel string - IgnoreConfig bool - Depth int - QuickMode string + Directories []string + LogLevel string + Depth int + QuickMode bool + Mode string } // Setup will handle pre-required operations. It is designed to be a wrapper for // main method right now. -func Setup(setupConfig SetupConfig) (*App, error) { +func Setup(setupConfig *SetupConfig) (*App, error) { // initiate the app and give it initial values app := &App{} - setLogLevel(setupConfig.LogLevel) - var err error - app.Config, err = LoadConfiguration() - if err != nil { - // the error types and handling is not considered yer - log.Error(err) - return app, err + if len(setupConfig.Directories) <= 0 { + d, _ := os.Getwd() + setupConfig.Directories = []string{d} } - var directories []string - if len(app.Config.Directories) <= 0 || setupConfig.IgnoreConfig { - directories = generateDirectories(setupConfig.Directories, setupConfig.Depth) - } else { - directories = generateDirectories(app.Config.Directories, setupConfig.Depth) + appConfig, err := overrideDefaults(setupConfig) + if err != nil { + return nil, err } - if len(setupConfig.QuickMode) > 0 { - x := setupConfig.QuickMode == "fetch" - y := setupConfig.QuickMode == "pull" + setLogLevel(appConfig.LogLevel) + directories := generateDirectories(appConfig.Directories, appConfig.Depth) + + if appConfig.QuickMode { + x := appConfig.Mode == "fetch" + y := appConfig.Mode == "pull" if x == y { - log.Fatal("Unrecognized quick mode: " + setupConfig.QuickMode) + log.Fatal("Unrecognized quick mode: " + appConfig.Mode) } - quick(directories, setupConfig.Depth, setupConfig.QuickMode) + quick(directories, appConfig.Depth, appConfig.Mode) log.Fatal("Finished") } // create a gui.Gui struct and set it as App's gui - app.Gui, err = gui.NewGui(app.Config.Mode, directories) + app.Gui, err = gui.NewGui(appConfig.Mode, directories) if err != nil { // the error types and handling is not considered yer log.Error(err) @@ -90,3 +89,23 @@ func setLogLevel(logLevel string) { "level": logLevel, }).Trace("logging level has been set") } + +func overrideDefaults(setupConfig *SetupConfig) (appConfig *SetupConfig, err error) { + appConfig, err = LoadConfiguration() + if len(setupConfig.Directories) > 0 { + appConfig.Directories = setupConfig.Directories + } + if len(setupConfig.LogLevel) > 0 { + appConfig.LogLevel = setupConfig.LogLevel + } + if setupConfig.Depth > 0 { + appConfig.Depth = setupConfig.Depth + } + if setupConfig.QuickMode { + appConfig.QuickMode = setupConfig.QuickMode + } + if len(setupConfig.Mode) > 0 { + appConfig.Mode = setupConfig.Mode + } + return appConfig, err +} diff --git a/pkg/app/config.go b/pkg/app/config.go index b613532..57c2e6e 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -9,12 +9,6 @@ import ( "github.com/spf13/viper" ) -// Config type is the configuration entity of the application -type Config struct { - Mode string - Directories []string -} - // config file stuff var ( configFileName = "config" @@ -28,14 +22,20 @@ var ( // configuration items var ( - modeKey = "mode" - modeKeyDefault = "fetch" - pathsKey = "paths" - pathsKeyDefault = []string{"."} + modeKey = "mode" + modeKeyDefault = "fetch" + pathsKey = "paths" + pathsKeyDefault = []string{"."} + logLevelKey = "loglevel" + logLevelKeyDefault = "error" + qucikKey = "quick" + qucikKeyDefault = false + recursionKey = "recursion" + recursionKeyDefault = 1 ) // LoadConfiguration returns a Config struct is filled -func LoadConfiguration() (*Config, error) { +func LoadConfiguration() (*SetupConfig, error) { if err := initializeConfigurationManager(); err != nil { return nil, err } @@ -45,15 +45,28 @@ func LoadConfiguration() (*Config, error) { if err := readConfiguration(); err != nil { return nil, err } - config := &Config{ + var directories []string + if len(viper.GetStringSlice(pathsKey)) <= 0 { + d, _ := os.Getwd() + directories = []string{d} + } else { + directories = viper.GetStringSlice(pathsKey) + } + config := &SetupConfig{ + Directories: directories, + LogLevel: viper.GetString(logLevelKey), + Depth: viper.GetInt(recursionKey), + QuickMode: viper.GetBool(qucikKey), Mode: viper.GetString(modeKey), - Directories: viper.GetStringSlice(pathsKey), } return config, nil } // set default configuration parameters func setDefaults() error { + viper.SetDefault(logLevelKey, logLevelKeyDefault) + viper.SetDefault(qucikKey, qucikKeyDefault) + viper.SetDefault(recursionKey, recursionKeyDefault) viper.SetDefault(modeKey, modeKeyDefault) // viper.SetDefault(pathsKey, pathsKeyDefault) return nil -- cgit v1.2.3 From da887d1c197b16bbf9e4c9457641b8acf22ce3aa Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Sun, 16 Dec 2018 22:12:18 +0300 Subject: add option to set upstream to branch and minor improvements --- pkg/app/quick.go | 8 +++++--- pkg/git/cmd-config.go | 8 ++++---- pkg/git/cmd-fetch.go | 8 +++++++- pkg/gui/keybindings.go | 26 ++++++++++++++++++++++++++ pkg/gui/sideviews.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 90 insertions(+), 9 deletions(-) diff --git a/pkg/app/quick.go b/pkg/app/quick.go index 0a845c4..fba5d8f 100644 --- a/pkg/app/quick.go +++ b/pkg/app/quick.go @@ -1,11 +1,11 @@ package app import ( + "fmt" "sync" "time" "github.com/isacikgoz/gitbatch/pkg/git" - log "github.com/sirupsen/logrus" ) func quick(directories []string, depth int, mode string) { @@ -18,13 +18,15 @@ func quick(directories []string, depth int, mode string) { defer wg.Done() err := operate(d, mode) if err != nil { - log.Errorf("%s: %s", d, err.Error()) + fmt.Printf("%s: %s\n", d, err.Error()) + } else { + fmt.Printf("%s: successful\n", d) } }(dir, mode) } wg.Wait() elapsed := time.Since(start) - log.Infof("%d repositories finished in: %s\n", len(directories), elapsed) + fmt.Printf("%d repositories finished in: %s\n", len(directories), elapsed) } func operate(directory, mode string) error { diff --git a/pkg/git/cmd-config.go b/pkg/git/cmd-config.go index 541b0be..dfbf661 100644 --- a/pkg/git/cmd-config.go +++ b/pkg/git/cmd-config.go @@ -28,7 +28,7 @@ type ConfigSite string const ( // ConfigStieLocal - ConfigStieLocal ConfigSite = "local" + ConfigSiteLocal ConfigSite = "local" // ConfgiSiteGlobal ConfgiSiteGlobal ConfigSite = "global" ) @@ -86,10 +86,10 @@ func AddConfig(entity *RepoEntity, options ConfigOptions, value string) (err err } -// addConfigWithGit is simply a bare git commit -m command which is flexible +// addConfigWithGit is simply a bare git config --add