mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-01-26 01:41:35 +03:00
Show an error when checking out a file would overwrite local modifications
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
@@ -283,6 +284,12 @@ func (self *CommitFilesController) openCopyMenu() error {
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
|
||||
hasModifiedFiles := helpers.AnyTrackedFilesInPathExceptSubmodules(node.GetPath(),
|
||||
self.c.Model().Files, self.c.Model().Submodules)
|
||||
if hasModifiedFiles {
|
||||
return errors.New(self.c.Tr.CannotCheckoutWithModifiedFilesErr)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
|
||||
_, to := self.context().GetFromAndToForDiff()
|
||||
if err := self.c.Git().WorkingTree.CheckoutFile(to, node.GetPath()); err != nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@@ -71,6 +72,23 @@ func AnyTrackedFilesExceptSubmodules(files []*models.File, submoduleConfigs []*m
|
||||
return lo.SomeBy(files, func(f *models.File) bool { return f.Tracked && !f.IsSubmodule(submoduleConfigs) })
|
||||
}
|
||||
|
||||
func isContainedInPath(candidate string, path string) bool {
|
||||
return (
|
||||
// If the path is the repo root (appears as "/" in the UI), then all candidates are contained in it
|
||||
path == "." ||
|
||||
// Exact match; will only be true for files
|
||||
candidate == path ||
|
||||
// Match for files within a directory. We need to match the trailing slash to avoid
|
||||
// matching files with longer names.
|
||||
strings.HasPrefix(candidate, path+"/"))
|
||||
}
|
||||
|
||||
func AnyTrackedFilesInPathExceptSubmodules(path string, files []*models.File, submoduleConfigs []*models.SubmoduleConfig) bool {
|
||||
return lo.SomeBy(files, func(f *models.File) bool {
|
||||
return f.Tracked && isContainedInPath(f.GetPath(), path) && !f.IsSubmodule(submoduleConfigs)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) IsWorkingTreeDirtyExceptSubmodules() bool {
|
||||
return IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
|
||||
}
|
||||
|
||||
@@ -425,6 +425,7 @@ type TranslationSet struct {
|
||||
ViewItemFiles string
|
||||
CommitFilesTitle string
|
||||
CheckoutCommitFileTooltip string
|
||||
CannotCheckoutWithModifiedFilesErr string
|
||||
CanOnlyDiscardFromLocalCommits string
|
||||
Remove string
|
||||
DiscardOldFileChangeTooltip string
|
||||
@@ -1525,6 +1526,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
ViewItemFiles: "View files",
|
||||
CommitFilesTitle: "Commit files",
|
||||
CheckoutCommitFileTooltip: "Checkout file. This replaces the file in your working tree with the version from the selected commit.",
|
||||
CannotCheckoutWithModifiedFilesErr: "You have local modifications for the file(s) you are trying to check out. You need to stash or discard these first.",
|
||||
CanOnlyDiscardFromLocalCommits: "Changes can only be discarded from local commits",
|
||||
Remove: "Remove",
|
||||
DiscardOldFileChangeTooltip: "Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file.",
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CheckoutFileWithLocalModifications = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Checkout a file from a commit that has local modifications",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("dir/file1.txt", "file1\n")
|
||||
shell.CreateFileAndAdd("dir/file2.txt", "file2\n")
|
||||
shell.Commit("one")
|
||||
shell.UpdateFile("dir/file1.txt", "file1\nfile1 change\n")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("one").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir").IsSelected(),
|
||||
Equals(" A file1.txt"),
|
||||
Equals(" A file2.txt"),
|
||||
).
|
||||
Press(keys.CommitFiles.CheckoutCommitFile)
|
||||
|
||||
t.ExpectPopup().Alert().Title(Equals("Error")).
|
||||
Content(Contains("local modifications")).
|
||||
Confirm()
|
||||
},
|
||||
})
|
||||
@@ -107,6 +107,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.Checkout,
|
||||
commit.CheckoutFileFromCommit,
|
||||
commit.CheckoutFileFromRangeSelectionOfCommits,
|
||||
commit.CheckoutFileWithLocalModifications,
|
||||
commit.Commit,
|
||||
commit.CommitMultiline,
|
||||
commit.CommitSkipHooks,
|
||||
|
||||
Reference in New Issue
Block a user