mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-24 08:52:21 +02:00
Add command to find base commit for creating a fixup
This commit is contained in:
parent
33f933ba21
commit
8ca78412ac
@ -209,6 +209,7 @@ keybinding:
|
||||
commitChangesWithoutHook: 'w' # commit changes without pre-commit hook
|
||||
amendLastCommit: 'A'
|
||||
commitChangesWithEditor: 'C'
|
||||
findBaseCommitForFixup: '<c-f>'
|
||||
confirmDiscard: 'x'
|
||||
ignoreFile: 'i'
|
||||
refreshFiles: 'r'
|
||||
|
64
docs/Fixup_Commits.md
Normal file
64
docs/Fixup_Commits.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Fixup Commits
|
||||
|
||||
## Background
|
||||
|
||||
There's this common scenario that you have a PR in review, the reviewer is
|
||||
requesting some changes, and you make those changes and would normally simply
|
||||
squash them into the original commit that they came from. If you do that,
|
||||
however, there's no way for the reviewer to see what you changed. You could just
|
||||
make a separate commit with those changes at the end of the branch, but this is
|
||||
not ideal because it results in a git history that is not very clean.
|
||||
|
||||
To help with this, git has a concept of fixup commits: you do make a separate
|
||||
commit, but the subject of this commit is the string "fixup! " followed by the
|
||||
original commit subject. This both tells the reviewer what's going on (you are
|
||||
making a change that you later will squash into the designated commit), and it
|
||||
provides an easy way to actually perform this squash operation when you are
|
||||
ready to do that (before merging).
|
||||
|
||||
## Creating fixup commits
|
||||
|
||||
You could of course create fixup commits manually by typing in the commit
|
||||
message with the prefix yourself. But lazygit has an easier way to do that:
|
||||
in the Commits view, select the commit that you want to create a fixup for, and
|
||||
press shift-F (for "Create fixup commit for this commit"). This automatically
|
||||
creates a commit with the appropriate subject line.
|
||||
|
||||
Don't confuse this with the lowercase "f" command ("Fixup commit"); that one
|
||||
squashes the selected commit into its parent, this is not what we want here.
|
||||
|
||||
## Squashing fixup commits
|
||||
|
||||
When you're ready to merge the branch and want to squash all these fixup commits
|
||||
that you created, that's very easy to do: select the first commit of your branch
|
||||
and hit shift-S (for "Squash all 'fixup!' commits above selected commit
|
||||
(autosquash)"). Boom, done.
|
||||
|
||||
## Finding the commit to create a fixup for
|
||||
|
||||
When you are making changes to code that you changed earlier in a long branch,
|
||||
it can be tedious to find the commit to squash it into. Lazygit has a command to
|
||||
help you with this, too: in the Files view, press ctrl-f to select the right
|
||||
base commit in the Commits view automatically. From there, you can either press
|
||||
shift-F to create a fixup commit for it, or shift-A to amend your changes into
|
||||
the commit if you haven't published your branch yet.
|
||||
|
||||
This command works in many cases, and when it does it almost feels like magic,
|
||||
but it's important to understand its limitations because it doesn't always work.
|
||||
The way it works is that it looks at the deleted lines of your current
|
||||
modifications, blames them to find out which commit those lines come from, and
|
||||
if they all come from the same commit, it selects it. So here are cases where it
|
||||
doesn't work:
|
||||
|
||||
- Your current diff has only added lines, but no deleted lines. In this case
|
||||
there's no way for lazygit to know which commit you want to add them to.
|
||||
- The deleted lines belong to multiple different commits. In this case you can
|
||||
help lazygit by staging a set of files or hunks that all belong to the same
|
||||
commit; if some changes are staged, the ctrl-f command works only on those.
|
||||
- The found commit is already on master; in this case, lazygit refuses to select
|
||||
it, because it doesn't make sense to create fixups for it, let alone amend to
|
||||
it.
|
||||
|
||||
To sum it up: the command works great if you are changing code again that you
|
||||
changed or added earlier in the same branch. This is a common enough case to
|
||||
make the command useful.
|
@ -123,6 +123,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: Amend last commit
|
||||
<kbd>C</kbd>: Commit changes using git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edit file
|
||||
<kbd>o</kbd>: Open file
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
|
@ -196,6 +196,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||
<kbd>A</kbd>: 最新のコミットにamend
|
||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>i</kbd>: ファイルをignore
|
||||
|
@ -336,6 +336,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: 마지맛 커밋 수정
|
||||
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>i</kbd>: Ignore file
|
||||
|
@ -56,6 +56,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
|
||||
<kbd>A</kbd>: Wijzig laatste commit
|
||||
<kbd>C</kbd>: Commit veranderingen met de git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Verander bestand
|
||||
<kbd>o</kbd>: Open bestand
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
|
@ -157,6 +157,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
|
||||
<kbd>A</kbd>: Zmień ostatni commit
|
||||
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edytuj plik
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
|
@ -330,6 +330,7 @@ _Связки клавиш_
|
||||
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
|
||||
<kbd>A</kbd>: Правка последнего коммита
|
||||
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Редактировать файл
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
<kbd>i</kbd>: Игнорировать или исключить файл
|
||||
|
@ -204,6 +204,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||
<kbd>A</kbd>: 修补最后一次提交
|
||||
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>i</kbd>: 忽略文件
|
||||
|
@ -299,6 +299,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
|
||||
<kbd>A</kbd>: 修正上次提交
|
||||
<kbd>C</kbd>: 使用 git 編輯器提交變更
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 編輯檔案
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
<kbd>i</kbd>: 忽略或排除檔案
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
// GitCommand is our main git interface
|
||||
type GitCommand struct {
|
||||
Blame *git_commands.BlameCommands
|
||||
Branch *git_commands.BranchCommands
|
||||
Commit *git_commands.CommitCommands
|
||||
Config *git_commands.ConfigCommands
|
||||
@ -160,6 +161,7 @@ func NewGitCommandAux(
|
||||
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
blameCommands := git_commands.NewBlameCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
@ -171,6 +173,7 @@ func NewGitCommandAux(
|
||||
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
||||
|
||||
return &GitCommand{
|
||||
Blame: blameCommands,
|
||||
Branch: branchCommands,
|
||||
Commit: commitCommands,
|
||||
Config: configCommands,
|
||||
|
33
pkg/commands/git_commands/blame.go
Normal file
33
pkg/commands/git_commands/blame.go
Normal file
@ -0,0 +1,33 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type BlameCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
return &BlameCommands{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
// Blame a range of lines. For each line, output the hash of the commit where
|
||||
// the line last changed, then a space, then a description of the commit (author
|
||||
// and date), another space, and then the line. For example:
|
||||
//
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 11) func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 12) return &BlameCommands{
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 13) GitCommon: gitCommon,
|
||||
func (self *BlameCommands) BlameLineRange(filename string, commit string, firstLine int, numLines int) (string, error) {
|
||||
cmdArgs := NewGitCmd("blame").
|
||||
Arg("-l").
|
||||
Arg(fmt.Sprintf("-L%d,+%d", firstLine, numLines)).
|
||||
Arg(commit).
|
||||
Arg("--").
|
||||
Arg(filename)
|
||||
|
||||
return self.cmd.New(cmdArgs.ToArgv()).RunWithOutput()
|
||||
}
|
@ -198,6 +198,20 @@ func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, e
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
// Example output:
|
||||
//
|
||||
// cd50c79ae Preserve the commit message correctly even if the description has blank lines
|
||||
// 3ebba5f32 Add test demonstrating a bug with preserving the commit message
|
||||
// 9a423c388 Remove unused function
|
||||
func (self *CommitCommands) GetShasAndCommitMessagesFirstLine(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:%h %s").
|
||||
Arg(shas...).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--oneline").
|
||||
|
@ -78,3 +78,11 @@ func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) oscommands
|
||||
Arg("--", opts.Filepath).
|
||||
ToArgv())
|
||||
}
|
||||
|
||||
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) oscommands.ICmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff-index").
|
||||
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
|
||||
Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
}
|
||||
|
@ -366,6 +366,7 @@ type KeybindingFilesConfig struct {
|
||||
CommitChangesWithoutHook string `yaml:"commitChangesWithoutHook"`
|
||||
AmendLastCommit string `yaml:"amendLastCommit"`
|
||||
CommitChangesWithEditor string `yaml:"commitChangesWithEditor"`
|
||||
FindBaseCommitForFixup string `yaml:"findBaseCommitForFixup"`
|
||||
ConfirmDiscard string `yaml:"confirmDiscard"`
|
||||
IgnoreFile string `yaml:"ignoreFile"`
|
||||
RefreshFiles string `yaml:"refreshFiles"`
|
||||
@ -762,6 +763,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
CommitChangesWithoutHook: "w",
|
||||
AmendLastCommit: "A",
|
||||
CommitChangesWithEditor: "C",
|
||||
FindBaseCommitForFixup: "<c-f>",
|
||||
IgnoreFile: "i",
|
||||
RefreshFiles: "r",
|
||||
StashAllChanges: "s",
|
||||
|
@ -103,6 +103,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
CherryPick: cherryPickHelper,
|
||||
Upstream: helpers.NewUpstreamHelper(helperCommon, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
||||
FixupHelper: helpers.NewFixupHelper(helperCommon),
|
||||
Commits: commitsHelper,
|
||||
Snake: helpers.NewSnakeHelper(helperCommon),
|
||||
Diff: diffHelper,
|
||||
|
@ -64,6 +64,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress,
|
||||
Description: self.c.Tr.CommitChangesWithEditor,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.FindBaseCommitForFixup),
|
||||
Handler: self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress,
|
||||
Description: self.c.Tr.FindBaseCommitForFixup,
|
||||
Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Edit),
|
||||
Handler: self.checkSelectedFileNode(self.edit),
|
||||
|
178
pkg/gui/controllers/helpers/fixup_helper.go
Normal file
178
pkg/gui/controllers/helpers/fixup_helper.go
Normal file
@ -0,0 +1,178 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type FixupHelper struct {
|
||||
c *HelperCommon
|
||||
}
|
||||
|
||||
func NewFixupHelper(
|
||||
c *HelperCommon,
|
||||
) *FixupHelper {
|
||||
return &FixupHelper{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
type deletedLineInfo struct {
|
||||
filename string
|
||||
startLineIdx int
|
||||
numLines int
|
||||
}
|
||||
|
||||
func (self *FixupHelper) HandleFindBaseCommitForFixupPress() error {
|
||||
diff, hasStagedChanges, err := self.getDiff()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if diff == "" {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoChangedFiles)
|
||||
}
|
||||
|
||||
deletedLineInfos := self.parseDiff(diff)
|
||||
if len(deletedLineInfos) == 0 {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoDeletedLinesInDiff)
|
||||
}
|
||||
|
||||
shas := self.blameDeletedLines(deletedLineInfos)
|
||||
|
||||
if len(shas) == 0 {
|
||||
// This should never happen
|
||||
return self.c.ErrorMsg(self.c.Tr.NoBaseCommitsFound)
|
||||
}
|
||||
if len(shas) > 1 {
|
||||
subjects, err := self.c.Git().Commit.GetShasAndCommitMessagesFirstLine(shas)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message := lo.Ternary(hasStagedChanges,
|
||||
self.c.Tr.MultipleBaseCommitsFoundStaged,
|
||||
self.c.Tr.MultipleBaseCommitsFoundUnstaged)
|
||||
return self.c.ErrorMsg(message + "\n\n" + subjects)
|
||||
}
|
||||
|
||||
commit, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(commit *models.Commit) bool {
|
||||
return commit.Sha == shas[0]
|
||||
})
|
||||
if !ok {
|
||||
commits := self.c.Model().Commits
|
||||
if commits[len(commits)-1].Status == models.StatusMerged {
|
||||
// If the commit is not found, it's most likely because it's already
|
||||
// merged, and more than 300 commits away. Check if the last known
|
||||
// commit is already merged; if so, show the "already merged" error.
|
||||
return self.c.ErrorMsg(self.c.Tr.BaseCommitIsAlreadyOnMainBranch)
|
||||
}
|
||||
// If we get here, the current branch must have more then 300 commits. Unlikely...
|
||||
return self.c.ErrorMsg(self.c.Tr.BaseCommitIsNotInCurrentView)
|
||||
}
|
||||
if commit.Status == models.StatusMerged {
|
||||
return self.c.ErrorMsg(self.c.Tr.BaseCommitIsAlreadyOnMainBranch)
|
||||
}
|
||||
|
||||
if !useIndex {
|
||||
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
self.c.Contexts().LocalCommits.SetSelectedLineIdx(index)
|
||||
return self.c.PushContext(self.c.Contexts().LocalCommits)
|
||||
}
|
||||
|
||||
func (self *FixupHelper) getDiff() (string, bool, error) {
|
||||
args := []string{"-U0", "--ignore-submodules=all", "HEAD", "--"}
|
||||
|
||||
// Try staged changes first
|
||||
hasStagedChanges := true
|
||||
diff, err := self.c.Git().Diff.DiffIndexCmdObj(append([]string{"--cached"}, args...)...).RunWithOutput()
|
||||
|
||||
if err == nil && diff == "" {
|
||||
hasStagedChanges = false
|
||||
// If there are no staged changes, try unstaged changes
|
||||
diff, err = self.c.Git().Diff.DiffIndexCmdObj(args...).RunWithOutput()
|
||||
}
|
||||
|
||||
return diff, hasStagedChanges, err
|
||||
}
|
||||
|
||||
func (self *FixupHelper) parseDiff(diff string) []*deletedLineInfo {
|
||||
lines := strings.Split(strings.TrimSuffix(diff, "\n"), "\n")
|
||||
|
||||
deletedLineInfos := []*deletedLineInfo{}
|
||||
|
||||
hunkHeaderRegexp := regexp.MustCompile(`@@ -(\d+)(?:,\d+)? \+\d+(?:,\d+)? @@`)
|
||||
|
||||
var filename string
|
||||
var currentLineInfo *deletedLineInfo
|
||||
finishHunk := func() {
|
||||
if currentLineInfo != nil && currentLineInfo.numLines > 0 {
|
||||
deletedLineInfos = append(deletedLineInfos, currentLineInfo)
|
||||
}
|
||||
}
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "diff --git") {
|
||||
finishHunk()
|
||||
currentLineInfo = nil
|
||||
} else if strings.HasPrefix(line, "--- ") {
|
||||
// For some reason, the line ends with a tab character if the file
|
||||
// name contains spaces
|
||||
filename = strings.TrimRight(line[6:], "\t")
|
||||
} else if strings.HasPrefix(line, "@@ ") {
|
||||
finishHunk()
|
||||
match := hunkHeaderRegexp.FindStringSubmatch(line)
|
||||
startIdx := utils.MustConvertToInt(match[1])
|
||||
currentLineInfo = &deletedLineInfo{filename, startIdx, 0}
|
||||
} else if currentLineInfo != nil && line[0] == '-' {
|
||||
currentLineInfo.numLines++
|
||||
}
|
||||
}
|
||||
finishHunk()
|
||||
|
||||
return deletedLineInfos
|
||||
}
|
||||
|
||||
// returns the list of commit hashes that introduced the lines which have now been deleted
|
||||
func (self *FixupHelper) blameDeletedLines(deletedLineInfos []*deletedLineInfo) []string {
|
||||
var wg sync.WaitGroup
|
||||
shaChan := make(chan string)
|
||||
|
||||
for _, info := range deletedLineInfos {
|
||||
wg.Add(1)
|
||||
go func(info *deletedLineInfo) {
|
||||
defer wg.Done()
|
||||
|
||||
blameOutput, err := self.c.Git().Blame.BlameLineRange(info.filename, "HEAD", info.startLineIdx, info.numLines)
|
||||
if err != nil {
|
||||
self.c.Log.Errorf("Error blaming file '%s': %v", info.filename, err)
|
||||
return
|
||||
}
|
||||
blameLines := strings.Split(strings.TrimSuffix(blameOutput, "\n"), "\n")
|
||||
for _, line := range blameLines {
|
||||
shaChan <- strings.Split(line, " ")[0]
|
||||
}
|
||||
}(info)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(shaChan)
|
||||
}()
|
||||
|
||||
result := set.New[string]()
|
||||
for sha := range shaChan {
|
||||
result.Add(sha)
|
||||
}
|
||||
|
||||
return result.ToSlice()
|
||||
}
|
@ -33,6 +33,7 @@ type Helpers struct {
|
||||
GPG *GpgHelper
|
||||
Upstream *UpstreamHelper
|
||||
AmendHelper *AmendHelper
|
||||
FixupHelper *FixupHelper
|
||||
Commits *CommitsHelper
|
||||
Snake *SnakeHelper
|
||||
// lives in context package because our contexts need it to render to main
|
||||
@ -70,6 +71,7 @@ func NewStubHelpers() *Helpers {
|
||||
GPG: &GpgHelper{},
|
||||
Upstream: &UpstreamHelper{},
|
||||
AmendHelper: &AmendHelper{},
|
||||
FixupHelper: &FixupHelper{},
|
||||
Commits: &CommitsHelper{},
|
||||
Snake: &SnakeHelper{},
|
||||
Diff: &DiffHelper{},
|
||||
|
@ -39,6 +39,14 @@ type TranslationSet struct {
|
||||
SureToAmend string
|
||||
NoCommitToAmend string
|
||||
CommitChangesWithEditor string
|
||||
FindBaseCommitForFixup string
|
||||
FindBaseCommitForFixupTooltip string
|
||||
NoDeletedLinesInDiff string
|
||||
NoBaseCommitsFound string
|
||||
MultipleBaseCommitsFoundStaged string
|
||||
MultipleBaseCommitsFoundUnstaged string
|
||||
BaseCommitIsAlreadyOnMainBranch string
|
||||
BaseCommitIsNotInCurrentView string
|
||||
StatusTitle string
|
||||
GlobalTitle string
|
||||
Menu string
|
||||
@ -858,6 +866,14 @@ func EnglishTranslationSet() TranslationSet {
|
||||
SureToAmend: "Are you sure you want to amend last commit? Afterwards, you can change the commit message from the commits panel.",
|
||||
NoCommitToAmend: "There's no commit to amend.",
|
||||
CommitChangesWithEditor: "Commit changes using git editor",
|
||||
FindBaseCommitForFixup: "Find base commit for fixup",
|
||||
FindBaseCommitForFixupTooltip: "Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md>",
|
||||
NoDeletedLinesInDiff: "No deleted lines in diff",
|
||||
NoBaseCommitsFound: "No base commits found",
|
||||
MultipleBaseCommitsFoundStaged: "Multiple base commits found. (Try staging fewer changes at once)",
|
||||
MultipleBaseCommitsFoundUnstaged: "Multiple base commits found. (Try staging some of the changes)",
|
||||
BaseCommitIsAlreadyOnMainBranch: "The base commit for this change is already on the main branch",
|
||||
BaseCommitIsNotInCurrentView: "Base commit is not in current view",
|
||||
StatusTitle: "Status",
|
||||
Menu: "Menu",
|
||||
Execute: "Execute",
|
||||
|
79
pkg/integration/tests/commit/find_base_commit_for_fixup.go
Normal file
79
pkg/integration/tests/commit/find_base_commit_for_fixup.go
Normal file
@ -0,0 +1,79 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FindBaseCommitForFixup = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Finds the base commit to create a fixup for",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.NewBranch("mybranch").
|
||||
EmptyCommit("1st commit").
|
||||
CreateFileAndAdd("file1", "file1 content\n").
|
||||
Commit("2nd commit").
|
||||
CreateFileAndAdd("file2", "file2 content\n").
|
||||
Commit("3rd commit").
|
||||
UpdateFile("file1", "file1 changed content").
|
||||
UpdateFile("file2", "file2 changed content")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("3rd commit"),
|
||||
Contains("2nd commit"),
|
||||
Contains("1st commit"),
|
||||
)
|
||||
|
||||
// Two changes from different commits: this fails
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Press(keys.Files.FindBaseCommitForFixup)
|
||||
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(
|
||||
Contains("Multiple base commits found").
|
||||
Contains("2nd commit").
|
||||
Contains("3rd commit"),
|
||||
).
|
||||
Confirm()
|
||||
|
||||
// Stage only one of the files: this succeeds
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("file1")).
|
||||
PressPrimaryAction().
|
||||
Press(keys.Files.FindBaseCommitForFixup)
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("3rd commit"),
|
||||
Contains("2nd commit").IsSelected(),
|
||||
Contains("1st commit"),
|
||||
).
|
||||
Press(keys.Commits.AmendToCommit)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Amend commit")).
|
||||
Content(Contains("Are you sure you want to amend this commit with your staged files?")).
|
||||
Confirm()
|
||||
|
||||
// Now only the other file is modified (and unstaged); this works now
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Press(keys.Files.FindBaseCommitForFixup)
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("3rd commit").IsSelected(),
|
||||
Contains("2nd commit"),
|
||||
Contains("1st commit"),
|
||||
)
|
||||
},
|
||||
})
|
@ -70,6 +70,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.CommitWithPrefix,
|
||||
commit.CreateTag,
|
||||
commit.DiscardOldFileChange,
|
||||
commit.FindBaseCommitForFixup,
|
||||
commit.Highlight,
|
||||
commit.History,
|
||||
commit.HistoryComplex,
|
||||
|
@ -914,6 +914,10 @@
|
||||
"type": "string",
|
||||
"default": "C"
|
||||
},
|
||||
"findBaseCommitForFixup": {
|
||||
"type": "string",
|
||||
"default": "\u003cc-f\u003e"
|
||||
},
|
||||
"confirmDiscard": {
|
||||
"type": "string",
|
||||
"default": "x"
|
||||
|
Loading…
Reference in New Issue
Block a user