worktree

package
v0.63.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AddForExistingBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a worktree that checks out an existing branch (no new branch), and confirm the option is disabled for an already-checked-out branch",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.NewBranchFrom("otherbranch", "mybranch")
		shell.Checkout("mybranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
				Contains("otherbranch"),
			).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().
					Menu().
					Title(Equals("New worktree")).
					Select(Contains("New worktree for 'mybranch'")).
					Tooltip(Contains("Branch mybranch is checked out by worktree repo")).
					Confirm().
					Tap(func() {
						t.ExpectToast(Contains("Branch mybranch is checked out by worktree repo"))
					}).
					Cancel()
			}).
			NavigateToLine(Contains("otherbranch")).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New worktree for 'otherbranch'")).
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			})

		t.Views().Branches().
			IsFocused().
			Lines(
				Contains("otherbranch").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)

		t.Views().Status().
			Content(Contains("repo(otherbranch) → otherbranch"))
	},
})
View Source
var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a new branch and worktree from a branch, then switch back to the main worktree via the branches view",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch"),
			).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New branch and worktree from 'mybranch'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					Type("newbranch").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			}).
			IsFocused().
			Lines(
				Contains("newbranch").IsSelected(),
				Contains("mybranch (worktree repo)"),
			).
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.Select).
			Tap(func() {
				t.ExpectPopup().Confirmation().
					Title(Equals("Switch to worktree")).
					Content(Equals("This branch is checked out by worktree repo. Do you want to switch to that worktree?")).
					Confirm()
			}).
			Lines(
				Contains("mybranch").IsSelected(),

				Contains("newbranch (worktree)"),
			).
			Press(keys.Universal.PrevBlock)

		t.Views().Files().
			IsFocused()
	},
})
View Source
var AddFromBranchDetached = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Add a detached worktree at a branch via the branches view, choosing a custom location",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch"),
			).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New detached worktree at 'mybranch'")).
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Select(Contains("Other…")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree path")).
					InitialText(Contains("mybranch")).
					Clear().
					Type("../linked-worktree").
					Confirm()
			}).
			IsFocused().
			Lines(
				Contains("(no branch)").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)

		t.Views().Status().
			Content(Contains("repo(linked-worktree)"))
	},
})
View Source
var AddFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a new branch and worktree from a commit via the commits view",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit two")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Commits().
			Focus().
			Lines(
				Contains("commit two").IsSelected(),
				Contains("initial commit"),
			).
			NavigateToLine(Contains("initial commit")).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New branch and worktree from")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					Type("newbranch").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			}).
			Lines(
				Contains("initial commit"),
			)

		t.Views().Branches().
			IsFocused().
			Lines(
				Contains("newbranch").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)
	},
})
View Source
var AddFromRemoteBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a new local tracking branch and worktree from a remote branch",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.NewBranch("feature")
		shell.CloneIntoRemote("origin")
		shell.Checkout("master")

		shell.RunCommand([]string{"git", "branch", "-D", "feature"})
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Remotes().
			Focus().
			Lines(
				Contains("origin").IsSelected(),
			).
			PressEnter()

		t.Views().RemoteBranches().
			IsFocused().
			NavigateToLine(Contains("feature")).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New local branch and worktree from 'origin/feature'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					InitialText(Equals("feature")).
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			})

		t.Views().Branches().
			IsFocused().
			Lines(
				Contains("feature").Contains("✓").IsSelected(),
				Contains("master (worktree repo)"),
			)
	},
})
View Source
var AddFromStash = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a new branch and worktree from a stash entry",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.UpdateFile("README.md", "work in progress")
		shell.Stash("my stash")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Stash().
			Focus().
			Lines(
				Contains("my stash").IsSelected(),
			).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New branch and worktree from 'stash@{0}'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					Type("from-stash").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			})

		t.Views().Branches().
			IsFocused().
			Lines(
				Contains("from-stash").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)
	},
})
View Source
var AddFromTag = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Create a detached worktree at a tag, entering a worktree name",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.CreateLightweightTag("v1.0", "HEAD")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Tags().
			Focus().
			Lines(
				Contains("v1.0").IsSelected(),
			).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New detached worktree at 'v1.0'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree name")).
					Type("tag-worktree").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			})

		t.Views().Branches().
			IsFocused().
			Lines(
				Contains("(no branch)").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)

		t.Views().Status().
			Content(Contains("repo(tag-worktree)"))
	},
})
View Source
var AssociateBranchBisect = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Verify that when you start a bisect in a linked worktree, Lazygit still associates the worktree with the branch",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
				Contains("newbranch (worktree linked-worktree)"),
			)

		t.Views().Commits().
			Focus().
			SelectedLine(Contains("commit 3")).
			Press(keys.Commits.ViewBisectOptions).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Bisect")).
					Select(MatchesRegexp(`Mark .* as bad`)).
					Confirm()

				t.Views().Information().Content(Contains("Bisecting"))
			}).
			NavigateToLine(Contains("initial commit")).
			Press(keys.Commits.ViewBisectOptions).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Bisect")).
					Select(MatchesRegexp(`Mark .* as good`)).
					Confirm()
			})

		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("newbranch")).
			Press(keys.Universal.Select).
			Tap(func() {
				t.ExpectPopup().Confirmation().
					Title(Equals("Switch to worktree")).
					Content(Equals("This branch is checked out by worktree linked-worktree. Do you want to switch to that worktree?")).
					Confirm()

				t.Views().Information().Content(DoesNotContain("Bisecting"))
			}).
			Lines(
				Contains("newbranch").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)

		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.Select).
			Tap(func() {
				t.ExpectPopup().Confirmation().
					Title(Equals("Switch to worktree")).
					Content(Equals("This branch is checked out by worktree repo. Do you want to switch to that worktree?")).
					Confirm()
			})
	},
})
View Source
var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Verify that when you start a rebase in a linked or main worktree, Lazygit still associates the worktree with the branch",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
				Contains("newbranch (worktree linked-worktree)"),
			)

		t.Views().Commits().
			Focus().
			NavigateToLine(Contains("commit 2")).
			Press(keys.Universal.Edit)

		t.Views().Information().Content(Contains("Rebasing"))

		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("newbranch")).
			Press(keys.Universal.Select).
			Tap(func() {
				t.ExpectPopup().Confirmation().
					Title(Equals("Switch to worktree")).
					Content(Equals("This branch is checked out by worktree linked-worktree. Do you want to switch to that worktree?")).
					Confirm()

				t.Views().Information().Content(DoesNotContain("Rebasing"))
			}).
			Lines(
				Contains("newbranch").IsSelected(),
				Contains("mybranch (worktree repo)"),
			)

		t.Views().Commits().
			Focus().
			NavigateToLine(Contains("commit 2")).
			Press(keys.Universal.Edit)

		t.Views().Information().Content(Contains("Rebasing"))

		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.Select).
			Tap(func() {
				t.ExpectPopup().Confirmation().
					Title(Equals("Switch to worktree")).
					Content(Equals("This branch is checked out by worktree repo. Do you want to switch to that worktree?")).
					Confirm()
			}).
			Lines(
				Contains("(no branch").IsSelected(),
				Contains("mybranch"),

				Contains("newbranch (worktree linked-worktree)"),
			)
	},
})
View Source
var BareRepo = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Open lazygit in the worktree of a bare repo and do a rebase/bisect",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {

		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("blah", "blah")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit two")
		shell.EmptyCommit("commit three")

		shell.RunCommand([]string{"git", "clone", "--bare", ".", "../.bare"})

		shell.DeleteFile(".git")

		shell.Chdir("..")

		shell.DeleteFile("repo")

		shell.RunCommand([]string{"git", "--git-dir", ".bare", "worktree", "add", "-b", "repo", "repo", "mybranch"})
		shell.RunCommand([]string{"git", "--git-dir", ".bare", "worktree", "add", "-b", "worktree2", "worktree2", "mybranch"})

		shell.Chdir("repo")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("repo"),
				Contains("mybranch"),
				Contains("worktree2 (worktree)"),
			)

		t.Views().Commits().
			Focus().
			Lines(
				Contains("commit three").IsSelected(),
				Contains("commit two"),
				Contains("initial commit"),
			).
			Press(keys.Commits.MoveDownCommit).
			Lines(
				Contains("commit two"),
				Contains("commit three").IsSelected(),
				Contains("initial commit"),
			).
			NavigateToLine(Contains("commit two")).
			Press(keys.Commits.ViewBisectOptions).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Bisect")).
					Select(MatchesRegexp(`Mark .* as bad`)).
					Confirm()

				t.Views().Information().Content(Contains("Bisecting"))
			}).
			NavigateToLine(Contains("initial commit")).
			Press(keys.Commits.ViewBisectOptions).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Bisect")).
					Select(MatchesRegexp(`Mark .* as good`)).
					Confirm()

				t.Views().Information().Content(Contains("Bisecting"))
			})

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("repo").IsSelected(),
				Contains("worktree2"),
			).
			NavigateToLine(Contains("worktree2")).
			Press(keys.Universal.Select).
			Lines(
				Contains("worktree2").IsSelected(),
				Contains("repo"),
			)
	},
})
View Source
var BareRepoWorktreeConfig = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Open lazygit in the worktree of a vcsh-style bare repo and add a file and commit",
	ExtraCmdArgs: []string{"--git-dir={{.actualPath}}/.bare"},
	Skip:         false,
	SetupConfig: func(config *config.AppConfig) {
		config.GetUserConfig().Gui.ShowFileTree = false
	},
	SetupRepo: func(shell *Shell) {

		shell.CreateFileAndAdd("a/b/c/blah", "blah\n")
		shell.Commit("initial commit")

		shell.CreateFileAndAdd(".gitignore", ".bare/\n/repo\n")
		shell.Commit("add .gitignore")

		shell.Chdir("..")

		shell.RunCommand([]string{"git", "--git-dir=./.bare", "init", "--shared=false"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "config", "core.bare", "false"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "config", "core.worktree", ".."})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "remote", "add", "origin", "./repo"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "checkout", "-b", "main"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "config", "branch.main.remote", "origin"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "config", "branch.main.merge", "refs/heads/master"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "fetch", "origin", "master"})
		shell.RunCommand([]string{"git", "--git-dir=./.bare", "-c", "merge.ff=true", "merge", "origin/master"})

		shell.DeleteFile("repo")

		shell.UpdateFile("a/b/c/blah", "updated content\n")
		shell.Chdir("a/b/c")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("main"),
			)

		t.Views().Commits().
			Lines(
				Contains("add .gitignore"),
				Contains("initial commit"),
			)

		t.Views().Files().
			IsFocused().
			Lines(
				Contains(" M a/b/c/blah"),
			).
			PressPrimaryAction().
			Press(keys.Files.CommitChanges)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("Add blah").
			Confirm()

		t.Views().Files().
			IsEmpty()

		t.Views().Commits().
			Lines(
				Contains("Add blah"),
				Contains("add .gitignore"),
				Contains("initial commit"),
			)
	},
})
View Source
var Crud = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "From the worktrees view, add a work tree, switch to it, switch back, and remove it",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("mybranch"),
			)

		t.Views().Status().
			Lines(
				Contains("repo → mybranch"),
			)

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)"),
			).
			Press(keys.Universal.New).
			Tap(func() {

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree for branch")).
					Type("newbranch").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Select(Contains("Other…")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree path")).
					Clear().
					Type("../linked-worktree").
					Confirm()
			}).
			Lines(
				Contains("linked-worktree").IsSelected(),
				Contains("(main worktree)"),
			).
			IsFocused()

		t.Views().Status().
			Lines(
				Contains("repo(linked-worktree) → newbranch"),
			)

		t.Views().Branches().
			Lines(
				Contains("newbranch"),
				Contains("mybranch"),
			)

		t.Views().Worktrees().
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Alert().
					Title(Equals("Error")).
					Content(Equals("You cannot remove the current worktree!")).
					Confirm()
			}).
			NavigateToLine(Contains("(main worktree)")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Alert().
					Title(Equals("Error")).
					Content(Equals("You cannot remove the main worktree!")).
					Confirm()
			}).
			Press(keys.Universal.Select).
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			)

		t.Views().Branches().
			Lines(
				Contains("mybranch"),
				Contains("newbranch"),
			)

		t.Views().Worktrees().
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'linked-worktree'?")).
					Select(MatchesRegexp("Remove worktree$")).
					Confirm()
			}).
			Lines(
				Contains("(main worktree)").IsSelected(),
			)
	},
})
View Source
var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Verify that custom commands work with worktrees by deleting a worktree via a custom command",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig: func(cfg *config.AppConfig) {
		cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
			{
				Key:     config.Keybinding{"d"},
				Context: "worktrees",
				Command: "git worktree remove {{ .SelectedWorktree.Path | quote }}",
			},
		}
	},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)"),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("linked-worktree")).
			Press(config.Keybinding{"d"}).
			Lines(
				Contains("(main worktree)"),
			)
	},
})
View Source
var DefaultPathTilde = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "A leading ~ in the worktree.defaultPath config is expanded to the home directory",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig: func(config *config.AppConfig) {
		config.GetUserConfig().Worktree.DefaultPath = "~/my-worktrees"
	},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New branch and worktree from 'mybranch'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					Type("newbranch").
					Confirm()

				home, _ := os.UserHomeDir()
				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					ContainsLines(
						Contains(filepath.Join(home, "my-worktrees", "newbranch")).DoesNotContain("~"),
					).
					Cancel()
			})
	},
})
View Source
var DetachWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Delete a branch that's checked out in another worktree by detaching that worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
				Contains("newbranch (worktree linked-worktree)"),
			).
			NavigateToLine(Contains("newbranch")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().
					Menu().
					Title(Equals("Delete branch 'newbranch'?")).
					Select(Contains("Delete local branch")).
					Confirm()
			}).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Branch newbranch is checked out by worktree linked-worktree")).
					Select(Contains("Detach worktree and delete branch")).
					Confirm()
			}).
			Lines(
				Contains("mybranch").IsSelected(),
			)

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			)
	},
})
View Source
var DotfileBareRepo = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Open lazygit in the worktree of a dotfile bare repo and add a file and commit",
	ExtraCmdArgs: []string{"--git-dir={{.actualPath}}/.bare", "--work-tree={{.actualPath}}/repo"},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {

		shell.DeleteFile(".git")

		shell.RunCommand([]string{"git", "init", "--bare", "../.bare"})
		shell.RunCommand([]string{"git", "--git-dir=../.bare", "--work-tree=.", "checkout", "-b", "mybranch"})
		shell.CreateFile("blah", "original content\n")

		shell.RunCommand([]string{"git", "--git-dir=../.bare", "--work-tree=.", "add", "blah"})
		shell.RunCommand([]string{"git", "--git-dir=../.bare", "--work-tree=.", "commit", "-m", "initial commit"})

		shell.UpdateFile("blah", "updated content\n")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("mybranch"),
			)

		t.Views().Commits().
			Lines(
				Contains("initial commit"),
			)

		t.Views().Files().
			IsFocused().
			Lines(
				Contains(" M blah"),
			).
			PressPrimaryAction().
			Press(keys.Files.CommitChanges)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("Add blah").
			Confirm()

		t.Views().Files().
			IsEmpty()

		t.Views().Commits().
			Lines(
				Contains("Add blah"),
				Contains("initial commit"),
			)
	},
})
View Source
var DoubleNestedLinkedSubmodule = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Open lazygit in a link to a repo's double nested submodules",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig: func(config *config.AppConfig) {
		config.GetUserConfig().Gui.ShowFileTree = false
	},
	SetupRepo: func(shell *Shell) {

		shell.CreateFileAndAdd("rootFile", "rootStuff")
		shell.Commit("initial repo commit")

		shell.Chdir("..")
		shell.CreateDir("innerSubmodule")
		shell.Chdir("innerSubmodule")
		shell.Init()
		shell.CreateFileAndAdd("a/b/c/blah", "blah\n")
		shell.Commit("initial inner commit")

		shell.Chdir("..")
		shell.CreateDir("outerSubmodule")
		shell.Chdir("outerSubmodule")
		shell.Init()
		shell.CreateFileAndAdd("foo", "foo")
		shell.Commit("initial outer commit")

		shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "add", "../innerSubmodule"})
		shell.Commit("add dependency as innerSubmodule")

		shell.Chdir("../repo")
		shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "add", "../outerSubmodule"})
		shell.Commit("add dependency as outerSubmodule")
		shell.Chdir("outerSubmodule")
		shell.RunCommand([]string{"git", "-c", "protocol.file.allow=always", "submodule", "update", "--init", "--recursive"})

		shell.Chdir("innerSubmodule")
		shell.UpdateFile("a/b/c/blah", "updated content\n")

		shell.Chdir("../../..")
		shell.RunCommand([]string{"ln", "-s", "repo/outerSubmodule/innerSubmodule/a/b/c", "link"})

		shell.Chdir("link")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("HEAD detached"),
				Contains("master"),
			)

		t.Views().Commits().
			Lines(
				Contains("initial inner commit"),
			)

		t.Views().Files().
			IsFocused().
			Lines(
				Contains(" M a/b/c/blah"),
			).
			PressPrimaryAction().
			Press(keys.Files.CommitChanges)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("Update blah").
			Confirm()

		t.Views().Files().
			IsEmpty()

		t.Views().Commits().
			Lines(
				Contains("Update blah"),
				Contains("initial inner commit"),
			)
	},
})

Even though this involves submodules, it's a worktree test since it's really exercising lazygit's ability to correctly do pathfinding in a complex use case.

View Source
var ExcludeFileInWorktree = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Add a file to .git/info/exclude in a worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.EmptyCommit("commit1")
		shell.AddWorktree("HEAD", "../linked-worktree", "mybranch")
		shell.CreateFile("../linked-worktree/toExclude", "")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			).
			SelectNextItem().
			PressPrimaryAction()

		t.Views().Files().
			Focus().
			Lines(
				Contains("toExclude"),
			).
			Press(keys.Files.IgnoreFile).
			Tap(func() {
				t.ExpectPopup().Menu().Title(Equals("Ignore or exclude file")).Select(Contains("Add to .git/info/exclude")).Confirm()
			}).
			IsEmpty()

		t.FileSystem().FileContent("../repo/.git/info/exclude", Contains("toExclude"))
	},
})
View Source
var FastForwardWorktreeBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Fast-forward a linked worktree branch from another worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {

		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("two")
		shell.EmptyCommit("three")
		shell.NewBranch("newbranch")

		shell.CloneIntoRemote("origin")
		shell.SetBranchUpstream("mybranch", "origin/mybranch")
		shell.SetBranchUpstream("newbranch", "origin/newbranch")

		shell.HardReset("HEAD^")
		shell.Checkout("mybranch")
		shell.HardReset("HEAD^")

		shell.AddWorktreeCheckout("newbranch", "../linked-worktree")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").Contains("↓1").IsSelected(),
				Contains("newbranch (worktree linked-worktree)").Contains("↓1"),
			).
			Press(keys.Branches.FastForward).
			Lines(
				Contains("mybranch").Contains("✓").IsSelected(),
				Contains("newbranch (worktree linked-worktree)").Contains("↓1"),
			).
			NavigateToLine(Contains("newbranch (worktree linked-worktree)")).
			Press(keys.Branches.FastForward).
			Lines(
				Contains("mybranch").Contains("✓"),
				Contains("newbranch (worktree linked-worktree)").Contains("✓").IsSelected(),
			)
	},
})
View Source
var FastForwardWorktreeBranchShouldNotPolluteCurrentWorktree = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Fast-forward a linked worktree branch from another worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {

		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("two")
		shell.EmptyCommit("three")
		shell.NewBranch("newbranch")

		shell.CloneIntoRemote("origin")
		shell.SetBranchUpstream("mybranch", "origin/mybranch")
		shell.SetBranchUpstream("newbranch", "origin/newbranch")

		shell.HardReset("HEAD^")
		shell.Checkout("mybranch")
		shell.HardReset("HEAD^")

		shell.AddWorktreeCheckout("newbranch", "../linked-worktree")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").Contains("↓1").IsSelected(),
				Contains("newbranch (worktree linked-worktree)").Contains("↓1"),
			).
			Press(keys.Branches.FastForward).
			Lines(
				Contains("mybranch").Contains("✓").IsSelected(),
				Contains("newbranch (worktree linked-worktree)").Contains("↓1"),
			).
			NavigateToLine(Contains("newbranch (worktree linked-worktree)")).
			Press(keys.Branches.FastForward).
			Lines(
				Contains("mybranch").Contains("✓"),
				Contains("newbranch (worktree linked-worktree)").Contains("✓").IsSelected(),
			).
			NavigateToLine(Contains("mybranch"))

		t.Views().Files().
			Focus().
			Press(keys.Files.RefreshFiles).
			LineCount(EqualsInt(0))
	},
})
View Source
var ForceRemoveWorktree = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Force remove a dirty worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
		shell.AddFileInWorktreeOrSubmodule("../linked-worktree", "file", "content")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'linked-worktree'?")).
					Select(MatchesRegexp("Remove worktree$")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Remove worktree")).
					Content(Equals("'linked-worktree' contains modified or untracked files, or submodules (or all of these). Are you sure you want to remove it?")).
					Confirm()
			}).
			Lines(
				Contains("(main worktree)").IsSelected(),
			)
	},
})
View Source
var ForceRemoveWorktreeWithSubmodules = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Force remove a worktree that contains submodules",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.CloneIntoSubmodule("submodule", "submodule")
		shell.Commit("Add submodule")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
		shell.RunCommand([]string{"git", "-C", "../linked-worktree", "submodule", "update", "--init"})
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'linked-worktree'?")).
					Select(MatchesRegexp("Remove worktree$")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Remove worktree")).
					Content(Equals("'linked-worktree' contains modified or untracked files, or submodules (or all of these). Are you sure you want to remove it?")).
					Confirm()
			}).
			Lines(
				Contains("(main worktree)").IsSelected(),
			)
	},
})
View Source
var LocationCandidates = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "The location menu offers the parents of existing worktrees and the configured default path, and sanitizes the typed name",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig: func(config *config.AppConfig) {
		config.GetUserConfig().Worktree.DefaultPath = "../config-worktrees"
	},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")

		shell.RunCommand([]string{"git", "worktree", "add", "-b", "existing", "../manual-worktrees/existing"})
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.NewWorktree).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("New worktree")).
					Select(Contains("New branch and worktree from 'mybranch'")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New branch and worktree name")).
					Type("new feature").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					ContainsLines(
						Contains("manual-worktrees").Contains("new-feature"),
						Contains("config-worktrees").Contains("new-feature"),
					).
					Cancel()
			})
	},
})
View Source
var NewWorktreePicker = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "From the worktrees panel, the picker suggests only branches not already checked out, guards verbatim type-ins, and checks out an existing branch",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.NewBranchFrom("feature", "mybranch")
		shell.Checkout("mybranch")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)"),
			).
			Press(keys.Universal.New).
			Tap(func() {

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree for branch")).
					SuggestionLines(Contains("feature")).
					Type("mybranch").
					Confirm()

				t.ExpectPopup().Alert().
					Title(Equals("Error")).
					Content(Contains("Branch mybranch is checked out by worktree repo")).
					Confirm()
			}).
			Press(keys.Universal.New).
			Tap(func() {

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree for branch")).
					Type("feature").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			}).
			IsFocused().
			Lines(
				Contains("feature").IsSelected(),
				Contains("(main worktree)"),
			)

		t.Views().Status().
			Content(Contains("repo(feature) → feature"))
	},
})
View Source
var NewWorktreePickerRemote = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "From the worktrees panel, picking a remote branch creates a new local tracking branch and worktree; remote branches whose local branch already exists are filtered out",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.NewBranch("feature")
		shell.NewBranch("existing")
		shell.CloneIntoRemote("origin")
		shell.Checkout("master")

		shell.RunCommand([]string{"git", "branch", "-D", "feature"})
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Press(keys.Universal.New).
			Tap(func() {

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree for branch")).
					SuggestionLines(
						Contains("existing"),
						Contains("origin/feature"),
					).
					Type("origin/feature").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Confirm()
			}).
			IsFocused().
			Lines(
				Contains("feature").IsSelected(),
				Contains("(main worktree)"),
			)

		t.Views().Branches().
			Focus().
			ContainsLines(
				Contains("feature").Contains("✓").IsSelected(),
			)
	},
})
View Source
var RemoveWorktreeAndBothBranches = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "From the worktrees panel, remove a worktree and delete both its local and remote branch in one go",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.CloneIntoRemote("origin")
		shell.EmptyCommit("initial commit")
		shell.NewBranch("mybranch")
		shell.EmptyCommit("commit on mybranch")
		shell.PushBranchAndSetUpstream("origin", "mybranch")
		shell.EmptyCommit("commit not pushed to the remote")
		shell.Checkout("master")
		shell.AddWorktreeCheckout("mybranch", "../linked-worktree")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'linked-worktree'?")).
					Select(Contains("Remove worktree and delete local and remote branch")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Force delete branch")).
					Content(Equals("'mybranch' is not fully merged. Are you sure you want to delete it?")).
					Confirm()
			}).
			Lines(
				Contains("(main worktree)").IsSelected(),
			)

		t.Views().Remotes().
			Focus().
			Lines(Contains("origin")).
			PressEnter()

		t.Views().RemoteBranches().
			IsEmpty()

		t.Views().Branches().
			Focus().
			Lines(
				Contains("master").IsSelected(),
			)
	},
})
View Source
var RemoveWorktreeAndBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "From the worktrees panel, remove a worktree and delete its branch in one go",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.NewBranch("newbranch")
		shell.EmptyCommit("commit on newbranch")
		shell.Checkout("mybranch")
		shell.AddWorktreeCheckout("newbranch", "../linked-worktree")
		shell.RunCommand([]string{"git", "worktree", "add", "--detach", "../detached-worktree"})
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("detached-worktree"),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("detached-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'detached-worktree'?")).
					Select(Contains("Remove worktree and delete branch")).
					Tooltip(Contains("This worktree is not checked out on a branch")).
					Select(Contains("Remove worktree and delete local and remote branch")).
					Tooltip(Contains("This worktree is not checked out on a branch")).
					Cancel()
			}).
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Remove worktree 'linked-worktree'?")).
					Select(Contains("Remove worktree and delete local and remote branch")).
					Tooltip(Contains("The selected branch has no upstream")).
					Select(Contains("Remove worktree and delete branch")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Force delete branch")).
					Content(Equals("'newbranch' is not fully merged. Are you sure you want to delete it?")).
					Confirm()
			}).
			Lines(
				Contains("(main worktree)"),
				Contains("detached-worktree"),
			)

		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
			)
	},
})
View Source
var RemoveWorktreeAndDeleteLocalAndRemoteBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Delete the local branch, the remote branch, and the worktree of a single branch checked out in another worktree, all at once",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.CloneIntoRemote("origin")
		shell.EmptyCommit("initial commit")
		shell.NewBranch("mybranch")
		shell.EmptyCommit("commit on mybranch")
		shell.PushBranchAndSetUpstream("origin", "mybranch")
		shell.EmptyCommit("commit not pushed to the remote")
		shell.Checkout("master")
		shell.AddWorktreeCheckout("mybranch", "../linked-worktree")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("master").IsSelected(),
				Contains("mybranch (worktree linked-worktree)"),
			).
			NavigateToLine(Contains("mybranch")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Delete branch 'mybranch'?")).
					Select(Contains("Delete local and remote branch")).
					Confirm()
			}).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Branch mybranch is checked out by worktree linked-worktree")).
					Select(Contains("Remove worktree and delete local and remote branch")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Force delete branch")).
					Content(Equals("'mybranch' is not fully merged. Are you sure you want to delete it?")).
					Confirm()
			}).
			Lines(
				Contains("master").IsSelected(),
			)

		t.Views().Remotes().
			Focus().
			Lines(Contains("origin")).
			PressEnter()

		t.Views().RemoteBranches().
			IsEmpty()

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
			)
	},
})
View Source
var RemoveWorktreeFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Delete a branch that's checked out in another worktree by removing that worktree",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
		shell.AddFileInWorktreeOrSubmodule("../linked-worktree", "file", "content")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Focus().
			Lines(
				Contains("mybranch").IsSelected(),
				Contains("newbranch (worktree linked-worktree)"),
			).
			NavigateToLine(Contains("newbranch")).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().
					Menu().
					Title(Equals("Delete branch 'newbranch'?")).
					Select(Contains("Delete local branch")).
					Confirm()
			}).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Branch newbranch is checked out by worktree linked-worktree")).
					Select(Contains("Remove worktree and delete branch")).
					Confirm()

				t.ExpectPopup().Confirmation().
					Title(Equals("Remove worktree")).
					Content(Equals("'linked-worktree' contains modified or untracked files, or submodules (or all of these). Are you sure you want to remove it?")).
					Confirm()
			}).
			Lines(
				Contains("mybranch").IsSelected(),
			)

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
			)
	},
})
View Source
var ResetWindowTabs = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Verify that window tabs are reset whenever switching repos",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
		shell.EmptyCommit("commit 2")
		shell.EmptyCommit("commit 3")
		shell.AddWorktree("mybranch", "../linked-worktree", "newbranch")
		shell.AddFileInWorktreeOrSubmodule("../linked-worktree", "file", "content")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {

		t.Views().Remotes().
			Focus()

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			).
			NavigateToLine(Contains("linked-worktree")).
			Press(keys.Universal.Select).
			Lines(
				Contains("linked-worktree").IsSelected(),
				Contains("(main worktree)"),
			).
			Press(keys.Universal.NextBlock)

		t.Views().Branches().
			IsFocused()
	},
})
View Source
var SymlinkIntoRepoSubdir = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Open lazygit in a symlink into a repo's subdirectory",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig: func(config *config.AppConfig) {
		config.GetUserConfig().Gui.ShowFileTree = false
	},
	SetupRepo: func(shell *Shell) {

		shell.CreateFileAndAdd("a/b/c/blah", "blah\n")
		shell.Commit("initial commit")
		shell.UpdateFile("a/b/c/blah", "updated content\n")

		shell.Chdir("..")
		shell.RunCommand([]string{"ln", "-s", "repo/a/b/c", "link"})

		shell.Chdir("link")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("master"),
			)

		t.Views().Commits().
			Lines(
				Contains("initial commit"),
			)

		t.Views().Files().
			IsFocused().
			Lines(
				Contains(" M a/b/c/blah"),
			).
			PressPrimaryAction().
			Press(keys.Files.CommitChanges)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("Add blah").
			Confirm()

		t.Views().Files().
			IsEmpty()

		t.Views().Commits().
			Lines(
				Contains("Add blah"),
				Contains("initial commit"),
			)
	},
})
View Source
var WorktreeInRepo = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Add a worktree inside the repo, then remove the directory and confirm the worktree is removed",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("mybranch")
		shell.CreateFileAndAdd("README.md", "hello world")
		shell.Commit("initial commit")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Branches().
			Lines(
				Contains("mybranch"),
			)

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)"),
			).
			Press(keys.Universal.New).
			Tap(func() {
				t.ExpectPopup().Prompt().
					Title(Equals("New worktree for branch")).
					Type("newbranch").
					Confirm()

				t.ExpectPopup().Menu().
					Title(Equals("Worktree location")).
					Select(Contains("Other…")).
					Confirm()

				t.ExpectPopup().Prompt().
					Title(Equals("New worktree path")).
					Clear().
					Type("linked-worktree").
					Confirm()
			}).
			Lines(
				Contains("linked-worktree").IsSelected(),
				Contains("(main worktree)"),
			).
			NavigateToLine(Contains("(main worktree)")).
			Press(keys.Universal.Select).
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree"),
			)

		t.Views().Files().
			Focus().
			Lines(
				Contains("linked-worktree"),
			).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().Menu().
					Title(Equals("Discard changes")).
					Select(Contains("Discard all changes")).
					Confirm()
			}).
			IsEmpty()

		t.Views().Worktrees().
			Focus().
			Lines(
				Contains("(main worktree)").IsSelected(),
				Contains("linked-worktree (missing)"),
			)
	},
})

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL