mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 04:15:48 +02:00
Provide conflict resolution dialogs for non-textual conflicts
This commit is contained in:
parent
efcd71b296
commit
ebb576feac
5 changed files with 168 additions and 1 deletions
|
@ -341,6 +341,13 @@ func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
|
|||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) RemoveConflictedFile(name string) error {
|
||||
cmdArgs := NewGitCmd("rm").Arg("--", name).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
// RemoveUntrackedFiles runs `git clean -fd`
|
||||
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
||||
cmdArgs := NewGitCmd("clean").Arg("-fd").ToArgv()
|
||||
|
|
|
@ -571,13 +571,59 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
|||
return self.switchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return errors.New(self.c.Tr.FileStagingRequirements)
|
||||
return self.handleNonInlineConflict(file)
|
||||
}
|
||||
|
||||
self.c.Context().Push(self.c.Contexts().Staging, opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FilesController) handleNonInlineConflict(file *models.File) error {
|
||||
handle := func(command func(command string) error, logText string) error {
|
||||
self.c.LogAction(logText)
|
||||
if err := command(file.GetPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
keepItem := &types.MenuItem{
|
||||
Label: self.c.Tr.MergeConflictKeepFile,
|
||||
OnPress: func() error {
|
||||
return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile)
|
||||
},
|
||||
Key: 'k',
|
||||
}
|
||||
deleteItem := &types.MenuItem{
|
||||
Label: self.c.Tr.MergeConflictDeleteFile,
|
||||
OnPress: func() error {
|
||||
return handle(self.c.Git().WorkingTree.RemoveConflictedFile, self.c.Tr.Actions.ResolveConflictByDeletingFile)
|
||||
},
|
||||
Key: 'd',
|
||||
}
|
||||
items := []*types.MenuItem{}
|
||||
switch file.ShortStatus {
|
||||
case "DD":
|
||||
// For "both deleted" conflicts, deleting the file is the only reasonable thing you can do.
|
||||
// Restoring to the state before deletion is not the responsibility of a conflict resolution tool.
|
||||
items = append(items, deleteItem)
|
||||
case "DU", "UD":
|
||||
// For these, we put the delete option first because it's the most common one,
|
||||
// even if it's more destructive.
|
||||
items = append(items, deleteItem, keepItem)
|
||||
case "AU", "UA":
|
||||
// For these, we put the keep option first because it's less destructive,
|
||||
// and the chances between keep and delete are 50/50.
|
||||
items = append(items, keepItem, deleteItem)
|
||||
default:
|
||||
panic("should only be called if there's a merge conflict")
|
||||
}
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.MergeConflictsTitle,
|
||||
Prompt: file.GetMergeStateDescription(self.c.Tr),
|
||||
Items: items,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *FilesController) toggleStagedAll() error {
|
||||
if err := self.toggleStagedAllWithLock(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -107,6 +107,8 @@ type TranslationSet struct {
|
|||
MergeConflictIncomingDiff string
|
||||
MergeConflictCurrentDiff string
|
||||
MergeConflictPressEnterToResolve string
|
||||
MergeConflictKeepFile string
|
||||
MergeConflictDeleteFile string
|
||||
Checkout string
|
||||
CheckoutTooltip string
|
||||
CantCheckoutBranchWhilePulling string
|
||||
|
@ -951,6 +953,8 @@ type Actions struct {
|
|||
UnstageFile string
|
||||
UnstageAllFiles string
|
||||
StageAllFiles string
|
||||
ResolveConflictByKeepingFile string
|
||||
ResolveConflictByDeletingFile string
|
||||
NotEnoughContextToStage string
|
||||
NotEnoughContextToDiscard string
|
||||
IgnoreExcludeFile string
|
||||
|
@ -1128,6 +1132,8 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
MergeConflictIncomingDiff: "Incoming changes:",
|
||||
MergeConflictCurrentDiff: "Current changes:",
|
||||
MergeConflictPressEnterToResolve: "Press %s to resolve.",
|
||||
MergeConflictKeepFile: "Keep file",
|
||||
MergeConflictDeleteFile: "Delete file",
|
||||
Checkout: "Checkout",
|
||||
CheckoutTooltip: "Checkout selected item.",
|
||||
CantCheckoutBranchWhilePulling: "You cannot checkout another branch while pulling the current branch",
|
||||
|
@ -1968,6 +1974,8 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
UnstageFile: "Unstage file",
|
||||
UnstageAllFiles: "Unstage all files",
|
||||
StageAllFiles: "Stage all files",
|
||||
ResolveConflictByKeepingFile: "Resolve by keeping file",
|
||||
ResolveConflictByDeletingFile: "Resolve by deleting file",
|
||||
NotEnoughContextToStage: "Staging or unstaging changes is not possible with a diff context size of 0. Increase the context using '%s'.",
|
||||
NotEnoughContextToDiscard: "Discarding changes is not possible with a diff context size of 0. Increase the context using '%s'.",
|
||||
IgnoreExcludeFile: "Ignore or exclude file",
|
||||
|
|
105
pkg/integration/tests/conflicts/resolve_non_textual_conflicts.go
Normal file
105
pkg/integration/tests/conflicts/resolve_non_textual_conflicts.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var ResolveNonTextualConflicts = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Resolve non-textual merge conflicts (e.g. one side modified, the other side deleted)",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.RunShellCommand(`echo test1 > both-deleted1.txt`)
|
||||
shell.RunShellCommand(`echo test2 > both-deleted2.txt`)
|
||||
shell.RunShellCommand(`git checkout -b conflict && git add both-deleted1.txt both-deleted2.txt`)
|
||||
shell.RunShellCommand(`echo haha1 > deleted-them1.txt && git add deleted-them1.txt`)
|
||||
shell.RunShellCommand(`echo haha2 > deleted-them2.txt && git add deleted-them2.txt`)
|
||||
shell.RunShellCommand(`echo haha1 > deleted-us1.txt && git add deleted-us1.txt`)
|
||||
shell.RunShellCommand(`echo haha2 > deleted-us2.txt && git add deleted-us2.txt`)
|
||||
shell.RunShellCommand(`git commit -m one`)
|
||||
|
||||
// stuff on other branch
|
||||
shell.RunShellCommand(`git branch conflict_second`)
|
||||
shell.RunShellCommand(`git mv both-deleted1.txt added-them-changed-us1.txt`)
|
||||
shell.RunShellCommand(`git mv both-deleted2.txt added-them-changed-us2.txt`)
|
||||
shell.RunShellCommand(`git rm deleted-them1.txt deleted-them2.txt`)
|
||||
shell.RunShellCommand(`echo modded1 > deleted-us1.txt && git add deleted-us1.txt`)
|
||||
shell.RunShellCommand(`echo modded2 > deleted-us2.txt && git add deleted-us2.txt`)
|
||||
shell.RunShellCommand(`git commit -m "two"`)
|
||||
|
||||
// stuff on our branch
|
||||
shell.RunShellCommand(`git checkout conflict_second`)
|
||||
shell.RunShellCommand(`git mv both-deleted1.txt changed-them-added-us1.txt`)
|
||||
shell.RunShellCommand(`git mv both-deleted2.txt changed-them-added-us2.txt`)
|
||||
shell.RunShellCommand(`echo modded1 > deleted-them1.txt && git add deleted-them1.txt`)
|
||||
shell.RunShellCommand(`echo modded2 > deleted-them2.txt && git add deleted-them2.txt`)
|
||||
shell.RunShellCommand(`git rm deleted-us1.txt deleted-us2.txt`)
|
||||
shell.RunShellCommand(`git commit -m "three"`)
|
||||
shell.RunShellCommand(`git reset --hard conflict_second`)
|
||||
shell.RunCommandExpectError([]string{"git", "merge", "conflict"})
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
resolve := func(filename string, menuChoice string) {
|
||||
t.Views().Files().
|
||||
NavigateToLine(Contains(filename)).
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains("Conflict:"))
|
||||
}).
|
||||
Press(keys.Universal.GoInto).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().Title(Equals("Merge conflicts")).
|
||||
Select(Contains(menuChoice)).
|
||||
Confirm()
|
||||
})
|
||||
}
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ /").IsSelected(),
|
||||
Equals(" UA added-them-changed-us1.txt"),
|
||||
Equals(" UA added-them-changed-us2.txt"),
|
||||
Equals(" DD both-deleted1.txt"),
|
||||
Equals(" DD both-deleted2.txt"),
|
||||
Equals(" AU changed-them-added-us1.txt"),
|
||||
Equals(" AU changed-them-added-us2.txt"),
|
||||
Equals(" UD deleted-them1.txt"),
|
||||
Equals(" UD deleted-them2.txt"),
|
||||
Equals(" DU deleted-us1.txt"),
|
||||
Equals(" DU deleted-us2.txt"),
|
||||
).
|
||||
Tap(func() {
|
||||
resolve("added-them-changed-us1.txt", "Delete file")
|
||||
resolve("added-them-changed-us2.txt", "Keep file")
|
||||
resolve("both-deleted1.txt", "Delete file")
|
||||
resolve("both-deleted2.txt", "Delete file")
|
||||
resolve("changed-them-added-us1.txt", "Delete file")
|
||||
resolve("changed-them-added-us2.txt", "Keep file")
|
||||
resolve("deleted-them1.txt", "Delete file")
|
||||
resolve("deleted-them2.txt", "Keep file")
|
||||
resolve("deleted-us1.txt", "Delete file")
|
||||
resolve("deleted-us2.txt", "Keep file")
|
||||
}).
|
||||
Lines(
|
||||
Equals("▼ /"),
|
||||
Equals(" A added-them-changed-us2.txt"),
|
||||
Equals(" D changed-them-added-us1.txt"),
|
||||
Equals(" D deleted-them1.txt"),
|
||||
Equals(" A deleted-us2.txt"),
|
||||
)
|
||||
|
||||
t.FileSystem().
|
||||
PathNotPresent("added-them-changed-us1.txt").
|
||||
FileContent("added-them-changed-us2.txt", Equals("test2\n")).
|
||||
PathNotPresent("both-deleted1.txt").
|
||||
PathNotPresent("both-deleted2.txt").
|
||||
PathNotPresent("changed-them-added-us1.txt").
|
||||
FileContent("changed-them-added-us2.txt", Equals("test2\n")).
|
||||
PathNotPresent("deleted-them1.txt").
|
||||
FileContent("deleted-them2.txt", Equals("modded2\n")).
|
||||
PathNotPresent("deleted-us1.txt").
|
||||
FileContent("deleted-us2.txt", Equals("modded2\n"))
|
||||
},
|
||||
})
|
|
@ -141,6 +141,7 @@ var tests = []*components.IntegrationTest{
|
|||
conflicts.ResolveExternally,
|
||||
conflicts.ResolveMultipleFiles,
|
||||
conflicts.ResolveNoAutoStage,
|
||||
conflicts.ResolveNonTextualConflicts,
|
||||
conflicts.ResolveWithoutTrailingLf,
|
||||
conflicts.UndoChooseHunk,
|
||||
custom_commands.AccessCommitProperties,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue