1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-01 13:17:53 +02:00

Support git config merge.conflictStyle diff3

This commit is contained in:
Ryooooooga 2021-08-22 01:03:05 +09:00 committed by Jesse Duffield
parent aedeba4fe3
commit a0e7604f61
7 changed files with 140 additions and 68 deletions

View File

@ -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",

View File

@ -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:

View File

@ -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:

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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,
middle: 9,
end: 11,
start: 6,
ancestor: -1,
target: 9,
end: 11,
},
{
start: 13,
middle: 15,
end: 17,
start: 13,
ancestor: -1,
target: 15,
end: 17,
},
{
start: 19,
middle: 21,
end: 23,
start: 19,
ancestor: -1,
target: 21,
end: 23,
},
{
start: 25,
middle: 27,
end: 29,
start: 25,
ancestor: -1,
target: 27,
end: 29,
},
{
start: 31,
middle: 34,
end: 36,
start: 31,
ancestor: -1,
target: 34,
end: 36,
},
{
start: 38,
ancestor: 40,
target: 42,
end: 44,
},
},
},