jesseduffield/lazygit · warning
Cannot squash commits in current branch: the HEAD commit is…
Error message
Cannot squash commits in current branch: the HEAD commit is a merge commit or is present on the main branch.
What it means
Returned by findCommitForSquashFixupsInCurrentBranch, the 'squash all fixup commits above' helper. It uses the same predicate as the quick-start rebase check — the first commit that IsMerge() or has Status == StatusMerged must exist below index 0 — because squashing fixups internally starts an interactive rebase from that commit. When no such base exists (or it is HEAD), the operation is impossible in the current branch.
Solutions
- Start an interactive rebase manually from a chosen commit and squash the fixups there.
- If HEAD is a merge commit, reset or rebase past it first, then retry.
- Unguard/deepen the clone (git fetch --unshallow) so a usable base commit is visible.
Defensive patterns
Strategy: validation
Validate before calling
_, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.IsMerge() || c.Status == models.StatusMerged
})
if !ok || index == 0 {
// disable squash-fixups action for this branch
} Prevention
- Gate the squash-fixups menu item with the same predicate used by findCommitForSquashFixupsInCurrentBranch.
- Avoid merging to main before cleaning up fixup commits.
- Use explicit interactive rebase from a selected commit when the guard trips.
When it happens
Trigger: Invoking squash-fixups-in-current-branch when lo.FindIndexOf(commits, IsMerge || StatusMerged) yields !ok or index == 0 — i.e. HEAD is a merge commit or is already merged into the main branch.
Common situations: Branch fully merged to main with only fixup commits above; HEAD is a merge commit (e.g. after merging a PR locally); shallow clone hiding the merge base.
Related errors
- Expected exactly one fixup hash, found
- Expected exactly one original hash, found
- Cannot start interactive rebase: the HEAD commit is a merge…
- Base commit is not in current view
- Entire file
AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15).
Data as JSON: /api/errors/f4ab9e04618cf088.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/gui/controllers/local_commits_controller.go:1640
Then: func() error {
if err == nil {
self.context().SetSelectedLineIdx(targetIdx)
self.c.PostRefreshUpdate(self.context())
}
return nil
},
})
})
}
func (self *LocalCommitsController) findCommitForSquashFixupsInCurrentBranch() (*models.Commit, int, error) {
commits := self.c.Model().Commits
_, index, ok := lo.FindIndexOf(commits, func(c *models.Commit) bool {
return c.IsMerge() || c.Status == models.StatusMerged
})
if !ok || index == 0 {
return nil, -1, errors.New(self.c.Tr.CannotSquashCommitsInCurrentBranch)
}
return commits[index-1], index - 1, nil
}
// Anticipate how many commits above the selectedIdx are going to get squashed
// by the SquashAllAboveFixupCommits call, so that we can adjust the selection
// afterwards. Let's hope we're matching git's behavior correctly here.
func countSquashableCommitsAbove(commits []*models.Commit, selectedIdx int, rebaseStartIdx int) int {
result := 0
// For each commit _above_ the selection, ...
for i, commit := range commits[0:selectedIdx] {
// ... see if it is a fixup commit, and get the base subject it applies to
if baseSubject, isFixup := helpers.IsFixupCommit(commit.Name); isFixup {
// Then, for each commit after the fixup, up to and including the
// rebase start commit, see if we find the base commit
for _, baseCommit := range commits[i+1 : rebaseStartIdx+1] {View on GitHub (pinned to c477a2959b)