Skip to content

Commit

Permalink
Fix merge conflict resolution when file doesn't end with a LF (jessed…
Browse files Browse the repository at this point in the history
…uffield#3976)

- **PR Description**

When resolving conflicts using lazygit's merge conflicts view in a file
that doesn't end with a trailing line feed, the last line would be lost.

Fixes jesseduffield#3444.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
  • Loading branch information
stefanhaller authored Oct 13, 2024
2 parents d11e11d + 696e78f commit 4e361e1
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
57 changes: 57 additions & 0 deletions pkg/integration/tests/conflicts/resolve_without_trailing_lf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package conflicts

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)

var ResolveWithoutTrailingLf = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Regression test for resolving a merge conflict when the file doesn't have a trailing newline",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
NewBranch("branch1").
CreateFileAndAdd("file", "a\n\nno eol").
Commit("initial commit").
UpdateFileAndAdd("file", "a1\n\nno eol").
Commit("commit on branch1").
NewBranchFrom("branch2", "HEAD^").
UpdateFileAndAdd("file", "a2\n\nno eol").
Commit("commit on branch2").
Checkout("branch1").
RunCommandExpectError([]string{"git", "merge", "--no-edit", "branch2"})
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Lines(
Contains("UU file").IsSelected(),
).
PressEnter()

t.Views().MergeConflicts().
IsFocused().
SelectedLines(
Contains("<<<<<<< HEAD"),
Contains("a1"),
Contains("======="),
).
SelectNextItem().
PressPrimaryAction()

t.ExpectPopup().Alert().
Title(Equals("Continue")).
Content(Contains("All merge conflicts resolved. Continue?")).
Cancel()

t.Views().Files().
Focus().
Lines(
Contains("M file").IsSelected(),
)

t.Views().Main().Content(Contains("-a1\n+a2\n").DoesNotContain("-no eol"))
},
})
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ var tests = []*components.IntegrationTest{
conflicts.ResolveExternally,
conflicts.ResolveMultipleFiles,
conflicts.ResolveNoAutoStage,
conflicts.ResolveWithoutTrailingLf,
conflicts.UndoChooseHunk,
custom_commands.AccessCommitProperties,
custom_commands.BasicCommand,
Expand Down
15 changes: 10 additions & 5 deletions pkg/utils/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"bufio"
"io"
"os"
)

Expand All @@ -12,14 +13,18 @@ func ForEachLineInFile(path string, f func(string, int)) error {
}
defer file.Close()

reader := bufio.NewReader(file)
forEachLineInStream(file, f)

return nil
}

func forEachLineInStream(reader io.Reader, f func(string, int)) {
bufferedReader := bufio.NewReader(reader)
for i := 0; true; i++ {
line, err := reader.ReadString('\n')
if err != nil {
line, _ := bufferedReader.ReadString('\n')
if len(line) == 0 {
break
}
f(line, i)
}

return nil
}
57 changes: 57 additions & 0 deletions pkg/utils/io_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package utils

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_forEachLineInStream(t *testing.T) {
scenarios := []struct {
name string
input string
expectedLines []string
}{
{
name: "empty input",
input: "",
expectedLines: []string{},
},
{
name: "single line",
input: "abc\n",
expectedLines: []string{"abc\n"},
},
{
name: "single line without line feed",
input: "abc",
expectedLines: []string{"abc"},
},
{
name: "multiple lines",
input: "abc\ndef\n",
expectedLines: []string{"abc\n", "def\n"},
},
{
name: "multiple lines including empty lines",
input: "abc\n\ndef\n",
expectedLines: []string{"abc\n", "\n", "def\n"},
},
{
name: "multiple lines without linefeed at end of file",
input: "abc\ndef\nghi",
expectedLines: []string{"abc\n", "def\n", "ghi"},
},
}

for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
lines := []string{}
forEachLineInStream(strings.NewReader(s.input), func(line string, i int) {
lines = append(lines, line)
})
assert.EqualValues(t, s.expectedLines, lines)
})
}
}

0 comments on commit 4e361e1

Please sign in to comment.