mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-15 10:06:26 -04:00
Drag selected commits to a new position
Pressing the left button on the current selection now starts a drag that moves the selected commits, both in the normal commits view and for todos during an interactive rebase. A press anywhere else falls through to the usual click handling, so dragging from an unselected line still creates a range selection, and releasing without having moved collapses the selection to the pressed commit like a plain click would. While dragging, the insertion point follows the pointer: rows below the dragged block insert after the pointed-at commit, rows above it insert before it, and during a rebase the destination is limited to the contiguous block of movable todos around the selection. gocui moves the view cursor along with the pointer, so each drag event moves it back to keep the original selection highlighted. The move happens on release. The model may have been refreshed during the drag, so the dragged commits are located again by their identity (hash, subject, todo action); if they no longer form a unique contiguous block, the drop is ignored rather than guessing.
This commit is contained in:
parent
b055d28fb1
commit
eda4ad1192
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
|
|
@ -28,7 +29,46 @@ type LocalCommitsController struct {
|
|||
*ListControllerTrait[*models.Commit]
|
||||
c *ControllerCommon
|
||||
|
||||
pullFiles PullFilesFn
|
||||
pullFiles PullFilesFn
|
||||
commitDrag *commitDragState
|
||||
}
|
||||
|
||||
// commitDragState tracks a mouse drag that moves the selected commits. It is
|
||||
// created when the left button is pressed on the current selection, and lives
|
||||
// until the button is released or the drag is canceled.
|
||||
type commitDragState struct {
|
||||
// Model index that was pressed; releasing without having moved collapses
|
||||
// the selection to this commit, like a plain click would.
|
||||
pressedIndex int
|
||||
// Bounds of the selection at press time.
|
||||
startIndex int
|
||||
endIndex int
|
||||
// Identifying information of the dragged commits, so that they can be
|
||||
// found again on release even if the model was refreshed during the drag.
|
||||
commitIdentities []commitDragIdentity
|
||||
// Cursor and range-start position relative to startIndex, for restoring
|
||||
// the selection after the move.
|
||||
selectedOffset int
|
||||
rangeStartOffset int
|
||||
rangeSelectMode traits.RangeSelectMode
|
||||
// Smallest and largest allowed insertion index. During a rebase this
|
||||
// restricts the drag to the contiguous block of movable todos around the
|
||||
// selection.
|
||||
minInsertion int
|
||||
maxInsertion int
|
||||
// Current insertion index, or -1 if dropping wouldn't move anything
|
||||
// (pointer over the dragged block itself).
|
||||
insertionIndex int
|
||||
// Whether any drag motion arrived since the press; distinguishes a drag
|
||||
// from a plain click on the selection.
|
||||
hasMoved bool
|
||||
}
|
||||
|
||||
type commitDragIdentity struct {
|
||||
hash string
|
||||
name string
|
||||
action todo.TodoCommand
|
||||
actionFlag string
|
||||
}
|
||||
|
||||
var _ types.IController = &LocalCommitsController{}
|
||||
|
|
@ -50,6 +90,235 @@ func NewLocalCommitsController(
|
|||
}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) GetMouseKeybindings(types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
viewName := self.context().GetViewName()
|
||||
return []*gocui.ViewMouseBinding{
|
||||
{
|
||||
ViewName: viewName,
|
||||
FocusedView: viewName,
|
||||
Key: gocui.MouseLeft,
|
||||
Handler: self.handleCommitDragPress,
|
||||
},
|
||||
{
|
||||
ViewName: viewName,
|
||||
FocusedView: viewName,
|
||||
Key: gocui.MouseLeft,
|
||||
Modifier: gocui.ModMotion,
|
||||
Handler: self.handleCommitDrag,
|
||||
},
|
||||
{
|
||||
ViewName: viewName,
|
||||
FocusedView: viewName,
|
||||
Key: gocui.MouseRelease,
|
||||
Handler: self.handleCommitDragRelease,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) handleCommitDragPress(opts gocui.ViewMouseBindingOpts) error {
|
||||
context := self.context()
|
||||
pressedIndex := context.ViewIndexToModelIndex(opts.Y)
|
||||
startIndex, endIndex := context.GetSelectionRange()
|
||||
selectedIndex, rangeStartIndex, rangeSelectMode := context.GetSelectionRangeAndMode()
|
||||
selectedCommits, _, _ := context.GetSelectedItems()
|
||||
// Only a single press on the current selection (of commits that may be
|
||||
// moved) starts a drag; everything else falls through to the generic
|
||||
// list click handling, i.e. selecting the pressed line, double-click
|
||||
// actions, or dragging out a range selection. The view-index comparison
|
||||
// rejects presses on section headers, which map to the model index of a
|
||||
// nearby commit.
|
||||
if opts.IsDoubleClick ||
|
||||
pressedIndex < startIndex || pressedIndex > endIndex ||
|
||||
context.ModelIndexToViewIndex(pressedIndex) != opts.Y ||
|
||||
self.midRebaseMoveCommandEnabled(selectedCommits, startIndex, endIndex) != nil {
|
||||
return gocui.ErrKeybindingNotHandled
|
||||
}
|
||||
|
||||
minInsertion, maxInsertion := self.commitDragInsertionBounds(startIndex, endIndex)
|
||||
self.commitDrag = &commitDragState{
|
||||
pressedIndex: pressedIndex,
|
||||
startIndex: startIndex,
|
||||
endIndex: endIndex,
|
||||
commitIdentities: lo.Map(selectedCommits, func(commit *models.Commit, _ int) commitDragIdentity {
|
||||
return commitDragIdentityForCommit(commit)
|
||||
}),
|
||||
selectedOffset: selectedIndex - startIndex,
|
||||
rangeStartOffset: rangeStartIndex - startIndex,
|
||||
rangeSelectMode: rangeSelectMode,
|
||||
minInsertion: minInsertion,
|
||||
maxInsertion: maxInsertion,
|
||||
insertionIndex: -1,
|
||||
}
|
||||
self.restoreCommitDragHighlight()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) commitDragInsertionBounds(startIndex int, endIndex int) (int, int) {
|
||||
commits := self.c.Model().Commits
|
||||
if !self.isRebasing() {
|
||||
return 0, len(commits)
|
||||
}
|
||||
|
||||
minInsertion := startIndex
|
||||
for minInsertion > 0 && commits[minInsertion-1].IsTODO() && commits[minInsertion-1].Status != models.StatusConflicted {
|
||||
minInsertion--
|
||||
}
|
||||
maxInsertion := endIndex + 1
|
||||
for maxInsertion < len(commits) && commits[maxInsertion].IsTODO() && commits[maxInsertion].Status != models.StatusConflicted {
|
||||
maxInsertion++
|
||||
}
|
||||
return minInsertion, maxInsertion
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBindingOpts) error {
|
||||
if self.commitDrag == nil {
|
||||
return gocui.ErrKeybindingNotHandled
|
||||
}
|
||||
|
||||
self.commitDrag.hasMoved = true
|
||||
self.restoreCommitDragHighlight()
|
||||
insertionIndex := self.commitDragInsertionIndex(opts.Y)
|
||||
if insertionIndex >= self.commitDrag.startIndex && insertionIndex <= self.commitDrag.endIndex+1 {
|
||||
insertionIndex = -1
|
||||
}
|
||||
if insertionIndex == self.commitDrag.insertionIndex {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.commitDrag.insertionIndex = insertionIndex
|
||||
if insertionIndex < 0 {
|
||||
self.context().ClearDropInsertionIndex()
|
||||
} else {
|
||||
self.context().SetDropInsertionIndex(insertionIndex)
|
||||
}
|
||||
self.c.PostRefreshUpdate(self.context())
|
||||
return nil
|
||||
}
|
||||
|
||||
// gocui moves the view cursor to the pointer position before invoking our
|
||||
// handlers; move it back so that the dragged commits stay highlighted for the
|
||||
// whole duration of the drag.
|
||||
func (self *LocalCommitsController) restoreCommitDragHighlight() {
|
||||
state := self.commitDrag
|
||||
context := self.context()
|
||||
view := context.GetView()
|
||||
selectedIndex := state.startIndex + state.selectedOffset
|
||||
rangeStartIndex := state.startIndex + state.rangeStartOffset
|
||||
|
||||
view.SetCursorY(context.ModelIndexToViewIndex(selectedIndex) - view.OriginY())
|
||||
view.SetRangeSelectStart(context.ModelIndexToViewIndex(rangeStartIndex))
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) commitDragInsertionIndex(viewIndex int) int {
|
||||
context := self.context()
|
||||
if viewIndex < 0 {
|
||||
return self.commitDrag.minInsertion
|
||||
}
|
||||
if viewIndex >= context.TotalContentHeight() {
|
||||
return self.commitDrag.maxInsertion
|
||||
}
|
||||
|
||||
// Rows above the dragged block insert before the pointed-at commit, rows
|
||||
// below it insert after it, so that in both directions the line under
|
||||
// the pointer is the one that makes way.
|
||||
modelIndex := context.ViewIndexToModelIndex(viewIndex)
|
||||
insertionIndex := modelIndex
|
||||
if modelIndex > self.commitDrag.endIndex {
|
||||
insertionIndex++
|
||||
}
|
||||
return max(self.commitDrag.minInsertion, min(insertionIndex, self.commitDrag.maxInsertion))
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) handleCommitDragRelease(gocui.ViewMouseBindingOpts) error {
|
||||
if self.commitDrag == nil {
|
||||
return gocui.ErrKeybindingNotHandled
|
||||
}
|
||||
|
||||
state := self.commitDrag
|
||||
self.commitDrag = nil
|
||||
self.context().ClearDropInsertionIndex()
|
||||
|
||||
if !state.hasMoved {
|
||||
self.context().SetSelection(state.pressedIndex)
|
||||
self.c.PostRefreshUpdate(self.context())
|
||||
return nil
|
||||
}
|
||||
self.c.PostRefreshUpdate(self.context())
|
||||
if state.insertionIndex < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
offset := state.insertionIndex - state.startIndex
|
||||
if state.insertionIndex > state.endIndex {
|
||||
offset = state.insertionIndex - state.endIndex - 1
|
||||
}
|
||||
selectedCommits, startIndex, endIndex, found := findCommitDragBlock(
|
||||
self.context().GetItems(), state.commitIdentities,
|
||||
)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
self.context().SetSelectionRangeAndMode(
|
||||
startIndex+state.selectedOffset,
|
||||
startIndex+state.rangeStartOffset,
|
||||
state.rangeSelectMode,
|
||||
)
|
||||
return self.move(selectedCommits, startIndex, endIndex, offset)
|
||||
}
|
||||
|
||||
func commitDragIdentityForCommit(commit *models.Commit) commitDragIdentity {
|
||||
return commitDragIdentity{
|
||||
hash: commit.Hash(),
|
||||
name: commit.Name,
|
||||
action: commit.Action,
|
||||
actionFlag: commit.ActionFlag,
|
||||
}
|
||||
}
|
||||
|
||||
// findCommitDragBlock locates the dragged commits in the (possibly refreshed)
|
||||
// commit list by their identity rather than by the indices recorded at press
|
||||
// time. If they no longer exist as a contiguous block, or more than one block
|
||||
// matches, we give up rather than guess.
|
||||
func findCommitDragBlock(
|
||||
commits []*models.Commit, identities []commitDragIdentity,
|
||||
) ([]*models.Commit, int, int, bool) {
|
||||
matchStart := -1
|
||||
for startIndex := 0; startIndex+len(identities) <= len(commits); startIndex++ {
|
||||
matches := true
|
||||
for offset, identity := range identities {
|
||||
if commitDragIdentityForCommit(commits[startIndex+offset]) != identity {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
if matchStart >= 0 {
|
||||
return nil, -1, -1, false
|
||||
}
|
||||
matchStart = startIndex
|
||||
}
|
||||
}
|
||||
|
||||
if matchStart < 0 {
|
||||
return nil, -1, -1, false
|
||||
}
|
||||
endIndex := matchStart + len(identities) - 1
|
||||
return commits[matchStart : endIndex+1], matchStart, endIndex, true
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
||||
return func(types.OnFocusLostOpts) {
|
||||
if self.commitDrag == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.commitDrag = nil
|
||||
self.c.GocuiGui().CancelMouseCapture()
|
||||
self.context().ClearDropInsertionIndex()
|
||||
self.c.PostRefreshUpdate(self.context())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
editCommitKey := opts.Config.Universal.Edit
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,47 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFindCommitDragBlock(t *testing.T) {
|
||||
commit := func(hash string) *models.Commit {
|
||||
return models.NewCommit(&utils.StringPool{}, models.NewCommitOpts{Hash: hash})
|
||||
}
|
||||
identities := []commitDragIdentity{
|
||||
commitDragIdentityForCommit(commit("b")),
|
||||
commitDragIdentityForCommit(commit("c")),
|
||||
}
|
||||
|
||||
t.Run("finds the original block after selection changes", func(t *testing.T) {
|
||||
commits := []*models.Commit{commit("a"), commit("b"), commit("c"), commit("d")}
|
||||
|
||||
actual, startIndex, endIndex, found := findCommitDragBlock(commits, identities)
|
||||
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, commits[1:3], actual)
|
||||
assert.Equal(t, 1, startIndex)
|
||||
assert.Equal(t, 2, endIndex)
|
||||
})
|
||||
|
||||
t.Run("rejects a block that is no longer contiguous", func(t *testing.T) {
|
||||
_, _, _, found := findCommitDragBlock(
|
||||
[]*models.Commit{commit("a"), commit("b"), commit("d"), commit("c")}, identities,
|
||||
)
|
||||
|
||||
assert.False(t, found)
|
||||
})
|
||||
|
||||
t.Run("rejects an ambiguous block", func(t *testing.T) {
|
||||
_, _, _, found := findCommitDragBlock(
|
||||
[]*models.Commit{commit("b"), commit("c"), commit("b"), commit("c")}, identities,
|
||||
)
|
||||
|
||||
assert.False(t, found)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_countSquashableCommitsAbove(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DragKeepsSelectionHighlighted = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Keep the original commit range highlighted while dragging sideways",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(5)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
ClickAndHold(1, 1).
|
||||
SelectedLines(
|
||||
Contains("commit-05"),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
).
|
||||
MouseMove(10, 1).
|
||||
SelectedLines(
|
||||
Contains("commit-05"),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
).
|
||||
MouseRelease()
|
||||
},
|
||||
})
|
||||
80
pkg/integration/tests/interactive_rebase/drag_to_reorder.go
Normal file
80
pkg/integration/tests/interactive_rebase/drag_to_reorder.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DragToReorder = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Drag a selected commit range multiple rows in one operation",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(5)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
ClickAndHold(1, 1).
|
||||
TopLines(
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
MouseMove(1, 3).
|
||||
TopLines(
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("drop here"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
SelectNextItem().
|
||||
SelectedLines(
|
||||
Contains("commit-03"),
|
||||
).
|
||||
MouseRelease().
|
||||
TopLines(
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
ClickAndHold(1, 2).
|
||||
MouseMove(1, 0).
|
||||
TopLines(
|
||||
Contains("drop here"),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
MouseRelease().
|
||||
TopLines(
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04").IsSelected(),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
ClickAndHold(1, 1).
|
||||
MouseRelease().
|
||||
SelectedLines(
|
||||
Contains("commit-04"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DragToReorderInRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Drag rebase todos without allowing real commits to move",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateNCommits(5)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
NavigateToLine(Contains("commit-01")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("─── Pending rebase todos"),
|
||||
Contains("commit-05"),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("─── Commits"),
|
||||
Contains("commit-01").IsSelected(),
|
||||
).
|
||||
NavigateToLine(Contains("commit-05")).
|
||||
ClickAndHold(1, 1).
|
||||
MouseMove(1, 6).
|
||||
Lines(
|
||||
Contains("─── Pending rebase todos"),
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("drop here"),
|
||||
Contains("─── Commits"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
MouseRelease().
|
||||
Lines(
|
||||
Contains("─── Pending rebase todos"),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("─── Commits"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit-01")).
|
||||
ClickAndHold(1, 6).
|
||||
MouseMove(1, 4).
|
||||
SelectedLines(
|
||||
Contains("commit-05"),
|
||||
Contains("─── Commits"),
|
||||
Contains("commit-01"),
|
||||
).
|
||||
MouseRelease().
|
||||
Lines(
|
||||
Contains("─── Pending rebase todos"),
|
||||
Contains("commit-04"),
|
||||
Contains("commit-03"),
|
||||
Contains("commit-02"),
|
||||
Contains("commit-05").IsSelected(),
|
||||
Contains("─── Commits").IsSelected(),
|
||||
Contains("commit-01").IsSelected(),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
@ -290,6 +290,9 @@ var tests = []*components.IntegrationTest{
|
|||
interactive_rebase.AmendNonHeadCommitDuringRebase,
|
||||
interactive_rebase.DeleteUpdateRefTodo,
|
||||
interactive_rebase.DontShowBranchHeadsForTodoItems,
|
||||
interactive_rebase.DragKeepsSelectionHighlighted,
|
||||
interactive_rebase.DragToReorder,
|
||||
interactive_rebase.DragToReorderInRebase,
|
||||
interactive_rebase.DropCommitInCopiedBranchWithUpdateRef,
|
||||
interactive_rebase.DropMergeCommit,
|
||||
interactive_rebase.DropTodoCommitWithUpdateRef,
|
||||
|
|
|
|||
Loading…
Reference in a new issue