mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-08 22:36:49 +02:00
Show all submodules recursively (#3341)
- **PR Description** Extend the submodules tab to show not only the top-level submodules, but also their nested submodules, recursively. Fixes #3306.
This commit is contained in:
@@ -60,6 +60,14 @@ func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) DirIf(condition bool, path string) *GitCommandBuilder {
|
||||
if condition {
|
||||
return self.Dir(path)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// Note, you may prefer to use the Dir method instead of this one
|
||||
func (self *GitCommandBuilder) Worktree(path string) *GitCommandBuilder {
|
||||
// worktree arg comes before the command
|
||||
|
@@ -26,8 +26,12 @@ func NewSubmoduleCommands(gitCommon *GitCommon) *SubmoduleCommands {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
|
||||
file, err := os.Open(".gitmodules")
|
||||
func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) {
|
||||
gitModulesPath := ".gitmodules"
|
||||
if parentModule != nil {
|
||||
gitModulesPath = filepath.Join(parentModule.FullPath(), gitModulesPath)
|
||||
}
|
||||
file, err := os.Open(gitModulesPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
@@ -51,21 +55,27 @@ func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
|
||||
}
|
||||
|
||||
configs := []*models.SubmoduleConfig{}
|
||||
lastConfigIdx := -1
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if name, ok := firstMatch(line, `\[submodule "(.*)"\]`); ok {
|
||||
configs = append(configs, &models.SubmoduleConfig{Name: name})
|
||||
configs = append(configs, &models.SubmoduleConfig{
|
||||
Name: name, ParentModule: parentModule,
|
||||
})
|
||||
lastConfigIdx = len(configs) - 1
|
||||
continue
|
||||
}
|
||||
|
||||
if len(configs) > 0 {
|
||||
lastConfig := configs[len(configs)-1]
|
||||
|
||||
if lastConfigIdx != -1 {
|
||||
if path, ok := firstMatch(line, `\s*path\s*=\s*(.*)\s*`); ok {
|
||||
lastConfig.Path = path
|
||||
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 {
|
||||
lastConfig.Url = url
|
||||
configs[lastConfigIdx].Url = url
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,12 +87,12 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
// if the path does not exist then it hasn't yet been initialized so we'll swallow the error
|
||||
// because the intention here is to have no dirty worktree state
|
||||
if _, err := os.Stat(submodule.Path); os.IsNotExist(err) {
|
||||
self.Log.Infof("submodule path %s does not exist, returning", submodule.Path)
|
||||
self.Log.Infof("submodule path %s does not exist, returning", submodule.FullPath())
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("stash").
|
||||
Dir(submodule.Path).
|
||||
Dir(submodule.FullPath()).
|
||||
Arg("--include-untracked").
|
||||
ToArgv()
|
||||
|
||||
@@ -90,8 +100,13 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
|
||||
parentDir := ""
|
||||
if submodule.ParentModule != nil {
|
||||
parentDir = submodule.ParentModule.FullPath()
|
||||
}
|
||||
cmdArgs := NewGitCmd("submodule").
|
||||
Arg("update", "--init", "--force", "--", submodule.Path).
|
||||
DirIf(parentDir != "", parentDir).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
@@ -107,6 +122,20 @@ func (self *SubmoduleCommands) UpdateAll() error {
|
||||
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||
|
||||
if submodule.ParentModule != nil {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(submodule.ParentModule.FullPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chdir(wd) }()
|
||||
}
|
||||
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("submodule").
|
||||
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
|
||||
@@ -141,7 +170,7 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
|
||||
// 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(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "modules", submodule.Path))
|
||||
return os.RemoveAll(submodule.GitDirPath(self.repoPaths.repoGitDirPath))
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
@@ -158,10 +187,24 @@ func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
|
||||
func (self *SubmoduleCommands) UpdateUrl(submodule *models.SubmoduleConfig, newUrl string) error {
|
||||
if submodule.ParentModule != nil {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(submodule.ParentModule.FullPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chdir(wd) }()
|
||||
}
|
||||
|
||||
setUrlCmdStr := NewGitCmd("config").
|
||||
Arg(
|
||||
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
|
||||
"--file", ".gitmodules", "submodule."+submodule.Name+".url", newUrl,
|
||||
).
|
||||
ToArgv()
|
||||
|
||||
@@ -170,7 +213,7 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
|
||||
return err
|
||||
}
|
||||
|
||||
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
|
||||
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", submodule.Path).
|
||||
ToArgv()
|
||||
|
||||
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
|
||||
|
@@ -343,7 +343,7 @@ func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
||||
|
||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||
func (self *WorkingTreeCommands) ResetAndClean() error {
|
||||
submoduleConfigs, err := self.submodule.GetConfigs()
|
||||
submoduleConfigs, err := self.submodule.GetConfigs(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,13 +1,33 @@
|
||||
package models
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
type SubmoduleConfig struct {
|
||||
Name string
|
||||
Path string
|
||||
Url string
|
||||
|
||||
ParentModule *SubmoduleConfig // nil if top-level
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) FullName() string {
|
||||
if r.ParentModule != nil {
|
||||
return r.ParentModule.FullName() + "/" + r.Name
|
||||
}
|
||||
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) FullPath() string {
|
||||
if r.ParentModule != nil {
|
||||
return r.ParentModule.FullPath() + "/" + r.Path
|
||||
}
|
||||
|
||||
return r.Path
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) RefName() string {
|
||||
return r.Name
|
||||
return r.FullName()
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) ID() string {
|
||||
@@ -17,3 +37,12 @@ func (r *SubmoduleConfig) ID() string {
|
||||
func (r *SubmoduleConfig) Description() string {
|
||||
return r.RefName()
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) GitDirPath(repoGitDirPath string) string {
|
||||
parentPath := repoGitDirPath
|
||||
if r.ParentModule != nil {
|
||||
parentPath = r.ParentModule.GitDirPath(repoGitDirPath)
|
||||
}
|
||||
|
||||
return filepath.Join(parentPath, "modules", r.Name)
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
|
||||
viewModel := NewFilteredListViewModel(
|
||||
func() []*models.SubmoduleConfig { return c.Model().Submodules },
|
||||
func(submodule *models.SubmoduleConfig) []string {
|
||||
return []string{submodule.Name}
|
||||
return []string{submodule.FullName()}
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
@@ -415,7 +415,7 @@ func (self *RefreshHelper) refreshTags() error {
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
|
||||
configs, err := self.c.Git().Submodule.GetConfigs()
|
||||
configs, err := self.c.Git().Submodule.GetConfigs(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ func (self *ReposHelper) EnterSubmodule(submodule *models.SubmoduleConfig) error
|
||||
}
|
||||
self.c.State().GetRepoPathStack().Push(wd)
|
||||
|
||||
return self.DispatchSwitchToRepo(submodule.Path, context.NO_CONTEXT)
|
||||
return self.DispatchSwitchToRepo(submodule.FullPath(), context.NO_CONTEXT)
|
||||
}
|
||||
|
||||
func (self *ReposHelper) getCurrentBranch(path string) string {
|
||||
|
@@ -116,8 +116,8 @@ func (self *SubmodulesController) GetOnRenderToMain() func() error {
|
||||
} else {
|
||||
prefix := fmt.Sprintf(
|
||||
"Name: %s\nPath: %s\nUrl: %s\n\n",
|
||||
style.FgGreen.Sprint(submodule.Name),
|
||||
style.FgYellow.Sprint(submodule.Path),
|
||||
style.FgGreen.Sprint(submodule.FullName()),
|
||||
style.FgYellow.Sprint(submodule.FullPath()),
|
||||
style.FgCyan.Sprint(submodule.Url),
|
||||
)
|
||||
|
||||
@@ -178,12 +178,12 @@ func (self *SubmodulesController) add() error {
|
||||
|
||||
func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: fmt.Sprintf(self.c.Tr.UpdateSubmoduleUrl, submodule.Name),
|
||||
Title: fmt.Sprintf(self.c.Tr.UpdateSubmoduleUrl, submodule.FullName()),
|
||||
InitialContent: submodule.Url,
|
||||
HandleConfirm: func(newUrl string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleUrlStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.UpdateSubmoduleUrl)
|
||||
err := self.c.Git().Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||
err := self.c.Git().Submodule.UpdateUrl(submodule, newUrl)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func (self *SubmodulesController) update(submodule *models.SubmoduleConfig) erro
|
||||
func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.RemoveSubmodule,
|
||||
Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||
Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.FullName()),
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RemoveSubmodule)
|
||||
if err := self.c.Git().Submodule.Delete(submodule); err != nil {
|
||||
|
@@ -13,5 +13,15 @@ func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]st
|
||||
}
|
||||
|
||||
func getSubmoduleDisplayStrings(s *models.SubmoduleConfig) []string {
|
||||
return []string{theme.DefaultTextColor.Sprint(s.Name)}
|
||||
name := s.Name
|
||||
if s.ParentModule != nil {
|
||||
indentation := ""
|
||||
for p := s.ParentModule; p != nil; p = p.ParentModule {
|
||||
indentation += " "
|
||||
}
|
||||
|
||||
name = indentation + "- " + s.Name
|
||||
}
|
||||
|
||||
return []string{theme.DefaultTextColor.Sprint(name)}
|
||||
}
|
||||
|
@@ -2,7 +2,10 @@ package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
)
|
||||
|
||||
type Git struct {
|
||||
@@ -44,3 +47,11 @@ func (self *Git) expect(cmdArgs []string, condition func(string) (bool, string))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Git) Version() *git_commands.GitVersion {
|
||||
version, err := getGitVersion()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get git version: %v", err)
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
@@ -345,9 +345,9 @@ func (self *Shell) CloneIntoRemote(name string) *Shell {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Shell) CloneIntoSubmodule(submoduleName string) *Shell {
|
||||
func (self *Shell) CloneIntoSubmodule(submoduleName string, submodulePath string) *Shell {
|
||||
self.Clone("other_repo")
|
||||
self.RunCommand([]string{"git", "submodule", "add", "../other_repo", submoduleName})
|
||||
self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../other_repo", submodulePath})
|
||||
|
||||
return self
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule_name", "my_submodule_path")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
},
|
||||
@@ -29,14 +29,18 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Status().Content(Contains("repo"))
|
||||
}
|
||||
assertInSubmodule := func() {
|
||||
t.Views().Status().Content(Contains("my_submodule"))
|
||||
if t.Git().Version().IsAtLeast(2, 22, 0) {
|
||||
t.Views().Status().Content(Contains("my_submodule_path(my_submodule_name)"))
|
||||
} else {
|
||||
t.Views().Status().Content(Contains("my_submodule_path"))
|
||||
}
|
||||
}
|
||||
|
||||
assertInParentRepo()
|
||||
|
||||
t.Views().Submodules().Focus().
|
||||
Lines(
|
||||
Contains("my_submodule").IsSelected(),
|
||||
Contains("my_submodule_name").IsSelected(),
|
||||
).
|
||||
// enter the submodule
|
||||
PressEnter()
|
||||
@@ -60,7 +64,7 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Files().Focus().
|
||||
Lines(
|
||||
MatchesRegexp(` M.*my_submodule \(submodule\)`).IsSelected(),
|
||||
MatchesRegexp(` M.*my_submodule_path \(submodule\)`).IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
// main view also shows the new commit when we're looking at the submodule within the files view
|
||||
|
52
pkg/integration/tests/submodule/enter_nested.go
Normal file
52
pkg/integration/tests/submodule/enter_nested.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package submodule
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var EnterNested = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Enter a nested submodule",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
setupNestedSubmodules(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Submodules().Focus().
|
||||
Lines(
|
||||
Equals("outerSubName").IsSelected(),
|
||||
Equals(" - innerSubName"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Views().Main().ContainsLines(
|
||||
Contains("Name: outerSubName"),
|
||||
Contains("Path: modules/outerSubPath"),
|
||||
Contains("Url: ../outerSubmodule"),
|
||||
)
|
||||
}).
|
||||
SelectNextItem().
|
||||
Tap(func() {
|
||||
t.Views().Main().ContainsLines(
|
||||
Contains("Name: outerSubName/innerSubName"),
|
||||
Contains("Path: modules/outerSubPath/modules/innerSubPath"),
|
||||
Contains("Url: ../innerSubmodule"),
|
||||
)
|
||||
}).
|
||||
// enter the nested submodule
|
||||
PressEnter()
|
||||
|
||||
if t.Git().Version().IsAtLeast(2, 22, 0) {
|
||||
t.Views().Status().Content(Contains("innerSubPath(innerSubName)"))
|
||||
} else {
|
||||
t.Views().Status().Content(Contains("innerSubPath"))
|
||||
}
|
||||
t.Views().Commits().ContainsLines(
|
||||
Contains("initial inner commit"),
|
||||
)
|
||||
|
||||
t.Views().Files().PressEscape()
|
||||
t.Views().Status().Content(Contains("repo"))
|
||||
},
|
||||
})
|
@@ -12,20 +12,23 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule_name", "my_submodule_path")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
gitDirSubmodulePath := ".git/modules/my_submodule_name"
|
||||
t.FileSystem().PathPresent(gitDirSubmodulePath)
|
||||
|
||||
t.Views().Submodules().Focus().
|
||||
Lines(
|
||||
Contains("my_submodule").IsSelected(),
|
||||
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' and its corresponding directory? This is irreversible.")).
|
||||
Content(Equals("Are you sure you want to remove submodule 'my_submodule_name' and its corresponding directory? This is irreversible.")).
|
||||
Confirm()
|
||||
}).
|
||||
IsEmpty()
|
||||
@@ -33,13 +36,15 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().Focus().
|
||||
Lines(
|
||||
MatchesRegexp(`M.*\.gitmodules`).IsSelected(),
|
||||
MatchesRegexp(`D.*my_submodule`),
|
||||
MatchesRegexp(`D.*my_submodule_path`),
|
||||
)
|
||||
|
||||
t.Views().Main().Content(
|
||||
Contains("-[submodule \"my_submodule\"]").
|
||||
Contains("- path = my_submodule").
|
||||
Contains("-[submodule \"my_submodule_name\"]").
|
||||
Contains("- path = my_submodule_path").
|
||||
Contains("- url = ../other_repo"),
|
||||
)
|
||||
|
||||
t.FileSystem().PathNotPresent(gitDirSubmodulePath)
|
||||
},
|
||||
})
|
||||
|
56
pkg/integration/tests/submodule/remove_nested.go
Normal file
56
pkg/integration/tests/submodule/remove_nested.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package submodule
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var RemoveNested = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Remove a nested submodule",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
setupNestedSubmodules(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
gitDirSubmodulePath, _ := filepath.Abs(".git/modules/outerSubName/modules/innerSubName")
|
||||
t.FileSystem().PathPresent(gitDirSubmodulePath)
|
||||
|
||||
t.Views().Submodules().Focus().
|
||||
Lines(
|
||||
Equals("outerSubName").IsSelected(),
|
||||
Equals(" - innerSubName"),
|
||||
).
|
||||
SelectNextItem().
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Remove submodule")).
|
||||
Content(Equals("Are you sure you want to remove submodule 'outerSubName/innerSubName' and its corresponding directory? This is irreversible.")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Equals("outerSubName").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.GoInto)
|
||||
|
||||
t.Views().Files().IsFocused().
|
||||
Lines(
|
||||
Contains("modules").IsSelected(),
|
||||
MatchesRegexp(`D.*innerSubPath`),
|
||||
MatchesRegexp(`M.*\.gitmodules`),
|
||||
).
|
||||
NavigateToLine(Contains(".gitmodules"))
|
||||
|
||||
t.Views().Main().Content(
|
||||
Contains("-[submodule \"innerSubName\"]").
|
||||
Contains("- path = modules/innerSubPath").
|
||||
Contains("- url = ../innerSubmodule"),
|
||||
)
|
||||
|
||||
t.FileSystem().PathNotPresent(gitDirSubmodulePath)
|
||||
},
|
||||
})
|
@@ -20,7 +20,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule_name", "my_submodule_path")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
|
||||
@@ -31,22 +31,24 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Status().Content(Contains("repo"))
|
||||
}
|
||||
assertInSubmodule := func() {
|
||||
t.Views().Status().Content(Contains("my_submodule"))
|
||||
if t.Git().Version().IsAtLeast(2, 22, 0) {
|
||||
t.Views().Status().Content(Contains("my_submodule_path(my_submodule_name)"))
|
||||
} else {
|
||||
t.Views().Status().Content(Contains("my_submodule_path"))
|
||||
}
|
||||
}
|
||||
|
||||
assertInParentRepo()
|
||||
|
||||
t.Views().Submodules().Focus().
|
||||
Lines(
|
||||
Contains("my_submodule").IsSelected(),
|
||||
Contains("my_submodule_name").IsSelected(),
|
||||
).
|
||||
// enter the submodule
|
||||
PressEnter()
|
||||
|
||||
assertInSubmodule()
|
||||
|
||||
t.Views().Status().Content(Contains("my_submodule"))
|
||||
|
||||
t.Views().Files().IsFocused().
|
||||
Press("e").
|
||||
Tap(func() {
|
||||
@@ -65,18 +67,18 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Submodules().IsFocused()
|
||||
|
||||
t.Views().Main().Content(Contains("Submodule my_submodule contains modified content"))
|
||||
t.Views().Main().Content(Contains("Submodule my_submodule_path contains modified content"))
|
||||
|
||||
t.Views().Files().Focus().
|
||||
Lines(
|
||||
MatchesRegexp(` M.*my_submodule \(submodule\)`),
|
||||
MatchesRegexp(` M.*my_submodule_path \(submodule\)`),
|
||||
Contains("other_file").IsSelected(),
|
||||
).
|
||||
// Verify we can't use range select on submodules
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectPreviousItem().
|
||||
Lines(
|
||||
MatchesRegexp(` M.*my_submodule \(submodule\)`).IsSelected(),
|
||||
MatchesRegexp(` M.*my_submodule_path \(submodule\)`).IsSelected(),
|
||||
Contains("other_file").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.Remove).
|
||||
@@ -85,13 +87,13 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
Lines(
|
||||
MatchesRegexp(` M.*my_submodule \(submodule\)`).IsSelected(),
|
||||
MatchesRegexp(` M.*my_submodule_path \(submodule\)`).IsSelected(),
|
||||
Contains("other_file"),
|
||||
).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("my_submodule")).
|
||||
Title(Equals("my_submodule_path")).
|
||||
Select(Contains("Stash uncommitted submodule changes and update")).
|
||||
Confirm()
|
||||
}).
|
||||
|
39
pkg/integration/tests/submodule/shared.go
Normal file
39
pkg/integration/tests/submodule/shared.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package submodule
|
||||
|
||||
import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
func setupNestedSubmodules(shell *Shell) {
|
||||
// we're going to have a directory structure like this:
|
||||
// project
|
||||
// - repo/modules/outerSubName/modules/innerSubName/
|
||||
//
|
||||
shell.CreateFileAndAdd("rootFile", "rootStuff")
|
||||
shell.Commit("initial repo commit")
|
||||
|
||||
shell.Chdir("..")
|
||||
shell.CreateDir("innerSubmodule")
|
||||
shell.Chdir("innerSubmodule")
|
||||
shell.Init()
|
||||
shell.CreateFileAndAdd("inner", "inner")
|
||||
shell.Commit("initial inner commit")
|
||||
|
||||
shell.Chdir("..")
|
||||
shell.CreateDir("outerSubmodule")
|
||||
shell.Chdir("outerSubmodule")
|
||||
shell.Init()
|
||||
shell.CreateFileAndAdd("outer", "outer")
|
||||
shell.Commit("initial outer commit")
|
||||
shell.CreateDir("modules")
|
||||
// the git config (-c) parameter below is required
|
||||
// to let git create a file-protocol/path submodule
|
||||
shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "add", "--name", "innerSubName", "../innerSubmodule", "modules/innerSubPath"})
|
||||
shell.Commit("add dependency as innerSubmodule")
|
||||
|
||||
shell.Chdir("../repo")
|
||||
shell.CreateDir("modules")
|
||||
shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "add", "--name", "outerSubName", "../outerSubmodule", "modules/outerSubPath"})
|
||||
shell.Commit("add dependency as outerSubmodule")
|
||||
shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "update", "--init", "--recursive"})
|
||||
}
|
@@ -245,7 +245,9 @@ var tests = []*components.IntegrationTest{
|
||||
stash.StashUnstaged,
|
||||
submodule.Add,
|
||||
submodule.Enter,
|
||||
submodule.EnterNested,
|
||||
submodule.Remove,
|
||||
submodule.RemoveNested,
|
||||
submodule.Reset,
|
||||
sync.FetchPrune,
|
||||
sync.FetchWhenSortedByDate,
|
||||
|
Reference in New Issue
Block a user