diff options
| author | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2018-12-08 18:50:45 +0300 |
|---|---|---|
| committer | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2018-12-08 18:50:45 +0300 |
| commit | d95367a707ffd121e78a07f6a0d1d030fc4ac6b9 (patch) | |
| tree | 7869fcdffa7d781c2c90cae249c07935b9d62aa9 | |
| parent | Merge remote-tracking branch 'origin/master' into develop (diff) | |
| download | gitbatch-d95367a707ffd121e78a07f6a0d1d030fc4ac6b9.tar.gz | |
git add, git reset and git stash implementation added.
| -rw-r--r-- | pkg/git/add.go | 52 | ||||
| -rw-r--r-- | pkg/git/commands.go | 9 | ||||
| -rw-r--r-- | pkg/git/repository.go | 4 | ||||
| -rw-r--r-- | pkg/git/reset.go | 52 | ||||
| -rw-r--r-- | pkg/git/stash.go | 44 | ||||
| -rw-r--r-- | pkg/git/status.go | 1 | ||||
| -rw-r--r-- | pkg/gui/gui-util.go | 4 | ||||
| -rw-r--r-- | pkg/gui/keybindings.go | 88 | ||||
| -rw-r--r-- | pkg/gui/stagedview.go | 35 | ||||
| -rw-r--r-- | pkg/gui/stashview.go | 45 | ||||
| -rw-r--r-- | pkg/gui/statusview.go | 18 | ||||
| -rw-r--r-- | pkg/gui/unstagedview.go | 37 |
12 files changed, 356 insertions, 33 deletions
diff --git a/pkg/git/add.go b/pkg/git/add.go new file mode 100644 index 0000000..73c7d9e --- /dev/null +++ b/pkg/git/add.go @@ -0,0 +1,52 @@ +package git + +import ( + "errors" + "strings" + + log "github.com/sirupsen/logrus" +) + +var addCommand = "add" + +type AddOptions struct { + Update bool + Force bool + DryRun bool +} + +func (file *File) Add(option AddOptions) error { + args := make([]string, 0) + args = append(args, addCommand) + args = append(args, file.Name) + if option.Update { + args = append(args, "--update") + } + if option.Force { + args = append(args, "--force") + } + if option.DryRun { + args = append(args, "--dry-run") + } + out, err := GenericGitCommandWithOutput(strings.TrimSuffix(file.AbsPath, file.Name), args) + if err != nil { + log.Warn("Error while add command") + return errors.New(out + "\n" + err.Error()) + } + return nil +} + +func (entity *RepoEntity) AddAll(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) + if err != nil { + log.Warn("Error while add command") + return errors.New(out + "\n" + err.Error()) + } + return nil +}
\ No newline at end of file diff --git a/pkg/git/commands.go b/pkg/git/commands.go index 0a1820b..825c712 100644 --- a/pkg/git/commands.go +++ b/pkg/git/commands.go @@ -22,6 +22,15 @@ func GenericGitCommandWithOutput(repoPath string, args []string) (string, error) return helpers.TrimTrailingNewline(out), nil } +// GenericGitCommand runs any git command with returning output +func GenericGitCommandWithErrorOutput(repoPath string, args []string) (string, error) { + out, err := helpers.RunCommandWithOutput(repoPath, "git", args) + if err != nil { + return helpers.TrimTrailingNewline(out), err + } + return helpers.TrimTrailingNewline(out), nil +} + // GitShow is conventional git show command without any argument func GitShow(repoPath, hash string) string { args := []string{"show", hash} diff --git a/pkg/git/repository.go b/pkg/git/repository.go index 7d1fd73..0ec6eaf 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -122,6 +122,7 @@ func (entity *RepoEntity) Refresh() error { if err := entity.loadLocalBranches(); err != nil { return err } + entity.Branch.Clean = entity.isClean() entity.RefreshPushPull() if err := entity.loadCommits(); err != nil { return err @@ -129,5 +130,8 @@ func (entity *RepoEntity) Refresh() error { if err := entity.loadRemotes(); err != nil { return err } + if err := entity.loadStashedItems(); err != nil { + return err + } return nil } diff --git a/pkg/git/reset.go b/pkg/git/reset.go new file mode 100644 index 0000000..1a4c71d --- /dev/null +++ b/pkg/git/reset.go @@ -0,0 +1,52 @@ +package git + +import ( + "errors" + "strings" + + log "github.com/sirupsen/logrus" +) + +var resetCommand = "reset" + +type ResetOptions struct { + Hard bool + Merge bool + Keep bool +} + +func (file *File) Reset(option ResetOptions) error { + args := make([]string, 0) + args = append(args, resetCommand) + args = append(args, "--") + args = append(args, file.Name) + if option.Hard { + args = append(args, "--hard") + } + if option.Merge { + args = append(args, "--merge") + } + if option.Keep { + args = append(args, "--keep") + } + out, err := GenericGitCommandWithOutput(strings.TrimSuffix(file.AbsPath, file.Name), args) + if err != nil { + log.Warn("Error while add command") + return errors.New(out + "\n" + err.Error()) + } + return nil +} + +func (entity *RepoEntity) ResetAll(option ResetOptions) error { + args := make([]string, 0) + args = append(args, resetCommand) + if option.Hard { + args = append(args, "--hard") + } + out, err := GenericGitCommandWithOutput(entity.AbsPath, args) + if err != nil { + log.Warn("Error while add command") + return errors.New(out + "\n" + err.Error()) + } + return nil +}
\ No newline at end of file diff --git a/pkg/git/stash.go b/pkg/git/stash.go index caaaf90..6c647ae 100644 --- a/pkg/git/stash.go +++ b/pkg/git/stash.go @@ -15,30 +15,9 @@ type StashedItem struct { BranchName string Hash string Description string + EntityPath string } -// // StashOption is the option argument for git stash command -// type StashOption string - -// var ( -// StashPop StashOption = "pop" -// StashPush StashOption = "push" -// StashDrop StashOption = "drop" -// ) - -// // Stash used when you want to record the current state of the working -// // directory and the index, but want to go back to a clean working directory. -// func Stash(entity *RepoEntity, option StashOption) error { -// args := make([]string, 0) -// args = append(args, stashCommand) - -// if err := GenericGitCommand(entity.AbsPath, args); err != nil { -// log.Warn("Error while stashing") -// return err -// } -// return nil -// } - func stashGet(entity *RepoEntity, option string) string { args := make([]string, 0) args = append(args, stashCommand) @@ -88,17 +67,26 @@ func (entity *RepoEntity) loadStashedItems() error { BranchName: branchName, Hash: hash, Description: desc, + EntityPath: entity.AbsPath, }) } return nil } -func (entity *RepoEntity) Stash() (error) { +func (entity *RepoEntity) Stash() (output string, err error) { args := make([]string, 0) args = append(args, stashCommand) - if err := GenericGitCommand(entity.AbsPath, args); err != nil { - log.Warn("Error while stashing") - return err - } - return nil + + output, err = GenericGitCommandWithErrorOutput(entity.AbsPath, args) + entity.Refresh() + return output, err +} + +func (stashedItem *StashedItem) Pop() (output string, err error) { + args := make([]string, 0) + args = append(args, stashCommand) + args = append(args, "pop") + args = append(args, "stash@{"+strconv.Itoa(stashedItem.StashID)+"}") + output, err = GenericGitCommandWithErrorOutput(stashedItem.EntityPath, args) + return output, err }
\ No newline at end of file diff --git a/pkg/git/status.go b/pkg/git/status.go index e1207ee..51a622b 100644 --- a/pkg/git/status.go +++ b/pkg/git/status.go @@ -26,6 +26,7 @@ var ( StatusDeleted FileStatus = 'D' StatusRenamed FileStatus = 'R' StatusCopied FileStatus = 'C' + StatusUpdated FileStatus = 'U' StatusUntracked FileStatus = '?' StatusIgnored FileStatus = '!' ) diff --git a/pkg/gui/gui-util.go b/pkg/gui/gui-util.go index 2338bac..a0780bd 100644 --- a/pkg/gui/gui-util.go +++ b/pkg/gui/gui-util.go @@ -166,7 +166,9 @@ 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 diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 55c86bd..e808959 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -152,6 +152,14 @@ func (gui *Gui) generateKeybindings() error { Display: "j", Description: "Down", Vital: false, + }, { + View: view.Name, + Key: 't', + Modifier: gocui.ModNone, + Handler: gui.stashChanges, + Display: "t", + Description: "Save to Stash", + Vital: true, }, } for _, binding := range statusKeybindings { @@ -159,6 +167,52 @@ func (gui *Gui) generateKeybindings() error { } } individualKeybindings := []*KeyBinding{ + // stash view + { + View: stashViewFeature.Name, + Key: 'p', + Modifier: gocui.ModNone, + Handler: gui.popStash, + Display: "p", + Description: "Pop Item", + Vital: true, + }, + // staged view + { + View: stageViewFeature.Name, + Key: 'r', + Modifier: gocui.ModNone, + Handler: gui.resetChanges, + Display: "r", + Description: "Reset Item", + Vital: true, + }, { + View: stageViewFeature.Name, + Key: gocui.KeyCtrlR, + Modifier: gocui.ModNone, + Handler: gui.resetAllChanges, + Display: "ctrl+r", + Description: "Reset All Items", + Vital: true, + }, + // unstaged view + { + View: unstageViewFeature.Name, + Key: 'a', + Modifier: gocui.ModNone, + Handler: gui.addChanges, + Display: "a", + Description: "Add Item", + Vital: true, + }, { + View: unstageViewFeature.Name, + Key: gocui.KeyCtrlA, + Modifier: gocui.ModNone, + Handler: gui.addAllChanges, + Display: "ctrl+a", + Description: "Add All Items", + Vital: true, + }, // Main view controls { View: mainViewFeature.Name, @@ -502,7 +556,39 @@ func (gui *Gui) generateKeybindings() error { Display: "c", Description: "close/cancel", Vital: true, - }, + }, { + View: errorViewFeature.Name, + Key: gocui.KeyArrowUp, + Modifier: gocui.ModNone, + Handler: gui.fastCursorUp, + Display: "↑", + Description: "Up", + Vital: true, + }, { + View: errorViewFeature.Name, + Key: gocui.KeyArrowDown, + Modifier: gocui.ModNone, + Handler: gui.fastCursorDown, + Display: "↓", + Description: "Down", + Vital: true, + }, { + View: errorViewFeature.Name, + Key: 'k', + Modifier: gocui.ModNone, + Handler: gui.fastCursorUp, + Display: "k", + Description: "Up", + Vital: false, + }, { + View: errorViewFeature.Name, + Key: 'j', + Modifier: gocui.ModNone, + Handler: gui.fastCursorDown, + Display: "j", + Description: "Down", + Vital: false, + }, } for _, binding := range individualKeybindings { gui.KeyBindings = append(gui.KeyBindings, binding) diff --git a/pkg/gui/stagedview.go b/pkg/gui/stagedview.go index e3684f0..bae80bd 100644 --- a/pkg/gui/stagedview.go +++ b/pkg/gui/stagedview.go @@ -30,6 +30,41 @@ func (gui *Gui) openStageView(g *gocui.Gui) error { return nil } +func (gui *Gui) resetChanges(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + files, _, err := generateFileLists(entity) + if err != nil { + return err + } + if len(files) <= 0 { + return nil + } + _, cy := v.Cursor() + _, oy := v.Origin() + if err := files[cy+oy].Reset(git.ResetOptions{ + + }); err != nil { + return err + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + +func (gui *Gui) resetAllChanges(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + if err := entity.ResetAll(git.ResetOptions{ + + }); err != nil { + return err + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + // refresh the main view and re-render the repository representations func refreshStagedView(g *gocui.Gui, entity *git.RepoEntity) error { stageView, err := g.View(stageViewFeature.Name) diff --git a/pkg/gui/stashview.go b/pkg/gui/stashview.go index 8cdc46c..7322328 100644 --- a/pkg/gui/stashview.go +++ b/pkg/gui/stashview.go @@ -26,6 +26,49 @@ func (gui *Gui) openStashView(g *gocui.Gui) error { return nil } +// +func (gui *Gui) stashChanges(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + output, err := entity.Stash() + if err != nil { + if err = gui.openErrorView(g, output, + "You should manually resolve this issue", + stashViewFeature.Name); err != nil { + return err + } + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + +// +func (gui *Gui) popStash(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + _, oy := v.Origin() + _, cy := v.Cursor() + if len(entity.Stasheds) <= 0 { + return nil + } + stashedItem := entity.Stasheds[oy+cy] + output, err := stashedItem.Pop() + if err != nil { + if err = gui.openErrorView(g, output, + "You should manually resolve this issue", + stashViewFeature.Name); err != nil { + return err + } + } + if err := entity.Refresh(); err != nil { + return err + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + // refresh the main view and re-render the repository representations func refreshStashView(g *gocui.Gui, entity *git.RepoEntity) error { stashView, err := g.View(stashViewFeature.Name) @@ -41,7 +84,7 @@ func refreshStashView(g *gocui.Gui, entity *git.RepoEntity) error { if i == cy+oy { prefix = prefix + selectionIndicator } - fmt.Fprintf(stashView, "%s%d %s: %s (%s)\n", prefix, stashedItem.StashID, cyan.Sprint(stashedItem.BranchName), stashedItem.Description, stashedItem.Hash) + fmt.Fprintf(stashView, "%s%d %s: %s (%s)\n", prefix, stashedItem.StashID, cyan.Sprint(stashedItem.BranchName), stashedItem.Description, cyan.Sprint(stashedItem.Hash)) } return nil }
\ No newline at end of file diff --git a/pkg/gui/statusview.go b/pkg/gui/statusview.go index b1873e8..fbc90ee 100644 --- a/pkg/gui/statusview.go +++ b/pkg/gui/statusview.go @@ -119,6 +119,13 @@ func (gui *Gui) closeStatusView(g *gocui.Gui, v *gocui.View) error { if _, err := g.SetCurrentView(mainViewFeature.Name); err != nil { return err } + entity := gui.getSelectedRepository() + if err := gui.refreshMain(g); err != nil { + return err + } + if err := gui.refreshViews(g, entity); err != nil { + return err + } gui.updateKeyBindingsView(g, mainViewFeature.Name) return nil } @@ -129,7 +136,7 @@ func generateFileLists(entity *git.RepoEntity) (staged, unstaged []*git.File, er return nil, nil, err } for _, file := range files { - if file.X != git.StatusNotupdated && file.X != git.StatusUntracked && file.X != git.StatusIgnored { + if file.X != git.StatusNotupdated && file.X != git.StatusUntracked && file.X != git.StatusIgnored && file.X != git.StatusUpdated { staged = append(staged, file) } if file.Y != git.StatusNotupdated { @@ -156,3 +163,12 @@ func refreshStatusView(viewName string, g *gocui.Gui, entity *git.RepoEntity) er } return nil } + +func refreshAllStatusView(g *gocui.Gui, entity *git.RepoEntity) error { + for _, v := range statusViews { + if err := refreshStatusView(v.Name, g, entity); err != nil { + return err + } + } + return nil +} diff --git a/pkg/gui/unstagedview.go b/pkg/gui/unstagedview.go index 5ee25a7..ede4844 100644 --- a/pkg/gui/unstagedview.go +++ b/pkg/gui/unstagedview.go @@ -26,6 +26,41 @@ func (gui *Gui) openUnStagedView(g *gocui.Gui) error { return nil } +func (gui *Gui) addChanges(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + _, files, err := generateFileLists(entity) + if err != nil { + return err + } + if len(files) <= 0 { + return nil + } + _, cy := v.Cursor() + _, oy := v.Origin() + if err := files[cy+oy].Add(git.AddOptions{ + + }); err != nil { + return err + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + +func (gui *Gui) addAllChanges(g *gocui.Gui, v *gocui.View) error { + entity := gui.getSelectedRepository() + if err := entity.AddAll(git.AddOptions{ + + }); err != nil { + return err + } + if err := refreshAllStatusView(g, entity); err != nil { + return err + } + return nil +} + // refresh the main view and re-render the repository representations func refreshUnstagedView(g *gocui.Gui, entity *git.RepoEntity) error { stageView, err := g.View(unstageViewFeature.Name) @@ -44,7 +79,7 @@ func refreshUnstagedView(g *gocui.Gui, entity *git.RepoEntity) error { if i == cy+oy { prefix = prefix + selectionIndicator } - fmt.Fprintf(stageView, "%s%s%s %s\n", prefix, green.Sprint(string(file.X)), red.Sprint(string(file.Y)), file.Name) + fmt.Fprintf(stageView, "%s%s%s %s\n", prefix, red.Sprint(string(file.X)), red.Sprint(string(file.Y)), file.Name) } return nil }
\ No newline at end of file |
