diff --git a/pkg/commands/models/branch.go b/pkg/commands/models/branch.go index 04f869ebd..4dc48a88d 100644 --- a/pkg/commands/models/branch.go +++ b/pkg/commands/models/branch.go @@ -53,6 +53,10 @@ func (b *Branch) RefName() string { return b.Name } +func (b *Branch) ShortRefName() string { + return b.RefName() +} + func (b *Branch) ParentRefName() string { return b.RefName() + "^" } diff --git a/pkg/commands/models/commit.go b/pkg/commands/models/commit.go index 95e3b9b18..50bcfab8f 100644 --- a/pkg/commands/models/commit.go +++ b/pkg/commands/models/commit.go @@ -70,6 +70,10 @@ func (c *Commit) RefName() string { return c.Hash } +func (c *Commit) ShortRefName() string { + return c.Hash[:7] +} + func (c *Commit) ParentRefName() string { if c.IsFirstCommit() { return EmptyTreeCommitHash diff --git a/pkg/commands/models/remote_branch.go b/pkg/commands/models/remote_branch.go index 6a26f05f9..1e89ef582 100644 --- a/pkg/commands/models/remote_branch.go +++ b/pkg/commands/models/remote_branch.go @@ -18,6 +18,10 @@ func (r *RemoteBranch) RefName() string { return r.FullName() } +func (r *RemoteBranch) ShortRefName() string { + return r.RefName() +} + func (r *RemoteBranch) ParentRefName() string { return r.RefName() + "^" } diff --git a/pkg/commands/models/stash_entry.go b/pkg/commands/models/stash_entry.go index 2a1cc8435..a88ab61f6 100644 --- a/pkg/commands/models/stash_entry.go +++ b/pkg/commands/models/stash_entry.go @@ -17,6 +17,10 @@ func (s *StashEntry) RefName() string { return fmt.Sprintf("stash@{%d}", s.Index) } +func (s *StashEntry) ShortRefName() string { + return s.RefName() +} + func (s *StashEntry) ParentRefName() string { return s.RefName() + "^" } diff --git a/pkg/commands/models/tag.go b/pkg/commands/models/tag.go index 24cb83254..876e2cd77 100644 --- a/pkg/commands/models/tag.go +++ b/pkg/commands/models/tag.go @@ -16,6 +16,10 @@ func (t *Tag) RefName() string { return t.Name } +func (t *Tag) ShortRefName() string { + return t.RefName() +} + func (t *Tag) ParentRefName() string { return t.RefName() + "^" } diff --git a/pkg/gui/context/commit_files_context.go b/pkg/gui/context/commit_files_context.go index 97c1728d1..4e9382481 100644 --- a/pkg/gui/context/commit_files_context.go +++ b/pkg/gui/context/commit_files_context.go @@ -1,6 +1,8 @@ package context import ( + "fmt" + "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/filetree" @@ -75,6 +77,25 @@ func (self *CommitFilesContext) GetDiffTerminals() []string { return []string{self.GetRef().RefName()} } +func (self *CommitFilesContext) GetFromAndToForDiff() (string, string) { + if refs := self.GetRefRange(); refs != nil { + return refs.From.ParentRefName(), refs.To.RefName() + } + ref := self.GetRef() + return ref.ParentRefName(), ref.RefName() +} + func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition { return nil } + +func (self *CommitFilesContext) ReInit(ref types.Ref, refRange *types.RefRange) { + self.SetRef(ref) + self.SetRefRange(refRange) + if refRange != nil { + self.SetTitleRef(fmt.Sprintf("%s-%s", refRange.From.ShortRefName(), refRange.To.ShortRefName())) + } else { + self.SetTitleRef(ref.Description()) + } + self.GetView().Title = self.Title() +} diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index eecb16107..6d1a72aae 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -128,6 +128,19 @@ func (self *LocalCommitsContext) GetSelectedRef() types.Ref { return commit } +func (self *LocalCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange { + commits, startIdx, endIdx := self.GetSelectedItems() + if commits == nil || startIdx == endIdx { + return nil + } + from := commits[len(commits)-1] + to := commits[0] + if from.IsTODO() || to.IsTODO() { + return nil + } + return &types.RefRange{From: from, To: to} +} + // Returns the commit hash of the selected commit, or an empty string if no // commit is selected func (self *LocalCommitsContext) GetSelectedCommitHash() string { diff --git a/pkg/gui/context/reflog_commits_context.go b/pkg/gui/context/reflog_commits_context.go index 403e5e91c..db33481e5 100644 --- a/pkg/gui/context/reflog_commits_context.go +++ b/pkg/gui/context/reflog_commits_context.go @@ -71,6 +71,11 @@ func (self *ReflogCommitsContext) GetSelectedRef() types.Ref { return commit } +func (self *ReflogCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange { + // It doesn't make much sense to show a range diff between two reflog entries. + return nil +} + func (self *ReflogCommitsContext) GetCommits() []*models.Commit { return self.getModel() } diff --git a/pkg/gui/context/stash_context.go b/pkg/gui/context/stash_context.go index c8d487688..64c7c9fc9 100644 --- a/pkg/gui/context/stash_context.go +++ b/pkg/gui/context/stash_context.go @@ -61,6 +61,11 @@ func (self *StashContext) GetSelectedRef() types.Ref { return stash } +func (self *StashContext) GetSelectedRefRangeForDiffFiles() *types.RefRange { + // It doesn't make much sense to show a range diff between two stash entries. + return nil +} + func (self *StashContext) GetDiffTerminals() []string { itemId := self.GetSelectedItemId() diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index 6adb7a53e..cd19dcae2 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -186,6 +186,19 @@ func (self *SubCommitsContext) GetSelectedRef() types.Ref { return commit } +func (self *SubCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange { + commits, startIdx, endIdx := self.GetSelectedItems() + if commits == nil || startIdx == endIdx { + return nil + } + from := commits[len(commits)-1] + to := commits[0] + if from.Divergence != to.Divergence { + return nil + } + return &types.RefRange{From: from, To: to} +} + func (self *SubCommitsContext) GetCommits() []*models.Commit { return self.getModel() } diff --git a/pkg/gui/controllers.go b/pkg/gui/controllers.go index d2f156a90..c784b4a1d 100644 --- a/pkg/gui/controllers.go +++ b/pkg/gui/controllers.go @@ -259,7 +259,7 @@ func (gui *Gui) resetHelpersAndControllers() { gui.State.Contexts.Stash, } { controllers.AttachControllers(context, controllers.NewSwitchToDiffFilesController( - common, context, gui.State.Contexts.CommitFiles, + common, context, )) } diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 2a9ba8271..8c4f7cf14 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -136,9 +136,8 @@ func (self *CommitFilesController) GetOnRenderToMain() func() error { return nil } - ref := self.context().GetRef() - to := ref.RefName() - from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName()) + from, to := self.context().GetFromAndToForDiff() + from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false) task := types.NewRunPtyTask(cmdObj.GetCmd()) @@ -250,9 +249,8 @@ func (self *CommitFilesController) canEditFiles(nodes []*filetree.CommitFileNode } func (self *CommitFilesController) openDiffTool(node *filetree.CommitFileNode) error { - ref := self.context().GetRef() - to := ref.RefName() - from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName()) + from, to := self.context().GetFromAndToForDiff() + from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) _, err := self.c.RunSubprocess(self.c.Git().Diff.OpenDiffToolCmdObj( git_commands.DiffToolCmdOptions{ Filepath: node.GetPath(), @@ -307,7 +305,8 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm }) } - if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() { + from, to, reverse := self.currentFromToReverseForPatchBuilding() + if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) { return self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.DiscardPatch, Prompt: self.c.Tr.DiscardPatchConfirm, @@ -330,14 +329,20 @@ func (self *CommitFilesController) startPatchBuilder() error { commitFilesContext := self.context() canRebase := commitFilesContext.GetCanRebase() - ref := commitFilesContext.GetRef() - to := ref.RefName() - from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName()) + from, to, reverse := self.currentFromToReverseForPatchBuilding() self.c.Git().Patch.PatchBuilder.Start(from, to, reverse, canRebase) return nil } +func (self *CommitFilesController) currentFromToReverseForPatchBuilding() (string, string, bool) { + commitFilesContext := self.context() + + from, to := commitFilesContext.GetFromAndToForDiff() + from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) + return from, to, reverse +} + func (self *CommitFilesController) enter(node *filetree.CommitFileNode) error { return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1}) } @@ -357,7 +362,8 @@ func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode return self.c.Context().Push(self.c.Contexts().CustomPatchBuilder, opts) } - if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() { + from, to, reverse := self.currentFromToReverseForPatchBuilding() + if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.NewPatchRequired(from, to, reverse) { return self.c.Confirm(types.ConfirmOpts{ Title: self.c.Tr.DiscardPatch, Prompt: self.c.Tr.DiscardPatchConfirm, diff --git a/pkg/gui/controllers/helpers/diff_helper.go b/pkg/gui/controllers/helpers/diff_helper.go index 94ac291b9..42cdb99dd 100644 --- a/pkg/gui/controllers/helpers/diff_helper.go +++ b/pkg/gui/controllers/helpers/diff_helper.go @@ -1,9 +1,13 @@ package helpers import ( + "strings" + "github.com/jesseduffield/lazygit/pkg/commands/git_commands" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/modes/diffing" + "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/samber/lo" ) @@ -19,7 +23,7 @@ func NewDiffHelper(c *HelperCommon) *DiffHelper { } func (self *DiffHelper) DiffArgs() []string { - output := []string{self.c.Modes().Diffing.Ref} + output := []string{"--stat", "-p", self.c.Modes().Diffing.Ref} right := self.currentDiffTerminal() if right != "" { @@ -46,14 +50,46 @@ func (self *DiffHelper) DiffArgs() []string { return output } +// Returns an update task that can be passed to RenderToMainViews to render a +// diff for the selected commit(s). We need to pass both the selected commit +// and the refRange for a range selection. If the refRange is nil (meaning that +// either there's no range, or it can't be diffed for some reason), then we want +// to fall back to rendering the diff for the single commit. +func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Commit, refRange *types.RefRange) types.UpdateTask { + if refRange != nil { + from, to := refRange.From, refRange.To + args := []string{from.ParentRefName(), to.RefName(), "--stat", "-p"} + if self.c.GetAppState().IgnoreWhitespaceInDiffView { + args = append(args, "--ignore-all-space") + } + args = append(args, "--") + if path := self.c.Modes().Filtering.GetPath(); path != "" { + args = append(args, path) + } + cmdObj := self.c.Git().Diff.DiffCmdObj(args) + task := types.NewRunPtyTask(cmdObj.GetCmd()) + task.Prefix = style.FgYellow.Sprintf("%s %s-%s\n\n", self.c.Tr.ShowingDiffForRange, from.ShortRefName(), to.ShortRefName()) + return task + } + + cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash, self.c.Modes().Filtering.GetPath()) + return types.NewRunPtyTask(cmdObj.GetCmd()) +} + func (self *DiffHelper) ExitDiffMode() error { self.c.Modes().Diffing = diffing.New() return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}) } func (self *DiffHelper) RenderDiff() error { - cmdObj := self.c.Git().Diff.DiffCmdObj(self.DiffArgs()) + args := self.DiffArgs() + cmdObj := self.c.Git().Diff.DiffCmdObj(args) task := types.NewRunPtyTask(cmdObj.GetCmd()) + task.Prefix = style.FgMagenta.Sprintf( + "%s %s\n\n", + self.c.Tr.ShowingGitDiff, + "git diff "+strings.Join(args, " "), + ) return self.c.RenderToMainViews(types.RefreshMainOpts{ Pair: self.c.MainViewPairs().Normal, diff --git a/pkg/gui/controllers/helpers/patch_building_helper.go b/pkg/gui/controllers/helpers/patch_building_helper.go index 4c2fc40d0..df6e34216 100644 --- a/pkg/gui/controllers/helpers/patch_building_helper.go +++ b/pkg/gui/controllers/helpers/patch_building_helper.go @@ -73,9 +73,8 @@ func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpt return nil } - ref := self.c.Contexts().CommitFiles.CommitFileTreeViewModel.GetRef() - to := ref.RefName() - from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName()) + from, to := self.c.Contexts().CommitFiles.GetFromAndToForDiff() + from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) diff, err := self.c.Git().WorkingTree.ShowFileDiff(from, to, reverse, path, true) if err != nil { return err diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index c43c3ddab..c1f187d07 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -285,8 +285,8 @@ func (self *RefreshHelper) refreshCommitsAndCommitFiles() { // For now the awkwardness remains. commit := self.c.Contexts().LocalCommits.GetSelected() if commit != nil && commit.RefName() != "" { - self.c.Contexts().CommitFiles.SetRef(commit) - self.c.Contexts().CommitFiles.SetTitleRef(commit.RefName()) + refRange := self.c.Contexts().LocalCommits.GetSelectedRefRangeForDiffFiles() + self.c.Contexts().CommitFiles.ReInit(commit, refRange) _ = self.refreshCommitFilesContext() } } @@ -387,9 +387,8 @@ func (self *RefreshHelper) RefreshAuthors(commits []*models.Commit) { } func (self *RefreshHelper) refreshCommitFilesContext() error { - ref := self.c.Contexts().CommitFiles.GetRef() - to := ref.RefName() - from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName()) + from, to := self.c.Contexts().CommitFiles.GetFromAndToForDiff() + from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from) files, err := self.c.Git().Loaders.CommitFileLoader.GetFilesInDiff(from, to, reverse) if err != nil { diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 2d4232f33..f0ca624c1 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -290,8 +290,8 @@ func (self *LocalCommitsController) GetOnRenderToMain() func() error { task = types.NewRenderStringTask( self.c.Tr.ExecCommandHere + "\n\n" + commit.Name) } else { - cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash, self.c.Modes().Filtering.GetPath()) - task = types.NewRunPtyTask(cmdObj.GetCmd()) + refRange := self.context().GetSelectedRefRangeForDiffFiles() + task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange) } return self.c.RenderToMainViews(types.RefreshMainOpts{ diff --git a/pkg/gui/controllers/sub_commits_controller.go b/pkg/gui/controllers/sub_commits_controller.go index 9ce708272..0f3ca9907 100644 --- a/pkg/gui/controllers/sub_commits_controller.go +++ b/pkg/gui/controllers/sub_commits_controller.go @@ -46,9 +46,8 @@ func (self *SubCommitsController) GetOnRenderToMain() func() error { if commit == nil { task = types.NewRenderStringTask("No commits") } else { - cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash, self.c.Modes().Filtering.GetPath()) - - task = types.NewRunPtyTask(cmdObj.GetCmd()) + refRange := self.context().GetSelectedRefRangeForDiffFiles() + task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange) } return self.c.RenderToMainViews(types.RefreshMainOpts{ diff --git a/pkg/gui/controllers/switch_to_diff_files_controller.go b/pkg/gui/controllers/switch_to_diff_files_controller.go index 92faefb79..2faa000bb 100644 --- a/pkg/gui/controllers/switch_to_diff_files_controller.go +++ b/pkg/gui/controllers/switch_to_diff_files_controller.go @@ -1,7 +1,6 @@ package controllers import ( - "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -13,36 +12,25 @@ type CanSwitchToDiffFiles interface { types.IListContext CanRebase() bool GetSelectedRef() types.Ref + GetSelectedRefRangeForDiffFiles() *types.RefRange } -// Not using our ListControllerTrait because our 'selected' item is not a list item -// but an attribute on it i.e. the ref of an item. +// Not using our ListControllerTrait because we have our own way of working with +// range selections that's different from ListControllerTrait's type SwitchToDiffFilesController struct { baseController - *ListControllerTrait[types.Ref] - c *ControllerCommon - context CanSwitchToDiffFiles - diffFilesContext *context.CommitFilesContext + c *ControllerCommon + context CanSwitchToDiffFiles } func NewSwitchToDiffFilesController( c *ControllerCommon, context CanSwitchToDiffFiles, - diffFilesContext *context.CommitFilesContext, ) *SwitchToDiffFilesController { return &SwitchToDiffFilesController{ baseController: baseController{}, - ListControllerTrait: NewListControllerTrait[types.Ref]( - c, - context, - context.GetSelectedRef, - func() ([]types.Ref, int, int) { - panic("Not implemented") - }, - ), - c: c, - context: context, - diffFilesContext: diffFilesContext, + c: c, + context: context, } } @@ -50,8 +38,8 @@ func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOp bindings := []*types.Binding{ { Key: opts.GetKey(opts.Config.Universal.GoInto), - Handler: self.withItem(self.enter), - GetDisabledReason: self.require(self.singleItemSelected(self.itemRepresentsCommit)), + Handler: self.enter, + GetDisabledReason: self.canEnter, Description: self.c.Tr.ViewItemFiles, }, } @@ -59,29 +47,43 @@ func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOp return bindings } +func (self *SwitchToDiffFilesController) Context() types.Context { + return self.context +} + func (self *SwitchToDiffFilesController) GetOnClick() func() error { - return self.withItemGraceful(self.enter) + return func() error { + if self.canEnter() == nil { + return self.enter() + } + + return nil + } } -func (self *SwitchToDiffFilesController) enter(ref types.Ref) error { - return self.viewFiles(SwitchToCommitFilesContextOpts{ - Ref: ref, - CanRebase: self.context.CanRebase(), - Context: self.context, - }) -} +func (self *SwitchToDiffFilesController) enter() error { + ref := self.context.GetSelectedRef() + refsRange := self.context.GetSelectedRefRangeForDiffFiles() + commitFilesContext := self.c.Contexts().CommitFiles -func (self *SwitchToDiffFilesController) viewFiles(opts SwitchToCommitFilesContextOpts) error { - diffFilesContext := self.diffFilesContext + canRebase := self.context.CanRebase() + if canRebase { + if self.c.Modes().Diffing.Active() { + if self.c.Modes().Diffing.Ref != ref.RefName() { + canRebase = false + } + } else if refsRange != nil { + canRebase = false + } + } - diffFilesContext.SetSelection(0) - diffFilesContext.SetRef(opts.Ref) - diffFilesContext.SetTitleRef(opts.Ref.Description()) - diffFilesContext.SetCanRebase(opts.CanRebase) - diffFilesContext.SetParentContext(opts.Context) - diffFilesContext.SetWindowName(opts.Context.GetWindowName()) - diffFilesContext.ClearSearchString() - diffFilesContext.GetView().TitlePrefix = opts.Context.GetView().TitlePrefix + commitFilesContext.ReInit(ref, refsRange) + commitFilesContext.SetSelection(0) + commitFilesContext.SetCanRebase(canRebase) + commitFilesContext.SetParentContext(self.context) + commitFilesContext.SetWindowName(self.context.GetWindowName()) + commitFilesContext.ClearSearchString() + commitFilesContext.GetView().TitlePrefix = self.context.GetView().TitlePrefix if err := self.c.Refresh(types.RefreshOptions{ Scope: []types.RefreshableView{types.COMMIT_FILES}, @@ -89,10 +91,18 @@ func (self *SwitchToDiffFilesController) viewFiles(opts SwitchToCommitFilesConte return err } - return self.c.Context().Push(diffFilesContext) + return self.c.Context().Push(commitFilesContext) } -func (self *SwitchToDiffFilesController) itemRepresentsCommit(ref types.Ref) *types.DisabledReason { +func (self *SwitchToDiffFilesController) canEnter() *types.DisabledReason { + refRange := self.context.GetSelectedRefRangeForDiffFiles() + if refRange != nil { + return nil + } + ref := self.context.GetSelectedRef() + if ref == nil { + return &types.DisabledReason{Text: self.c.Tr.NoItemSelected} + } if ref.RefName() == "" { return &types.DisabledReason{Text: self.c.Tr.SelectedItemDoesNotHaveFiles} } diff --git a/pkg/gui/controllers/types.go b/pkg/gui/controllers/types.go deleted file mode 100644 index f719b5de0..000000000 --- a/pkg/gui/controllers/types.go +++ /dev/null @@ -1,18 +0,0 @@ -package controllers - -import ( - "github.com/jesseduffield/lazygit/pkg/gui/types" -) - -// all fields mandatory (except `CanRebase` because it's boolean) -type SwitchToCommitFilesContextOpts struct { - // this is something like a commit or branch - Ref types.Ref - - // from the local commits view we're allowed to do rebase stuff with any patch - // we generate from the diff files context, but we don't have that same ability - // with say the sub commits context or the reflog context. - CanRebase bool - - Context types.Context -} diff --git a/pkg/gui/filetree/commit_file_tree_view_model.go b/pkg/gui/filetree/commit_file_tree_view_model.go index 95cb1a140..cbbb2fbcf 100644 --- a/pkg/gui/filetree/commit_file_tree_view_model.go +++ b/pkg/gui/filetree/commit_file_tree_view_model.go @@ -16,6 +16,8 @@ type ICommitFileTreeViewModel interface { GetRef() types.Ref SetRef(types.Ref) + GetRefRange() *types.RefRange // can be nil, in which case GetRef should be used + SetRefRange(*types.RefRange) // should be set to nil when selection is not a range GetCanRebase() bool SetCanRebase(bool) } @@ -25,9 +27,14 @@ type CommitFileTreeViewModel struct { types.IListCursor ICommitFileTree - // this is e.g. the commit for which we're viewing the files + // this is e.g. the commit for which we're viewing the files, if there is no + // range selection, or if the range selection can't be used for some reason ref types.Ref + // this is a commit range for which we're viewing the files. Can be nil, in + // which case ref is used. + refRange *types.RefRange + // we set this to true when you're viewing the files within the checked-out branch's commits. // If you're viewing the files of some random other branch we can't do any rebase stuff. canRebase bool @@ -42,6 +49,7 @@ func NewCommitFileTreeViewModel(getFiles func() []*models.CommitFile, log *logru ICommitFileTree: fileTree, IListCursor: listCursor, ref: nil, + refRange: nil, canRebase: false, } } @@ -54,6 +62,14 @@ func (self *CommitFileTreeViewModel) SetRef(ref types.Ref) { self.ref = ref } +func (self *CommitFileTreeViewModel) GetRefRange() *types.RefRange { + return self.refRange +} + +func (self *CommitFileTreeViewModel) SetRefRange(refsForRange *types.RefRange) { + self.refRange = refsForRange +} + func (self *CommitFileTreeViewModel) GetCanRebase() bool { return self.canRebase } diff --git a/pkg/gui/types/ref.go b/pkg/gui/types/ref.go index e83d91b65..18fc7e998 100644 --- a/pkg/gui/types/ref.go +++ b/pkg/gui/types/ref.go @@ -3,6 +3,12 @@ package types type Ref interface { FullRefName() string RefName() string + ShortRefName() string ParentRefName() string Description() string } + +type RefRange struct { + From Ref + To Ref +} diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index aa88d397a..b09a14d79 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -445,7 +445,6 @@ type TranslationSet struct { ScrollRight string DiscardPatch string DiscardPatchConfirm string - DiscardPatchSameCommitConfirm string CantPatchWhileRebasingError string ToggleAddToPatch string ToggleAddToPatchTooltip string @@ -582,6 +581,7 @@ type TranslationSet struct { OpenCommandLogMenu string OpenCommandLogMenuTooltip string ShowingGitDiff string + ShowingDiffForRange string CommitDiff string CopyCommitHashToClipboard string CommitHash string @@ -1435,7 +1435,6 @@ func EnglishTranslationSet() *TranslationSet { ScrollRight: "Scroll right", DiscardPatch: "Discard patch", DiscardPatchConfirm: "You can only build a patch from one commit/stash-entry at a time. Discard current patch?", - DiscardPatchSameCommitConfirm: "You currently have changes added to a patch for this commit. Discard current patch?", CantPatchWhileRebasingError: "You cannot build a patch or run patch commands while in a merging or rebasing state", ToggleAddToPatch: "Toggle file included in patch", ToggleAddToPatchTooltip: "Toggle whether the file is included in the custom patch. See {{.doc}}.", @@ -1571,6 +1570,7 @@ func EnglishTranslationSet() *TranslationSet { OpenCommandLogMenu: "View command log options", OpenCommandLogMenuTooltip: "View options for the command log e.g. show/hide the command log and focus the command log.", ShowingGitDiff: "Showing output for:", + ShowingDiffForRange: "Showing diff for range", CommitDiff: "Commit diff", CopyCommitHashToClipboard: "Copy commit hash to clipboard", CommitHash: "Commit hash", diff --git a/pkg/integration/tests/diff/diff.go b/pkg/integration/tests/diff/diff.go index ef1eb40a6..06e320418 100644 --- a/pkg/integration/tests/diff/diff.go +++ b/pkg/integration/tests/diff/diff.go @@ -35,11 +35,11 @@ var Diff = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Branches(). IsFocused(). Tap(func() { - t.Views().Information().Content(Contains("Showing output for: git diff branch-a branch-a")) + t.Views().Information().Content(Contains("Showing output for: git diff --stat -p branch-a branch-a")) }). SelectNextItem(). Tap(func() { - t.Views().Information().Content(Contains("Showing output for: git diff branch-a branch-b")) + t.Views().Information().Content(Contains("Showing output for: git diff --stat -p branch-a branch-b")) t.Views().Main().Content(Contains("+second line")) }). PressEnter() @@ -67,7 +67,7 @@ var Diff = NewIntegrationTest(NewIntegrationTestArgs{ Press(keys.Universal.DiffingMenu) t.ExpectPopup().Menu().Title(Equals("Diffing")).Select(Contains("Reverse diff direction")).Confirm() - t.Views().Information().Content(Contains("Showing output for: git diff branch-a branch-b -R")) + t.Views().Information().Content(Contains("Showing output for: git diff --stat -p branch-a branch-b -R")) t.Views().Main().Content(Contains("-second line")) }, }) diff --git a/pkg/integration/tests/diff/diff_and_apply_patch.go b/pkg/integration/tests/diff/diff_and_apply_patch.go index c0c95cc17..6801e7bea 100644 --- a/pkg/integration/tests/diff/diff_and_apply_patch.go +++ b/pkg/integration/tests/diff/diff_and_apply_patch.go @@ -32,13 +32,13 @@ var DiffAndApplyPatch = NewIntegrationTest(NewIntegrationTestArgs{ t.ExpectPopup().Menu().Title(Equals("Diffing")).Select(Equals("Diff branch-a")).Confirm() - t.Views().Information().Content(Contains("Showing output for: git diff branch-a branch-a")) + t.Views().Information().Content(Contains("Showing output for: git diff --stat -p branch-a branch-a")) t.Views().Branches(). IsFocused(). SelectNextItem(). Tap(func() { - t.Views().Information().Content(Contains("Showing output for: git diff branch-a branch-b")) + t.Views().Information().Content(Contains("Showing output for: git diff --stat -p branch-a branch-b")) t.Views().Main().Content(Contains("+second line")) }). PressEnter() diff --git a/pkg/integration/tests/diff/diff_non_sticky_range.go b/pkg/integration/tests/diff/diff_non_sticky_range.go new file mode 100644 index 000000000..672f744a9 --- /dev/null +++ b/pkg/integration/tests/diff/diff_non_sticky_range.go @@ -0,0 +1,45 @@ +package diff + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DiffNonStickyRange = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "View the combined diff of multiple commits using a range selection", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("initial commit") + shell.CreateFileAndAdd("file1", "first line\n") + shell.Commit("first commit") + shell.UpdateFileAndAdd("file1", "first line\nsecond line\n") + shell.Commit("second commit") + shell.UpdateFileAndAdd("file1", "first line\nsecond line\nthird line\n") + shell.Commit("third commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("third commit").IsSelected(), + Contains("second commit"), + Contains("first commit"), + Contains("initial commit"), + ). + Press(keys.Universal.RangeSelectDown). + Press(keys.Universal.RangeSelectDown). + Tap(func() { + t.Views().Main().Content(Contains("Showing diff for range "). + Contains("+first line\n+second line\n+third line")) + }). + PressEnter() + + t.Views().CommitFiles(). + IsFocused(). + SelectedLine(Contains("file1")) + + t.Views().Main().Content(Contains("+first line\n+second line\n+third line")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index de074232f..584d7a07d 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -148,6 +148,7 @@ var tests = []*components.IntegrationTest{ diff.Diff, diff.DiffAndApplyPatch, diff.DiffCommits, + diff.DiffNonStickyRange, diff.IgnoreWhitespace, diff.RenameSimilarityThresholdChange, file.CopyMenu,