Skip to content

Commit 3936224

Browse files
committed
Show an error when checking out a file would overwrite local modifications
1 parent 72fa54d commit 3936224

File tree

5 files changed

+68
-0
lines changed

5 files changed

+68
-0
lines changed

pkg/gui/controllers/commits_files_controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/jesseduffield/lazygit/pkg/commands/patch"
1313
"github.com/jesseduffield/lazygit/pkg/constants"
1414
"github.com/jesseduffield/lazygit/pkg/gui/context"
15+
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
1516
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
1617
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
1718
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -283,6 +284,12 @@ func (self *CommitFilesController) openCopyMenu() error {
283284
}
284285

285286
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
287+
hasModifiedFiles := helpers.AnyTrackedFilesInPathExceptSubmodules(node.GetPath(),
288+
self.c.Model().Files, self.c.Model().Submodules)
289+
if hasModifiedFiles {
290+
return errors.New(self.c.Tr.CannotCheckoutWithModifiedFilesErr)
291+
}
292+
286293
self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
287294
_, to := self.context().GetFromAndToForDiff()
288295
if err := self.c.Git().WorkingTree.CheckoutFile(to, node.GetPath()); err != nil {

pkg/gui/controllers/helpers/working_tree_helper.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"regexp"
8+
"strings"
89

910
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
1011
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -71,6 +72,23 @@ func AnyTrackedFilesExceptSubmodules(files []*models.File, submoduleConfigs []*m
7172
return lo.SomeBy(files, func(f *models.File) bool { return f.Tracked && !f.IsSubmodule(submoduleConfigs) })
7273
}
7374

75+
func isContainedInPath(candidate string, path string) bool {
76+
return (
77+
// If the path is the repo root (appears as "/" in the UI), then all candidates are contained in it
78+
path == "." ||
79+
// Exact match; will only be true for files
80+
candidate == path ||
81+
// Match for files within a directory. We need to match the trailing slash to avoid
82+
// matching files with longer names.
83+
strings.HasPrefix(candidate, path+"/"))
84+
}
85+
86+
func AnyTrackedFilesInPathExceptSubmodules(path string, files []*models.File, submoduleConfigs []*models.SubmoduleConfig) bool {
87+
return lo.SomeBy(files, func(f *models.File) bool {
88+
return f.Tracked && isContainedInPath(f.GetPath(), path) && !f.IsSubmodule(submoduleConfigs)
89+
})
90+
}
91+
7492
func (self *WorkingTreeHelper) IsWorkingTreeDirtyExceptSubmodules() bool {
7593
return IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules)
7694
}

pkg/i18n/english.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ type TranslationSet struct {
425425
ViewItemFiles string
426426
CommitFilesTitle string
427427
CheckoutCommitFileTooltip string
428+
CannotCheckoutWithModifiedFilesErr string
428429
CanOnlyDiscardFromLocalCommits string
429430
Remove string
430431
DiscardOldFileChangeTooltip string
@@ -1525,6 +1526,7 @@ func EnglishTranslationSet() *TranslationSet {
15251526
ViewItemFiles: "View files",
15261527
CommitFilesTitle: "Commit files",
15271528
CheckoutCommitFileTooltip: "Checkout file. This replaces the file in your working tree with the version from the selected commit.",
1529+
CannotCheckoutWithModifiedFilesErr: "You have local modifications for the file(s) you are trying to check out. You need to stash or discard these first.",
15281530
CanOnlyDiscardFromLocalCommits: "Changes can only be discarded from local commits",
15291531
Remove: "Remove",
15301532
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.",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package commit
2+
3+
import (
4+
"github.com/jesseduffield/lazygit/pkg/config"
5+
. "github.com/jesseduffield/lazygit/pkg/integration/components"
6+
)
7+
8+
var CheckoutFileWithLocalModifications = NewIntegrationTest(NewIntegrationTestArgs{
9+
Description: "Checkout a file from a commit that has local modifications",
10+
ExtraCmdArgs: []string{},
11+
Skip: false,
12+
SetupConfig: func(config *config.AppConfig) {},
13+
SetupRepo: func(shell *Shell) {
14+
shell.CreateFileAndAdd("dir/file1.txt", "file1\n")
15+
shell.CreateFileAndAdd("dir/file2.txt", "file2\n")
16+
shell.Commit("one")
17+
shell.UpdateFile("dir/file1.txt", "file1\nfile1 change\n")
18+
},
19+
Run: func(t *TestDriver, keys config.KeybindingConfig) {
20+
t.Views().Commits().
21+
Focus().
22+
Lines(
23+
Contains("one").IsSelected(),
24+
).
25+
PressEnter()
26+
27+
t.Views().CommitFiles().
28+
IsFocused().
29+
Lines(
30+
Equals("▼ dir").IsSelected(),
31+
Equals(" A file1.txt"),
32+
Equals(" A file2.txt"),
33+
).
34+
Press(keys.CommitFiles.CheckoutCommitFile)
35+
36+
t.ExpectPopup().Alert().Title(Equals("Error")).
37+
Content(Contains("local modifications")).
38+
Confirm()
39+
},
40+
})

pkg/integration/tests/test_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ var tests = []*components.IntegrationTest{
107107
commit.Checkout,
108108
commit.CheckoutFileFromCommit,
109109
commit.CheckoutFileFromRangeSelectionOfCommits,
110+
commit.CheckoutFileWithLocalModifications,
110111
commit.Commit,
111112
commit.CommitMultiline,
112113
commit.CommitSkipHooks,

0 commit comments

Comments
 (0)