mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-13 05:15:53 +02:00
support discarding unstaged changes
This commit is contained in:
parent
a2c780b085
commit
ff97ef7b94
8 changed files with 151 additions and 51 deletions
|
@ -438,8 +438,8 @@ func (c *GitCommand) RebaseMode() (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveFile directly
|
// DiscardAllFileChanges directly
|
||||||
func (c *GitCommand) RemoveFile(file *File) error {
|
func (c *GitCommand) DiscardAllFileChanges(file *File) error {
|
||||||
// if the file isn't tracked, we assume you want to delete it
|
// if the file isn't tracked, we assume you want to delete it
|
||||||
quotedFileName := c.OSCommand.Quote(file.Name)
|
quotedFileName := c.OSCommand.Quote(file.Name)
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges {
|
||||||
|
@ -450,7 +450,12 @@ func (c *GitCommand) RemoveFile(file *File) error {
|
||||||
if !file.Tracked {
|
if !file.Tracked {
|
||||||
return c.removeFile(file.Name)
|
return c.removeFile(file.Name)
|
||||||
}
|
}
|
||||||
// if the file is tracked, we assume you want to just check it out
|
return c.DiscardUnstagedFileChanges(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardUnstagedFileChanges directly
|
||||||
|
func (c *GitCommand) DiscardUnstagedFileChanges(file *File) error {
|
||||||
|
quotedFileName := c.OSCommand.Quote(file.Name)
|
||||||
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", quotedFileName))
|
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", quotedFileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +580,7 @@ func (c *GitCommand) ApplyPatch(patch string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = c.OSCommand.RemoveFile(filename) }()
|
defer func() { _ = c.OSCommand.Remove(filename) }()
|
||||||
|
|
||||||
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", c.OSCommand.Quote(filename)))
|
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", c.OSCommand.Quote(filename)))
|
||||||
}
|
}
|
||||||
|
@ -861,7 +866,7 @@ func (c *GitCommand) DiscardOldFileChanges(commits []*Commit, commitIndex int, f
|
||||||
|
|
||||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||||
if err := c.OSCommand.RunCommand(fmt.Sprintf("git cat-file -e HEAD^:%s", fileName)); err != nil {
|
if err := c.OSCommand.RunCommand(fmt.Sprintf("git cat-file -e HEAD^:%s", fileName)); err != nil {
|
||||||
if err := c.OSCommand.RemoveFile(fileName); err != nil {
|
if err := c.OSCommand.Remove(fileName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.StageFile(fileName); err != nil {
|
if err := c.StageFile(fileName); err != nil {
|
||||||
|
|
|
@ -1140,8 +1140,8 @@ func TestGitCommandIsInMergeState(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGitCommandRemoveFile is a function.
|
// TestGitCommandDiscardAllFileChanges is a function.
|
||||||
func TestGitCommandRemoveFile(t *testing.T) {
|
func TestGitCommandDiscardAllFileChanges(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
command func() (func(string, ...string) *exec.Cmd, *[][]string)
|
command func() (func(string, ...string) *exec.Cmd, *[][]string)
|
||||||
|
@ -1337,7 +1337,7 @@ func TestGitCommandRemoveFile(t *testing.T) {
|
||||||
gitCmd := NewDummyGitCommand()
|
gitCmd := NewDummyGitCommand()
|
||||||
gitCmd.OSCommand.command, cmdsCalled = s.command()
|
gitCmd.OSCommand.command, cmdsCalled = s.command()
|
||||||
gitCmd.removeFile = s.removeFile
|
gitCmd.removeFile = s.removeFile
|
||||||
s.test(cmdsCalled, gitCmd.RemoveFile(s.file))
|
s.test(cmdsCalled, gitCmd.DiscardAllFileChanges(s.file))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1932,3 +1932,38 @@ func TestGitCommandGetCommitFiles(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGitCommandDiscardUnstagedChanges is a function.
|
||||||
|
func TestGitCommandDiscardUnstagedChanges(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
file *File
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"valid case",
|
||||||
|
&File{Name: "test.txt"},
|
||||||
|
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||||
|
{
|
||||||
|
Expect: `git checkout -- "test.txt"`,
|
||||||
|
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.DiscardUnstagedFileChanges(s.file))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -255,9 +255,9 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
|
||||||
return tmpfile.Name(), nil
|
return tmpfile.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveFile removes a file at the specified path
|
// Remove removes a file or directory at the specified path
|
||||||
func (c *OSCommand) RemoveFile(filename string) error {
|
func (c *OSCommand) Remove(filename string) error {
|
||||||
err := os.Remove(filename)
|
err := os.RemoveAll(filename)
|
||||||
return WrapError(err)
|
return WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -259,35 +259,6 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
file, err := gui.getSelectedFile(g)
|
|
||||||
if err != nil {
|
|
||||||
if err == gui.Errors.ErrNoFiles {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var deleteVerb string
|
|
||||||
if file.Tracked {
|
|
||||||
deleteVerb = gui.Tr.SLocalize("checkout")
|
|
||||||
} else {
|
|
||||||
deleteVerb = gui.Tr.SLocalize("delete")
|
|
||||||
}
|
|
||||||
message := gui.Tr.TemplateLocalize(
|
|
||||||
"SureTo",
|
|
||||||
Teml{
|
|
||||||
"deleteVerb": deleteVerb,
|
|
||||||
"fileName": file.Name,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return gui.createConfirmationPanel(g, v, strings.Title(deleteVerb)+" file", message, func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if err := gui.GitCommand.RemoveFile(file); err != nil {
|
|
||||||
return gui.createErrorPanel(gui.g, err.Error())
|
|
||||||
}
|
|
||||||
return gui.refreshFiles()
|
|
||||||
}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
||||||
file, err := gui.getSelectedFile(g)
|
file, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -521,3 +492,65 @@ func (gui *Gui) handleSoftReset(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.refreshFiles()
|
return gui.refreshFiles()
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type discardOption struct {
|
||||||
|
handler func(fileName *commands.File) error
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
type discardOptionValue int
|
||||||
|
|
||||||
|
// GetDisplayStrings is a function.
|
||||||
|
func (r *discardOption) GetDisplayStrings(isFocused bool) []string {
|
||||||
|
return []string{r.description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
file, err := gui.getSelectedFile(g)
|
||||||
|
if err != nil {
|
||||||
|
if err != gui.Errors.ErrNoFiles {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
options := []*discardOption{
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("discardAllChanges"),
|
||||||
|
handler: func(file *commands.File) error {
|
||||||
|
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshFiles()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: gui.Tr.SLocalize("cancel"),
|
||||||
|
handler: func(file *commands.File) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||||
|
discardUnstagedChanges := &discardOption{
|
||||||
|
description: gui.Tr.SLocalize("discardUnstagedChanges"),
|
||||||
|
handler: func(file *commands.File) error {
|
||||||
|
if err := gui.GitCommand.DiscardUnstagedFileChanges(file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshFiles()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options[:1], append([]*discardOption{discardUnstagedChanges}, options[1:]...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMenuPress := func(index int) error {
|
||||||
|
return options[index].handler(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.createMenu(file.Name, options, handleMenuPress)
|
||||||
|
}
|
||||||
|
|
|
@ -176,8 +176,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
Key: 'd',
|
Key: 'd',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleFileRemove,
|
Handler: gui.handleCreateDiscardMenu,
|
||||||
Description: gui.Tr.SLocalize("removeFile"),
|
Description: gui.Tr.SLocalize("viewDiscardOptions"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
Key: 'e',
|
Key: 'e',
|
||||||
|
|
|
@ -382,9 +382,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "GitconfigParseErr",
|
ID: "GitconfigParseErr",
|
||||||
Other: `Gogit kon je gitconfig bestand niet goed parsen door de aanwezigheid van losstaande '\' tekens. Het weghalen van deze tekens zou het probleem moeten oplossen. `,
|
Other: `Gogit kon je gitconfig bestand niet goed parsen door de aanwezigheid van losstaande '\' tekens. Het weghalen van deze tekens zou het probleem moeten oplossen. `,
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "removeFile",
|
|
||||||
Other: `Verwijder als untracked / uitchecken wordt gevolgd (ga weg)`,
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "editFile",
|
ID: "editFile",
|
||||||
Other: `verander bestand`,
|
Other: `verander bestand`,
|
||||||
|
@ -685,6 +682,18 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StashPrefix",
|
ID: "StashPrefix",
|
||||||
Other: "Auto-stashing changes for ",
|
Other: "Auto-stashing changes for ",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "viewDiscardOptions",
|
||||||
|
Other: "view 'discard changes' options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "cancel",
|
||||||
|
Other: "cancel",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChanges",
|
||||||
|
Other: "discard all changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,9 +447,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "GitconfigParseErr",
|
ID: "GitconfigParseErr",
|
||||||
Other: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
Other: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "removeFile",
|
|
||||||
Other: `delete if untracked / checkout if tracked`,
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "editFile",
|
ID: "editFile",
|
||||||
Other: `edit file`,
|
Other: `edit file`,
|
||||||
|
@ -708,6 +705,18 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StashPrefix",
|
ID: "StashPrefix",
|
||||||
Other: "Auto-stashing changes for ",
|
Other: "Auto-stashing changes for ",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "viewDiscardOptions",
|
||||||
|
Other: "view 'discard changes' options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "cancel",
|
||||||
|
Other: "cancel",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChanges",
|
||||||
|
Other: "discard all changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,9 +371,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "AnonymousReportingPrompt",
|
ID: "AnonymousReportingPrompt",
|
||||||
Other: "Włączyć anonimowe raportowanie błędów w celu pomocy w usprawnianiu lazygita (enter/esc)?",
|
Other: "Włączyć anonimowe raportowanie błędów w celu pomocy w usprawnianiu lazygita (enter/esc)?",
|
||||||
}, &i18n.Message{
|
|
||||||
ID: "removeFile",
|
|
||||||
Other: `usuń jeśli nie śledzony / przełącz jeśli śledzony`,
|
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "editFile",
|
ID: "editFile",
|
||||||
Other: `edytuj plik`,
|
Other: `edytuj plik`,
|
||||||
|
@ -668,6 +665,18 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StashPrefix",
|
ID: "StashPrefix",
|
||||||
Other: "Auto-stashing changes for ",
|
Other: "Auto-stashing changes for ",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "viewDiscardOptions",
|
||||||
|
Other: "view 'discard changes' options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "cancel",
|
||||||
|
Other: "cancel",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardAllChanges",
|
||||||
|
Other: "discard all changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "discardUnstagedChanges",
|
||||||
|
Other: "discard unstaged changes",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue