lazygit icon indicating copy to clipboard operation
lazygit copied to clipboard

Continuing a revert after fixing a merge conflict does not work

Open peppy opened this issue 2 years ago • 5 comments

When attempting to revert a commit that results in a conflict, there is no way to continue the revert after resolving the conflict (ie. git revert --continue):

iTerm2 2022-03-14 at 08 58 52

Given that this works in lazygit for other similar flows like rebasing, I'd expect this one to also be feasible.

peppy avatar Mar 14 '22 09:03 peppy

interesting, it never occurred to me that that git revert would have its own continue/abort/skip commands. This should be easy to support by extending the genericMergeCommand function in pkg/gui/controllers/helpers/merge_and_rebase_helper.go but that's on my refactor branch which I still haven't quite merged

jesseduffield avatar Mar 15 '22 10:03 jesseduffield

Just ran into this as well. Not a huge deal to break out of lazygit every now and then, and now have my $0.02 FWIW 😄

corytheboyd-smartsheet avatar Aug 16 '23 17:08 corytheboyd-smartsheet

I have run into this too in the past. And it's not just revert, there are other commands that have the same issue (cherry-pick and am are the ones I can think of off the top of my head). To be fair, these can't be invoked from within lazygit itself, so they are maybe less important to support.

stefanhaller avatar Aug 16 '23 17:08 stefanhaller

The code I referred to in my last comment is now merged so this is unblocked.

Fleshing out what's specifically required here: we need a way of knowing whether we're mid-cherry-pick or mid-revert so that we can issue the right command to continue.

The important code is in pkg/gui/controllers/helpers/merge_and_rebase_helper.go, specifically genericMergeCommand:

	commandType := ""
	switch status {
	case enums.REBASE_MODE_MERGING:
		commandType = "merge"
	case enums.REBASE_MODE_REBASING:
		commandType = "rebase"
	default:
		// shouldn't be possible to land here
	}

In pkg/commands/git_commands/status.go we have:

func (self *StatusCommands) IsInNormalRebase() (bool, error) {
	return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
}

func (self *StatusCommands) IsInInteractiveRebase() (bool, error) {
	return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
}

// IsInMergeState states whether we are still mid-merge
func (self *StatusCommands) IsInMergeState() (bool, error) {
	return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
}

And now we need to add one for cherry-pick/revert i.e. looking for CHERRY_PICK_HEAD and REVERT_HEAD.

In pkg/commands/types/enums/enums.go we have our RebaseMode enum:

type RebaseMode int

const (
	// this means we're neither rebasing nor merging
	REBASE_MODE_NONE RebaseMode = iota
	// this means normal rebase as opposed to interactive rebase
	REBASE_MODE_NORMAL
	REBASE_MODE_INTERACTIVE
	// REBASE_MODE_REBASING is a general state that captures both REBASE_MODE_NORMAL and REBASE_MODE_INTERACTIVE
	REBASE_MODE_REBASING
	REBASE_MODE_MERGING
)

And as you can see it's being used for two different things in pkg/commands/git_commands/status.go:

func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) {
	ok, err := self.IsInNormalRebase()
	if err == nil && ok {
		return enums.REBASE_MODE_NORMAL, nil
	}
	ok, err = self.IsInInteractiveRebase()
	if err == nil && ok {
		return enums.REBASE_MODE_INTERACTIVE, err
	}

	return enums.REBASE_MODE_NONE, err
}

func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
	rebaseMode, _ := self.RebaseMode()
	if rebaseMode != enums.REBASE_MODE_NONE {
		return enums.REBASE_MODE_REBASING
	}
	merging, _ := self.IsInMergeState()
	if merging {
		return enums.REBASE_MODE_MERGING
	}
	return enums.REBASE_MODE_NONE
}

These are used in a few places so we would need to go to each place and see if it needs to be updated to support new enum values. I think we should be encapsulating some details e.g.

func (self RebaseMode) CanContinue() bool {
	return self == REBASE_MODE_NORMAL || self == REBASE_MODE_INTERACTIVE || self == REBASE_MODE_CHERRY_PICK || self == REBASE_MODE_REVERT
}

func (self RebaseMode) CanAbort() bool {
	...
}

func (self RebaseMode) Command() string {
	switch self {
	case REBASE_MODE_NORMAL, REBASE_MODE_INTERACTIVE:
		return "rebase"
	case REBASE_MODE_REVERT:
		return "revert"
	case REBASE_MODE_CHERRY_PICK:
		return "cherry-pick"
	case REBASE_MODE_MERGING:
		return "merge"
	}
}

So that we don't need to be listing all the different variants throughout the code.

jesseduffield avatar Jan 11 '24 12:01 jesseduffield

I would suggest to support git am as well, while we're at it.

stk-ableton avatar Jan 11 '24 12:01 stk-ableton