mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-12 02:16:50 +00:00
Open files of a branch's diff from the worktree that has it checked out (#6104)
If you enter a branch in the branches panel and press `e` on a line of a commit's diff, or on a file in the commit files panel entered from there, lazygit opens the file in the current worktree. If another worktree has the branch checked out, this is the wrong copy of the file. The current worktree may have it in a different state, or not have it at all. Clicking a lazygit-edit:// hyperlink of a diff renderer in such a diff has the same problem. Fix this by opening the file from the worktree that has the branch checked out instead, and carry the line number forward to the file as that worktree has it. If no other worktree has the branch checked out, the file is still opened from the current worktree. Also, fix the same problem for the `o` command, and for the "Absolute path" entry in the "Copy to clipboard" menu. Finally, fix a long-standing annoyance when trying to open a file that has since been deleted or renamed; in that case, most editors would create it as a new, empty file, which is not useful. Show an error message in that case instead. Fixes #6102.
This commit is contained in:
@@ -186,15 +186,26 @@ func (self *DiffCommands) CustomPatchDiffCmdObj(dir string, mode DiffMode) *osco
|
||||
// want to restrict the diff to specific paths, pass them in additionalArgs
|
||||
// after the `--`.
|
||||
func (self *DiffCommands) GetDiff(staged bool, additionalArgs ...string) (string, error) {
|
||||
return self.getDiffCmdObj(self.repoPaths.worktreePath, staged, additionalArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
// GetDiffInOtherWorktree is GetDiff without --staged, run in the worktree at
|
||||
// worktreePath. That has to be a worktree of our repo other than the current one. A
|
||||
// diff against the working copy is then a diff against that worktree's files.
|
||||
func (self *DiffCommands) GetDiffInOtherWorktree(worktreePath string, additionalArgs ...string) (string, error) {
|
||||
return ForOtherRepo(self.getDiffCmdObj(worktreePath, false, additionalArgs)).RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *DiffCommands) getDiffCmdObj(worktreePath string, staged bool, additionalArgs []string) *oscommands.CmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff").
|
||||
ParseableDiffPrefixes().
|
||||
Arg("--no-ext-diff", "--no-color").
|
||||
ArgIf(staged, "--staged").
|
||||
Dir(self.repoPaths.worktreePath).
|
||||
Dir(worktreePath).
|
||||
Arg(additionalArgs...).
|
||||
ToArgv(),
|
||||
).DontLog().RunWithOutput()
|
||||
).DontLog()
|
||||
}
|
||||
|
||||
type DiffToolCmdOptions struct {
|
||||
|
||||
@@ -24,6 +24,7 @@ var (
|
||||
_ types.IFilterableContext = (*CommitFilesContext)(nil)
|
||||
_ types.DiffMainViewContext = (*CommitFilesContext)(nil)
|
||||
_ types.PullRequestDiffContext = (*CommitFilesContext)(nil)
|
||||
_ types.BranchDiffContext = (*CommitFilesContext)(nil)
|
||||
)
|
||||
|
||||
func (self *CommitFilesContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
@@ -40,6 +41,15 @@ func (self *CommitFilesContext) PullRequestDiff() types.PullRequestDiff {
|
||||
return types.PullRequestDiff{}
|
||||
}
|
||||
|
||||
// BranchOfDiff asks the panel this one was entered from, since the files listed here
|
||||
// are those of the commits selected there.
|
||||
func (self *CommitFilesContext) BranchOfDiff() *models.Branch {
|
||||
if parent, ok := self.GetParentContext().(types.BranchDiffContext); ok {
|
||||
return parent.BranchOfDiff()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||
|
||||
@@ -26,6 +26,7 @@ var (
|
||||
_ types.ISearchableContext = (*SubCommitsContext)(nil)
|
||||
_ types.DiffMainViewContext = (*SubCommitsContext)(nil)
|
||||
_ types.PullRequestDiffContext = (*SubCommitsContext)(nil)
|
||||
_ types.BranchDiffContext = (*SubCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func (self *SubCommitsContext) GetDiffMainViewType() types.DiffMainViewType {
|
||||
@@ -49,6 +50,13 @@ func (self *SubCommitsContext) PullRequestDiff() types.PullRequestDiff {
|
||||
self.GetCommits(), startIdx, endIdx, branch.Name, self.c.Model().Branches, self.c.Model().PullRequestsMap)
|
||||
}
|
||||
|
||||
// BranchOfDiff is nil if the panel was entered from a tag, a remote branch or the
|
||||
// reflog instead of a local branch.
|
||||
func (self *SubCommitsContext) BranchOfDiff() *models.Branch {
|
||||
branch, _ := self.GetRef().(*models.Branch)
|
||||
return branch
|
||||
}
|
||||
|
||||
func NewSubCommitsContext(
|
||||
c *ContextCommon,
|
||||
) *SubCommitsContext {
|
||||
|
||||
@@ -65,7 +65,8 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
searchHelper,
|
||||
)
|
||||
diffLineHelper := helpers.NewDiffLineHelper(helperCommon)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon, diffLineHelper)
|
||||
filesHelper := helpers.NewFilesHelper(helperCommon)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon, diffLineHelper, filesHelper)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
helperCommon,
|
||||
rebaseHelper,
|
||||
@@ -92,7 +93,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
CustomPatch: customPatchHelper,
|
||||
Bisect: bisectHelper,
|
||||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon),
|
||||
Files: filesHelper,
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper, rebaseHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon, commitsHelper, gpgHelper),
|
||||
BranchesHelper: helpers.NewBranchesHelper(helperCommon, worktreeHelper),
|
||||
|
||||
@@ -3,7 +3,6 @@ package controllers
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -267,7 +266,7 @@ func (self *CommitFilesController) openCopyMenu() error {
|
||||
copyAbsolutePathItem := &types.MenuItem{
|
||||
Label: self.c.Tr.CopyAbsoluteFilePath,
|
||||
OnPress: func() error {
|
||||
absPath, err := filepath.Abs(node.GetPath())
|
||||
absPath, err := self.c.Helpers().Diff.AbsolutePathOfFileOfDiff(node.GetPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -428,11 +427,11 @@ func (self *CommitFilesController) canDiscardFileChanges(nodes []*filetree.Commi
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
|
||||
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||
return self.c.Helpers().Diff.OpenFileOfDiff(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) edit(nodes []*filetree.CommitFileNode) error {
|
||||
return self.c.Helpers().Files.EditFiles(lo.FilterMap(nodes,
|
||||
return self.c.Helpers().Diff.EditFilesOfDiff(lo.FilterMap(nodes,
|
||||
func(node *filetree.CommitFileNode, _ int) (string, bool) {
|
||||
return node.GetPath(), node.IsFile()
|
||||
}))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -10,18 +12,21 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type DiffHelper struct {
|
||||
c *HelperCommon
|
||||
diffLineHelper *DiffLineHelper
|
||||
filesHelper *FilesHelper
|
||||
}
|
||||
|
||||
func NewDiffHelper(c *HelperCommon, diffLineHelper *DiffLineHelper) *DiffHelper {
|
||||
func NewDiffHelper(c *HelperCommon, diffLineHelper *DiffLineHelper, filesHelper *FilesHelper) *DiffHelper {
|
||||
return &DiffHelper{
|
||||
c: c,
|
||||
diffLineHelper: diffLineHelper,
|
||||
filesHelper: filesHelper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,13 +229,144 @@ func (self *DiffHelper) OpenDiffToolForRef(selectedRef models.Ref) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// EditFilesOfDiff opens files of the diff that the current side panel shows in the
|
||||
// editor. They are opened from the worktree of the diff (see otherWorktreeOfDiff).
|
||||
// Files that don't exist there are left out, and if none of them exist, it returns an
|
||||
// error (see existingPathsInWorktreeOfDiff).
|
||||
func (self *DiffHelper) EditFilesOfDiff(paths []string) error {
|
||||
existingPaths, err := self.existingPathsInWorktreeOfDiff(paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.filesHelper.EditFiles(existingPaths)
|
||||
}
|
||||
|
||||
// EditFileOfDiffAtLine opens a file of the diff shown in the given view in the editor,
|
||||
// at a line of that diff. The line is carried forward to where it is in the file now
|
||||
// (see AdjustLineNumber). Like EditFilesOfDiff, it opens the file from the worktree of
|
||||
// the diff, and returns an error if the file doesn't exist there.
|
||||
func (self *DiffHelper) EditFileOfDiffAtLine(path string, lineNumber int, viewName string) error {
|
||||
path, err := self.existingPathInWorktreeOfDiff(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lineNumber = self.AdjustLineNumber(path, lineNumber, viewName)
|
||||
return self.filesHelper.EditFileAtLine(path, lineNumber)
|
||||
}
|
||||
|
||||
// OpenFileOfDiff opens a file of the diff that the current side panel shows with the
|
||||
// default application for it. Like EditFilesOfDiff, it opens the file from the
|
||||
// worktree of the diff, and returns an error if the file doesn't exist there.
|
||||
func (self *DiffHelper) OpenFileOfDiff(path string) error {
|
||||
path, err := self.existingPathInWorktreeOfDiff(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.filesHelper.OpenFile(path)
|
||||
}
|
||||
|
||||
// existingPathsInWorktreeOfDiff returns the absolute paths of those of the given files
|
||||
// of the diff that exist in the worktree of the diff. A file of a diff may be missing
|
||||
// there, for example if a later commit deleted it, or if the worktree has a branch
|
||||
// checked out that doesn't have it. An editor would create such a file as a new, empty
|
||||
// one, so it is left out.
|
||||
//
|
||||
// If none of the files exist, it returns an error. If only some of them are missing, it
|
||||
// leaves them out silently. The user would see such an error only after returning from
|
||||
// the editor.
|
||||
func (self *DiffHelper) existingPathsInWorktreeOfDiff(paths []string) ([]string, error) {
|
||||
absPaths := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
absPath, err := self.AbsolutePathOfFileOfDiff(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absPaths = append(absPaths, absPath)
|
||||
}
|
||||
|
||||
existingPaths := lo.Filter(absPaths, func(path string, _ int) bool {
|
||||
exists, err := self.c.OS().FileExists(path)
|
||||
// If we can't tell, the editor reports the problem
|
||||
return exists || err != nil
|
||||
})
|
||||
if len(existingPaths) > 0 {
|
||||
return existingPaths, nil
|
||||
}
|
||||
|
||||
if len(absPaths) == 1 {
|
||||
return nil, errors.New(utils.ResolvePlaceholderString(
|
||||
self.c.Tr.FileOfDiffDoesNotExist, map[string]string{"path": absPaths[0]}))
|
||||
}
|
||||
worktreePath, ok := self.otherWorktreeOfDiff()
|
||||
if !ok {
|
||||
worktreePath = self.c.Git().RepoPaths.WorktreePath()
|
||||
}
|
||||
return nil, errors.New(utils.ResolvePlaceholderString(
|
||||
self.c.Tr.NoFileOfDiffExists, map[string]string{"worktreePath": worktreePath}))
|
||||
}
|
||||
|
||||
// existingPathInWorktreeOfDiff is existingPathsInWorktreeOfDiff for a single file.
|
||||
func (self *DiffHelper) existingPathInWorktreeOfDiff(path string) (string, error) {
|
||||
existingPaths, err := self.existingPathsInWorktreeOfDiff([]string{path})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return existingPaths[0], nil
|
||||
}
|
||||
|
||||
// AbsolutePathOfFileOfDiff returns the absolute path of a file of the diff that the
|
||||
// current side panel shows, in the worktree that EditFilesOfDiff opens it from.
|
||||
func (self *DiffHelper) AbsolutePathOfFileOfDiff(path string) (string, error) {
|
||||
return filepath.Abs(self.pathInWorktreeOfDiff(path))
|
||||
}
|
||||
|
||||
// otherWorktreeOfDiff returns the path of the worktree to open files of the diff in
|
||||
// the current side panel from, if that isn't the current worktree. If the diff is of a
|
||||
// commit of a branch, and another worktree has that branch checked out, that is the
|
||||
// other worktree. It has the files as the branch has them; the current worktree may
|
||||
// have them in a different state, or not at all.
|
||||
func (self *DiffHelper) otherWorktreeOfDiff() (string, bool) {
|
||||
branchDiffContext, ok := self.c.Context().CurrentSide().(types.BranchDiffContext)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
branch := branchDiffContext.BranchOfDiff()
|
||||
if branch == nil {
|
||||
return "", false
|
||||
}
|
||||
worktree, ok := git_commands.WorktreeForBranch(branch, self.c.Model().Worktrees)
|
||||
if !ok || worktree.IsCurrent || worktree.IsPathMissing {
|
||||
return "", false
|
||||
}
|
||||
return worktree.Path, true
|
||||
}
|
||||
|
||||
// pathInWorktreeOfDiff returns the path to open a file of the diff from. The path
|
||||
// passed in is either relative to the current worktree, or an absolute path in it, as
|
||||
// a diff renderer may state paths.
|
||||
func (self *DiffHelper) pathInWorktreeOfDiff(path string) string {
|
||||
worktreePath, ok := self.otherWorktreeOfDiff()
|
||||
if !ok {
|
||||
return path
|
||||
}
|
||||
if filepath.IsAbs(path) {
|
||||
relativePath := repoRelativePath(self.c.Git().RepoPaths.WorktreePath(), path)
|
||||
if relativePath == "" {
|
||||
return path
|
||||
}
|
||||
path = relativePath
|
||||
}
|
||||
return filepath.Join(worktreePath, path)
|
||||
}
|
||||
|
||||
// AdjustLineNumber is used to adjust a line number in the diff that's currently
|
||||
// being viewed, so that it corresponds to the line number in the actual working
|
||||
// copy state of the file. It is used when clicking on a delta hyperlink in a
|
||||
// diff, or when pressing `e` in a focused diff. It works
|
||||
// by getting a diff of what's being viewed in the main view against the working
|
||||
// copy, and then using that diff to adjust the line number.
|
||||
// path is the file path of the file being viewed
|
||||
// copy, and then using that diff to adjust the line number. The working copy is that
|
||||
// of the worktree the file is opened from (see otherWorktreeOfDiff).
|
||||
// path is the file path of the file being viewed, in that worktree
|
||||
// linenumber is the line number to adjust (one-based)
|
||||
// viewname is the name of the view that shows the diff. We need to pass it
|
||||
// because the diff adjustment is slightly different depending on which view is
|
||||
@@ -258,7 +394,13 @@ func (self *DiffHelper) AdjustLineNumber(path string, linenumber int, viewname s
|
||||
|
||||
func (self *DiffHelper) adjustLineNumber(linenumber int, diffArgs ...string) int {
|
||||
args := append([]string{"--unified=0"}, diffArgs...)
|
||||
diff, err := self.c.Git().Diff.GetDiff(false, args...)
|
||||
var diff string
|
||||
var err error
|
||||
if worktreePath, ok := self.otherWorktreeOfDiff(); ok {
|
||||
diff, err = self.c.Git().Diff.GetDiffInOtherWorktree(worktreePath, args...)
|
||||
} else {
|
||||
diff, err = self.c.Git().Diff.GetDiff(false, args...)
|
||||
}
|
||||
if err != nil {
|
||||
return linenumber
|
||||
}
|
||||
|
||||
@@ -1085,13 +1085,10 @@ func (self *MainViewController) editDiffLine(viewLine int, beforeEdit func()) er
|
||||
// it opens the file without jumping anywhere — as pressing edit on a file in a side
|
||||
// panel does.
|
||||
if info.Type == types.DiffLineFileHeader {
|
||||
return self.c.Helpers().Files.EditFiles([]string{info.Path})
|
||||
return self.c.Helpers().Diff.EditFilesOfDiff([]string{info.Path})
|
||||
}
|
||||
|
||||
// The diff may be of an older commit, whose line numbers aren't the file's current
|
||||
// ones, so they have to be carried forward before we can point an editor at them.
|
||||
lineNumber := self.c.Helpers().Diff.AdjustLineNumber(info.Path, info.NewLine, self.context.GetViewName())
|
||||
return self.c.Helpers().Files.EditFileAtLine(info.Path, lineNumber)
|
||||
return self.c.Helpers().Diff.EditFileOfDiffAtLine(info.Path, info.NewLine, self.context.GetViewName())
|
||||
}
|
||||
|
||||
// openPullRequestAtSelectedLine opens the pull request of the branch whose commit the
|
||||
|
||||
+2
-3
@@ -417,10 +417,9 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||
filepath := matches[1]
|
||||
if matches[2] != "" {
|
||||
lineNumber := utils.MustConvertToInt(matches[2])
|
||||
lineNumber = gui.helpers.Diff.AdjustLineNumber(filepath, lineNumber, viewname)
|
||||
return gui.helpers.Files.EditFileAtLine(filepath, lineNumber)
|
||||
return gui.helpers.Diff.EditFileOfDiffAtLine(filepath, lineNumber, viewname)
|
||||
}
|
||||
return gui.helpers.Files.EditFiles([]string{filepath})
|
||||
return gui.helpers.Diff.EditFilesOfDiff([]string{filepath})
|
||||
}
|
||||
|
||||
if entry, ok := strings.CutPrefix(url, helpers.DiffStatLinkScheme); ok {
|
||||
|
||||
@@ -253,6 +253,19 @@ type PullRequestDiff struct {
|
||||
BaseHash string
|
||||
}
|
||||
|
||||
// BranchDiffContext is implemented by the side panel contexts that show, in their
|
||||
// focused main view, the diff of a commit of a branch entered from the branches panel:
|
||||
// the sub-commits panel, and the commit files panel entered from it. If another
|
||||
// worktree has that branch checked out, files of the diff are opened from there (see
|
||||
// DiffHelper.EditFilesOfDiff).
|
||||
type BranchDiffContext interface {
|
||||
Context
|
||||
|
||||
// BranchOfDiff returns the local branch whose commits the panel shows the diff of,
|
||||
// or nil if the panel wasn't entered from a local branch.
|
||||
BranchOfDiff() *models.Branch
|
||||
}
|
||||
|
||||
// DiffPaneContext is one of the two panes the main section can show, as the thing
|
||||
// that holds a diff with a selection in it. The panels that act on such a selection
|
||||
// are handed the pane it was made in, and speak to it through this.
|
||||
|
||||
@@ -422,6 +422,8 @@ type TranslationSet struct {
|
||||
JumpToFileInDiffTooltip string
|
||||
OnlyOneFileInDiff string
|
||||
NoFileInDiffNamed string
|
||||
FileOfDiffDoesNotExist string
|
||||
NoFileOfDiffExists string
|
||||
PrevConflict string
|
||||
NextConflict string
|
||||
SelectPrevHunk string
|
||||
@@ -1617,6 +1619,8 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
JumpToFileInDiffTooltip: "Pick one of the files of the diff shown in the main view, and scroll the main view to it. The focus stays in this panel.",
|
||||
OnlyOneFileInDiff: "There is only one file in this diff",
|
||||
NoFileInDiffNamed: "This diff has no file named '{{.path}}'",
|
||||
FileOfDiffDoesNotExist: "File '{{.path}}' doesn't exist in the working tree",
|
||||
NoFileOfDiffExists: "None of the selected files exist in the working tree at '{{.worktreePath}}'",
|
||||
PrevConflict: "Previous conflict",
|
||||
NextConflict: "Next conflict",
|
||||
SelectPrevHunk: "Previous hunk",
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var EditFilesMissingFromWorkingTree = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Edit or open files of a commit that a later commit deleted",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Gui.UseHunkModeInDiffView = false
|
||||
config.GetUserConfig().OS.Edit = "echo {{filename}} > edit-command"
|
||||
config.GetUserConfig().OS.EditAtLine = "echo {{filename}}:{{line}} > edit-command"
|
||||
config.GetUserConfig().OS.Open = "echo {{filename}} > open-command"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file1", "one\n")
|
||||
shell.CreateFileAndAdd("file2", "two\n")
|
||||
shell.CreateFileAndAdd("file3", "three\n")
|
||||
shell.Commit("add files")
|
||||
shell.DeleteFileAndAdd("file2")
|
||||
shell.DeleteFileAndAdd("file3")
|
||||
shell.Commit("delete files")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("delete files").IsSelected(),
|
||||
Contains("add files"),
|
||||
).
|
||||
NavigateToLine(Contains("add files")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ /").IsSelected(),
|
||||
Contains("A file1"),
|
||||
Contains("A file2"),
|
||||
Contains("A file3"),
|
||||
).
|
||||
NavigateToLine(Contains("file2")).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("/repo/file2' doesn't exist in the working tree")).
|
||||
Confirm()
|
||||
}).
|
||||
Press(keys.Universal.OpenFile).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("/repo/file2' doesn't exist in the working tree")).
|
||||
Confirm()
|
||||
}).
|
||||
Press(keys.Universal.FocusMainView)
|
||||
|
||||
t.Views().Main().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("+two")).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("/repo/file2' doesn't exist in the working tree")).
|
||||
Confirm()
|
||||
}).
|
||||
PressEscape()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("A file2"),
|
||||
Contains("A file3"),
|
||||
).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("None of the selected files exist in the working tree at '")).
|
||||
Confirm()
|
||||
}).
|
||||
// If only some of the files are missing, the others are opened without
|
||||
// complaint
|
||||
NavigateToLine(Contains("file1")).
|
||||
SelectedLines(
|
||||
Contains("A file1"),
|
||||
Contains("A file2"),
|
||||
).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.FileSystem().FileContent("edit-command",
|
||||
Contains("/repo/file1\n").DoesNotContain("file2"))
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -135,6 +135,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.DiscardRenamedFile,
|
||||
commit.DiscardSubmoduleChanges,
|
||||
commit.DoNotShowBranchMarkerForHeadCommit,
|
||||
commit.EditFilesMissingFromWorkingTree,
|
||||
commit.FailHooksThenCommitNoHooks,
|
||||
commit.FindBaseCommitForFixup,
|
||||
commit.FindBaseCommitForFixupDisregardFixupsForSameBaseCommit,
|
||||
@@ -694,6 +695,7 @@ var tests = []*components.IntegrationTest{
|
||||
worktree.DetachWorktreeFromBranch,
|
||||
worktree.DotfileBareRepo,
|
||||
worktree.DoubleNestedLinkedSubmodule,
|
||||
worktree.EditFileOfBranchInOtherWorktree,
|
||||
worktree.ExcludeFileInWorktree,
|
||||
worktree.FastForwardWorktreeBranch,
|
||||
worktree.FastForwardWorktreeBranchShouldNotPolluteCurrentWorktree,
|
||||
@@ -702,6 +704,7 @@ var tests = []*components.IntegrationTest{
|
||||
worktree.LocationCandidates,
|
||||
worktree.NewWorktreePicker,
|
||||
worktree.NewWorktreePickerRemote,
|
||||
worktree.OpenFileOfBranchInOtherWorktree,
|
||||
worktree.RemoveWorktreeAndBothBranches,
|
||||
worktree.RemoveWorktreeAndBranch,
|
||||
worktree.RemoveWorktreeAndDeleteLocalAndRemoteBranch,
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package worktree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var EditFileOfBranchInOtherWorktree = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Edit a file of a commit of a branch that is checked out in another worktree",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Gui.UseHunkModeInDiffView = false
|
||||
config.GetUserConfig().OS.Edit = "echo {{filename}} > edit-command"
|
||||
config.GetUserConfig().OS.EditAtLine = "echo {{filename}}:{{line}} > edit-command"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("README.md", "hello world")
|
||||
shell.Commit("initial commit")
|
||||
shell.NewBranch("mybranch")
|
||||
shell.CreateFileAndAdd("file.txt", "1\n2\n3\n")
|
||||
shell.Commit("add file")
|
||||
shell.Checkout("master")
|
||||
shell.AddWorktreeCheckout("mybranch", "../linked-worktree")
|
||||
|
||||
// Move the lines of the file down in the linked worktree, so that a line of
|
||||
// the commit's diff is at a different line of the file there
|
||||
shell.UpdateFile("../linked-worktree/file.txt", "0\n1\n2\n3\n")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("master").IsSelected(),
|
||||
Contains("mybranch (worktree linked-worktree)"),
|
||||
).
|
||||
NavigateToLine(Contains("mybranch")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("add file").IsSelected(),
|
||||
Contains("initial commit"),
|
||||
).
|
||||
Press(keys.Universal.FocusMainView)
|
||||
|
||||
t.Views().Main().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("+2")).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.FileSystem().FileContent("edit-command", Contains("/linked-worktree/file.txt:3\n"))
|
||||
}).
|
||||
PressEscape()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("A file.txt").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
t.FileSystem().FileContent("edit-command", Contains("/linked-worktree/file.txt\n"))
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
package worktree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var OpenFileOfBranchInOtherWorktree = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Open a file of a commit of a branch that is checked out in another worktree, and copy its absolute path",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().OS.Open = "echo {{filename}} > open-command"
|
||||
config.GetUserConfig().OS.CopyToClipboardCmd = "printf '%s' {{text}} > clipboard"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("README.md", "hello world")
|
||||
shell.Commit("initial commit")
|
||||
shell.NewBranch("mybranch")
|
||||
shell.CreateFileAndAdd("file.txt", "content")
|
||||
shell.Commit("add file")
|
||||
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")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("add file").IsSelected(),
|
||||
Contains("initial commit"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("A file.txt").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.OpenFile).
|
||||
Tap(func() {
|
||||
t.FileSystem().FileContent("open-command", Contains("/linked-worktree/file.txt\n"))
|
||||
}).
|
||||
Press(keys.Files.CopyFileInfoToClipboard).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Copy to clipboard")).
|
||||
Select(Contains("Absolute path")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Equals("File path copied to clipboard"))
|
||||
|
||||
t.FileSystem().FileContent("clipboard", Contains("/linked-worktree/file.txt"))
|
||||
})
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user