mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-20 05:19:24 +02:00
support creating patches from files in diff mode
This commit is contained in:
parent
2eee079d3a
commit
43d891b8d6
@ -2,6 +2,7 @@ package commands
|
||||
|
||||
// CommitFile : A git commit file
|
||||
type CommitFile struct {
|
||||
// Parent is the identifier of the parent object e.g. a commit SHA if this commit file is for a commit, or a stash entry ref like 'stash@{1}'
|
||||
Parent string
|
||||
Name string
|
||||
DisplayString string
|
||||
|
@ -144,7 +144,7 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer,
|
||||
PushToCurrent: pushToCurrent,
|
||||
}
|
||||
|
||||
gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch, gitCommand.ShowCommitFile)
|
||||
gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch, gitCommand.ShowFileDiff)
|
||||
|
||||
return gitCommand, nil
|
||||
}
|
||||
@ -1046,18 +1046,18 @@ func (c *GitCommand) CherryPickCommits(commits []*Commit) error {
|
||||
}
|
||||
|
||||
// GetFilesInRef get the specified commit files
|
||||
func (c *GitCommand) GetFilesInRef(parent string, isStash bool, patchManager *patch.PatchManager) ([]*CommitFile, error) {
|
||||
func (c *GitCommand) GetFilesInRef(refName string, isStash bool, patchManager *patch.PatchManager) ([]*CommitFile, error) {
|
||||
command := "git diff-tree"
|
||||
if isStash {
|
||||
command = "git stash show"
|
||||
}
|
||||
|
||||
filenames, err := c.OSCommand.RunCommandWithOutput("%s --no-commit-id --name-only -r --no-renames %s", command, parent)
|
||||
filenames, err := c.OSCommand.RunCommandWithOutput("%s --no-commit-id --name-only -r --no-renames %s", command, refName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.GetCommitFilesFromFilenames(filenames, parent, patchManager), nil
|
||||
return c.GetCommitFilesFromFilenames(filenames, refName, patchManager), nil
|
||||
}
|
||||
|
||||
// GetFilesInDiff get the specified commit files
|
||||
@ -1076,7 +1076,7 @@ func (c *GitCommand) GetCommitFilesFromFilenames(filenames string, parent string
|
||||
|
||||
for _, file := range strings.Split(strings.TrimRight(filenames, "\n"), "\n") {
|
||||
status := patch.UNSELECTED
|
||||
if patchManager != nil && patchManager.Parent == parent {
|
||||
if patchManager != nil && patchManager.To == parent {
|
||||
status = patchManager.GetFileStatus(file)
|
||||
}
|
||||
|
||||
@ -1091,19 +1091,25 @@ func (c *GitCommand) GetCommitFilesFromFilenames(filenames string, parent string
|
||||
return commitFiles
|
||||
}
|
||||
|
||||
// ShowCommitFile get the diff of specified commit file
|
||||
func (c *GitCommand) ShowCommitFile(commitSha, fileName string, plain bool) (string, error) {
|
||||
cmdStr := c.ShowCommitFileCmdStr(commitSha, fileName, plain)
|
||||
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
||||
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
|
||||
func (c *GitCommand) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
|
||||
cmdStr := c.ShowFileDiffCmdStr(from, to, reverse, fileName, plain)
|
||||
return c.OSCommand.RunCommandWithOutput(cmdStr)
|
||||
}
|
||||
|
||||
func (c *GitCommand) ShowCommitFileCmdStr(commitSha, fileName string, plain bool) string {
|
||||
func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fileName string, plain bool) string {
|
||||
colorArg := c.colorArg()
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git diff --no-renames --color=%s %s^..%s -- %s", colorArg, commitSha, commitSha, fileName)
|
||||
reverseFlag := ""
|
||||
if reverse {
|
||||
reverseFlag = " -R "
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git diff --no-renames --color=%s %s %s %s -- %s", colorArg, from, to, reverseFlag, fileName)
|
||||
}
|
||||
|
||||
// CheckoutFile checks out the file for the given commit
|
||||
|
@ -1847,7 +1847,7 @@ func TestGitCommandShowCommitFile(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
gitCmd.OSCommand.command = s.command
|
||||
s.test(gitCmd.ShowCommitFile(s.commitSha, s.fileName, true))
|
||||
s.test(gitCmd.ShowFileDiff(s.commitSha+"^", s.commitSha, false, s.fileName, true))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -26,12 +26,14 @@ type fileInfo struct {
|
||||
}
|
||||
|
||||
type applyPatchFunc func(patch string, flags ...string) error
|
||||
type loadFileDiffFunc func(parent string, filename string, plain bool) (string, error)
|
||||
type loadFileDiffFunc func(from string, to string, reverse bool, filename string, plain bool) (string, error)
|
||||
|
||||
// PatchManager manages the building of a patch for a commit to be applied to another commit (or the working tree, or removed from the current commit). We also support building patches from things like stashes, for which there is less flexibility
|
||||
type PatchManager struct {
|
||||
// Parent is the commit sha if we're dealing with files of a commit, or a stash ref for a stash
|
||||
Parent string
|
||||
// To is the commit sha if we're dealing with files of a commit, or a stash ref for a stash
|
||||
To string // TODO: rename to 'to'
|
||||
From string
|
||||
Reverse bool
|
||||
|
||||
// CanRebase tells us whether we're allowed to modify our commits. CanRebase should be true for commits of the currently checked out branch and false for everything else
|
||||
CanRebase bool
|
||||
@ -41,7 +43,7 @@ type PatchManager struct {
|
||||
Log *logrus.Entry
|
||||
ApplyPatch applyPatchFunc
|
||||
|
||||
// LoadFileDiff loads the diff of a file, for a given parent (typically a commit SHA)
|
||||
// LoadFileDiff loads the diff of a file, for a given to (typically a commit SHA)
|
||||
LoadFileDiff loadFileDiffFunc
|
||||
}
|
||||
|
||||
@ -55,8 +57,10 @@ func NewPatchManager(log *logrus.Entry, applyPatch applyPatchFunc, loadFileDiff
|
||||
}
|
||||
|
||||
// NewPatchManager returns a new PatchManager
|
||||
func (p *PatchManager) Start(parent string, canRebase bool) {
|
||||
p.Parent = parent
|
||||
func (p *PatchManager) Start(from, to string, reverse bool, canRebase bool) {
|
||||
p.To = to
|
||||
p.From = from
|
||||
p.Reverse = reverse
|
||||
p.CanRebase = canRebase
|
||||
p.fileInfoMap = map[string]*fileInfo{}
|
||||
}
|
||||
@ -107,7 +111,7 @@ func (p *PatchManager) getFileInfo(filename string) (*fileInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
diff, err := p.LoadFileDiff(p.Parent, filename, true)
|
||||
diff, err := p.LoadFileDiff(p.From, p.To, p.Reverse, filename, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -269,12 +273,12 @@ func (p *PatchManager) ApplyPatches(reverse bool) error {
|
||||
|
||||
// clears the patch
|
||||
func (p *PatchManager) Reset() {
|
||||
p.Parent = ""
|
||||
p.To = ""
|
||||
p.fileInfoMap = map[string]*fileInfo{}
|
||||
}
|
||||
|
||||
func (p *PatchManager) Active() bool {
|
||||
return p.Parent != ""
|
||||
return p.To != ""
|
||||
}
|
||||
|
||||
func (p *PatchManager) IsEmpty() bool {
|
||||
@ -286,3 +290,8 @@ func (p *PatchManager) IsEmpty() bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// if any of these things change we'll need to reset and start a new patch
|
||||
func (p *PatchManager) NewPatchRequired(from string, to string, reverse bool) bool {
|
||||
return from != p.From || to != p.To || reverse != p.Reverse
|
||||
}
|
||||
|
@ -38,8 +38,11 @@ func (gui *Gui) handleCommitFileSelect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
to := commitFile.Parent
|
||||
from, reverse := gui.getFromAndReverseArgsForDiff(to)
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowCommitFileCmdStr(commitFile.Parent, commitFile.Name, false),
|
||||
gui.GitCommand.ShowFileDiffCmdStr(from, to, reverse, commitFile.Name, false),
|
||||
)
|
||||
task := gui.createRunPtyTask(cmd)
|
||||
|
||||
@ -167,7 +170,7 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.refreshCommitFilesView()
|
||||
}
|
||||
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.Parent != commitFile.Parent {
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||
return gui.ask(askOpts{
|
||||
returnToView: v,
|
||||
returnFocusOnClose: true,
|
||||
@ -185,7 +188,11 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
func (gui *Gui) startPatchManager() error {
|
||||
canRebase := gui.State.Panels.CommitFiles.refType == REF_TYPE_LOCAL_COMMIT
|
||||
gui.GitCommand.PatchManager.Start(gui.State.Panels.CommitFiles.refName, canRebase)
|
||||
|
||||
to := gui.State.Panels.CommitFiles.refName
|
||||
from, reverse := gui.getFromAndReverseArgsForDiff(to)
|
||||
|
||||
gui.GitCommand.PatchManager.Start(from, to, reverse, canRebase)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -217,7 +224,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
|
||||
return gui.refreshPatchBuildingPanel(selectedLineIdx)
|
||||
}
|
||||
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.Parent != commitFile.Parent {
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||
return gui.ask(askOpts{
|
||||
returnToView: gui.getCommitFilesView(),
|
||||
returnFocusOnClose: false,
|
||||
|
@ -5,6 +5,19 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// getFromAndReverseArgsForDiff tells us the from and reverse args to be used in a diff command. If we're not in diff mode we'll end up with the equivalent of a `git show` i.e `git diff blah^..blah`.
|
||||
func (gui *Gui) getFromAndReverseArgsForDiff(to string) (string, bool) {
|
||||
from := to + "^"
|
||||
reverse := false
|
||||
|
||||
if gui.State.Modes.Diffing.Active() {
|
||||
reverse = gui.State.Modes.Diffing.Reverse
|
||||
from = gui.State.Modes.Diffing.Ref
|
||||
}
|
||||
|
||||
return from, reverse
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
|
||||
if !gui.GitCommand.PatchManager.Active() {
|
||||
return gui.handleEscapePatchBuildingPanel()
|
||||
@ -22,7 +35,9 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
diff, err := gui.GitCommand.ShowCommitFile(commitFile.Parent, commitFile.Name, true)
|
||||
to := commitFile.Parent
|
||||
from, reverse := gui.getFromAndReverseArgsForDiff(to)
|
||||
diff, err := gui.GitCommand.ShowFileDiff(from, to, reverse, commitFile.Name, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
|
||||
if gui.GitCommand.PatchManager.CanRebase {
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf("remove patch from original commit (%s)", gui.GitCommand.PatchManager.Parent),
|
||||
displayString: fmt.Sprintf("remove patch from original commit (%s)", gui.GitCommand.PatchManager.To),
|
||||
onPress: gui.handleDeletePatchFromCommit,
|
||||
},
|
||||
{
|
||||
@ -44,7 +44,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
|
||||
|
||||
if gui.currentContext() == gui.Contexts.BranchCommits.Context {
|
||||
selectedCommit := gui.getSelectedLocalCommit()
|
||||
if selectedCommit != nil && gui.GitCommand.PatchManager.Parent != selectedCommit.Sha {
|
||||
if selectedCommit != nil && gui.GitCommand.PatchManager.To != selectedCommit.Sha {
|
||||
// adding this option to index 1
|
||||
menuItems = append(
|
||||
menuItems[:1],
|
||||
@ -66,7 +66,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
|
||||
|
||||
func (gui *Gui) getPatchCommitIndex() int {
|
||||
for index, commit := range gui.State.Commits {
|
||||
if commit.Sha == gui.GitCommand.PatchManager.Parent {
|
||||
if commit.Sha == gui.GitCommand.PatchManager.To {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user