mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-06 05:57:38 -04:00
Reading a change in lazygit and saying something about it on GitHub means finding the line again in the browser: open the pull request, find the commit, find the file, scroll to the line. The line is already under the cursor here. Bind G in the focused main view, the key the commits panel opens the pull request with, to open it at the line the selection is on. The URL names the commits whose diff is on screen, so that the line numbers of the diff are the ones the page shows, the file by the SHA-256 of its repo-relative path, and the line by the side of the diff it is on: R for the new version of the file, L for the old one, where a deleted line is. GitHub documents none of that; the form was read off the URLs its own pages carry. One commit is named by its hash. A range of them is named by the commit the range starts after and the commit it ends at, the form the chooser above a pull request's files uses. The commit a range starts after is the parent of its oldest commit; where the range starts where the pull request itself does, that parent is none of the pull request's own commits, and the keyword BASE stands for it. Which branch's pull request that is depends on the panel beneath. The commits panel lists the commits of the checked-out branch, the sub-commits panel those of the branch drilled into, and the commit files panel shows the files of a commit from either. In a stack of branches, each with a pull request of its own, those lists include the commits of the branches below, and each of those commits is in the pull request of its own branch. So the command looks upwards from the commit for the nearest head of a branch with a pull request, and takes the listed branch if it finds none. Panels showing a diff that no pull request has a view of don't answer, and the command isn't offered over their diffs at all. Neither is it offered over a diff that is not the commit's own, where the line numbers on screen are not the ones the page shows: a diff against another ref in diffing mode, and the custom patch, whose lines sit at the numbers the patch gives them. A pull request holds only the commits of its branch that are pushed, and its pages say they can't find any other commit. So the command refuses where a commit of the diff is not one of the pull request's. Amend a commit in the middle of the branch, and the diffs of the commits below it still open; the ones above it sit on hashes the remote doesn't have. A commit from before the branch, in a main branch already, is refused too, and so is a range of commits that reaches across the head of a branch in a stack, since its commits are in two pull requests. Whether a commit is pushed is known only for the upstream of the listed branch. For a branch lower in a stack, that is right as long as the branches of the stack are pushed together. Only GitHub pull requests are known, since that is where the pull request data comes from. The whole path can't be exercised headlessly: no pull request reaches the model without a GitHub token, so the test covers where the command is offered and the three reasons it refuses. The URL is unit-tested instead, both the anchor of a line and the way the commits are named, and so is the choice of a branch in a stack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
package context
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type CommitFilesContext struct {
|
|
*filetree.CommitFileTreeViewModel
|
|
*ListContextTrait
|
|
*DynamicTitleBuilder
|
|
}
|
|
|
|
var (
|
|
_ types.IListContext = (*CommitFilesContext)(nil)
|
|
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
|
_ types.IFilterableContext = (*CommitFilesContext)(nil)
|
|
_ types.DiffMainViewContext = (*CommitFilesContext)(nil)
|
|
_ types.PullRequestDiffContext = (*CommitFilesContext)(nil)
|
|
)
|
|
|
|
func (self *CommitFilesContext) GetDiffMainViewType() types.DiffMainViewType {
|
|
return types.DiffMainViewTypePatchBuilding
|
|
}
|
|
|
|
// PullRequestDiff asks the panel this one was entered from. The files listed here are
|
|
// those of the commits selected there, and that panel knows which branch's pull request
|
|
// those commits are in.
|
|
func (self *CommitFilesContext) PullRequestDiff() types.PullRequestDiff {
|
|
if parent, ok := self.GetParentContext().(types.PullRequestDiffContext); ok {
|
|
return parent.PullRequestDiff()
|
|
}
|
|
return types.PullRequestDiff{}
|
|
}
|
|
|
|
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|
viewModel := filetree.NewCommitFileTreeViewModel(
|
|
func() []*models.CommitFile { return c.Model().CommitFiles },
|
|
c.Common,
|
|
c.UserConfig().Gui.ShowFileTree,
|
|
)
|
|
|
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
|
if viewModel.Len() == 0 {
|
|
return [][]string{{style.FgRed.Sprint("(none)")}}
|
|
}
|
|
|
|
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
|
lines := presentation.RenderCommitFileTree(viewModel, c.Git().Patch.PatchBuilder, showFileIcons, &c.UserConfig().Gui.CustomIcons)
|
|
return lo.Map(lines, func(line string, _ int) []string {
|
|
return []string{line}
|
|
})
|
|
}
|
|
|
|
ctx := &CommitFilesContext{
|
|
CommitFileTreeViewModel: viewModel,
|
|
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle),
|
|
ListContextTrait: &ListContextTrait{
|
|
Context: NewSimpleContext(
|
|
NewBaseContext(NewBaseContextOpts{
|
|
View: c.Views().CommitFiles,
|
|
WindowName: "commits",
|
|
Key: COMMIT_FILES_CONTEXT_KEY,
|
|
Kind: types.SIDE_CONTEXT,
|
|
Focusable: true,
|
|
Transient: true,
|
|
}),
|
|
),
|
|
ListRenderer: ListRenderer{
|
|
list: viewModel,
|
|
getDisplayStrings: getDisplayStrings,
|
|
},
|
|
c: c,
|
|
},
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
func (self *CommitFilesContext) GetDiffTerminals() []string {
|
|
return []string{self.GetRef().RefName()}
|
|
}
|
|
|
|
func (self *CommitFilesContext) RefForAdjustingLineNumberInDiff() string {
|
|
if refs := self.GetRefRange(); refs != nil {
|
|
return refs.To.RefName()
|
|
}
|
|
return self.GetRef().RefName()
|
|
}
|
|
|
|
func (self *CommitFilesContext) GetFromAndToForDiff() (string, string) {
|
|
return FromAndToForDiff(self.GetRef(), self.GetRefRange())
|
|
}
|
|
|
|
// FromAndToForDiff gives the two ends to diff for a ref, or for a range of them: a
|
|
// range runs from the parent of its first ref to its last, a single ref from its own
|
|
// parent to itself.
|
|
func FromAndToForDiff(ref models.Ref, refRange *types.RefRange) (string, string) {
|
|
if refRange != nil {
|
|
return refRange.From.ParentRefName(), refRange.To.RefName()
|
|
}
|
|
return ref.ParentRefName(), ref.RefName()
|
|
}
|
|
|
|
func (self *CommitFilesContext) ReInit(ref models.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()
|
|
}
|