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.

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)

Solutions

  1. Start an interactive rebase manually from a chosen commit and squash the fixups there.
  2. If HEAD is a merge commit, reset or rebase past it first, then retry.
  3. 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

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


AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15). Data as JSON: /api/errors/f4ab9e04618cf088. Report an issue: GitHub.