1package gui
2
3import (
4	"fmt"
5
6	"github.com/jesseduffield/lazygit/pkg/commands"
7)
8
9func (gui *Gui) handleCreatePatchOptionsMenu() error {
10	if !gui.GitCommand.PatchManager.Active() {
11		return gui.createErrorPanel(gui.Tr.NoPatchError)
12	}
13
14	menuItems := []*menuItem{
15		{
16			displayString: "reset patch",
17			onPress:       gui.handleResetPatch,
18		},
19		{
20			displayString: "apply patch",
21			onPress:       func() error { return gui.handleApplyPatch(false) },
22		},
23		{
24			displayString: "apply patch in reverse",
25			onPress:       func() error { return gui.handleApplyPatch(true) },
26		},
27	}
28
29	if gui.GitCommand.PatchManager.CanRebase && gui.workingTreeState() == commands.REBASE_MODE_NORMAL {
30		menuItems = append(menuItems, []*menuItem{
31			{
32				displayString: fmt.Sprintf("remove patch from original commit (%s)", gui.GitCommand.PatchManager.To),
33				onPress:       gui.handleDeletePatchFromCommit,
34			},
35			{
36				displayString: "move patch out into index",
37				onPress:       gui.handleMovePatchIntoWorkingTree,
38			},
39			{
40				displayString: "move patch into new commit",
41				onPress:       gui.handlePullPatchIntoNewCommit,
42			},
43		}...)
44
45		if gui.currentContext().GetKey() == gui.State.Contexts.BranchCommits.GetKey() {
46			selectedCommit := gui.getSelectedLocalCommit()
47			if selectedCommit != nil && gui.GitCommand.PatchManager.To != selectedCommit.Sha {
48				// adding this option to index 1
49				menuItems = append(
50					menuItems[:1],
51					append(
52						[]*menuItem{
53							{
54								displayString: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
55								onPress:       gui.handleMovePatchToSelectedCommit,
56							},
57						}, menuItems[1:]...,
58					)...,
59				)
60			}
61		}
62	}
63
64	return gui.createMenu(gui.Tr.PatchOptionsTitle, menuItems, createMenuOptions{showCancel: true})
65}
66
67func (gui *Gui) getPatchCommitIndex() int {
68	for index, commit := range gui.State.Commits {
69		if commit.Sha == gui.GitCommand.PatchManager.To {
70			return index
71		}
72	}
73	return -1
74}
75
76func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
77	if gui.GitCommand.WorkingTreeState() != commands.REBASE_MODE_NORMAL {
78		return false, gui.createErrorPanel(gui.Tr.CantPatchWhileRebasingError)
79	}
80	return true, nil
81}
82
83func (gui *Gui) returnFocusFromLineByLinePanelIfNecessary() error {
84	if gui.State.MainContext == MAIN_PATCH_BUILDING_CONTEXT_KEY {
85		return gui.handleEscapePatchBuildingPanel()
86	}
87	return nil
88}
89
90func (gui *Gui) handleDeletePatchFromCommit() error {
91	if ok, err := gui.validateNormalWorkingTreeState(); !ok {
92		return err
93	}
94
95	if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
96		return err
97	}
98
99	return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
100		commitIndex := gui.getPatchCommitIndex()
101		err := gui.GitCommand.WithSpan(gui.Tr.Spans.RemovePatchFromCommit).DeletePatchesFromCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
102		return gui.handleGenericMergeCommandResult(err)
103	})
104}
105
106func (gui *Gui) handleMovePatchToSelectedCommit() error {
107	if ok, err := gui.validateNormalWorkingTreeState(); !ok {
108		return err
109	}
110
111	if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
112		return err
113	}
114
115	return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
116		commitIndex := gui.getPatchCommitIndex()
117		err := gui.GitCommand.WithSpan(gui.Tr.Spans.MovePatchToSelectedCommit).MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx, gui.GitCommand.PatchManager)
118		return gui.handleGenericMergeCommandResult(err)
119	})
120}
121
122func (gui *Gui) handleMovePatchIntoWorkingTree() error {
123	if ok, err := gui.validateNormalWorkingTreeState(); !ok {
124		return err
125	}
126
127	if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
128		return err
129	}
130
131	pull := func(stash bool) error {
132		return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
133			commitIndex := gui.getPatchCommitIndex()
134			err := gui.GitCommand.WithSpan(gui.Tr.Spans.MovePatchIntoIndex).MovePatchIntoIndex(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager, stash)
135			return gui.handleGenericMergeCommandResult(err)
136		})
137	}
138
139	if len(gui.trackedFiles()) > 0 {
140		return gui.ask(askOpts{
141			title:  gui.Tr.MustStashTitle,
142			prompt: gui.Tr.MustStashWarning,
143			handleConfirm: func() error {
144				return pull(true)
145			},
146		})
147	} else {
148		return pull(false)
149	}
150}
151
152func (gui *Gui) handlePullPatchIntoNewCommit() error {
153	if ok, err := gui.validateNormalWorkingTreeState(); !ok {
154		return err
155	}
156
157	if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
158		return err
159	}
160
161	return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
162		commitIndex := gui.getPatchCommitIndex()
163		err := gui.GitCommand.WithSpan(gui.Tr.Spans.MovePatchIntoNewCommit).PullPatchIntoNewCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
164		return gui.handleGenericMergeCommandResult(err)
165	})
166}
167
168func (gui *Gui) handleApplyPatch(reverse bool) error {
169	if err := gui.returnFocusFromLineByLinePanelIfNecessary(); err != nil {
170		return err
171	}
172
173	span := gui.Tr.Spans.ApplyPatch
174	if reverse {
175		span = "Apply patch in reverse"
176	}
177	if err := gui.GitCommand.WithSpan(span).PatchManager.ApplyPatches(reverse); err != nil {
178		return gui.surfaceError(err)
179	}
180	return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
181}
182
183func (gui *Gui) handleResetPatch() error {
184	gui.GitCommand.PatchManager.Reset()
185	if gui.currentContextKeyIgnoringPopups() == MAIN_PATCH_BUILDING_CONTEXT_KEY {
186		if err := gui.pushContext(gui.State.Contexts.CommitFiles); err != nil {
187			return err
188		}
189	}
190	return gui.refreshCommitFilesView()
191}
192