1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-11-24 08:52:21 +02:00
This commit is contained in:
Jesse Duffield 2018-05-21 22:34:02 +10:00
parent f465a5fe48
commit 7d70ed5be1
3 changed files with 166 additions and 57 deletions

View File

@ -56,8 +56,10 @@ func mergeGitStatusFiles(oldGitFiles, newGitFiles []GitFile) []GitFile {
return result
}
func getGitBranchOutput() (string, error) {
cmdOut, err := exec.Command("bash", "-c", getBranchesCommand).Output()
func runDirectCommand(command string) (string, error) {
cmdOut, err := exec.Command("bash", "-c", command).Output()
devLog(string(cmdOut))
devLog(fmt.Sprint(err))
return string(cmdOut), err
}
@ -70,7 +72,7 @@ func branchNameFromString(branchString string) string {
func getGitBranches() []Branch {
branches := make([]Branch, 0)
rawString, _ := getGitBranchOutput()
rawString, _ := runDirectCommand(getBranchesCommand)
branchLines := splitLines(rawString)
for _, line := range branchLines {
name := branchNameFromString(line)
@ -136,7 +138,7 @@ func runCommand(cmd string) (string, error) {
splitCmd := strings.Split(cmd, " ")
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).Output()
devLog(cmd)
devLog(string(cmdOut))
devLog(string(cmdOut[:]))
return string(cmdOut), err
}
@ -150,10 +152,14 @@ func getDiff(file GitFile) string {
cachedArg = "--cached "
}
deletedArg := ""
if file.Deleted || !file.Tracked {
deletedArg = "--no-index /dev/null "
if file.Deleted {
deletedArg = "-- "
}
command := "git diff --color " + cachedArg + deletedArg + file.Name
trackedArg := ""
if !file.Tracked {
trackedArg = "--no-index /dev/null "
}
command := "git diff --color " + cachedArg + deletedArg + trackedArg + file.Name
s, err := runCommand(command)
if err != nil {
// for now we assume an error means the file was deleted
@ -176,6 +182,22 @@ func getGitStatus() (string, error) {
return runCommand("git status --untracked-files=all --short")
}
func removeFile(file GitFile) error {
// if the file isn't tracked, we assume you want to delete it
if !file.Tracked {
_, err := runCommand("rm -rf ./" + file.Name)
return err
}
// if the file is tracked, we assume you want to just check it out
_, err := runCommand("git checkout " + file.Name)
return err
}
func gitCommit(message string) error {
_, err := runDirectCommand("git commit -m \"" + message + "\"")
return err
}
const getBranchesCommand = `set -e
git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD | {
seen=":"

184
gui.go
View File

@ -28,6 +28,16 @@ var state = stateType{GitFiles: make([]GitFile, 0)}
var cyclableViews = []string{"files", "branches"}
func stagedFiles(files []GitFile) []GitFile {
result := make([]GitFile, 0)
for _, file := range files {
if file.HasStagedChanges {
result = append(result, file)
}
}
return result
}
func nextView(g *gocui.Gui, v *gocui.View) error {
var focusedViewName string
if v == nil || v.Name() == cyclableViews[len(cyclableViews)-1] {
@ -48,21 +58,27 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
panic(err)
return err
}
if v != nil {
v.Highlight = false
return switchFocus(g, v, focusedView)
}
func switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
if oldView != nil {
oldView.Highlight = false
}
focusedView.Highlight = true
devLog(focusedViewName)
_, err = g.SetCurrentView(focusedViewName)
itemSelected(g, focusedView)
showViewOptions(g, focusedViewName)
newView.Highlight = true
devLog(newView.Name())
_, err := g.SetCurrentView(newView.Name()) // not mega proud of the delayed
// return of err
itemSelected(g, newView)
showViewOptions(g, newView.Name())
return err
}
func showViewOptions(g *gocui.Gui, viewName string) error {
optionsMap := map[string]string{
"files": "space: toggle staged, c: commit changes",
"files": "space: toggle staged, c: commit changes, shift+d: remove",
"branches": "space: checkout",
"prompt": "esc: cancel, enter: commit",
}
g.Update(func(*gocui.Gui) error {
v, err := g.View("options")
@ -133,6 +149,8 @@ func itemSelected(g *gocui.Gui, v *gocui.View) error {
return handleFileSelect(g, v)
case "branches":
return handleBranchSelect(g, v)
case "prompt":
return nil
default:
panic("No view matching itemSelected switch statement")
}
@ -166,7 +184,7 @@ func devLog(s string) {
func handleBranchPress(g *gocui.Gui, v *gocui.View) error {
branch := getSelectedBranch(v)
if err := gitCheckout(branch.Name, false); err != nil {
return err
panic(err)
}
refreshBranches(v)
refreshFiles(g)
@ -192,8 +210,67 @@ func handleFilePress(g *gocui.Gui, v *gocui.View) error {
return nil
}
func handleCommitPrompt(g *gocui.Gui, currentView *gocui.View) error {
devLog(fmt.Sprint(stagedFiles(state.GitFiles)))
if len(stagedFiles(state.GitFiles)) == 0 {
return nil
}
maxX, maxY := g.Size()
// var v *gocui.View
if v, err := g.SetView("prompt", maxX/2-30, maxY/2-1, maxX/2+30, maxY/2+1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Commit Message: "
v.Editable = true
v.Highlight = true
v.Autoscroll = true
v.Wrap = true
v.Overwrite = true
v.Caret = true
// fmt.Fprintln(v, "commit message: ")
if _, err := g.SetCurrentView("prompt"); err != nil {
return err
}
switchFocus(g, currentView, v)
}
return nil
}
func handleCommitSubmit(g *gocui.Gui, v *gocui.View) error {
if len(v.BufferLines()) == 0 {
return closePrompt(g, v)
}
message := fmt.Sprint(v.BufferLines()[0])
// for whatever reason, a successful commit returns an error, so we're not
// going to check for an error here
if err := gitCommit(message); err != nil {
devLog(fmt.Sprint(err))
panic(err)
}
refreshFiles(g)
return closePrompt(g, v)
}
func handleFileRemove(g *gocui.Gui, v *gocui.View) error {
file := getSelectedFile(v)
removeFile(file)
refreshFiles(g)
return nil
}
func getSelectedFile(v *gocui.View) GitFile {
lineNumber := getItemPosition(v)
if len(state.GitFiles) == 0 {
return GitFile{
Name: "noFile",
DisplayString: "none",
HasStagedChanges: false,
HasUnstagedChanges: false,
Tracked: false,
Deleted: false,
}
}
return state.GitFiles[lineNumber]
}
@ -215,25 +292,14 @@ func handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
func handleFileSelect(g *gocui.Gui, v *gocui.View) error {
item := getSelectedFile(v)
diff := getDiff(item)
if err := renderString(g, diff); err != nil {
return err
}
// maxX, maxY := g.Size()
// if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
// if err != gocui.ErrUnknownView {
// return errkjhgkhj
// }
// fmt.Fprintln(v, l)
// if _, err := g.SetCurrentView("msg"); err != nil {
// return err
// }
// }
return nil
return renderString(g, diff)
}
func delMsg(g *gocui.Gui, v *gocui.View) error {
if err := g.DeleteView("msg"); err != nil {
func closePrompt(g *gocui.Gui, v *gocui.View) error {
filesView, _ := g.View("files")
switchFocus(g, v, filesView)
devLog("test prompt close")
if err := g.DeleteView("prompt"); err != nil {
return err
}
if _, err := g.SetCurrentView("files"); err != nil {
@ -247,32 +313,42 @@ func quit(g *gocui.Gui, v *gocui.View) error {
}
func keybindings(g *gocui.Gui) error {
for _, view := range cyclableViews {
if err := g.SetKeybinding(view, gocui.KeyTab, gocui.ModNone, nextView); err != nil {
return err
}
if err := g.SetKeybinding(view, 'q', gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding(view, gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding(view, gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
return err
}
if err := g.SetKeybinding(view, gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
return err
}
if err := g.SetKeybinding(view, gocui.KeyPgup, gocui.ModNone, scrollUp); err != nil {
return err
}
if err := g.SetKeybinding(view, gocui.KeyPgdn, gocui.ModNone, scrollDown); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
return err
}
if err := g.SetKeybinding("", 'q', gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, scrollUp); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, scrollDown); err != nil {
return err
}
if err := g.SetKeybinding("", 'C', gocui.ModNone, handleCommitPrompt); err != nil {
return err
}
if err := g.SetKeybinding("files", gocui.KeySpace, gocui.ModNone, handleFilePress); err != nil {
return err
}
if err := g.SetKeybinding("files", 'D', gocui.ModNone, handleFileRemove); err != nil {
return err
}
if err := g.SetKeybinding("prompt", gocui.KeyEsc, gocui.ModNone, closePrompt); err != nil {
return err
}
if err := g.SetKeybinding("prompt", gocui.KeyEnter, gocui.ModNone, handleCommitSubmit); err != nil {
return err
}
if err := g.SetKeybinding("branches", gocui.KeySpace, gocui.ModNone, handleBranchPress); err != nil {
return err
}
@ -317,6 +393,17 @@ func refreshBranches(v *gocui.View) error {
return nil
}
// if the cursor down past the last item, move it up one
func correctCursor(v *gocui.View) error {
cx, cy := v.Cursor()
_, oy := v.Origin()
lineCount := len(v.BufferLines()) - 2
if cy >= lineCount-oy {
return v.SetCursor(cx, lineCount-oy)
}
return nil
}
func refreshFiles(g *gocui.Gui) error {
filesView, err := g.View("files")
if err != nil {
@ -343,6 +430,7 @@ func refreshFiles(g *gocui.Gui) error {
green.Fprintln(filesView, gitFile.Name)
}
}
correctCursor(filesView)
return nil
}

View File

@ -1,7 +1,6 @@
package main
func main() {
devLog("\n\n\n\n\n\n\n\n\n\n")
run()
}