mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-27 12:32:37 +02:00
update
This commit is contained in:
parent
f465a5fe48
commit
7d70ed5be1
@ -56,8 +56,10 @@ func mergeGitStatusFiles(oldGitFiles, newGitFiles []GitFile) []GitFile {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGitBranchOutput() (string, error) {
|
func runDirectCommand(command string) (string, error) {
|
||||||
cmdOut, err := exec.Command("bash", "-c", getBranchesCommand).Output()
|
cmdOut, err := exec.Command("bash", "-c", command).Output()
|
||||||
|
devLog(string(cmdOut))
|
||||||
|
devLog(fmt.Sprint(err))
|
||||||
return string(cmdOut), err
|
return string(cmdOut), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ func branchNameFromString(branchString string) string {
|
|||||||
|
|
||||||
func getGitBranches() []Branch {
|
func getGitBranches() []Branch {
|
||||||
branches := make([]Branch, 0)
|
branches := make([]Branch, 0)
|
||||||
rawString, _ := getGitBranchOutput()
|
rawString, _ := runDirectCommand(getBranchesCommand)
|
||||||
branchLines := splitLines(rawString)
|
branchLines := splitLines(rawString)
|
||||||
for _, line := range branchLines {
|
for _, line := range branchLines {
|
||||||
name := branchNameFromString(line)
|
name := branchNameFromString(line)
|
||||||
@ -136,7 +138,7 @@ func runCommand(cmd string) (string, error) {
|
|||||||
splitCmd := strings.Split(cmd, " ")
|
splitCmd := strings.Split(cmd, " ")
|
||||||
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).Output()
|
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).Output()
|
||||||
devLog(cmd)
|
devLog(cmd)
|
||||||
devLog(string(cmdOut))
|
devLog(string(cmdOut[:]))
|
||||||
return string(cmdOut), err
|
return string(cmdOut), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +152,14 @@ func getDiff(file GitFile) string {
|
|||||||
cachedArg = "--cached "
|
cachedArg = "--cached "
|
||||||
}
|
}
|
||||||
deletedArg := ""
|
deletedArg := ""
|
||||||
if file.Deleted || !file.Tracked {
|
if file.Deleted {
|
||||||
deletedArg = "--no-index /dev/null "
|
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)
|
s, err := runCommand(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// for now we assume an error means the file was deleted
|
// 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")
|
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
|
const getBranchesCommand = `set -e
|
||||||
git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD | {
|
git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD | {
|
||||||
seen=":"
|
seen=":"
|
||||||
|
184
gui.go
184
gui.go
@ -28,6 +28,16 @@ var state = stateType{GitFiles: make([]GitFile, 0)}
|
|||||||
|
|
||||||
var cyclableViews = []string{"files", "branches"}
|
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 {
|
func nextView(g *gocui.Gui, v *gocui.View) error {
|
||||||
var focusedViewName string
|
var focusedViewName string
|
||||||
if v == nil || v.Name() == cyclableViews[len(cyclableViews)-1] {
|
if v == nil || v.Name() == cyclableViews[len(cyclableViews)-1] {
|
||||||
@ -48,21 +58,27 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panic(err)
|
panic(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v != nil {
|
return switchFocus(g, v, focusedView)
|
||||||
v.Highlight = false
|
}
|
||||||
|
|
||||||
|
func switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
||||||
|
if oldView != nil {
|
||||||
|
oldView.Highlight = false
|
||||||
}
|
}
|
||||||
focusedView.Highlight = true
|
newView.Highlight = true
|
||||||
devLog(focusedViewName)
|
devLog(newView.Name())
|
||||||
_, err = g.SetCurrentView(focusedViewName)
|
_, err := g.SetCurrentView(newView.Name()) // not mega proud of the delayed
|
||||||
itemSelected(g, focusedView)
|
// return of err
|
||||||
showViewOptions(g, focusedViewName)
|
itemSelected(g, newView)
|
||||||
|
showViewOptions(g, newView.Name())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func showViewOptions(g *gocui.Gui, viewName string) error {
|
func showViewOptions(g *gocui.Gui, viewName string) error {
|
||||||
optionsMap := map[string]string{
|
optionsMap := map[string]string{
|
||||||
"files": "space: toggle staged, c: commit changes",
|
"files": "space: toggle staged, c: commit changes, shift+d: remove",
|
||||||
"branches": "space: checkout",
|
"branches": "space: checkout",
|
||||||
|
"prompt": "esc: cancel, enter: commit",
|
||||||
}
|
}
|
||||||
g.Update(func(*gocui.Gui) error {
|
g.Update(func(*gocui.Gui) error {
|
||||||
v, err := g.View("options")
|
v, err := g.View("options")
|
||||||
@ -133,6 +149,8 @@ func itemSelected(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return handleFileSelect(g, v)
|
return handleFileSelect(g, v)
|
||||||
case "branches":
|
case "branches":
|
||||||
return handleBranchSelect(g, v)
|
return handleBranchSelect(g, v)
|
||||||
|
case "prompt":
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
panic("No view matching itemSelected switch statement")
|
panic("No view matching itemSelected switch statement")
|
||||||
}
|
}
|
||||||
@ -166,7 +184,7 @@ func devLog(s string) {
|
|||||||
func handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
func handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
branch := getSelectedBranch(v)
|
branch := getSelectedBranch(v)
|
||||||
if err := gitCheckout(branch.Name, false); err != nil {
|
if err := gitCheckout(branch.Name, false); err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
refreshBranches(v)
|
refreshBranches(v)
|
||||||
refreshFiles(g)
|
refreshFiles(g)
|
||||||
@ -192,8 +210,67 @@ func handleFilePress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
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 {
|
func getSelectedFile(v *gocui.View) GitFile {
|
||||||
lineNumber := getItemPosition(v)
|
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]
|
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 {
|
func handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
item := getSelectedFile(v)
|
item := getSelectedFile(v)
|
||||||
diff := getDiff(item)
|
diff := getDiff(item)
|
||||||
if err := renderString(g, diff); err != nil {
|
return renderString(g, diff)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func delMsg(g *gocui.Gui, v *gocui.View) error {
|
func closePrompt(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err := g.DeleteView("msg"); err != nil {
|
filesView, _ := g.View("files")
|
||||||
|
switchFocus(g, v, filesView)
|
||||||
|
devLog("test prompt close")
|
||||||
|
if err := g.DeleteView("prompt"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := g.SetCurrentView("files"); err != nil {
|
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 {
|
func keybindings(g *gocui.Gui) error {
|
||||||
for _, view := range cyclableViews {
|
if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyTab, gocui.ModNone, nextView); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", 'q', gocui.ModNone, quit); err != nil {
|
||||||
if err := g.SetKeybinding(view, 'q', gocui.ModNone, quit); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, scrollUp); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyPgup, gocui.ModNone, scrollUp); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := g.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, scrollDown); err != nil {
|
||||||
if err := g.SetKeybinding(view, gocui.KeyPgdn, gocui.ModNone, scrollDown); err != nil {
|
return err
|
||||||
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 {
|
if err := g.SetKeybinding("files", gocui.KeySpace, gocui.ModNone, handleFilePress); err != nil {
|
||||||
return err
|
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 {
|
if err := g.SetKeybinding("branches", gocui.KeySpace, gocui.ModNone, handleBranchPress); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -317,6 +393,17 @@ func refreshBranches(v *gocui.View) error {
|
|||||||
return nil
|
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 {
|
func refreshFiles(g *gocui.Gui) error {
|
||||||
filesView, err := g.View("files")
|
filesView, err := g.View("files")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -343,6 +430,7 @@ func refreshFiles(g *gocui.Gui) error {
|
|||||||
green.Fprintln(filesView, gitFile.Name)
|
green.Fprintln(filesView, gitFile.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
correctCursor(filesView)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user