mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-15 00:15:32 +02:00
add more options for resetting files in the working tree
This commit is contained in:
@ -217,11 +217,11 @@ func includesInt(list []int, a int) bool {
|
|||||||
|
|
||||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||||
func (c *GitCommand) ResetAndClean() error {
|
func (c *GitCommand) ResetAndClean() error {
|
||||||
if err := c.OSCommand.RunCommand("git reset --hard HEAD"); err != nil {
|
if err := c.ResetHardHead(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OSCommand.RunCommand("git clean -fd")
|
return c.RemoveUntrackedFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||||
@ -890,3 +890,18 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*Commit, commitIndex int, f
|
|||||||
// continue
|
// continue
|
||||||
return c.GenericMerge("rebase", "continue")
|
return c.GenericMerge("rebase", "continue")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
|
||||||
|
func (c *GitCommand) DiscardAnyUnstagedFileChanges() error {
|
||||||
|
return c.OSCommand.RunCommand("git checkout -- .")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUntrackedFiles runs `git clean -fd`
|
||||||
|
func (c *GitCommand) RemoveUntrackedFiles() error {
|
||||||
|
return c.OSCommand.RunCommand("git clean -fd")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetHardHead runs `git reset --hard HEAD`
|
||||||
|
func (c *GitCommand) ResetHardHead() error {
|
||||||
|
return c.OSCommand.RunCommand("git reset --hard HEAD")
|
||||||
|
}
|
||||||
|
@ -1933,8 +1933,8 @@ func TestGitCommandGetCommitFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGitCommandDiscardUnstagedChanges is a function.
|
// TestGitCommandDiscardUnstagedFileChanges is a function.
|
||||||
func TestGitCommandDiscardUnstagedChanges(t *testing.T) {
|
func TestGitCommandDiscardUnstagedFileChanges(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
file *File
|
file *File
|
||||||
@ -1967,3 +1967,102 @@ func TestGitCommandDiscardUnstagedChanges(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGitCommandDiscardAnyUnstagedFileChanges is a function.
|
||||||
|
func TestGitCommandDiscardAnyUnstagedFileChanges(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"valid case",
|
||||||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||||
|
{
|
||||||
|
Expect: `git checkout -- .`,
|
||||||
|
Replace: "echo",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
gitCmd := NewDummyGitCommand()
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCmd.OSCommand.command = s.command
|
||||||
|
s.test(gitCmd.DiscardAnyUnstagedFileChanges())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGitCommandRemoveUntrackedFiles is a function.
|
||||||
|
func TestGitCommandRemoveUntrackedFiles(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"valid case",
|
||||||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||||
|
{
|
||||||
|
Expect: `git clean -fd`,
|
||||||
|
Replace: "echo",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
gitCmd := NewDummyGitCommand()
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCmd.OSCommand.command = s.command
|
||||||
|
s.test(gitCmd.RemoveUntrackedFiles())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGitCommandResetHardHead is a function.
|
||||||
|
func TestGitCommandResetHardHead(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"valid case",
|
||||||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||||
|
{
|
||||||
|
Expect: `git reset --hard HEAD`,
|
||||||
|
Replace: "echo",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
gitCmd := NewDummyGitCommand()
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCmd.OSCommand.command = s.command
|
||||||
|
s.test(gitCmd.ResetHardHead())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -455,15 +456,6 @@ func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.refreshFiles()
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("ClearFilePanel"), gui.Tr.SLocalize("SureResetHardHead"), func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if err := gui.GitCommand.ResetAndClean(); err != nil {
|
|
||||||
gui.createErrorPanel(g, err.Error())
|
|
||||||
}
|
|
||||||
return gui.refreshFiles()
|
|
||||||
}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) openFile(filename string) error {
|
func (gui *Gui) openFile(filename string) error {
|
||||||
if err := gui.OSCommand.OpenFile(filename); err != nil {
|
if err := gui.OSCommand.OpenFile(filename); err != nil {
|
||||||
return gui.createErrorPanel(gui.g, err.Error())
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
@ -498,13 +490,22 @@ type discardOption struct {
|
|||||||
description string
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
type discardOptionValue int
|
type discardAllOption struct {
|
||||||
|
handler func() error
|
||||||
|
description string
|
||||||
|
command string
|
||||||
|
}
|
||||||
|
|
||||||
// GetDisplayStrings is a function.
|
// GetDisplayStrings is a function.
|
||||||
func (r *discardOption) GetDisplayStrings(isFocused bool) []string {
|
func (r *discardOption) GetDisplayStrings(isFocused bool) []string {
|
||||||
return []string{r.description}
|
return []string{r.description}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDisplayStrings is a function.
|
||||||
|
func (r *discardAllOption) GetDisplayStrings(isFocused bool) []string {
|
||||||
|
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
file, err := gui.getSelectedFile(g)
|
file, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -518,11 +519,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
{
|
{
|
||||||
description: gui.Tr.SLocalize("discardAllChanges"),
|
description: gui.Tr.SLocalize("discardAllChanges"),
|
||||||
handler: func(file *commands.File) error {
|
handler: func(file *commands.File) error {
|
||||||
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
return gui.GitCommand.DiscardAllFileChanges(file)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.refreshFiles()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -537,11 +534,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
discardUnstagedChanges := &discardOption{
|
discardUnstagedChanges := &discardOption{
|
||||||
description: gui.Tr.SLocalize("discardUnstagedChanges"),
|
description: gui.Tr.SLocalize("discardUnstagedChanges"),
|
||||||
handler: func(file *commands.File) error {
|
handler: func(file *commands.File) error {
|
||||||
if err := gui.GitCommand.DiscardUnstagedFileChanges(file); err != nil {
|
return gui.GitCommand.DiscardUnstagedFileChanges(file)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return gui.refreshFiles()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,8 +542,61 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMenuPress := func(index int) error {
|
handleMenuPress := func(index int) error {
|
||||||
return options[index].handler(file)
|
if err := options[index].handler(file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createMenu(file.Name, options, handleMenuPress)
|
return gui.createMenu(file.Name, options, handleMenuPress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
options := []*discardAllOption{
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("discardAllChangesToAllFiles"),
|
||||||
|
command: "reset --hard HEAD && git clean -fd",
|
||||||
|
handler: func() error {
|
||||||
|
return gui.GitCommand.ResetAndClean()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("discardAnyUnstagedChanges"),
|
||||||
|
command: "git checkout -- .",
|
||||||
|
handler: func() error {
|
||||||
|
return gui.GitCommand.DiscardAnyUnstagedFileChanges()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("discardUntrackedFiles"),
|
||||||
|
command: "git clean -fd",
|
||||||
|
handler: func() error {
|
||||||
|
return gui.GitCommand.RemoveUntrackedFiles()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("hardReset"),
|
||||||
|
command: "git reset --hard HEAD",
|
||||||
|
handler: func() error {
|
||||||
|
return gui.GitCommand.ResetHardHead()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("cancel"),
|
||||||
|
handler: func() error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMenuPress := func(index int) error {
|
||||||
|
if err := options[index].handler(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.createMenu("", options, handleMenuPress)
|
||||||
|
}
|
||||||
|
@ -157,9 +157,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "FileNoMergeCons",
|
ID: "FileNoMergeCons",
|
||||||
Other: "Dit bestand heeft geen merge conflicten",
|
Other: "Dit bestand heeft geen merge conflicten",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "SureResetHardHead",
|
|
||||||
Other: "Weet je het zeker dat je `reset --hard HEAD` en `clean -fd` wil uitvoeren? Het kan dat je hierdoor bestanden verliest",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureTo",
|
ID: "SureTo",
|
||||||
Other: "Weet je het zeker dat je {{.fileName}} wilt {{.deleteVerb}} (je veranderingen zullen worden verwijderd)",
|
Other: "Weet je het zeker dat je {{.fileName}} wilt {{.deleteVerb}} (je veranderingen zullen worden verwijderd)",
|
||||||
@ -340,9 +337,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CantCloseConfirmationPrompt",
|
ID: "CantCloseConfirmationPrompt",
|
||||||
Other: "Kon de bevestiging prompt niet sluiten: {{.error}}",
|
Other: "Kon de bevestiging prompt niet sluiten: {{.error}}",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "ClearFilePanel",
|
|
||||||
Other: "maak bestandsvenster leeg",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "MergeAborted",
|
ID: "MergeAborted",
|
||||||
Other: "Merge afgebroken",
|
Other: "Merge afgebroken",
|
||||||
@ -694,6 +688,18 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "discardUnstagedChanges",
|
ID: "discardUnstagedChanges",
|
||||||
Other: "discard unstaged changes",
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChangesToAllFiles",
|
||||||
|
Other: "nuke working tree",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAnyUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUntrackedFiles",
|
||||||
|
Other: "discard untracked files",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "hardReset",
|
||||||
|
Other: "hard reset",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -180,9 +180,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "FileNoMergeCons",
|
ID: "FileNoMergeCons",
|
||||||
Other: "This file has no inline merge conflicts",
|
Other: "This file has no inline merge conflicts",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "SureResetHardHead",
|
|
||||||
Other: "Are you sure you want to `reset --hard HEAD` and `clean -fd`? You may lose changes",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SoftReset",
|
ID: "SoftReset",
|
||||||
Other: "Soft reset",
|
Other: "Soft reset",
|
||||||
@ -405,9 +402,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "NoChangedFiles",
|
ID: "NoChangedFiles",
|
||||||
Other: "No changed files",
|
Other: "No changed files",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "ClearFilePanel",
|
|
||||||
Other: "Clear file panel",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "MergeAborted",
|
ID: "MergeAborted",
|
||||||
Other: "Merge aborted",
|
Other: "Merge aborted",
|
||||||
@ -717,6 +711,18 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "discardUnstagedChanges",
|
ID: "discardUnstagedChanges",
|
||||||
Other: "discard unstaged changes",
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChangesToAllFiles",
|
||||||
|
Other: "nuke working tree",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAnyUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUntrackedFiles",
|
||||||
|
Other: "discard untracked files",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "hardReset",
|
||||||
|
Other: "hard reset",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -146,9 +146,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "FileNoMergeCons",
|
ID: "FileNoMergeCons",
|
||||||
Other: "Ten plik nie powoduje konfliktów scalania",
|
Other: "Ten plik nie powoduje konfliktów scalania",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "SureResetHardHead",
|
|
||||||
Other: "Jesteś pewny, że chcesz wykonać `reset --hard HEAD` i `clean -fd`? Możesz stracić wprowadzone zmiany",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureTo",
|
ID: "SureTo",
|
||||||
Other: "Jesteś pewny, że chcesz {{.deleteVerb}} {{.fileName}} (stracisz swoje wprowadzone zmiany)?",
|
Other: "Jesteś pewny, że chcesz {{.deleteVerb}} {{.fileName}} (stracisz swoje wprowadzone zmiany)?",
|
||||||
@ -332,9 +329,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CantCloseConfirmationPrompt",
|
ID: "CantCloseConfirmationPrompt",
|
||||||
Other: "Nie można zamknąć monitu potwierdzenia: {{.error}}",
|
Other: "Nie można zamknąć monitu potwierdzenia: {{.error}}",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "ClearFilePanel",
|
|
||||||
Other: "Wyczyść panel plików",
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "MergeAborted",
|
ID: "MergeAborted",
|
||||||
Other: "Scalanie anulowane",
|
Other: "Scalanie anulowane",
|
||||||
@ -677,6 +671,18 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "discardUnstagedChanges",
|
ID: "discardUnstagedChanges",
|
||||||
Other: "discard unstaged changes",
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChangesToAllFiles",
|
||||||
|
Other: "nuke working tree",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAnyUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUntrackedFiles",
|
||||||
|
Other: "discard untracked files",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "hardReset",
|
||||||
|
Other: "hard reset",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user