mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-06 05:57:38 -04:00
Always show a file's staged changes in the lower pane
Which pane a side of a file's diff appeared in depended on what else the file had: the staged side had the lower pane while there were unstaged changes above it, and took over the upper one when there weren't. So the same content moved about, and which side a pane was showing was something the code had to work out from the file's status rather than knowing from the pane. Now each side has a pane of its own — unstaged above, staged below — shown when there is something on it. A file with nothing unstaged shows its staged changes in the lower pane alone, which then has the whole space, including the label of the key that focuses it. Nothing about this is visible to the user: the same diff appears in the same place, with the same title and the same label, and the same keys focus and scroll it. What it is for is the code, which no longer has to ask the file what a pane is showing — and, once the diff can be staged from, no longer has to make one key mean opposite things in the same pane. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
228090232f
commit
5e559cabec
+3
-3
@@ -1484,9 +1484,9 @@ func (v *View) CopyContent(from *View) {
|
||||
|
||||
// A background task may be streaming output into the source view's buffer
|
||||
// via Write, so read it under its own lock. The source is always a
|
||||
// different view than the destination (see the sole caller,
|
||||
// moveMainContextToTop), and no other code holds two view write locks at
|
||||
// once, so this can't deadlock.
|
||||
// different view than the destination — its callers hand content from one
|
||||
// view to another — and no other code holds two view write locks at once, so
|
||||
// this can't deadlock.
|
||||
from.writeMutex.Lock()
|
||||
defer from.writeMutex.Unlock()
|
||||
|
||||
|
||||
@@ -366,36 +366,34 @@ func (self *FilesController) renderNonTextualConflict(node *filetree.FileNode) {
|
||||
func (self *FilesController) renderWorkingTreeDiff(node *filetree.FileNode) {
|
||||
self.c.Helpers().MergeConflicts.ResetMergeState()
|
||||
|
||||
split := self.c.UserConfig().Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
mainShowsStaged := !split && node.GetHasStagedChanges()
|
||||
// The unstaged side of a file's diff is shown in the main pane and the staged side
|
||||
// in the secondary one, each only where there is a side to show — so a side is
|
||||
// always in the same place, whatever the file happens to have. A file with nothing
|
||||
// unstaged therefore shows its staged changes in the secondary pane, which then has
|
||||
// the whole section to itself. Configured to always split, both panes are shown
|
||||
// whether or not there is anything on either side.
|
||||
alwaysSplit := self.c.UserConfig().Gui.SplitDiff == "always"
|
||||
showStaged := node.GetHasStagedChanges() || alwaysSplit
|
||||
showUnstaged := node.GetHasUnstagedChanges() || alwaysSplit || !showStaged
|
||||
|
||||
paths := self.pathsForDiff(node)
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, paths)
|
||||
title := self.c.Tr.UnstagedChanges
|
||||
if mainShowsStaged {
|
||||
title = self.c.Tr.StagedChanges
|
||||
}
|
||||
refreshOpts := types.RefreshMainOpts{
|
||||
Pair: self.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
refreshOpts := types.RefreshMainOpts{Pair: self.c.MainViewPairs().Normal}
|
||||
|
||||
if showUnstaged {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, false, paths)
|
||||
refreshOpts.Main = &types.ViewUpdateOpts{
|
||||
Task: types.NewRunDiffRendererTask(cmdObj.GetCmd()),
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Title: title,
|
||||
},
|
||||
}
|
||||
|
||||
if split {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, paths)
|
||||
|
||||
title := self.c.Tr.StagedChanges
|
||||
if mainShowsStaged {
|
||||
title = self.c.Tr.UnstagedChanges
|
||||
Title: self.c.Tr.UnstagedChanges,
|
||||
}
|
||||
}
|
||||
|
||||
if showStaged {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, paths)
|
||||
refreshOpts.Secondary = &types.ViewUpdateOpts{
|
||||
Title: title,
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Task: types.NewRunDiffRendererTask(cmdObj.GetCmd()),
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Title: self.c.Tr.StagedChanges,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,13 @@ func (self *SwitchToFocusedMainViewController) onClickSecondary(opts gocui.ViewM
|
||||
}
|
||||
|
||||
func (self *SwitchToFocusedMainViewController) handleFocusMainView() error {
|
||||
return self.focusMainView(self.c.Contexts().Normal, -1)
|
||||
// Usually the main pane, but the content can be in the secondary one alone: a file
|
||||
// with nothing but staged changes shows them there.
|
||||
mainViewContext := self.c.Contexts().Normal
|
||||
if self.c.State().GetRepoState().GetMainPanes() == types.SecondaryPaneOnly {
|
||||
mainViewContext = self.c.Contexts().NormalSecondary
|
||||
}
|
||||
return self.focusMainView(mainViewContext, -1)
|
||||
}
|
||||
|
||||
func (self *SwitchToFocusedMainViewController) focusMainView(mainViewContext *context.MainContext, clickedLineIdx int) error {
|
||||
|
||||
+13
-12
@@ -22,12 +22,7 @@ func (gui *Gui) scrollDownView(view *gocui.View) {
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollUpMain() error {
|
||||
var view *gocui.View
|
||||
if gui.c.Context().Current().GetWindowName() == "secondary" {
|
||||
view = gui.secondaryView()
|
||||
} else {
|
||||
view = gui.mainView()
|
||||
}
|
||||
view := gui.mainSectionView()
|
||||
|
||||
if view.Name() == "mergeConflicts" {
|
||||
// although we have this same logic in the controller, this method can be invoked
|
||||
@@ -43,12 +38,7 @@ func (gui *Gui) scrollUpMain() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) scrollDownMain() error {
|
||||
var view *gocui.View
|
||||
if gui.c.Context().Current().GetWindowName() == "secondary" {
|
||||
view = gui.secondaryView()
|
||||
} else {
|
||||
view = gui.mainView()
|
||||
}
|
||||
view := gui.mainSectionView()
|
||||
|
||||
if view.Name() == "mergeConflicts" {
|
||||
gui.State.Contexts.MergeConflicts.SetUserScrolling(true)
|
||||
@@ -59,6 +49,17 @@ func (gui *Gui) scrollDownMain() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mainSectionView returns the view that the keys for scrolling the main section act
|
||||
// on: the pane the focus is in when it is in one of them, and otherwise the pane the
|
||||
// section is showing — which is the lower one whenever it has the section to itself.
|
||||
func (gui *Gui) mainSectionView() *gocui.View {
|
||||
if gui.c.Context().Current().GetWindowName() == "secondary" ||
|
||||
gui.State.MainPanes == types.SecondaryPaneOnly {
|
||||
return gui.secondaryView()
|
||||
}
|
||||
return gui.mainView()
|
||||
}
|
||||
|
||||
func (gui *Gui) mainView() *gocui.View {
|
||||
viewName := gui.helpers.Window.GetViewNameForWindow("main")
|
||||
view, _ := gui.g.View(viewName)
|
||||
|
||||
@@ -101,6 +101,10 @@ type Gui struct {
|
||||
// this tells us whether our views have been initially set up
|
||||
ViewsSetup bool
|
||||
|
||||
// the label for the key that focuses the main view, worn by whichever of the two
|
||||
// main panes that key focuses (see showFocusMainViewJumpLabelOn)
|
||||
focusMainViewJumpLabel string
|
||||
|
||||
Views types.Views
|
||||
|
||||
// Log of the commands/actions logged in the Command Log panel.
|
||||
|
||||
+51
-2
@@ -110,10 +110,13 @@ func (gui *Gui) allMainContextPairs() []types.MainContextPair {
|
||||
func (gui *Gui) refreshMainViews(opts types.RefreshMainOpts) {
|
||||
gui.moveMainContextPairToTop(opts.Pair)
|
||||
|
||||
panes := mainPanesFor(opts)
|
||||
gui.handOverMainSection(opts.Pair, panes)
|
||||
|
||||
if opts.Main != nil {
|
||||
gui.RefreshMainView(opts.Main, opts.Pair.Main)
|
||||
} else {
|
||||
opts.Pair.Main.GetView().Clear()
|
||||
gui.clearMainView(opts.Pair.Main)
|
||||
}
|
||||
|
||||
if opts.Secondary != nil {
|
||||
@@ -136,7 +139,37 @@ func (gui *Gui) refreshMainViews(opts types.RefreshMainOpts) {
|
||||
}
|
||||
}
|
||||
|
||||
gui.setMainPanes(mainPanesFor(opts))
|
||||
gui.setMainPanes(panes)
|
||||
}
|
||||
|
||||
// handOverMainSection carries the content of the main section from the pane that has
|
||||
// been showing it on its own to the pane about to, when a render moves the section's
|
||||
// content from one to the other — a file's changes going from unstaged to staged, say.
|
||||
//
|
||||
// The section is one region of the screen to the user, so a change of which pane holds
|
||||
// it has to look like that region re-rendering rather than blanking and filling in
|
||||
// again: the incoming pane shows what the outgoing one was showing, where it was
|
||||
// showing it, until its own render has read enough to be swapped in. It renders from
|
||||
// the top when it does, the content it took over not being its own (see
|
||||
// clearMainView).
|
||||
func (gui *Gui) handOverMainSection(pair types.MainContextPair, panes types.MainPanes) {
|
||||
// The lower pane is always the same view, being the only one a render can leave
|
||||
// holding the section on its own; the upper one is whichever view of the main
|
||||
// window this render is for, which moveMainContextPairToTop has just given a copy
|
||||
// of what that window was showing.
|
||||
upper, lower := pair.Main.GetView(), gui.Views.Secondary
|
||||
|
||||
var from, to *gocui.View
|
||||
switch {
|
||||
case gui.State.MainPanes == types.MainPaneOnly && panes == types.SecondaryPaneOnly:
|
||||
from, to = upper, lower
|
||||
case gui.State.MainPanes == types.SecondaryPaneOnly && panes == types.MainPaneOnly:
|
||||
from, to = lower, upper
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
gui.g.CopyContent(from, to)
|
||||
}
|
||||
|
||||
// mainPanesFor says which panes the given render occupies: the one it has content for,
|
||||
@@ -315,6 +348,22 @@ func (gui *Gui) mainContextForView(view *gocui.View) *context.MainContext {
|
||||
|
||||
func (gui *Gui) setMainPanes(panes types.MainPanes) {
|
||||
gui.State.MainPanes = panes
|
||||
|
||||
// The label for the key that focuses the main view belongs on the pane that key
|
||||
// focuses, which is the secondary one while it is the only one shown.
|
||||
if panes == types.SecondaryPaneOnly {
|
||||
gui.showFocusMainViewJumpLabelOn(gui.Views.Secondary)
|
||||
} else {
|
||||
gui.showFocusMainViewJumpLabelOn(gui.Views.Main)
|
||||
}
|
||||
}
|
||||
|
||||
// showFocusMainViewJumpLabelOn puts the main view's jump label on the given pane and
|
||||
// takes it off the other one, so that only the pane the key focuses wears it.
|
||||
func (gui *Gui) showFocusMainViewJumpLabelOn(view *gocui.View) {
|
||||
gui.Views.Main.TitlePrefix = ""
|
||||
gui.Views.Secondary.TitlePrefix = ""
|
||||
view.TitlePrefix = gui.focusMainViewJumpLabel
|
||||
}
|
||||
|
||||
// reApplySearch runs a search the view holds again over the content a render has just
|
||||
|
||||
+3
-3
@@ -283,11 +283,11 @@ func (gui *Gui) configureViewProperties() {
|
||||
}
|
||||
}
|
||||
|
||||
gui.focusMainViewJumpLabel = ""
|
||||
if gui.c.UserConfig().Gui.ShowPanelJumps {
|
||||
gui.Views.Main.TitlePrefix = keyToTitlePrefix(gui.c.UserConfig().Keybinding.Universal.FocusMainView)
|
||||
} else {
|
||||
gui.Views.Main.TitlePrefix = ""
|
||||
gui.focusMainViewJumpLabel = keyToTitlePrefix(gui.c.UserConfig().Keybinding.Universal.FocusMainView)
|
||||
}
|
||||
gui.showFocusMainViewJumpLabelOn(gui.Views.Main)
|
||||
|
||||
// Index the tab strips by view so we can both set them on views that are
|
||||
// part of a multi-tab panel and clear them on views that no longer are
|
||||
|
||||
@@ -88,6 +88,17 @@ func (self *ViewDriver) IsImmediatelyBelow(upper *ViewDriver) *ViewDriver {
|
||||
return self
|
||||
}
|
||||
|
||||
// TitlePrefix asserts on the label a view wears in front of its title, which is the
|
||||
// key that jumps to it.
|
||||
func (self *ViewDriver) TitlePrefix(expected *TextMatcher) *ViewDriver {
|
||||
self.t.assertWithRetries(func() (bool, string) {
|
||||
actual := self.getView().TitlePrefix
|
||||
return expected.context(fmt.Sprintf("%s title prefix", self.context)).test(actual)
|
||||
})
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *ViewDriver) Clear() *ViewDriver {
|
||||
// clearing multiple times in case there's multiple lines
|
||||
// (the clear button only clears a single line at a time)
|
||||
|
||||
@@ -54,6 +54,6 @@ var ResolveWithoutTrailingLf = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("M file").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Main().Content(Contains("-a1\n+a2\n").DoesNotContain("-no eol"))
|
||||
t.Views().Secondary().Content(Contains("-a1\n+a2\n").DoesNotContain("-no eol"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -73,6 +73,7 @@ var DiffAndApplyPatch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Focus().
|
||||
SelectedLine(Contains("file1"))
|
||||
|
||||
t.Views().Main().Content(Contains("+second line"))
|
||||
// The patch was applied to the index, so the file's changes are all staged.
|
||||
t.Views().Secondary().Content(Contains("+second line"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ var DirectoryDiffWithRenamedFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Equals(" R dir/nested/file3 → file3"),
|
||||
)
|
||||
|
||||
t.Views().Main().ContainsLines(
|
||||
t.Views().Secondary().ContainsLines(
|
||||
Equals("diff --git a/file1 b/dir/file1"),
|
||||
Equals("similarity index 100%"),
|
||||
Equals("rename from file1"),
|
||||
@@ -51,7 +51,7 @@ var DirectoryDiffWithRenamedFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SelectNextItem().
|
||||
SelectedLine(Equals(" ▼ dir"))
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
ContainsLines(
|
||||
Equals("diff --git a/file1 b/dir/file1"),
|
||||
Equals("similarity index 100%"),
|
||||
@@ -75,7 +75,7 @@ var DirectoryDiffWithRenamedFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Equals(" R file1 → file1"),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
ContainsLines(
|
||||
Equals("diff --git a/file1 b/dir/file1"),
|
||||
Equals("similarity index 100%"),
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
// changedLines is a file's worth of numbered lines, prefixed so that each file's diff
|
||||
// can be told from the other's on screen.
|
||||
func changedLines(prefix string) string {
|
||||
lines := make([]string, 40)
|
||||
for i := range lines {
|
||||
lines[i] = fmt.Sprintf("%s%02d", prefix, i+1)
|
||||
}
|
||||
return strings.Join(lines, "\n") + "\n"
|
||||
}
|
||||
|
||||
var PaneTakingOverStartsAtTheTop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "A pane taking the main section over shows its diff from the top, rather than at the offset it was left at",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
Width: 120,
|
||||
Height: 30,
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file1", changedLines("one"))
|
||||
shell.CreateFileAndAdd("file2", changedLines("two"))
|
||||
shell.Commit("one")
|
||||
|
||||
// One file's changes are unstaged and the other's are staged, so each is shown
|
||||
// in a pane of its own — and selecting one after the other hands the section
|
||||
// from one pane to the other. Both diffs are longer than the section, so either
|
||||
// pane can be scrolled.
|
||||
shell.UpdateFile("file1", changedLines("ONE"))
|
||||
shell.UpdateFileAndAdd("file2", changedLines("TWO"))
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("file1"))
|
||||
|
||||
t.Views().Secondary().IsInvisible()
|
||||
t.Views().Main().
|
||||
IsVisible().
|
||||
Title(Equals("Unstaged changes")).
|
||||
ScrollWheelDown().
|
||||
ScrollWheelDown().
|
||||
OriginYAtLeast(1)
|
||||
|
||||
t.Views().Files().NavigateToLine(Contains("file2"))
|
||||
|
||||
t.Views().Main().IsInvisible()
|
||||
t.Views().Secondary().
|
||||
IsVisible().
|
||||
Title(Equals("Staged changes")).
|
||||
Content(Contains("+TWO40")).
|
||||
OriginY(0).
|
||||
ScrollWheelDown().
|
||||
ScrollWheelDown().
|
||||
OriginYAtLeast(1)
|
||||
|
||||
// Back to the pane that was left scrolled: what it is given is a diff the user
|
||||
// hasn't seen there, so it starts at the top like any other.
|
||||
t.Views().Files().NavigateToLine(Contains("file1"))
|
||||
|
||||
t.Views().Secondary().IsInvisible()
|
||||
t.Views().Main().
|
||||
IsVisible().
|
||||
Title(Equals("Unstaged changes")).
|
||||
Content(Contains("+ONE40")).
|
||||
OriginY(0)
|
||||
|
||||
t.Views().Files().NavigateToLine(Contains("file2"))
|
||||
|
||||
t.Views().Main().IsInvisible()
|
||||
t.Views().Secondary().
|
||||
IsVisible().
|
||||
Title(Equals("Staged changes")).
|
||||
Content(Contains("+TWO40")).
|
||||
OriginY(0)
|
||||
},
|
||||
})
|
||||
@@ -34,7 +34,7 @@ var RenameSimilarityThresholdChange = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
Press(keys.Universal.FocusMainView).
|
||||
Tap(func() {
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Press(keys.Universal.IncreaseRenameSimilarityThreshold)
|
||||
t.ExpectToast(Equals("Changed rename similarity threshold to 50%"))
|
||||
}).
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var StagedChangesInLowerPane = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "A file's staged changes are shown in the lower pane whether or not it also has unstaged ones",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("both", "one\n")
|
||||
shell.CreateFileAndAdd("indexOnly", "one\n")
|
||||
shell.CreateFileAndAdd("worktreeOnly", "one\n")
|
||||
shell.Commit("one")
|
||||
|
||||
shell.UpdateFileAndAdd("both", "one\nstaged\n")
|
||||
shell.UpdateFile("both", "one\nstaged\nunstaged\n")
|
||||
// More staged lines than fit in the pane, so that it can be scrolled.
|
||||
staged := make([]string, 40)
|
||||
for i := range staged {
|
||||
staged[i] = fmt.Sprintf("staged%02d", i+1)
|
||||
}
|
||||
shell.UpdateFileAndAdd("indexOnly", "one\n"+strings.Join(staged, "\n")+"\n")
|
||||
shell.UpdateFile("worktreeOnly", "one\nunstaged\n")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("both"))
|
||||
|
||||
// With changes on both sides, each side has its own pane.
|
||||
t.Views().Main().
|
||||
Title(Equals("Unstaged changes")).
|
||||
Content(Contains("+unstaged"))
|
||||
t.Views().Secondary().
|
||||
Title(Equals("Staged changes")).
|
||||
Content(Contains("+staged"))
|
||||
|
||||
// With nothing unstaged, the staged side keeps its pane, which then has the
|
||||
// whole space to itself.
|
||||
t.Views().Files().NavigateToLine(Contains("indexOnly"))
|
||||
|
||||
t.Views().Main().IsInvisible()
|
||||
t.Views().Secondary().
|
||||
IsVisible().
|
||||
Title(Equals("Staged changes")).
|
||||
Content(Contains("+staged01")).
|
||||
// The key that focuses the diff wears its label, wherever the diff is.
|
||||
TitlePrefix(Equals("[0]")).
|
||||
OriginY(0)
|
||||
|
||||
// And the keys for scrolling the diff scroll the pane it is in.
|
||||
t.GlobalPress(keys.Universal.ScrollDownMain)
|
||||
t.Views().Secondary().OriginYAtLeast(1)
|
||||
t.GlobalPress(keys.Universal.ScrollUpMain)
|
||||
t.Views().Secondary().OriginY(0)
|
||||
|
||||
// And focusing the diff focuses the pane it is in.
|
||||
t.Views().Files().Press(keys.Universal.FocusMainView)
|
||||
t.Views().Secondary().IsFocused()
|
||||
t.Views().Secondary().PressEscape()
|
||||
|
||||
// With nothing staged, only the upper pane is shown.
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("worktreeOnly"))
|
||||
|
||||
t.Views().Secondary().IsInvisible()
|
||||
t.Views().Main().
|
||||
IsVisible().
|
||||
Title(Equals("Unstaged changes")).
|
||||
Content(Contains("+unstaged")).
|
||||
TitlePrefix(Equals("[0]"))
|
||||
},
|
||||
})
|
||||
@@ -26,7 +26,7 @@ var SelectLineWhenWholeFileIsOneHunk = NewIntegrationTest(NewIntegrationTestArgs
|
||||
|
||||
// Every line of the file is an addition, so widening to the change block would
|
||||
// select the file entire; hunk mode gives way to a single line.
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
IsFocused().
|
||||
SelectedLines(
|
||||
Contains("+one"),
|
||||
|
||||
@@ -58,7 +58,7 @@ var Apply = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("file1").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("second line"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -45,7 +45,7 @@ var ApplyInReverse = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("D").Contains("file1").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("-file1 content"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ var ApplyInReverseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Equals(" M file2"),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
ContainsLines(
|
||||
Contains(" file1 content"),
|
||||
Contains("-more file1 content"),
|
||||
|
||||
@@ -63,7 +63,7 @@ var ApplyWithModifiedFileNoConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Equals("M file1").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("-1\n+11\n 2\n 3\n+4"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -62,12 +62,12 @@ var MoveRangeToIndex = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Equals(" A file2"),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("second line"))
|
||||
|
||||
t.Views().Files().Focus().NavigateToLine(Contains("file2"))
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("file two content"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ var MoveToIndex = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
Focus()
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("file1 content"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -63,7 +63,7 @@ var MoveToIndexPartOfAdjacentAddedLines = NewIntegrationTest(NewIntegrationTestA
|
||||
Contains("M").Contains("file1"),
|
||||
)
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
Content(Contains("+1st line\n 2nd line"))
|
||||
},
|
||||
})
|
||||
|
||||
@@ -85,7 +85,7 @@ var MoveToIndexPartial = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
Focus()
|
||||
|
||||
t.Views().Main().
|
||||
t.Views().Secondary().
|
||||
ContainsLines(
|
||||
Contains(`-first line`),
|
||||
Contains(`+first line2`),
|
||||
|
||||
@@ -50,7 +50,7 @@ var Add = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
SelectNextItem().
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(
|
||||
t.Views().Secondary().Content(
|
||||
Contains("[submodule \"my_submodule\"]").
|
||||
Contains("path = my_submodule_path").
|
||||
Contains("url = ../other_repo"),
|
||||
@@ -58,7 +58,7 @@ var Add = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
SelectNextItem().
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(
|
||||
t.Views().Secondary().Content(
|
||||
Contains("Submodule my_submodule_path").
|
||||
Contains("(new submodule)"),
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
SelectNextItem()
|
||||
|
||||
t.Views().Main().Content(
|
||||
t.Views().Secondary().Content(
|
||||
Contains("-[submodule \"my_submodule_name\"]").
|
||||
Contains("- path = my_submodule_path").
|
||||
Contains("- url = ../my_submodule_name"),
|
||||
|
||||
@@ -46,7 +46,7 @@ var RemoveNested = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
NavigateToLine(Contains(".gitmodules"))
|
||||
|
||||
t.Views().Main().Content(
|
||||
t.Views().Secondary().Content(
|
||||
Contains("-[submodule \"innerSubName\"]").
|
||||
Contains("- path = modules/innerSubPath").
|
||||
Contains("- url = ../innerSubmodule"),
|
||||
|
||||
@@ -254,6 +254,7 @@ var tests = []*components.IntegrationTest{
|
||||
file.Gitignore,
|
||||
file.GitignoreSpecialCharacters,
|
||||
file.PaneShownAgainStartsAtTheTop,
|
||||
file.PaneTakingOverStartsAtTheTop,
|
||||
file.RememberCommitMessageAfterFail,
|
||||
file.RenameSimilarityThresholdChange,
|
||||
file.RenamedFiles,
|
||||
@@ -262,6 +263,7 @@ var tests = []*components.IntegrationTest{
|
||||
file.StageChildrenRangeSelect,
|
||||
file.StageDeletedRangeSelect,
|
||||
file.StageRangeSelect,
|
||||
file.StagedChangesInLowerPane,
|
||||
filter_and_search.FilterByFileStatus,
|
||||
filter_and_search.FilterCommitFiles,
|
||||
filter_and_search.FilterCommitFilesToggleDirectory,
|
||||
|
||||
Reference in New Issue
Block a user