1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-26 05:37:18 +02:00

Merge pull request #2370 from AzraelSec/rebase-with-todo-edit

This commit is contained in:
Jesse Duffield 2023-04-15 19:17:06 +10:00 committed by GitHub
commit 1efb565b22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 197 additions and 48 deletions

View File

@ -29,6 +29,11 @@ const (
const ( const (
DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND" DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND"
RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO" RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO"
// The `PrependLinesEnvKey` env variable is set to `true` to tell our daemon
// to prepend the content of `RebaseTODOEnvKey` to the default `git-rebase-todo`
// file instead of using it as a replacement.
PrependLinesEnvKey string = "LAZYGIT_PREPEND_LINES"
) )
type Daemon interface { type Daemon interface {
@ -74,12 +79,11 @@ type rebaseDaemon struct {
func (self *rebaseDaemon) Run() error { func (self *rebaseDaemon) Run() error {
self.c.Log.Info("Lazygit invoked as interactive rebase demon") self.c.Log.Info("Lazygit invoked as interactive rebase demon")
self.c.Log.Info("args: ", os.Args) self.c.Log.Info("args: ", os.Args)
path := os.Args[1]
if strings.HasSuffix(os.Args[1], "git-rebase-todo") { if strings.HasSuffix(path, "git-rebase-todo") {
if err := os.WriteFile(os.Args[1], []byte(os.Getenv(RebaseTODOEnvKey)), 0o644); err != nil { return self.writeTodoFile(path)
return err } else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
}
} else if strings.HasSuffix(os.Args[1], filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG // if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
// but in this case we don't need to edit it, so we'll just return // but in this case we don't need to edit it, so we'll just return
} else { } else {
@ -89,6 +93,22 @@ func (self *rebaseDaemon) Run() error {
return nil return nil
} }
func (self *rebaseDaemon) writeTodoFile(path string) error {
todoContent := []byte(os.Getenv(RebaseTODOEnvKey))
prependLines := os.Getenv(PrependLinesEnvKey) != ""
if prependLines {
existingContent, err := os.ReadFile(path)
if err != nil {
return err
}
todoContent = append(todoContent, existingContent...)
}
return os.WriteFile(path, todoContent, 0o644)
}
func gitDir() string { func gitDir() string {
dir := env.GetGitDirEnv() dir := env.GetGitDirEnv()
if dir == "" { if dir == "" {

View File

@ -111,7 +111,11 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
} }
}) })
err := self.rebase.PrepareInteractiveRebaseCommand(commits[baseIndex].Sha, todoLines, true).Run() err := self.rebase.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: commits[baseIndex].Sha,
todoLines: todoLines,
overrideEditor: true,
}).Run()
if err != nil { if err != nil {
return err return err
} }

View File

@ -60,7 +60,10 @@ func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index
return nil, err return nil, err
} }
return self.PrepareInteractiveRebaseCommand(sha, todo, false), nil return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: sha,
todoLines: todo,
}), nil
} }
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error { func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error {
@ -104,7 +107,11 @@ func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int)
baseShaOrRoot := getBaseShaOrRoot(commits, index+2) baseShaOrRoot := getBaseShaOrRoot(commits, index+2)
return self.PrepareInteractiveRebaseCommand(baseShaOrRoot, todoLines, true).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseShaOrRoot,
todoLines: todoLines,
overrideEditor: true,
}).Run()
} }
func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index int, action string) error { func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index int, action string) error {
@ -113,7 +120,11 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index in
return err return err
} }
return self.PrepareInteractiveRebaseCommand(sha, todo, true).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: sha,
todoLines: todo,
overrideEditor: true,
}).Run()
} }
func (self *RebaseCommands) InteractiveRebaseBreakAfter(commits []*models.Commit, index int) error { func (self *RebaseCommands) InteractiveRebaseBreakAfter(commits []*models.Commit, index int) error {
@ -123,22 +134,46 @@ func (self *RebaseCommands) InteractiveRebaseBreakAfter(commits []*models.Commit
} }
todo = append(todo, TodoLine{Action: "break", Commit: nil}) todo = append(todo, TodoLine{Action: "break", Commit: nil})
return self.PrepareInteractiveRebaseCommand(sha, todo, true).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: sha,
todoLines: todo,
overrideEditor: true,
}).Run()
}
func (self *RebaseCommands) EditRebase(branchRef string) error {
commands := []TodoLine{{Action: "break"}}
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: branchRef,
todoLines: commands,
prepend: true,
}).Run()
}
type PrepareInteractiveRebaseCommandOpts struct {
baseShaOrRoot string
todoLines []TodoLine
overrideEditor bool
prepend bool
} }
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase // PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
// we tell git to run lazygit to edit the todo list, and we pass the client // we tell git to run lazygit to edit the todo list, and we pass the client
// lazygit a todo string to write to the todo file // lazygit a todo string to write to the todo file
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(baseShaOrRoot string, todoLines []TodoLine, overrideEditor bool) oscommands.ICmdObj { func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
todo := self.buildTodo(todoLines) todo := self.buildTodo(opts.todoLines)
ex := oscommands.GetLazygitPath() ex := oscommands.GetLazygitPath()
prependLines := ""
if opts.prepend {
prependLines = "TRUE"
}
debug := "FALSE" debug := "FALSE"
if self.Debug { if self.Debug {
debug = "TRUE" debug = "TRUE"
} }
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash %s", baseShaOrRoot) cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash %s", opts.baseShaOrRoot)
self.Log.WithField("command", cmdStr).Debug("RunCommand") self.Log.WithField("command", cmdStr).Debug("RunCommand")
cmdObj := self.cmd.New(cmdStr) cmdObj := self.cmd.New(cmdStr)
@ -153,13 +188,14 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(baseShaOrRoot string
cmdObj.AddEnvVars( cmdObj.AddEnvVars(
daemon.DaemonKindEnvKey+"="+string(daemon.InteractiveRebase), daemon.DaemonKindEnvKey+"="+string(daemon.InteractiveRebase),
daemon.RebaseTODOEnvKey+"="+todo, daemon.RebaseTODOEnvKey+"="+todo,
daemon.PrependLinesEnvKey+"="+prependLines,
"DEBUG="+debug, "DEBUG="+debug,
"LANG=en_US.UTF-8", // Force using EN as language "LANG=en_US.UTF-8", // Force using EN as language
"LC_ALL=en_US.UTF-8", // Force using EN as language "LC_ALL=en_US.UTF-8", // Force using EN as language
"GIT_SEQUENCE_EDITOR="+gitSequenceEditor, "GIT_SEQUENCE_EDITOR="+gitSequenceEditor,
) )
if overrideEditor { if opts.overrideEditor {
cmdObj.AddEnvVars("GIT_EDITOR=" + ex) cmdObj.AddEnvVars("GIT_EDITOR=" + ex)
} }
@ -273,12 +309,16 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Co
return err return err
} }
return self.PrepareInteractiveRebaseCommand(sha, todo, true).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: sha,
todoLines: todo,
overrideEditor: true,
}).Run()
} }
// RebaseBranch interactive rebases onto a branch // RebaseBranch interactive rebases onto a branch
func (self *RebaseCommands) RebaseBranch(branchName string) error { func (self *RebaseCommands) RebaseBranch(branchName string) error {
return self.PrepareInteractiveRebaseCommand(branchName, nil, false).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run()
} }
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj { func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
@ -363,7 +403,10 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error { func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
todoLines := self.BuildTodoLinesSingleAction(commits, "pick") todoLines := self.BuildTodoLinesSingleAction(commits, "pick")
return self.PrepareInteractiveRebaseCommand("HEAD", todoLines, false).Run() return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: "HEAD",
todoLines: todoLines,
}).Run()
} }
func (self *RebaseCommands) buildTodo(todoLines []TodoLine) string { func (self *RebaseCommands) buildTodo(todoLines []TodoLine) string {

View File

@ -201,22 +201,42 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
if ref == checkedOutBranch { if ref == checkedOutBranch {
return self.c.ErrorMsg(self.c.Tr.CantRebaseOntoSelf) return self.c.ErrorMsg(self.c.Tr.CantRebaseOntoSelf)
} }
prompt := utils.ResolvePlaceholderString( menuItems := []*types.MenuItem{
self.c.Tr.ConfirmRebase, {
map[string]string{ Label: self.c.Tr.SimpleRebase,
"checkedOutBranch": checkedOutBranch, Key: 's',
"selectedBranch": ref, OnPress: func() error {
},
)
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.RebasingTitle,
Prompt: prompt,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.RebaseBranch) self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
err := self.git.Rebase.RebaseBranch(ref) err := self.git.Rebase.RebaseBranch(ref)
return self.CheckMergeOrRebase(err) return self.CheckMergeOrRebase(err)
}, },
},
{
Label: self.c.Tr.InteractiveRebase,
Key: 'i',
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
OnPress: func() error {
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
err := self.git.Rebase.EditRebase(ref)
if err = self.CheckMergeOrRebase(err); err != nil {
return err
}
return self.c.PushContext(self.contexts.LocalCommits)
},
},
}
title := utils.ResolvePlaceholderString(
self.c.Tr.RebasingTitle,
map[string]string{
"checkedOutBranch": checkedOutBranch,
"ref": ref,
},
)
return self.c.Menu(types.CreateMenuOptions{
Title: title,
Items: menuItems,
}) })
} }

View File

@ -201,8 +201,6 @@ func chineseTranslationSet() TranslationSet {
ReflogCommitsTitle: "Reflog 页面", ReflogCommitsTitle: "Reflog 页面",
GlobalTitle: "全局键绑定", GlobalTitle: "全局键绑定",
ConflictsResolved: "已解决所有冲突。是否继续?", ConflictsResolved: "已解决所有冲突。是否继续?",
RebasingTitle: "变基",
ConfirmRebase: "您确定要将分支 {{.checkedOutBranch}} 变基到 {{.selectedBranch}} 吗?",
ConfirmMerge: "您确定要将分支 {{.selectedBranch}} 合并到 {{.checkedOutBranch}} 吗?", ConfirmMerge: "您确定要将分支 {{.selectedBranch}} 合并到 {{.checkedOutBranch}} 吗?",
FwdNoUpstream: "此分支没有上游,无法快进", FwdNoUpstream: "此分支没有上游,无法快进",
FwdNoLocalUpstream: "此分支的远程未在本地注册,无法快进", FwdNoLocalUpstream: "此分支的远程未在本地注册,无法快进",

View File

@ -166,9 +166,7 @@ func dutchTranslationSet() TranslationSet {
ReflogCommitsTitle: "Reflog", ReflogCommitsTitle: "Reflog",
GlobalTitle: "Globale Sneltoetsen", GlobalTitle: "Globale Sneltoetsen",
ConflictsResolved: "alle merge conflicten zijn opgelost. Wilt je verder gaan?", ConflictsResolved: "alle merge conflicten zijn opgelost. Wilt je verder gaan?",
RebasingTitle: "Rebasen",
MergingTitle: "Mergen", MergingTitle: "Mergen",
ConfirmRebase: "Weet je zeker dat je '{{.checkedOutBranch}}' op '{{.selectedBranch}}' wil rebasen?",
ConfirmMerge: "Weet je zeker dat je '{{.selectedBranch}}' in '{{.checkedOutBranch}}' wil mergen?", ConfirmMerge: "Weet je zeker dat je '{{.selectedBranch}}' in '{{.checkedOutBranch}}' wil mergen?",
FwdNoUpstream: "Kan niet de branch vooruitspoelen zonder upstream", FwdNoUpstream: "Kan niet de branch vooruitspoelen zonder upstream",
FwdCommitsToPush: "Je kan niet vooruitspoelen als de branch geen nieuwe commits heeft", FwdCommitsToPush: "Je kan niet vooruitspoelen als de branch geen nieuwe commits heeft",

View File

@ -209,7 +209,9 @@ type TranslationSet struct {
ReflogCommitsTitle string ReflogCommitsTitle string
ConflictsResolved string ConflictsResolved string
RebasingTitle string RebasingTitle string
ConfirmRebase string SimpleRebase string
InteractiveRebase string
InteractiveRebaseTooltip string
ConfirmMerge string ConfirmMerge string
FwdNoUpstream string FwdNoUpstream string
FwdNoLocalUpstream string FwdNoLocalUpstream string
@ -877,8 +879,10 @@ func EnglishTranslationSet() TranslationSet {
ReflogCommitsTitle: "Reflog", ReflogCommitsTitle: "Reflog",
GlobalTitle: "Global Keybindings", GlobalTitle: "Global Keybindings",
ConflictsResolved: "all merge conflicts resolved. Continue?", ConflictsResolved: "all merge conflicts resolved. Continue?",
RebasingTitle: "Rebasing", RebasingTitle: "Rebase '{{.checkedOutBranch}}' onto '{{.ref}}'",
ConfirmRebase: "Are you sure you want to rebase '{{.checkedOutBranch}}' on top of '{{.selectedBranch}}'?", SimpleRebase: "simple rebase",
InteractiveRebase: "interactive rebase",
InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing",
ConfirmMerge: "Are you sure you want to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?", ConfirmMerge: "Are you sure you want to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
FwdNoUpstream: "Cannot fast-forward a branch with no upstream", FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally", FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally",

View File

@ -202,8 +202,6 @@ func koreanTranslationSet() TranslationSet {
ReflogCommitsTitle: "Reflog", ReflogCommitsTitle: "Reflog",
GlobalTitle: "글로벌 키 바인딩", GlobalTitle: "글로벌 키 바인딩",
ConflictsResolved: "모든 병합 충돌이 해결되었습니다. 계속 할까요?", ConflictsResolved: "모든 병합 충돌이 해결되었습니다. 계속 할까요?",
RebasingTitle: "리베이스 중",
ConfirmRebase: "정말로 '{{.checkedOutBranch}}' 을(를) '{{.selectedBranch}}'에 리베이스 하시겠습니까?",
ConfirmMerge: "정말로 '{{.selectedBranch}}' 을(를) '{{.checkedOutBranch}}'에 병합하시겠습니까?", ConfirmMerge: "정말로 '{{.selectedBranch}}' 을(를) '{{.checkedOutBranch}}'에 병합하시겠습니까?",
FwdNoUpstream: "Cannot fast-forward a branch with no upstream", FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally", FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally",

View File

@ -112,9 +112,7 @@ func polishTranslationSet() TranslationSet {
FileStagingRequirements: "Można tylko zatwierdzić pojedyncze linie dla śledzonych plików z niezatwierdzonymi zmianami", FileStagingRequirements: "Można tylko zatwierdzić pojedyncze linie dla śledzonych plików z niezatwierdzonymi zmianami",
StagingTitle: "Poczekalnia", StagingTitle: "Poczekalnia",
ReturnToFilesPanel: "wróć do panelu plików", ReturnToFilesPanel: "wróć do panelu plików",
RebasingTitle: "Zmiana bazy",
MergingTitle: "Scalanie", MergingTitle: "Scalanie",
ConfirmRebase: "Czy napewno chcesz zmienić bazę '{{.checkedOutBranch}}' na '{{.selectedBranch}}'?",
ConfirmMerge: "Czy na pewno chcesz scalić '{{.selectedBranch}}' do '{{.checkedOutBranch}}'?", ConfirmMerge: "Czy na pewno chcesz scalić '{{.selectedBranch}}' do '{{.checkedOutBranch}}'?",
FwdNoUpstream: "Nie można przewinąć gałęzi bez gałęzi nadrzędnej", FwdNoUpstream: "Nie można przewinąć gałęzi bez gałęzi nadrzędnej",
FwdCommitsToPush: "Nie można przewinąć gałęzi z commitami do wysłania", FwdCommitsToPush: "Nie można przewinąć gałęzi z commitami do wysłania",

View File

@ -30,9 +30,9 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
Press(keys.Branches.RebaseBranch) Press(keys.Branches.RebaseBranch)
t.ExpectPopup().Confirmation(). t.ExpectPopup().Menu().
Title(Equals("Rebasing")). Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
Content(Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?")). Select(Contains("simple rebase")).
Confirm() Confirm()
t.Common().AcknowledgeConflicts() t.Common().AcknowledgeConflicts()

View File

@ -36,9 +36,9 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
Press(keys.Branches.RebaseBranch) Press(keys.Branches.RebaseBranch)
t.ExpectPopup().Confirmation(). t.ExpectPopup().Menu().
Title(Equals("Rebasing")). Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
Content(Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?")). Select(Contains("simple rebase")).
Confirm() Confirm()
t.Views().Information().Content(Contains("rebasing")) t.Views().Information().Content(Contains("rebasing"))

View File

@ -39,9 +39,9 @@ var RebaseDoesNotAutosquash = NewIntegrationTest(NewIntegrationTestArgs{
SelectNextItem(). SelectNextItem().
Press(keys.Branches.RebaseBranch) Press(keys.Branches.RebaseBranch)
t.ExpectPopup().Confirmation(). t.ExpectPopup().Menu().
Title(Equals("Rebasing")). Title(Equals("Rebase 'my-branch' onto 'master'")).
Content(Contains("Are you sure you want to rebase 'my-branch' on top of 'master'?")). Select(Contains("simple rebase")).
Confirm() Confirm()
t.Views().Commits().Lines( t.Views().Commits().Lines(

View File

@ -0,0 +1,65 @@
package interactive_rebase
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
const (
BASE_BRANCH = "base-branch"
TOP_BRANCH = "top-branch"
BASE_COMMIT = "base-commit"
TOP_COMMIT = "top-commit"
)
var AdvancedInteractiveRebase = NewIntegrationTest(NewIntegrationTestArgs{
Description: "It begins an interactive rebase and verifies to have the possibility of editing the commits of the branch before proceeding with the actual rebase",
ExtraCmdArgs: "",
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
NewBranch(BASE_BRANCH).
EmptyCommit(BASE_COMMIT).
NewBranch(TOP_BRANCH).
EmptyCommit(TOP_COMMIT)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains(TOP_COMMIT),
Contains(BASE_COMMIT),
)
t.Views().Branches().
Focus().
NavigateToLine(Contains(BASE_BRANCH)).
Press(keys.Branches.RebaseBranch)
t.ExpectPopup().Menu().
Title(Equals(fmt.Sprintf("Rebase '%s' onto '%s'", TOP_BRANCH, BASE_BRANCH))).
Select(Contains("interactive rebase")).
Confirm()
t.Views().Commits().
IsFocused().
Lines(
Contains(TOP_COMMIT),
Contains(BASE_COMMIT).Contains("YOU ARE HERE"),
).
NavigateToLine(Contains(TOP_COMMIT)).
Press(keys.Universal.Edit).
Lines(
Contains(TOP_COMMIT).Contains("edit"),
Contains(BASE_COMMIT).Contains("YOU ARE HERE"),
).
Tap(func() {
t.Common().ContinueRebase()
}).
Lines(
Contains(TOP_COMMIT).Contains("YOU ARE HERE"),
Contains(BASE_COMMIT),
)
},
})

View File

@ -83,6 +83,7 @@ var tests = []*components.IntegrationTest{
filter_by_path.CliArg, filter_by_path.CliArg,
filter_by_path.SelectFile, filter_by_path.SelectFile,
filter_by_path.TypeFile, filter_by_path.TypeFile,
interactive_rebase.AdvancedInteractiveRebase,
interactive_rebase.AmendFirstCommit, interactive_rebase.AmendFirstCommit,
interactive_rebase.AmendHeadCommitDuringRebase, interactive_rebase.AmendHeadCommitDuringRebase,
interactive_rebase.AmendMerge, interactive_rebase.AmendMerge,