Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make auto-staging resolved conflicts optional #3870

Merged
merged 2 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ git:
# If true, pass the --all arg to git fetch
fetchAll: true

# If true, lazygit will automatically stage files that used to have merge
# conflicts but no longer do; and it will also ask you if you want to
# continue a merge or rebase if you've resolved all conflicts. If false, it
# won't do either of these things.
autoStageResolvedConflicts: true

# Command used when displaying the current branch git log in the main window
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --

Expand Down
6 changes: 6 additions & 0 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ type GitConfig struct {
AutoRefresh bool `yaml:"autoRefresh"`
// If true, pass the --all arg to git fetch
FetchAll bool `yaml:"fetchAll"`
// If true, lazygit will automatically stage files that used to have merge
// conflicts but no longer do; and it will also ask you if you want to
// continue a merge or rebase if you've resolved all conflicts. If false, it
// won't do either of these things.
AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"`
// Command used when displaying the current branch git log in the main window
BranchLogCmd string `yaml:"branchLogCmd"`
// Command used to display git log of all branches in the main window.
Expand Down Expand Up @@ -753,6 +758,7 @@ func GetDefaultConfig() *UserConfig {
AutoFetch: true,
AutoRefresh: true,
FetchAll: true,
AutoStageResolvedConflicts: true,
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
DisableForcePushing: false,
Expand Down
48 changes: 25 additions & 23 deletions pkg/gui/controllers/helpers/refresh_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,33 +546,35 @@ func (self *RefreshHelper) refreshFilesAndSubmodules() error {
func (self *RefreshHelper) refreshStateFiles() error {
fileTreeViewModel := self.c.Contexts().Files.FileTreeViewModel

// If git thinks any of our files have inline merge conflicts, but they actually don't,
// we stage them.
// Note that if files with merge conflicts have both arisen and have been resolved
// between refreshes, we won't stage them here. This is super unlikely though,
// and this approach spares us from having to call `git status` twice in a row.
// Although this also means that at startup we won't be staging anything until
// we call git status again.
pathsToStage := []string{}
prevConflictFileCount := 0
for _, file := range self.c.Model().Files {
if file.HasMergeConflicts {
prevConflictFileCount++
}
if file.HasInlineMergeConflicts {
hasConflicts, err := mergeconflicts.FileHasConflictMarkers(file.Name)
if err != nil {
self.c.Log.Error(err)
} else if !hasConflicts {
pathsToStage = append(pathsToStage, file.Name)
if self.c.UserConfig().Git.AutoStageResolvedConflicts {
// If git thinks any of our files have inline merge conflicts, but they actually don't,
// we stage them.
// Note that if files with merge conflicts have both arisen and have been resolved
// between refreshes, we won't stage them here. This is super unlikely though,
// and this approach spares us from having to call `git status` twice in a row.
// Although this also means that at startup we won't be staging anything until
// we call git status again.
pathsToStage := []string{}
for _, file := range self.c.Model().Files {
if file.HasMergeConflicts {
prevConflictFileCount++
}
if file.HasInlineMergeConflicts {
hasConflicts, err := mergeconflicts.FileHasConflictMarkers(file.Name)
if err != nil {
self.c.Log.Error(err)
} else if !hasConflicts {
pathsToStage = append(pathsToStage, file.Name)
}
}
}
}

if len(pathsToStage) > 0 {
self.c.LogAction(self.c.Tr.Actions.StageResolvedFiles)
if err := self.c.Git().WorkingTree.StageFiles(pathsToStage); err != nil {
return err
if len(pathsToStage) > 0 {
self.c.LogAction(self.c.Tr.Actions.StageResolvedFiles)
if err := self.c.Git().WorkingTree.StageFiles(pathsToStage); err != nil {
return err
}
}
}

Expand Down
13 changes: 11 additions & 2 deletions pkg/gui/filetree/file_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package filetree

import "github.com/jesseduffield/lazygit/pkg/commands/models"
import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
)

// FileNode wraps a node and provides some file-specific methods for it.
type FileNode struct {
Expand Down Expand Up @@ -44,7 +47,13 @@ func (self *FileNode) GetHasStagedChanges() bool {
}

func (self *FileNode) GetHasInlineMergeConflicts() bool {
return self.SomeFile(func(file *models.File) bool { return file.HasInlineMergeConflicts })
return self.SomeFile(func(file *models.File) bool {
if !file.HasInlineMergeConflicts {
return false
}
hasConflicts, _ := mergeconflicts.FileHasConflictMarkers(file.Name)
return hasConflicts
})
}

func (self *FileNode) GetIsTracked() bool {
Expand Down
81 changes: 81 additions & 0 deletions pkg/integration/tests/conflicts/resolve_no_auto_stage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package conflicts

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

var ResolveNoAutoStage = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Resolving conflicts without auto-staging",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {
config.GetUserConfig().Git.AutoStageResolvedConflicts = false
},
SetupRepo: func(shell *Shell) {
shared.CreateMergeConflictFiles(shell)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Lines(
Contains("UU").Contains("file1").IsSelected(),
Contains("UU").Contains("file2"),
).
PressEnter()

t.Views().MergeConflicts().
IsFocused().
SelectedLines(
Contains("<<<<<<< HEAD"),
Contains("First Change"),
Contains("======="),
).
PressPrimaryAction()

t.Views().Files().
IsFocused().
// Resolving the conflict didn't auto-stage it
Lines(
Contains("UU").Contains("file1").IsSelected(),
Contains("UU").Contains("file2"),
).
// So do that manually
PressPrimaryAction().
Lines(
Contains("UU").Contains("file2").IsSelected(),
).
// Trying to stage a file that still has conflicts is not allowed:
PressPrimaryAction().
Tap(func() {
t.ExpectPopup().Alert().
Title(Equals("Error")).
Content(Contains("Cannot stage/unstage directory containing files with inline merge conflicts.")).
Confirm()
}).
PressEnter()

// coincidentally these files have the same conflict
t.Views().MergeConflicts().
IsFocused().
SelectedLines(
Contains("<<<<<<< HEAD"),
Contains("First Change"),
Contains("======="),
).
PressPrimaryAction()

t.Views().Files().
IsFocused().
// Again, resolving the conflict didn't auto-stage it
Lines(
Contains("UU").Contains("file2").IsSelected(),
).
// Doing that manually now works:
PressPrimaryAction().
Lines(
Contains("A").Contains("file3").IsSelected(),
)
},
})
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ var tests = []*components.IntegrationTest{
conflicts.Filter,
conflicts.ResolveExternally,
conflicts.ResolveMultipleFiles,
conflicts.ResolveNoAutoStage,
conflicts.UndoChooseHunk,
custom_commands.AccessCommitProperties,
custom_commands.BasicCommand,
Expand Down
5 changes: 5 additions & 0 deletions schema/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,11 @@
"description": "If true, pass the --all arg to git fetch",
"default": true
},
"autoStageResolvedConflicts": {
"type": "boolean",
"description": "If true, lazygit will automatically stage files that used to have merge\nconflicts but no longer do; and it will also ask you if you want to\ncontinue a merge or rebase if you've resolved all conflicts. If false, it\nwon't do either of these things.",
"default": true
},
"branchLogCmd": {
"type": "string",
"description": "Command used when displaying the current branch git log in the main window",
Expand Down
Loading