Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions cmd/finish.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ func handleContinue(cfg *config.Config, state *mergestate.MergeState, branchConf
}

func handleAbort(state *mergestate.MergeState) error {
// Abort the merge based on strategy
// Abort the merge based on strategy.
var err error
switch state.MergeStrategy {
case strategyMerge:
Expand All @@ -567,20 +567,36 @@ func handleAbort(state *mergestate.MergeState) error {
err = git.MergeAbort() // Default to merge abort
}

if err != nil {
return &errors.GitError{Operation: "abort merge", Err: err}
}

// Checkout the original branch
if err := git.Checkout(state.FullBranchName); err != nil {
return &errors.GitError{Operation: fmt.Sprintf("checkout original branch '%s'", state.FullBranchName), Err: err}
// Git returns exit 128 from `merge --abort` when there's no MERGE_HEAD
// (user resolved the conflict manually with a commit, bypassing
// --continue). The stale merge.json state file then blocked every
// future `feature finish` call. Treat abort failures as warnings and
// continue cleaning up so ClearMergeState is always reached. See
// gittower/git-flow-next#110.
abortErr := err

// Checkout the original branch. This may also no-op / fail when
// already on that branch; propagate any actual error only if we
// can't also clear state below.
if checkoutErr := git.Checkout(state.FullBranchName); checkoutErr != nil && abortErr == nil {
// If the abort itself succeeded but the checkout failed, that's
// a genuine problem the caller should see.
return &errors.GitError{Operation: fmt.Sprintf("checkout original branch '%s'", state.FullBranchName), Err: checkoutErr}
}

// Clear the merge state
// Always clear the merge state so the repository is unblocked for
// the next feature finish, regardless of whether the underlying
// git abort believed there was anything to abort.
if err := mergestate.ClearMergeState(); err != nil {
return &errors.GitError{Operation: "clear merge state", Err: err}
}

if abortErr != nil {
// Surface the git abort failure for visibility but don't return
// an error — state has been cleared and the user is unblocked.
fmt.Fprintf(os.Stderr, "git-flow: git abort reported no active merge/rebase; cleared stale merge state anyway (%v)\n", abortErr)
}

return nil
}

Expand Down