Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package lazygit for openSUSE:Factory checked in at 2026-11-10 14:40:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/lazygit (Old) and /work/SRC/openSUSE:Factory/.lazygit.new.3759290 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "lazygit" Tue Nov 10 14:40:21 2026 rev:19 rq:1396403 version:0.66.3 Changes: -------- --- /work/SRC/openSUSE:Factory/lazygit/lazygit.changes 2026-10-20 12:42:14.353325291 +0200 +++ /work/SRC/openSUSE:Factory/.lazygit.new.3759290/lazygit.changes 2026-11-10 14:41:40.016699004 +0100 @@ -1,0 +2,16 @@ +Mon Nov 09 10:16:40 UTC 2026 - [email protected] + +- Update to version 0.66.3: + * Only recognize .gitmodules keys and section headers at the start of a line + * Add a test showing that .gitmodules keys are found in the middle of a line + * Ignore submodules with a suspicious path in .gitmodules + * Add a test showing that nuking the working tree can stash changes in other repositories + * Refuse to remove a submodule whose git dir isn't below .git/modules + * Add a test showing that removing a submodule can delete more than its git dir + * Ignore submodules with a suspicious name in .gitmodules + * Add a test showing that removing a submodule can delete a directory outside the repository + * Parse .gitmodules completely before loading nested submodules + * Delete the right git dir when removing a submodule in a linked worktree + * Add a test showing that removing a submodule in a linked worktree deletes the wrong git dir + +------------------------------------------------------------------- Old: ---- lazygit-0.66.2.tar.gz New: ---- lazygit-0.66.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ lazygit.spec ++++++ --- /var/tmp/diff_new_pack.HwaWGm/_old 2026-11-10 14:41:42.070785054 +0100 +++ /var/tmp/diff_new_pack.HwaWGm/_new 2026-11-10 14:41:42.072785138 +0100 @@ -17,7 +17,7 @@ Name: lazygit -Version: 0.66.2 +Version: 0.66.3 Release: 0 Summary: Simple terminal UI for git commands License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.HwaWGm/_old 2026-11-10 14:41:42.107786604 +0100 +++ /var/tmp/diff_new_pack.HwaWGm/_new 2026-11-10 14:41:42.110786729 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/jesseduffield/lazygit.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.66.2</param> + <param name="revision">v0.66.3</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.HwaWGm/_old 2026-11-10 14:41:42.138787902 +0100 +++ /var/tmp/diff_new_pack.HwaWGm/_new 2026-11-10 14:41:42.142788070 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/jesseduffield/lazygit.git</param> - <param name="changesrevision">63ab89fff478854deb4a35ef8902676b3cc5ca72</param></service></servicedata> + <param name="changesrevision">e0fbfd50c36e1ef88c52297ca34750e640b4f5f4</param></service></servicedata> (No newline at EOF) ++++++ lazygit-0.66.2.tar.gz -> lazygit-0.66.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/commands/git_commands/submodule.go new/lazygit-0.66.3/pkg/commands/git_commands/submodule.go --- old/lazygit-0.66.2/pkg/commands/git_commands/submodule.go 2026-10-16 09:56:51.000000000 +0200 +++ new/lazygit-0.66.3/pkg/commands/git_commands/submodule.go 2026-11-08 00:16:11.000000000 +0100 @@ -2,6 +2,8 @@ import ( "bufio" + "fmt" + "io" "os" "path/filepath" "regexp" @@ -46,7 +48,36 @@ } defer file.Close() - scanner := bufio.NewScanner(file) + parsedConfigs, err := parseGitmodules(file, parentModule) + if err != nil { + return nil, err + } + + configs := []*models.SubmoduleConfig{} + for _, config := range parsedConfigs { + if isSuspiciousSubmoduleName(config.Name) { + self.Log.Warnf("Ignoring suspicious submodule name: %s", config.Name) + continue + } + + if isSuspiciousSubmodulePath(dir, config.Path) { + self.Log.Warnf("Ignoring suspicious submodule path: %s", config.Path) + continue + } + + configs = append(configs, config) + + nestedConfigs, err := self.GetConfigs(config) + if err == nil { + configs = append(configs, nestedConfigs...) + } + } + + return configs, nil +} + +func parseGitmodules(reader io.Reader, parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) { + scanner := bufio.NewScanner(reader) scanner.Split(bufio.ScanLines) firstMatch := func(str string, regex string) (string, bool) { @@ -60,27 +91,22 @@ } configs := []*models.SubmoduleConfig{} - lastConfigIdx := -1 for scanner.Scan() { line := scanner.Text() - if name, ok := firstMatch(line, `\[submodule "(.*)"\]`); ok { + if name, ok := firstMatch(line, `^\s*\[submodule "(.*)"\]`); ok { configs = append(configs, &models.SubmoduleConfig{ Name: name, ParentModule: parentModule, }) - lastConfigIdx = len(configs) - 1 continue } - if lastConfigIdx != -1 { - if path, ok := firstMatch(line, `\s*path\s*=\s*(.*)\s*`); ok { - configs[lastConfigIdx].Path = path - nestedConfigs, err := self.GetConfigs(configs[lastConfigIdx]) - if err == nil { - configs = append(configs, nestedConfigs...) - } - } else if url, ok := firstMatch(line, `\s*url\s*=\s*(.*)\s*`); ok { - configs[lastConfigIdx].Url = url + if len(configs) > 0 { + config := configs[len(configs)-1] + if path, ok := firstMatch(line, `^\s*path\s*=\s*(.*)\s*`); ok { + config.Path = path + } else if url, ok := firstMatch(line, `^\s*url\s*=\s*(.*)\s*`); ok { + config.Url = url } } } @@ -92,6 +118,52 @@ return configs, nil } +// isSuspiciousSubmoduleName applies the same rule as git's +// check_submodule_name(); git ignores a submodule whose name fails it. The +// git dir of a submodule is .git/modules/<name>, so a ".." component in the +// name could put it outside the repository. Like git, we treat both slashes +// and backslashes as separators on every platform. +func isSuspiciousSubmoduleName(name string) bool { + if name == "" { + return true + } + + components := strings.FieldsFunc(name, func(r rune) bool { + return r == '/' || r == '\\' + }) + return lo.Contains(components, "..") +} + +// isSuspiciousSubmodulePath reports whether a submodule path from the +// .gitmodules file in parentDir might lead somewhere other than a directory +// below parentDir. We run commands in the submodule's directory and read the +// .gitmodules file in it, so the path must not lead anywhere else. +// +// Git never checks out a file through a symlink, so a path that goes through +// a symlink can't be a submodule in the first place. We reject such paths +// even if the symlink points to a directory below parentDir. +func isSuspiciousSubmodulePath(parentDir string, path string) bool { + if !filepath.IsLocal(path) || filepath.Clean(path) == "." { + return true + } + + dir := parentDir + for _, component := range strings.Split(filepath.Clean(path), string(filepath.Separator)) { + dir = filepath.Join(dir, component) + fileInfo, err := os.Lstat(dir) + if os.IsNotExist(err) { + // Nothing below this point exists (e.g. because the submodule + // isn't checked out), so it can't lead anywhere else either + return false + } + if err != nil || !fileInfo.IsDir() { + return true + } + } + + return false +} + // AnyHaveStageableChanges reports whether any of the given submodule paths has // a checked-out commit that differs from the one recorded in the // superproject's index, i.e. a change that `git add <path>` would actually @@ -237,6 +309,18 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error { // based on https://gist.github.com/myusuf3/7f645819ded92bda6677 + // Git keeps the git dirs of submodules separately for each worktree + gitDirPath := submodule.GitDirPath(self.repoPaths.WorktreeGitDirPath()) + + // The name of the submodule comes from .gitmodules, so it is under the + // control of whoever committed that file. Make sure that we never delete + // anything other than a directory below the "modules" directory of the + // worktree's git dir. + modulesDirPath := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "modules") + if relPath, err := filepath.Rel(modulesDirPath, gitDirPath); err != nil || relPath == "." || !filepath.IsLocal(relPath) { + return fmt.Errorf("refusing to delete %s because it is not inside %s", gitDirPath, modulesDirPath) + } + if err := self.runInParentModule(submodule, self.cmd.New( NewGitCmd("submodule"). Arg("deinit", "--force", "--", submodule.Path).ToArgv(), @@ -269,9 +353,7 @@ self.Log.Error(err) } - // We may in fact want to use the repo's git dir path but git docs say not to - // mix submodules and worktrees anyway. - return os.RemoveAll(submodule.GitDirPath(self.repoPaths.repoGitDirPath)) + return os.RemoveAll(gitDirPath) } func (self *SubmoduleCommands) Add(name string, path string, url string) error { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/commands/git_commands/submodule_test.go new/lazygit-0.66.3/pkg/commands/git_commands/submodule_test.go --- old/lazygit-0.66.2/pkg/commands/git_commands/submodule_test.go 2026-10-16 09:56:51.000000000 +0200 +++ new/lazygit-0.66.3/pkg/commands/git_commands/submodule_test.go 2026-11-08 00:16:11.000000000 +0100 @@ -1,16 +1,210 @@ package git_commands import ( + "os" + "path/filepath" "strings" "testing" "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/env" "github.com/samber/lo" "github.com/stretchr/testify/assert" ) +func TestParseGitmodules(t *testing.T) { + scenarios := []struct { + testName string + lines []string + expected []*models.SubmoduleConfig + }{ + { + testName: "two submodules", + lines: []string{ + `[submodule "one"]`, + "\tpath = path/to/one", + "\turl = https://example.com/one.git", + `[submodule "two"]`, + "\tpath = two", + "\turl = ../two", + }, + expected: []*models.SubmoduleConfig{ + {Name: "one", Path: "path/to/one", Url: "https://example.com/one.git"}, + {Name: "two", Path: "two", Url: "../two"}, + }, + }, + { + testName: "url that contains path=", + lines: []string{ + `[submodule "sub"]`, + "\tpath = sub", + "\turl = https://example.com/repo?path=other", + }, + expected: []*models.SubmoduleConfig{ + {Name: "sub", Path: "sub", Url: "https://example.com/repo?path=other"}, + }, + }, + { + testName: "commented-out section", + lines: []string{ + `[submodule "sub"]`, + "\tpath = sub", + "\turl = ../sub", + `#[submodule "old"]`, + "#\tpath = old", + "#\turl = ../old", + }, + expected: []*models.SubmoduleConfig{ + {Name: "sub", Path: "sub", Url: "../sub"}, + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + configs, err := parseGitmodules(strings.NewReader(strings.Join(s.lines, "\n")), nil) + assert.NoError(t, err) + assert.Equal(t, s.expected, configs) + }) + } +} + +func TestIsSuspiciousSubmoduleName(t *testing.T) { + scenarios := []struct { + name string + suspicious bool + }{ + {name: "sub", suspicious: false}, + {name: "path/to/sub", suspicious: false}, + {name: "path\\to\\sub", suspicious: false}, + {name: ".", suspicious: false}, + {name: "...", suspicious: false}, + {name: "..sub", suspicious: false}, + {name: "sub..", suspicious: false}, + {name: "a..b/c", suspicious: false}, + {name: "", suspicious: true}, + {name: "..", suspicious: true}, + {name: "../victim", suspicious: true}, + {name: "../../../victim", suspicious: true}, + {name: "sub/..", suspicious: true}, + {name: "sub/../../victim", suspicious: true}, + {name: "..\\victim", suspicious: true}, + {name: "sub\\..\\..\\victim", suspicious: true}, + {name: "sub/..\\..\\victim", suspicious: true}, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + assert.Equal(t, s.suspicious, isSuspiciousSubmoduleName(s.name)) + }) + } +} + +func TestIsSuspiciousSubmodulePath(t *testing.T) { + dir := t.TempDir() + parentDir := filepath.Join(dir, "repo") + assert.NoError(t, os.MkdirAll(filepath.Join(parentDir, "sub"), 0o755)) + assert.NoError(t, os.MkdirAll(filepath.Join(parentDir, "path", "to", "sub"), 0o755)) + assert.NoError(t, os.MkdirAll(filepath.Join(dir, "other"), 0o755)) + assert.NoError(t, os.WriteFile(filepath.Join(parentDir, "file"), []byte("content"), 0o644)) + + scenarios := []struct { + path string + suspicious bool + }{ + {path: "sub", suspicious: false}, + {path: "sub/", suspicious: false}, + {path: "path/to/sub", suspicious: false}, + {path: "not_checked_out", suspicious: false}, + {path: "path/not_checked_out/sub", suspicious: false}, + {path: "", suspicious: true}, + {path: ".", suspicious: true}, + {path: "./", suspicious: true}, + {path: "sub/..", suspicious: true}, + {path: "..", suspicious: true}, + {path: "../other", suspicious: true}, + {path: "sub/../../other", suspicious: true}, + {path: filepath.Join(dir, "other"), suspicious: true}, + {path: "file", suspicious: true}, + {path: "file/sub", suspicious: true}, + } + + for _, s := range scenarios { + t.Run(s.path, func(t *testing.T) { + assert.Equal(t, s.suspicious, isSuspiciousSubmodulePath(parentDir, s.path)) + }) + } +} + +func TestIsSuspiciousSubmodulePathWithSymlinks(t *testing.T) { + dir := t.TempDir() + parentDir := filepath.Join(dir, "repo") + assert.NoError(t, os.MkdirAll(filepath.Join(parentDir, "sub"), 0o755)) + assert.NoError(t, os.MkdirAll(filepath.Join(dir, "other", "sub"), 0o755)) + if err := os.Symlink(filepath.Join("..", "other"), filepath.Join(parentDir, "link_outside")); err != nil { + t.Skipf("Can't create symlinks: %v", err) + } + assert.NoError(t, os.Symlink("sub", filepath.Join(parentDir, "link_inside"))) + + scenarios := []struct { + path string + suspicious bool + }{ + {path: "sub", suspicious: false}, + {path: "link_outside", suspicious: true}, + {path: "link_outside/sub", suspicious: true}, + {path: "link_inside", suspicious: true}, + } + + for _, s := range scenarios { + t.Run(s.path, func(t *testing.T) { + assert.Equal(t, s.suspicious, isSuspiciousSubmodulePath(parentDir, s.path)) + }) + } +} + +func TestSubmoduleDeleteOutsideModulesDir(t *testing.T) { + scenarios := []struct { + testName string + name string + // relative to the directory that contains the repo + victimPath string + }{ + { + testName: "modules dir itself", + name: ".", + victimPath: "repo/.git/modules/other_submodule", + }, + { + testName: "outside the repo", + name: "../../../victim", + victimPath: "victim", + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + dir := t.TempDir() + victimFile := filepath.Join(dir, s.victimPath, "file") + assert.NoError(t, os.MkdirAll(filepath.Dir(victimFile), 0o755)) + assert.NoError(t, os.WriteFile(victimFile, []byte("content"), 0o644)) + + runner := oscommands.NewFakeRunner(t) + instance := buildSubmoduleCommands(commonDeps{ + runner: runner, + repoPaths: MockRepoPaths(filepath.Join(dir, "repo")), + }) + + err := instance.Delete(&models.SubmoduleConfig{Name: s.name, Path: "sub"}) + assert.Error(t, err) + assert.FileExists(t, victimFile) + runner.CheckForMissingCalls() + }) + } +} + func TestSubmoduleGetConflictCommits(t *testing.T) { type scenario struct { testName string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/commands/models/submodule_config.go new/lazygit-0.66.3/pkg/commands/models/submodule_config.go --- old/lazygit-0.66.2/pkg/commands/models/submodule_config.go 2026-10-16 09:56:51.000000000 +0200 +++ new/lazygit-0.66.3/pkg/commands/models/submodule_config.go 2026-11-08 00:16:11.000000000 +0100 @@ -38,10 +38,10 @@ return r.RefName() } -func (r *SubmoduleConfig) GitDirPath(repoGitDirPath string) string { - parentPath := repoGitDirPath +func (r *SubmoduleConfig) GitDirPath(worktreeGitDirPath string) string { + parentPath := worktreeGitDirPath if r.ParentModule != nil { - parentPath = r.ParentModule.GitDirPath(repoGitDirPath) + parentPath = r.ParentModule.GitDirPath(worktreeGitDirPath) } return filepath.Join(parentPath, "modules", r.Name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/integration/tests/submodule/ignore_suspicious_name.go new/lazygit-0.66.3/pkg/integration/tests/submodule/ignore_suspicious_name.go --- old/lazygit-0.66.2/pkg/integration/tests/submodule/ignore_suspicious_name.go 1970-01-01 01:00:00.000000000 +0100 +++ new/lazygit-0.66.3/pkg/integration/tests/submodule/ignore_suspicious_name.go 2026-11-08 00:16:11.000000000 +0100 @@ -0,0 +1,36 @@ +package submodule + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var IgnoreSuspiciousName = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ignore a submodule whose name in .gitmodules would put its git dir outside the repository", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFile("../victim/file", "content") + + shell.EmptyCommit("first commit") + shell.CloneIntoSubmodule("my_submodule_name", "my_submodule_path") + shell.GitAddAll() + shell.Commit("add submodule") + + // The git dir of a submodule is .git/modules/<name>, so this name + // points at the victim directory next to the repository + shell.UpdateFileAndAdd(".gitmodules", `[submodule "../../../victim"] + path = my_submodule_path + url = ../my_submodule_name +`) + shell.Commit("rename submodule") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Submodules().Focus(). + IsEmpty() + + t.FileSystem(). + PathPresent("../victim/file") + }, +}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/integration/tests/submodule/ignore_suspicious_path.go new/lazygit-0.66.3/pkg/integration/tests/submodule/ignore_suspicious_path.go --- old/lazygit-0.66.2/pkg/integration/tests/submodule/ignore_suspicious_path.go 1970-01-01 01:00:00.000000000 +0100 +++ new/lazygit-0.66.3/pkg/integration/tests/submodule/ignore_suspicious_path.go 2026-11-08 00:16:11.000000000 +0100 @@ -0,0 +1,65 @@ +package submodule + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var IgnoreSuspiciousPath = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ignore a submodule whose path in .gitmodules leads outside the repository", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // Two unrelated repos next to ours, each with an uncommitted change + for _, otherRepo := range []string{"other_repo", "linked_repo"} { + shell.Chdir("..") + shell.CreateDir(otherRepo) + shell.Chdir(otherRepo) + shell.Init() + shell.CreateFileAndAdd("file", "original") + shell.Commit("initial commit") + shell.UpdateFile("file", "modified") + shell.Chdir("../repo") + } + + shell.EmptyCommit("first commit") + shell.RunCommand([]string{"ln", "-s", "../linked_repo", "link"}) + shell.GitAdd("link") + shell.CreateFileAndAdd(".gitmodules", `[submodule "other"] + path = ../other_repo + url = ../other_repo +[submodule "linked"] + path = link + url = ../linked_repo +`) + shell.Commit("add submodules") + shell.CreateFile("untracked", "content") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Submodules(). + IsEmpty() + + t.Views().Files().Focus(). + Lines( + Contains("untracked"), + ). + Press(keys.Files.ViewResetOptions). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("")). + Select(Contains("Nuke working tree")). + Confirm() + + t.ExpectPopup().Confirmation(). + Title(Equals("Nuke working tree")). + Content(AnyString()). + Confirm() + }). + IsEmpty() + + t.FileSystem(). + FileContent("../other_repo/file", Equals("modified")). + FileContent("../linked_repo/file", Equals("modified")) + }, +}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/integration/tests/submodule/remove_in_linked_worktree.go new/lazygit-0.66.3/pkg/integration/tests/submodule/remove_in_linked_worktree.go --- old/lazygit-0.66.2/pkg/integration/tests/submodule/remove_in_linked_worktree.go 1970-01-01 01:00:00.000000000 +0100 +++ new/lazygit-0.66.3/pkg/integration/tests/submodule/remove_in_linked_worktree.go 2026-11-08 00:16:11.000000000 +0100 @@ -0,0 +1,48 @@ +package submodule + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var RemoveInLinkedWorktree = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Remove a submodule in a linked worktree", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("first commit") + shell.CloneIntoSubmodule("my_submodule_name", "my_submodule_path") + shell.GitAddAll() + shell.Commit("add submodule") + + shell.AddWorktree("HEAD", "../linked-worktree", "mybranch") + shell.Chdir("../linked-worktree") + shell.RunCommand([]string{"git", "submodule", "update", "--init"}) + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + // Git keeps the git dirs of submodules separately for each worktree + mainGitDirSubmodulePath := "../repo/.git/modules/my_submodule_name" + linkedGitDirSubmodulePath := "../repo/.git/worktrees/linked-worktree/modules/my_submodule_name" + t.FileSystem(). + PathPresent(mainGitDirSubmodulePath). + PathPresent(linkedGitDirSubmodulePath) + + t.Views().Submodules().Focus(). + Lines( + Contains("my_submodule_name").IsSelected(), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Remove submodule")). + Content(Equals("Are you sure you want to remove submodule 'my_submodule_name' and its corresponding directory? This is irreversible.")). + Confirm() + }). + IsEmpty() + + t.FileSystem(). + PathPresent(mainGitDirSubmodulePath). + PathNotPresent(linkedGitDirSubmodulePath) + }, +}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazygit-0.66.2/pkg/integration/tests/test_list.go new/lazygit-0.66.3/pkg/integration/tests/test_list.go --- old/lazygit-0.66.2/pkg/integration/tests/test_list.go 2026-10-16 09:56:51.000000000 +0200 +++ new/lazygit-0.66.3/pkg/integration/tests/test_list.go 2026-11-08 00:16:11.000000000 +0100 @@ -578,7 +578,10 @@ submodule.Enter, submodule.EnterFromDotfileBareRepo, submodule.EnterNested, + submodule.IgnoreSuspiciousName, + submodule.IgnoreSuspiciousPath, submodule.Remove, + submodule.RemoveInLinkedWorktree, submodule.RemoveNested, submodule.Reset, submodule.ResetFolder, ++++++ vendor.tar.gz ++++++
