summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyooooooga <eial5q265e5@gmail.com>2021-08-22 01:03:05 +0900
committerJesse Duffield <jessedduffield@gmail.com>2021-08-25 22:23:55 +1000
commita0e7604f61f19f829f76966fb3f97cab09307ab1 (patch)
tree36bde2f20620974664d4acee44a900bcf032b129
parentaedeba4fe3bb38a7467dccc2cbe461d8d014dca0 (diff)
Support `git config merge.conflictStyle diff3`
-rw-r--r--pkg/gui/keybindings.go12
-rw-r--r--pkg/gui/merge_panel.go10
-rw-r--r--pkg/gui/mergeconflicts/find_conflicts.go17
-rw-r--r--pkg/gui/mergeconflicts/find_conflicts_test.go6
-rw-r--r--pkg/gui/mergeconflicts/rendering.go21
-rw-r--r--pkg/gui/mergeconflicts/state.go86
-rw-r--r--pkg/gui/mergeconflicts/state_test.go56
7 files changed, 140 insertions, 68 deletions
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 4d593dbd0..a74f3c68c 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -1485,14 +1485,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main",
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gui.getKey(config.Universal.PrevItem),
- Handler: gui.handleSelectTop,
+ Handler: gui.handleSelectPrevConflictHunk,
Description: gui.Tr.SelectTop,
},
{
ViewName: "main",
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gui.getKey(config.Universal.NextItem),
- Handler: gui.handleSelectBottom,
+ Handler: gui.handleSelectNextConflictHunk,
Description: gui.Tr.SelectBottom,
},
{
@@ -1500,14 +1500,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone,
- Handler: gui.handleSelectTop,
+ Handler: gui.handleSelectPrevConflictHunk,
},
{
ViewName: "main",
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone,
- Handler: gui.handleSelectBottom,
+ Handler: gui.handleSelectNextConflictHunk,
},
{
ViewName: "main",
@@ -1528,14 +1528,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gui.getKey(config.Universal.PrevItemAlt),
Modifier: gocui.ModNone,
- Handler: gui.handleSelectTop,
+ Handler: gui.handleSelectPrevConflictHunk,
},
{
ViewName: "main",
Contexts: []string{string(MAIN_MERGING_CONTEXT_KEY)},
Key: gui.getKey(config.Universal.NextItemAlt),
Modifier: gocui.ModNone,
- Handler: gui.handleSelectBottom,
+ Handler: gui.handleSelectNextConflictHunk,
},
{
ViewName: "main",
diff --git a/pkg/gui/merge_panel.go b/pkg/gui/merge_panel.go
index 51376ccf8..0206ba164 100644
--- a/pkg/gui/merge_panel.go
+++ b/pkg/gui/merge_panel.go
@@ -14,18 +14,18 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
)
-func (gui *Gui) handleSelectTop() error {
+func (gui *Gui) handleSelectPrevConflictHunk() error {
return gui.withMergeConflictLock(func() error {
gui.takeOverMergeConflictScrolling()
- gui.State.Panels.Merging.SelectTopOption()
+ gui.State.Panels.Merging.SelectPrevConflictHunk()
return gui.refreshMergePanel()
})
}
-func (gui *Gui) handleSelectBottom() error {
+func (gui *Gui) handleSelectNextConflictHunk() error {
return gui.withMergeConflictLock(func() error {
gui.takeOverMergeConflictScrolling()
- gui.State.Panels.Merging.SelectBottomOption()
+ gui.State.Panels.Merging.SelectNextConflictHunk()
return gui.refreshMergePanel()
})
}
@@ -135,6 +135,8 @@ func (gui *Gui) resolveConflict(selection mergeconflicts.Selection) (bool, error
switch selection {
case mergeconflicts.TOP:
logStr = "Picking top hunk"
+ case mergeconflicts.MIDDLE:
+ logStr = "Picking middle hunk"
case mergeconflicts.BOTTOM:
logStr = "Picking bottom hunk"
case mergeconflicts.BOTH:
diff --git a/pkg/gui/mergeconflicts/find_conflicts.go b/pkg/gui/mergeconflicts/find_conflicts.go
index dc6e99189..80e487f85 100644
--- a/pkg/gui/mergeconflicts/find_conflicts.go
+++ b/pkg/gui/mergeconflicts/find_conflicts.go
@@ -12,7 +12,8 @@ type LineType int
const (
START LineType = iota
- MIDDLE
+ ANCESTOR
+ TARGET
END
NOT_A_MARKER
)
@@ -28,10 +29,14 @@ func findConflicts(content string) []*mergeConflict {
for i, line := range utils.SplitLines(content) {
switch determineLineType(line) {
case START:
- newConflict = &mergeConflict{start: i}
- case MIDDLE:
+ newConflict = &mergeConflict{start: i, ancestor: -1}
+ case ANCESTOR:
if newConflict != nil {
- newConflict.middle = i
+ newConflict.ancestor = i
+ }
+ case TARGET:
+ if newConflict != nil {
+ newConflict.target = i
}
case END:
if newConflict != nil {
@@ -54,8 +59,10 @@ func determineLineType(line string) LineType {
switch {
case strings.HasPrefix(trimmedLine, "<<<<<<< "):
return START
+ case strings.HasPrefix(trimmedLine, "||||||| "):
+ return ANCESTOR
case trimmedLine == "=======":
- return MIDDLE
+ return TARGET
case strings.HasPrefix(trimmedLine, ">>>>>>> "):
return END
default:
diff --git a/pkg/gui/mergeconflicts/find_conflicts_test.go b/pkg/gui/mergeconflicts/find_conflicts_test.go
index c0a8e3e5b..bcc70ce73 100644
--- a/pkg/gui/mergeconflicts/find_conflicts_test.go
+++ b/pkg/gui/mergeconflicts/find_conflicts_test.go
@@ -43,12 +43,16 @@ func TestDetermineLineType(t *testing.T) {
},
{
line: "=======",
- expected: MIDDLE,
+ expected: TARGET,
},
{
line: ">>>>>>> blah",
expected: END,
},
+ {
+ line: "||||||| adf33b9",
+ expected: ANCESTOR,
+ },
}
for _, s := range scenarios {
diff --git a/pkg/gui/mergeconflicts/rendering.go b/pkg/gui/mergeconflicts/rendering.go
index 45fff634a..8255b6ce9 100644
--- a/pkg/gui/mergeconflicts/rendering.go
+++ b/pkg/gui/mergeconflicts/rendering.go
@@ -16,11 +16,11 @@ func ColoredConflictFile(content string, state *State, hasFocus bool) string {
var outputBuffer bytes.Buffer
for i, line := range utils.SplitLines(content) {
textStyle := theme.DefaultTextColor
- if i == conflict.start || i == conflict.middle || i == conflict.end {
+ if i == conflict.start || i == conflict.ancestor || i == conflict.target || i == conflict.end {
textStyle = style.FgRed
}
- if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
+ if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictSelection) {
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor).SetBold()
}
if i == conflict.end && len(remainingConflicts) > 0 {
@@ -35,6 +35,19 @@ func shiftConflict(conflicts []*mergeConflict) (*mergeConflict, []*mergeConflict
return conflicts[0], conflicts[1:]
}
-func shouldHighlightLine(index int, conflict *mergeConflict, top bool) bool {
- return (index >= conflict.start && index <= conflict.middle && top) || (index >= conflict.middle && index <= conflict.end && !top)
+func shouldHighlightLine(index int, conflict *mergeConflict, selection Selection) bool {
+ switch selection {
+ case TOP:
+ if conflict.ancestor >= 0 {
+ return index >= conflict.start && index <= conflict.ancestor
+ } else {
+ return index >= conflict.start && index <= conflict.target
+ }
+ case MIDDLE:
+ return index >= conflict.ancestor && index <= conflict.target
+ case BOTTOM:
+ return index >= conflict.target && index <= conflict.end
+ default:
+ return false
+ }
}
diff --git a/pkg/gui/mergeconflicts/state.go b/pkg/gui/mergeconflicts/state.go
index 9d8320ffb..343f282ad 100644
--- a/pkg/gui/mergeconflicts/state.go
+++ b/pkg/gui/mergeconflicts/state.go
@@ -11,42 +11,62 @@ type Selection int
const (
TOP Selection = iota
+ MIDDLE
BOTTOM
BOTH
)
-// mergeConflict : A git conflict with a start middle and end corresponding to line
+// mergeConflict : A git conflict with a start, ancestor (if exists), target, and end corresponding to line
// numbers in the file where the conflict markers appear
type mergeConflict struct {
- start int
- middle int
- end int
+ start int
+ ancestor int
+ target int
+ end int
}
type State struct {
sync.Mutex
- conflictIndex int
- conflictTop bool
- conflicts []*mergeConflict
- EditHistory *stack.Stack
+ conflictIndex int
+ conflictSelection Selection
+ conflicts []*mergeConflict
+ EditHistory *stack.Stack
}
func NewState() *State {
return &State{
- Mutex: sync.Mutex{},
- conflictIndex: 0,
- conflictTop: true,
- conflicts: []*mergeConflict{},
- EditHistory: stack.New(),
+ Mutex: sync.Mutex{},
+ conflictIndex: 0,
+ conflictSelection: TOP,
+ conflicts: []*mergeConflict{},
+ EditHistory: stack.New(),
}
}
-func (s *State) SelectTopOption() {
- s.conflictTop = true
+func (s *State) SelectPrevConflictHunk() {
+ switch s.conflictSelection {
+ case MIDDLE:
+ s.conflictSelection = TOP
+ case BOTTOM:
+ if s.currentConflict().ancestor >= 0 {
+ s.conflictSelection = MIDDLE
+ } else {
+ s.conflictSelection = TOP
+ }
+ }
}
-func (s *State) SelectBottomOption() {
- s.conflictTop = false
+func (s *State) SelectNextConflictHunk() {
+ switch s.conflictSelection {
+ case TOP:
+ if s.currentConflict().ancestor >= 0 {
+ s.conflictSelection = MIDDLE
+ } else {
+ s.conflictSelection = BOTTOM
+ }
+ case MIDDLE:
+ s.conflictSelection = BOTTOM
+ }
}
func (s *State) SelectNextConflict() {
@@ -100,11 +120,7 @@ func (s *State) NoConflicts() bool {
}
func (s *State) Selection() Selection {
- if s.conflictTop {
- return TOP
- } else {
- return BOTTOM
- }
+ return s.conflictSelection
}
func (s *State) IsFinalConflict() bool {
@@ -116,7 +132,7 @@ func (s *State) Reset() {
}
func (s *State) GetConflictMiddle() int {
- return s.currentConflict().middle
+ return s.currentConflict().target
}
func (s *State) ContentAfterConflictResolve(path string, selection Selection) (bool, string, error) {
@@ -141,13 +157,23 @@ func (s *State) ContentAfterConflictResolve(path string, selection Selection) (b
func isIndexToDelete(i int, conflict *mergeConflict, selection Selection) bool {
isMarkerLine :=
- i == conflict.middle ||
- i == conflict.start ||
+ i == conflict.start ||
+ i == conflict.ancestor ||
+ i == conflict.target ||
i == conflict.end
- isUnwantedContent :=
- (selection == BOTTOM && conflict.start < i && i < conflict.middle) ||
- (selection == TOP && conflict.middle < i && i < conflict.end)
-
- return isMarkerLine || isUnwantedContent
+ var isWantedContent bool
+ switch selection {
+ case TOP:
+ if conflict.ancestor >= 0 {
+ isWantedContent = conflict.start < i && i < conflict.ancestor
+ } else {
+ isWantedContent = conflict.start < i && i < conflict.target
+ }
+ case MIDDLE:
+ isWantedContent = conflict.ancestor < i && i < conflict.target
+ case BOTTOM:
+ isWantedContent = conflict.target < i && i < conflict.end
+ }
+ return isMarkerLine || !isWantedContent
}
diff --git a/pkg/gui/mergeconflicts/state_test.go b/pkg/gui/mergeconflicts/state_test.go
index a71d05a7a..861833d32 100644
--- a/pkg/gui/mergeconflicts/state_test.go
+++ b/pkg/gui/mergeconflicts/state_test.go
@@ -58,37 +58,57 @@ bar
=======
baz
>>>>>>> branch
+
+<<<<<<< HEAD
+foo
+||||||| fffffff
+bar
+=======
+baz
+>>>>>>> branch
`,
expected: []*mergeConflict{
{
- start: 0,
- middle: 2,
- end: 4,
+ start: 0,
+ ancestor: -1,
+ target: 2,
+ end: 4,
+ },
+ {
+ start: 6,
+ ancestor: -1,
+ target: 9,
+ end: 11,
},
{
- start: 6,
- middle: 9,
- end: 11,
+ start: 13,
+ ancestor: -1,
+ target: 15,
+ end: 17,
},
{
- start: 13,
- middle: 15,
- end: 17,
+ start: 19,
+ ancestor: -1,
+ target: 21,
+ end: 23,
},
{
- start: 19,
- middle: 21,
- end: 23,
+ start: 25,
+ ancestor: -1,
+ target: 27,
+ end: 29,
},
{
- start: 25,
- middle: 27,
- end: 29,
+ start: 31,
+ ancestor: -1,
+ target: 34,
+ end: 36,
},
{
- start: 31,
- middle: 34,
- end: 36,
+ start: 38,
+ ancestor: 40,
+ target: 42,
+ end: 44,
},
},
},