Repository: jesseduffield/lazygit (language: go)

Issue:
CRITICAL: unwanted squash during rebase
**Describe the bug/To reproduce**
A
B <= start rebase
modify foo.py content in my code editor
in lazygit I select foo;py and amend it (<space>A)
rebase continue
commit A conflicts with B
So I must modify foo.py now and solve the problem
A popup appears should we continue the merge. I hit esape because obviously I gotta check the diff of my commit, possibly edit the commit messag etc.
Commit seems ok. I append (click A).
And here's the problem. Instead of editing last, conflicting, commit it SQUASH without telling me with the last commit.
So now instead of
A
B
I just have 
B (B only has the commit message of B. But it has the code of A and B)

So basically the bug is: if you ever happen to <space>A while in a rebase conflict then the commit A wil merge with commit B.

So hopefully no one will say "hum technically it's not a bug". IDK the technicalities but I'll add personnal context: I work for weeks on commit. I have to constantly rebase them (company policy + we use gerrit which is rebase only unlike GH). I have long series (+20 patches) and can spend a day rebasing just one. Having one commit randomly merged inside my series is not always obvious to notice. I cannot unmerge the commit. I gotta manually split them or redo the whole rebase. It is *really* distraughting to see my very long rebase turned into random merge sessions.
I love your app but I cannot stress enough of critical I feel like this bug/bad UX is. 

**Expected behavior**
Make amend works the same for conflicting commit as for non conflicting commit.

**Screenshots**

**Version info:**
commit=c03b89227092b852d50015d289a7c6d8c69811c5, build date=2025-01-17T13:37:10Z, build source=binaryRelease, version=0.45.2, os=linux, arch=amd64, git version=2.34.1

⟩ git --version
git version 2.34.1

**Additional context**
git config --list
user.name=<Ilan Schemoul>
user.email=<ilan.schemoul@intersec.com>
include.path=/home/ilan/dev/tools/dotfiles/gitinclude
core.excludesfile=~/.gitignore
core.autocrlf=input
alias.br=branch
alias.co=checkout
alias.ci=commit
alias.ri=rebase -i
alias.st=status
alias.stat=status
alias.pop=reset HEAD~
alias.undo=reset HEAD~
alias.glog=log --graph
alias.tempo=commit -a -m tempo
alias.su=submodule update --init --recursive
alias.rh=reset --hard
alias.cp=cherry-pick
alias.amend=commit --amend
alias.squash=commit --amend -C HEAD
alias.fixup=commit --amend -C HEAD
alias.unstash=stash apply
alias.track=add -N
alias.untrack=rm --cached
alias.vimdiff=difftool -y -t vimdiff
alias.workdir=!sh /usr/share/doc/git/contrib/workdir/git-new-workdir
alias.branch-contains=branch origin/* --remotes --contains
alias.find-merge=!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'
alias.diff-no-blank=diff --ignore-space-change --ignore-all-space --ignore-blank-lines
alias.diff-word=diff --word-diff --word-diff-regex="[^ ;]+"
alias.show-word=show --word-diff --word-diff-regex="[^ ;]+"
alias.diff-char=diff --word-diff --word-diff-regex=.
alias.show-char=show --word-diff --word-diff-regex=.
alias.picore=cherry-pick
alias.decoupe=bisect
alias.journal=log
alias.graffiti=tag
alias.ajoute=add
alias.dichotomie=bisect
alias.transaction=commit
alias.deploiement=checkout
alias.cerise=cherry
alias.recherche-par-expression-rationelle=grep
alias.fusionne=merge
alias.planque=stash
alias.ramasse-miettes=gc
alias.tire=pull
alias.pousse=push
alias.pousse-fort=push-for
alias.montre=show
alias.rembobine=reset
alias.supprime-cette-merde=revert
alias.balance=blame
alias.poop=stash pop
alias.truite=merge
color.ui=auto
color.diff=auto
color.branch=auto
color.status=auto
color.pager=true
color.interactive=auto
color.diff.meta=green
color.diff.frag=yellow
color.diff.old=magenta
color.diff.new=bold cyan
color.status.header=bold blue
color.status.added=green
color.status.changed=bold red
color.status.untracked=bold yellow
notes.displayref=refs/notes/*
commit.template=/srv/tools/share/dotfiles/git-commit-template
push.default=upstream
merge.verbosity=1
merge.conflictstyle=diff3
merge.stat=true
merge.renamelimit=100000
merge.ff=false
merge.ours.driver=true
rebase.stat=true
rebase.autosquash=true
diff.renames=copies
url.ssh://git.corp:29418/.insteadof=git.corp:/srv/git
url.ssh://git.corp:29418/.insteadof=ssh://git.corp/srv/git
url.ssh://git.corp:29418/.insteadof=gitolite@git.corp:
url.ssh://git.corp:29418/mmsx.insteadof=git.corp:/srv/git/qdb
url.ssh://git.corp:29418/mmsx.insteadof=ssh://git.corp/srv/git/qdb
url.ssh://git.corp:29418/mmsx.insteadof=ssh://git.corp:29418/qdb
url.ssh://git.corp:29418/mmsx.insteadof=gitolite@git.corp:qdb
url.ssh://git.corp:29418/mmsx.insteadof=git.corp:/srv/git/qrrd
url.ssh://git.corp:29418/mmsx.insteadof=ssh://git.corp/srv/git/qrrd
url.ssh://git.corp:29418/mmsx.insteadof=ssh://git.corp:29418/qrrd
url.ssh://git.corp:29418/mmsx.insteadof=gitolite@git.corp:qrrd
url.ssh://git.corp:29418/mmsx.insteadof=ssh://git.corp:29418/platform
rerere.enabled=true
submodule.platform.update=!/srv/tools/share/scripts/git-submodule-update.sh
submodule.lib-common.update=!/srv/tools/share/scripts/git-submodule-update.sh
tig.bind.generic=r !git rebase -i %(commit)
tig.bind.generic=p @sh -c "echo -n %(commit) | xclip"
tig.bind.generic=f !git commit --fixup=%(commit)
pull.rebase=true
maintenance.repo=/home/ilan/dev/mmsx-master
maintenance.repo=/home/ilan/dev/mmsx-2022-dev
maintenance.repo=/home/ilan/dev/mmsx-4
alias.gl=config --global -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.fsmonitor=false
core.untrackedcache=true
remote.origin.url=ssh://git.corp:29418/mmsx
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
submodule.platform/lib-common.active=true
submodule.platform/lib-common.url=ssh://git.corp:29418/lib-common
submodule.platform/www/modules/tcpdf/external.active=true
submodule.platform/www/modules/tcpdf/external.url=ssh://git.corp:29418/tcpdf
remote.mob.url=ssh://git.corp:29418/mmsx-mob.git
remote.mob.fetch=+refs/heads/*:refs/remotes/mob/*
branch.2022-dev.remote=origin
branch.2022-dev.merge=refs/heads/2022-dev
maintenance.auto=false
maintenance.strategy=incremental
log.excludedecoration=refs/prefetch/
submodule.platform.active=true
submodule.platform.url=ssh://git.corp:29418/platform
submodule.platform/www/modules/core/htdocs/javascript/ext/ckeditor-releases.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/ckeditor-releases.url=ssh://git.corp:29418/ckeditor-releases
submodule.platform/www/modules/core/htdocs/javascript/ext/jasmine.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/jasmine.url=ssh://git.corp:29418/jasmine
submodule.platform/www/modules/core/htdocs/javascript/ext/jasmine-jquery.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/jasmine-jquery.url=ssh://git.corp:29418/jasmine-jquery
branch.geom.remote=mob
branch.geom.merge=refs/heads/ilan/geom-minimal-lib
branch.2024-dev.remote=origin
branch.2024-dev.merge=refs/heads/2024-dev
branch.ARCHIVE-preview.remote=mob
branch.ARCHIVE-preview.merge=refs/heads/ilan/keyword-replacement
submodule.platform/www/modules/core/htdocs/javascript/ext/highcharts.com.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/highcharts.com.url=ssh://git.corp:29418/highcharts.com
submodule.platform/www/modules/core/htdocs/javascript/ext/jsplumb.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/jsplumb.url=ssh://git.corp:29418/jsplumb
submodule.platform/www/modules/core/htdocs/javascript/ext/pace.active=true
submodule.platform/www/modules/core/htdocs/javascript/ext/pace.url=ssh://git.corp:29418/pace

Lazy git config

os:
  edit: "command nvim --server $NVIM --remote-send \"<C-\\><C-N>:FromFTToTab {{filename}}<cr>\""
  editAtLine: "command nvim --server $NVIM --remote-send \"<C-\\><C-N>:FromFTToTab {{filename}} {{line}}<cr>\""
  openDirInEditor: "command nvim --server $NVIM --remote-send \"<C-\\><C-N>:FromFTToTab {{filename}}<cr>\""
  open: "command nvim --server $NVIM --remote-send \"<C-\\><C-N>:FromFTToTab {{filename}}<cr>\""
customCommands:
  - key: 'G'
    showOutput: true
    context: 'global'
    command: 'git push-for {{.Form.Branch | quote}}'
    prompts:
      - type: 'input'
        title: 'Which branch ?'
        key: 'Branch'
        initialValue: '2024-dev'
        suggestions:
          preset: 'branches' # use built-in logic for obtaining branches
  - key: 'b'
    showOutput: true
    context: 'files'
    command: 'git absorb'
  - key: 'B'
    showOutput: true
    context: 'files'
    command: 'git absorb --and-rebase'
gui:
  nerdFontsVersion: "3"
  # The number of lines you scroll by when scrolling the main window
  scrollHeight: 10
  commitHashLength: 3
disableStartupPopups: true
git:
  commit:
    autoWrapWidth: 72
  log:
    order: default


**Note:** please try updating to the latest version or [manually building](https://github.com/jesseduffield/lazygit/#manual) the latest `master` to see if the issue still occurs.

I won't do master but I have a super fresh version and the behaviour was also exhibited by 0.42. It's a not a new behaviour and unlikely to change in master as 0.45.2 is super new.


Discussion / hints:
At first I was going to say I would chalk this up to user error. This is simply how git works; the same happens when you do these things on the command line.

After thinking about it more, I guess that lazygit makes it more likely to make this mistake because of the way it represents the conflicting commit in the commits panel. This is something that command-line git doesn't do (typing git log doesn't show it). However, I find it important that we show this "pseudo commit", because it gives you more context about where you are in the rebase, and it lets you look at the diff of the original commit, which is often very useful for resolving the conflicts. But it does make it look like amending things would now amend into that pseudo commit, whereas in reality it amends into the last real commit, which is the one before.

This is made even worse by my insistence of having the "You are here" pointer point at that pseudo commit rather than at your real current head commit. We had a long discussion about this [here](https://github.com/jesseduffield/lazygit/pull/2682).

So what can we do to improve this? I don't think I'm willing to reconsider where the "You are here" marker points; I really like it the way it is, and I would find it very confusing if we changed this.

But I suppose we could simply disable the "Amend" command when there is a "conflict" pseudo commit; I can't think of a real use case where you want to amend into the previous commit after resolving conflicts, so this might help.

@jesseduffield Any thoughts?
I'll just give my user feedback on this: I did not know that it was a pseudo commit.
Indeed the git cli UI is quite different because I think it clearly tell you solve conflict and do git rebase --continue so I couldn't do any mistake.

But as a user I really wish I'd know I'm working on a "pseudo commit" (maybe different colors, diferent indentation or different icon IDK). If you are positive that the only difference between pseudo commit and commit is just this commit amend behaviour then I guess you can leave commit and pseudo commit with same UI.
Otherwise I'd like to be aware that I work on a pseudo commit.

About You are here of course keep it ! It's the most brilliant thing of lazgit. With our tool (gerrit) we gotta rebase all day long (sad but necessary for clean history), git cli is confusing, "You are here" is my north star !

EDIT: if the change is not particularly hard I can tackle it
My belief that the 'you are here' market belongs on the head commit hasn't changed, and I would happily switch to that. I think that this issue is an example of where confusion arises from pointing at the wrong commit. I think that the commits panel needs to talk in terms of commits, not in terms of commits + working tree.

If you include the working tree, then 'here' should indeed point to the conflicting commit given that the working tree is going to match it (ignoring conflicting files), but I think the commits panel is for commits and the state of the working tree shouldn't factor in to what we consider the current commit to be, especially given that when you're outside a rebase, the head commit remains the head commit regardless of what's in your working tree.

I think that disabling amend to patch up a UX problem is the wrong approach, regardless of how likely somebody is to need to do that. It's just going to look silly if a user needs to do it and our excuse for disabling it was that our UX led to the action not doing what people expected it to.

I think having 'you are here' pointing at the head commit, with the head commit clearly labeled as a conflict (which we already have), resolves the confusion.


I kind of expected this reaction given our earlier discussion. 😄 But I still disagree very strongly.

> but I think the commits panel is for commits

When you're in a rebase, the commits panel shows a mixture of real commits and virtual (or pseudo) commits. It did so already before I added the "conflict" pseudo entry. The "conflict" pseudo entry is just as much a virtual commit as the other rebase todos are. The only difference is that it is synthesized from different information, because it's no longer contained in the git-rebase-todo file, so we need to synthesize it from some heuristic instead.

> not in terms of commits + working tree

What we display in case of a conflict isn't the working tree. What we display is a "pick" todo that just doesn't show up anywhere else (since it was removed from git-rebase-todo already, but isn't a real commit yet; it's kind of in flight). The working tree is just where conflict handling for this pick todo happens.

> having 'you are here' pointing at the head commit,

This is the point that I disagree with the most. I have said this before: "You are here" doesn't mean "This is your current head commit". We don't need to visualize that information, because you can already visually distinguish real commits from virtual commits (the latter have an action column and a blue hash). If we need even more visual cues for making it more obvious what the current head is (e.g. by making the colours even more different or whatever) then we can think about that, but putting a marker next to the head is not the way to do that in my opinion. (In a real GUI app rather than a TUI we might think about drawing a line between the real commits and the virtual ones.)

"You are here" means "this is where you are in the rebase", or "this is the current rebase step that is being carried out". In the normal case, when stopping at an "edit" todo, this happens to be the same as the current head, but for a conflicting commit it's not.

> with the head commit clearly labeled as a conflict

But that's wrong! It isn't the head commit that has the conflict, it is the next "pick" todo after it.

> I think that disabling amend to patch up a UX problem is the wrong approach

I disagree again. I think it's a misfeature of git that you can amend into the previous finished commit while you are resolving conflicts, so we can help people avoid that mistake. Maybe outright disabling it is a bit too drastic, but we can put up a big fat warning. Maybe something like
```
╭─Amend changes────────────────────────────────────────────────────────────────────────────────╮
│ WARNING: you are about to amend your resolved conflicts into the last finished commit. This  │ 
│ is very unlikely to be what you want at this point. More likely, you simply want to continue │ 
│ the rebase instead.                                                                          │
│                                                                                              │
│ Do you still want to amend your changes?                                                     │
│ - Cancel                                                                                     │
│ - No, continue rebase                                                                        │
│ - Yes, amend into previous commit                                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
```
> When you're in a rebase, the commits panel shows a mixture of real commits and virtual (or pseudo) commits. It did so already before I added the "conflict" pseudo entry. The "conflict" pseudo entry is just as much a virtual commit as the other rebase todos are. The only difference is that it is synthesized from different information, because it's no longer contained in the git-rebase-todo file, so we need to synthesize it from some heuristic instead.

That is true, however, it doesn't contradict my statement: I'm saying the commits panel is for commits, and each item is indeed a commit (even if put there based on information outside of `git log`). My point is that saying that 'here' is the conflicting commit only makes sense to me if our definition of 'here' involves both commits and the working tree, and I think it shouldn't. Though it seems like your argument doesn't require this and that you instead think of 'here' as where we are up to in a rebase.

> This is the point that I disagree with the most. I have said this before: "You are here" doesn't mean "This is your current head commit". We don't need to visualize that information, because you can already visually distinguish real commits from virtual commits (the latter have an action column and a blue hash). If we need even more visual cues for making it more obvious what the current head is (e.g. by making the colours even more different or whatever) then we can think about that, but putting a marker next to the head is not the way to do that in my opinion. (In a real GUI app rather than a TUI we might think about drawing a line between the real commits and the virtual ones.)

The reason I originally added the 'you are here' marker is because I didn't think that showing the todo commits in blue was sufficient to explain to the user which commit was the head commit, given that in all other situations the head commit is just the top commit. In other cases where we commits with different colours (red, green, yellow), the head commit is still the top commit so I wanted a clearer indicator for rebases. I think we both agree that we don't need to be confined to a single marker, given that a conflict is a kind of half-half state (you've got the head commit plus the commit that you're conflicting with and your tree matches the head commit but the index matches the conflicting commit). But I think it's fine to say 'this is the head' and 'this is the conflicting commit'.

> But that's wrong! It isn't the head commit that has the conflict, it is the next "pick" todo after it.

Sorry I misspoke here, I meant the commit above the head commit should be clearly labeled as a conflict (which it currently is)

> I disagree again. I think it's a misfeature of git that you can amend into the previous finished commit while you are resolving conflicts, so we can help people avoid that mistake. Maybe outright disabling it is a bit too drastic, but we can put up a big fat warning. Maybe something like

I'm okay with a warning

FWIW I have a twitter poll asking about where to point the 'you are here' marker and early results favour the conflict commit: https://x.com/DuffieldJesse/status/1883640424914628723

Maybe we need to think up some new ways of communicating the separation between commits and pseudo-commits. Perhaps even a marker against the head commit that's there at all times (it would help in situations where you don't realise that you've scrolled down in the commits panel).
> My point is that saying that 'here' is the conflicting commit only makes sense to me if our definition of 'here' involves both commits and the working tree, and I think it shouldn't.

No, the working tree is not involved here at all as far as I'm concerned. The "conflict" entry that we're displaying in the commits panel has nothing to do with the working tree. For example, whether the conflicts have already been resolved or not doesn't have any influence on how it is displayed (that information is shown in the files panel, not in the commits panel).

The "conflict" entry that we display is simply what used to be a "pick" todo, and it is conceptually exactly the same as all the other rebase todos that we display. The only difference is that normally, rebase todos are yet to be executed, i.e. they are completely in the future; once executed, they turn into real commits, there is no in between. The conflict entry is the only one that is _currently being executed_, and I think this deserves to be shown with a marker.

> The reason I originally added the 'you are here' marker is because I didn't think that showing the todo commits in blue was sufficient to explain to the user which commit was the head commit,

And my question is why it is so important to know which commit is the head commit. You need to know this because that's where amended changes go to, but if we disallow this while there are conflicts (or strongly warn about it as suggested above), then what other reason remains why you need to know? I find it more important to know what the current rebase step is; I have said this several times now, and I haven't heard a reason yet why you disagree with that.
The twitter poll ended in a 2:1 split in favour of a pointing at the conflicting commit:

![Image](https://github.com/user-attachments/assets/cd664847-431a-486a-9f53-d16816795f38)

So I'm happy to go with majority opinion.

But to answer your last question: The file changes in the working tree only make sense if you know what the head commit is, because those file changes are relative to that commit. When you encounter a conflict, you'll typically have a bunch of staged files (which we hide by default) which are from the conflicting commit as well as some conflict files which contain content from both. Either way, the files changes that appear are _relative_ to the head commit, not the conflicting commit, and that's why I care about what the head commit is.

Fix patch:
diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go
index ac72565ad8f..00317f4cf3d 100644
--- a/pkg/gui/controllers/files_controller.go
+++ b/pkg/gui/controllers/files_controller.go
@@ -690,23 +690,68 @@ func (self *FilesController) refresh() error {
 }
 
 func (self *FilesController) handleAmendCommitPress() error {
-	self.c.Confirm(types.ConfirmOpts{
-		Title:  self.c.Tr.AmendLastCommitTitle,
-		Prompt: self.c.Tr.SureToAmend,
-		HandleConfirm: func() error {
-			return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error {
-				if len(self.c.Model().Commits) == 0 {
-					return errors.New(self.c.Tr.NoCommitToAmend)
-				}
+	doAmend := func() error {
+		return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error {
+			if len(self.c.Model().Commits) == 0 {
+				return errors.New(self.c.Tr.NoCommitToAmend)
+			}
 
-				return self.c.Helpers().AmendHelper.AmendHead()
-			})
-		},
-	})
+			return self.c.Helpers().AmendHelper.AmendHead()
+		})
+	}
+
+	if self.isResolvingConflicts() {
+		return self.c.Menu(types.CreateMenuOptions{
+			Title:      self.c.Tr.AmendCommitTitle,
+			Prompt:     self.c.Tr.AmendCommitWithConflictsMenuPrompt,
+			HideCancel: true, // We want the cancel item first, so we add one manually
+			Items: []*types.MenuItem{
+				{
+					Label: self.c.Tr.Cancel,
+					OnPress: func() error {
+						return nil
+					},
+				},
+				{
+					Label: self.c.Tr.AmendCommitWithConflictsContinue,
+					OnPress: func() error {
+						return self.c.Helpers().MergeAndRebase.ContinueRebase()
+					},
+				},
+				{
+					Label: self.c.Tr.AmendCommitWithConflictsAmend,
+					OnPress: func() error {
+						return doAmend()
+					},
+				},
+			},
+		})
+	} else {
+		self.c.Confirm(types.ConfirmOpts{
+			Title:  self.c.Tr.AmendLastCommitTitle,
+			Prompt: self.c.Tr.SureToAmend,
+			HandleConfirm: func() error {
+				return doAmend()
+			},
+		})
+	}
 
 	return nil
 }
 
+func (self *FilesController) isResolvingConflicts() bool {
+	commits := self.c.Model().Commits
+	for _, c := range commits {
+		if c.Status != models.StatusRebasing {
+			break
+		}
+		if c.Action == models.ActionConflict {
+			return true
+		}
+	}
+	return false
+}
+
 func (self *FilesController) handleStatusFilterPressed() error {
 	return self.c.Menu(types.CreateMenuOptions{
 		Title: self.c.Tr.FilteringMenuTitle,
diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go
index 2fb30372b84..40d9e6df2c6 100644
--- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go
+++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go
@@ -77,6 +77,10 @@ func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error {
 	return self.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems})
 }
 
+func (self *MergeAndRebaseHelper) ContinueRebase() error {
+	return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
+}
+
 func (self *MergeAndRebaseHelper) genericMergeCommand(command string) error {
 	status := self.c.Git().Status.WorkingTreeState()
 
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index 87744f24d71..28e12635626 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -353,6 +353,9 @@ type TranslationSet struct {
 	ScrollDownMainWindow                  string
 	AmendCommitTitle                      string
 	AmendCommitPrompt                     string
+	AmendCommitWithConflictsMenuPrompt    string
+	AmendCommitWithConflictsContinue      string
+	AmendCommitWithConflictsAmend         string
 	DropCommitTitle                       string
 	DropCommitPrompt                      string
 	DropUpdateRefPrompt                   string
@@ -1375,6 +1378,9 @@ func EnglishTranslationSet() *TranslationSet {
 		ScrollDownMainWindow:                 "Scroll down main window",
 		AmendCommitTitle:                     "Amend commit",
 		AmendCommitPrompt:                    "Are you sure you want to amend this commit with your staged files?",
+		AmendCommitWithConflictsMenuPrompt:   "WARNING: you are about to amend the last finished commit with your resolved conflicts. This is very unlikely to be what you want at this point. More likely, you simply want to continue the rebase instead.\n\nDo you still want to amend the previous commit?",
+		AmendCommitWithConflictsContinue:     "No, continue rebase",
+		AmendCommitWithConflictsAmend:        "Yes, amend previous commit",
 		DropCommitTitle:                      "Drop commit",
 		DropCommitPrompt:                     "Are you sure you want to drop the selected commit(s)?",
 		DropMergeCommitPrompt:                "Are you sure you want to drop the selected merge commit? Note that it will also drop all the commits that were merged in by it.",


Test patch:
diff --git a/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_amend.go b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_amend.go
new file mode 100644
index 00000000000..8541310f0c0
--- /dev/null
+++ b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_amend.go
@@ -0,0 +1,41 @@
+package commit
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var AmendWhenThereAreConflictsAndAmend = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Amends the last commit from the files panel while a rebase is stopped due to conflicts, and amends the commit",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig:  func(config *config.AppConfig) {},
+	SetupRepo: func(shell *Shell) {
+		setupForAmendTests(shell)
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		doTheRebaseForAmendTests(t, keys)
+
+		t.Views().Files().
+			Press(keys.Commits.AmendToCommit)
+
+		t.ExpectPopup().Menu().
+			Title(Equals("Amend commit")).
+			Select(Equals("Yes, amend previous commit")).
+			Confirm()
+
+		t.Views().Files().IsEmpty()
+
+		t.Views().Commits().
+			Focus().
+			Lines(
+				Contains("pick").Contains("commit three"),
+				Contains("conflict").Contains("<-- YOU ARE HERE --- file1 changed in branch"),
+				Contains("commit two"),
+				Contains("file1 changed in master"),
+				Contains("base commit"),
+			)
+
+		checkCommitContainsChange(t, "commit two", "+branch")
+	},
+})
diff --git a/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_cancel.go b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_cancel.go
new file mode 100644
index 00000000000..2f16fdf809a
--- /dev/null
+++ b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_cancel.go
@@ -0,0 +1,43 @@
+package commit
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var AmendWhenThereAreConflictsAndCancel = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Amends the last commit from the files panel while a rebase is stopped due to conflicts, and cancels the confirmation",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig:  func(config *config.AppConfig) {},
+	SetupRepo: func(shell *Shell) {
+		setupForAmendTests(shell)
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		doTheRebaseForAmendTests(t, keys)
+
+		t.Views().Files().
+			Press(keys.Commits.AmendToCommit)
+
+		t.ExpectPopup().Menu().
+			Title(Equals("Amend commit")).
+			Select(Equals("Cancel")).
+			Confirm()
+
+		// Check that nothing happened:
+		t.Views().Files().
+			Lines(
+				Contains("M  file1"),
+			)
+
+		t.Views().Commits().
+			Focus().
+			Lines(
+				Contains("pick").Contains("commit three"),
+				Contains("conflict").Contains("<-- YOU ARE HERE --- file1 changed in branch"),
+				Contains("commit two"),
+				Contains("file1 changed in master"),
+				Contains("base commit"),
+			)
+	},
+})
diff --git a/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_continue.go b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_continue.go
new file mode 100644
index 00000000000..8f679ba6d91
--- /dev/null
+++ b/pkg/integration/tests/commit/amend_when_there_are_conflicts_and_continue.go
@@ -0,0 +1,41 @@
+package commit
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var AmendWhenThereAreConflictsAndContinue = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Amends the last commit from the files panel while a rebase is stopped due to conflicts, and continues the rebase",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig:  func(config *config.AppConfig) {},
+	SetupRepo: func(shell *Shell) {
+		setupForAmendTests(shell)
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		doTheRebaseForAmendTests(t, keys)
+
+		t.Views().Files().
+			Press(keys.Commits.AmendToCommit)
+
+		t.ExpectPopup().Menu().
+			Title(Equals("Amend commit")).
+			Select(Equals("No, continue rebase")).
+			Confirm()
+
+		t.Views().Files().IsEmpty()
+
+		t.Views().Commits().
+			Focus().
+			Lines(
+				Contains("commit three"),
+				Contains("file1 changed in branch"),
+				Contains("commit two"),
+				Contains("file1 changed in master"),
+				Contains("base commit"),
+			)
+
+		checkCommitContainsChange(t, "file1 changed in branch", "+branch")
+	},
+})
diff --git a/pkg/integration/tests/commit/shared.go b/pkg/integration/tests/commit/shared.go
new file mode 100644
index 00000000000..ee1d3e09333
--- /dev/null
+++ b/pkg/integration/tests/commit/shared.go
@@ -0,0 +1,74 @@
+package commit
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+func setupForAmendTests(shell *Shell) {
+	shell.EmptyCommit("base commit")
+	shell.NewBranch("branch")
+	shell.Checkout("master")
+	shell.CreateFileAndAdd("file1", "master")
+	shell.Commit("file1 changed in master")
+	shell.Checkout("branch")
+	shell.UpdateFileAndAdd("file2", "two")
+	shell.Commit("commit two")
+	shell.CreateFileAndAdd("file1", "branch")
+	shell.Commit("file1 changed in branch")
+	shell.UpdateFileAndAdd("file3", "three")
+	shell.Commit("commit three")
+}
+
+func doTheRebaseForAmendTests(t *TestDriver, keys config.KeybindingConfig) {
+	t.Views().Commits().
+		Focus().
+		Lines(
+			Contains("commit three").IsSelected(),
+			Contains("file1 changed in branch"),
+			Contains("commit two"),
+			Contains("base commit"),
+		)
+	t.Views().Branches().
+		Focus().
+		NavigateToLine(Contains("master")).
+		Press(keys.Branches.RebaseBranch).
+		Tap(func() {
+			t.ExpectPopup().Menu().
+				Title(Equals("Rebase 'branch'")).
+				Select(Contains("Simple rebase")).
+				Confirm()
+			t.Common().AcknowledgeConflicts()
+		})
+
+	t.Views().Commits().
+		Lines(
+			Contains("pick").Contains("commit three"),
+			Contains("conflict").Contains("<-- YOU ARE HERE --- file1 changed in branch"),
+			Contains("commit two"),
+			Contains("file1 changed in master"),
+			Contains("base commit"),
+		)
+
+	t.Views().Files().
+		Focus().
+		PressEnter()
+
+	t.Views().MergeConflicts().
+		IsFocused().
+		SelectNextItem(). // choose "incoming"
+		PressPrimaryAction()
+
+	t.ExpectPopup().Confirmation().
+		Title(Equals("Continue")).
+		Content(Contains("All merge conflicts resolved. Continue?")).
+		Cancel()
+}
+
+func checkCommitContainsChange(t *TestDriver, commitSubject string, change string) {
+	t.Views().Commits().
+		Focus().
+		NavigateToLine(Contains(commitSubject))
+	t.Views().Main().
+		Content(Contains(change))
+}
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index 54a78bd1eb2..c28ab0cdb94 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -83,6 +83,9 @@ var tests = []*components.IntegrationTest{
 	commit.AddCoAuthorRange,
 	commit.AddCoAuthorWhileCommitting,
 	commit.Amend,
+	commit.AmendWhenThereAreConflictsAndAmend,
+	commit.AmendWhenThereAreConflictsAndCancel,
+	commit.AmendWhenThereAreConflictsAndContinue,
 	commit.AutoWrapMessage,
 	commit.Checkout,
 	commit.Commit,