better handling of merge conflicts

This commit is contained in:
Jesse Duffield 2018-07-21 17:48:27 +10:00
parent a47c889cbb
commit fab194e923
4 changed files with 248 additions and 187 deletions

View file

@ -29,12 +29,24 @@ func stagedFiles(files []GitFile) []GitFile {
return result return result
} }
func stageSelectedFile(g *gocui.Gui) error {
file, err := getSelectedFile(g)
if err != nil {
return err
}
return stageFile(file.Name)
}
func handleFilePress(g *gocui.Gui, v *gocui.View) error { func handleFilePress(g *gocui.Gui, v *gocui.View) error {
file, err := getSelectedFile(g) file, err := getSelectedFile(g)
if err != nil { if err != nil {
return err return err
} }
if file.HasMergeConflicts {
return handleSwitchToMerge(g, v)
}
if file.HasUnstagedChanges { if file.HasUnstagedChanges {
stageFile(file.Name) stageFile(file.Name)
} else { } else {
@ -291,7 +303,7 @@ func handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
if !file.HasMergeConflicts { if !file.HasMergeConflicts {
return nil return createErrorPanel(g, "This file has no merge conflicts")
} }
switchFocus(g, v, mergeView) switchFocus(g, v, mergeView)
return refreshMergePanel(g) return refreshMergePanel(g)

5
gui.go
View file

@ -135,7 +135,10 @@ func keybindings(g *gocui.Gui) error {
if err := g.SetKeybinding("main", gocui.KeyArrowDown, gocui.ModNone, handleSelectBottom); err != nil { if err := g.SetKeybinding("main", gocui.KeyArrowDown, gocui.ModNone, handleSelectBottom); err != nil {
return err return err
} }
if err := g.SetKeybinding("main", gocui.KeySpace, gocui.ModNone, handlePickConflict); err != nil { if err := g.SetKeybinding("main", gocui.KeySpace, gocui.ModNone, handlePickHunk); err != nil {
return err
}
if err := g.SetKeybinding("main", 'b', gocui.ModNone, handlePickBothHunks); err != nil {
return err return err
} }
if err := g.SetKeybinding("main", gocui.KeyArrowLeft, gocui.ModNone, handleSelectPrevConflict); err != nil { if err := g.SetKeybinding("main", gocui.KeyArrowLeft, gocui.ModNone, handleSelectPrevConflict); err != nil {

View file

@ -6,6 +6,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"strings" "strings"
@ -86,16 +87,21 @@ func handleSelectPrevConflict(g *gocui.Gui, v *gocui.View) error {
return refreshMergePanel(g) return refreshMergePanel(g)
} }
func isIndexToDelete(i int, conflict conflict, top bool) bool { func isIndexToDelete(i int, conflict conflict, pick string) bool {
return i == conflict.middle || return i == conflict.middle ||
i == conflict.start || i == conflict.start ||
i == conflict.end || i == conflict.end ||
(!top && i > conflict.start && i < conflict.middle) || pick != "both" &&
(top && i > conflict.middle && i < conflict.end) (pick == "bottom" && i > conflict.start && i < conflict.middle) ||
(pick == "top" && i > conflict.middle && i < conflict.end)
} }
func resolveConflict(filename string, conflict conflict, top bool) error { func resolveConflict(g *gocui.Gui, conflict conflict, pick string) error {
file, err := os.Open(filename) gitFile, err := getSelectedFile(g)
if err != nil {
return err
}
file, err := os.Open(gitFile.Name)
if err != nil { if err != nil {
return err return err
} }
@ -108,16 +114,20 @@ func resolveConflict(filename string, conflict conflict, top bool) error {
if err != nil { if err != nil {
break break
} }
if !isIndexToDelete(i, conflict, top) { if !isIndexToDelete(i, conflict, pick) {
output += line output += line
} }
} }
devLog(output) devLog(output)
return ioutil.WriteFile(filename, []byte(output), 0644) return ioutil.WriteFile(gitFile.Name, []byte(output), 0644)
} }
func pushFileSnapshot(filename string) error { func pushFileSnapshot(g *gocui.Gui) error {
content, err := catFile(filename) gitFile, err := getSelectedFile(g)
if err != nil {
return err
}
content, err := catFile(gitFile.Name)
if err != nil { if err != nil {
return err return err
} }
@ -139,14 +149,25 @@ func handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
return refreshMergePanel(g) return refreshMergePanel(g)
} }
func handlePickConflict(g *gocui.Gui, v *gocui.View) error { func handlePickHunk(g *gocui.Gui, v *gocui.View) error {
conflict := state.Conflicts[state.ConflictIndex] conflict := state.Conflicts[state.ConflictIndex]
gitFile, err := getSelectedFile(g) pushFileSnapshot(g)
if err != nil { pick := "bottom"
return err if state.ConflictTop {
pick = "top"
} }
pushFileSnapshot(gitFile.Name) err := resolveConflict(g, conflict, pick)
err = resolveConflict(gitFile.Name, conflict, state.ConflictTop) if err != nil {
panic(err)
}
refreshMergePanel(g)
return nil
}
func handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
conflict := state.Conflicts[state.ConflictIndex]
pushFileSnapshot(g)
err := resolveConflict(g, conflict, "both")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -169,7 +190,7 @@ func refreshMergePanel(g *gocui.Gui) error {
} }
if len(state.Conflicts) == 0 { if len(state.Conflicts) == 0 {
state.ConflictIndex = 0 return handleCompleteMerge(g)
} else if state.ConflictIndex > len(state.Conflicts)-1 { } else if state.ConflictIndex > len(state.Conflicts)-1 {
state.ConflictIndex = len(state.Conflicts) - 1 state.ConflictIndex = len(state.Conflicts) - 1
} }
@ -196,9 +217,11 @@ func scrollToConflict(g *gocui.Gui) error {
return nil return nil
} }
conflict := state.Conflicts[state.ConflictIndex] conflict := state.Conflicts[state.ConflictIndex]
ox, oy := mainView.Origin() ox, _ := mainView.Origin()
devLog(oy, conflict.start) _, height := mainView.Size()
return mainView.SetOrigin(ox, conflict.start) conflictMiddle := (conflict.end + conflict.start) / 2
newOriginY := int(math.Max(0, float64(conflictMiddle-(height/2))))
return mainView.SetOrigin(ox, newOriginY)
} }
func switchToMerging(g *gocui.Gui) error { func switchToMerging(g *gocui.Gui) error {
@ -216,6 +239,7 @@ func renderMergeOptions(g *gocui.Gui) error {
"up/down": "pick hunk", "up/down": "pick hunk",
"left/right": "previous/next commit", "left/right": "previous/next commit",
"space": "pick hunk", "space": "pick hunk",
"b": "pick both hunks",
"z": "undo", "z": "undo",
}) })
} }
@ -228,3 +252,13 @@ func handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
refreshFiles(g) refreshFiles(g)
return switchFocus(g, v, filesView) return switchFocus(g, v, filesView)
} }
func handleCompleteMerge(g *gocui.Gui) error {
filesView, err := g.View("files")
if err != nil {
return err
}
stageSelectedFile(g)
refreshFiles(g)
return switchFocus(g, nil, filesView)
}

View file

@ -18,19 +18,31 @@ cd ${reponame}
git init git init
function add_spacing {
for i in {1..60}
do
echo "..." >> $1
done
}
echo "Here is a story that has been told throuhg the ages" >> file1 echo "Here is a story that has been told throuhg the ages" >> file1
git add file1 git add file1
git commit -m "first commit" git commit -m "first commit"
git checkout -b develop git checkout -b develop
echo "once upon a time there was a dog" >> file1 echo "once upon a time there was a dog" >> file1
add_spacing file1
echo "once upon a time there was another dog" >> file1
git add file1 git add file1
git commit -m "first commit on develop" git commit -m "first commit on develop"
git checkout master git checkout master
echo "once upon a time there was a cat" >> file1 echo "once upon a time there was a cat" >> file1
add_spacing file1
echo "once upon a time there was another cat" >> file1
git add file1 git add file1
git commit -m "first commit on develop" git commit -m "first commit on develop"