# Recovering from bad state

To take back the last jjpr command, locally and on the forge, run
`jjpr undo`. See [undo and redo](commands/undo.md).

```
jjpr undo --dry-run            # what it would take back
jjpr undo                      # take it back
```

## jjpr undo limits

When jjpr can't undo, either:

- It'll tell you that you must rerun with `--force`, such as when undo
  would close a PR but it's received a comment.
- It can't undo at all. In this case, you'll need to use Jujutsu to get
  in the state you want. Then, run regular `jjpr submit`.

## A merged PR's commits are still in the PR above it

The PR below was squash- or rebase-merged on the forge, and the PR
above it still carries its commits. Rebase onto trunk, dropping what
already landed:

```
jj log -r 'roots(trunk()..<bookmark>)'   # the oldest commit above trunk
jj rebase -s <that change> -d main --skip-emptied
jjpr submit
```

## jjpr could not check whether a PR below was merged

If nothing below was merged, carry on. If something was, run the
`jj rebase` the warning prints, then `jjpr submit`. If the error repeats,
run `jjpr auth test`.

## A merged PR sits under a merge commit

Move the merge commit onto trunk, keeping each parent that was not
merged:

```
jj log -r 'trunk()..<bookmark>'    # find the merge commit and its parents
jj rebase -s <merge commit> -d main -d <unmerged parent>
jjpr submit
```

## Merged commits are still in `jj log` after a restack

They are no longer part of any PR. Remove them with the `jj abandon`
the warning prints, or leave them.

## A bookmark is skipped as stale

The bookmark points at a commit jj cannot place, usually because its
PR was merged on the forge. `jjpr submit` removes it once the forge
confirms the merge. To remove it yourself:

```
jj bookmark forget <name>
```

## A rebase left conflicts

jjpr pushes nothing that has conflicts. Resolve them on top of the
conflicted change and fold the fix in:

```
jj new <change-id>       # the change named in the message
# fix the conflicted files
jj squash
jjpr submit
```

Or undo the rebase with `jj op log` and `jj op restore`.

## Submit or merge stops on a divergent change

Two commits share one change ID. Keep the one you want and abandon the
other:

```
jj log -r 'change_id(<change-id>)'
jj abandon <commit-id of the copy to drop>
jjpr submit
```

To keep both as separate changes, run `jj duplicate <commit-id>`, then
`jj abandon <commit-id>`.

On jj 0.45 or later, `jj converge -r 'change_id(<change-id>)' --no-interactive`
resolves it automatically, or exits without changes if it cannot decide.

## Fetching rewrote commits under your working copy

After a merge, jjpr fetches. If the remote had changed a commit your working
copy sits on, jj rebases the working copy, and the stack can come out
divergent. jjpr stops and says so:

```
Warning: fetching rewrote commits under your working copy (the remote changed
a commit your working copy sits on). jj may now report it as stale. Run jj
workspace update-stale, then check jj log for a divergent change before
continuing.
```

Run `jj workspace update-stale`, then `jj log` to look for a divergent change.
If there is one, the section above on divergent changes applies. watch tries
again on the next poll.

## A push failed

Someone may have pushed to the branch, or the forge may protect it.
Look before pushing again:

```
jj git fetch
jj log -r '<bookmark> | <bookmark>@origin'
```

To keep the forge's version, run
`jj bookmark set <bookmark> -r <bookmark>@origin`. To keep yours, run
`jjpr submit`.

## Local sync failed

After a merge, jjpr could not update the PRs above it on your machine.
The merged PR is fine, and the rest stay open. Either rebase them:

```
jj git fetch
jj rebase -s <change-id> -d main
jjpr submit
```

Or take the forge's version of each bookmark:

```
jj git fetch
jj bookmark set <bookmark> -r <bookmark>@origin
```

Then run `jjpr merge` again. `jjpr watch` picks it up by itself.

## Forge reconcile failed

The merge happened, but a follow-up change on the forge did not (a
base branch or the stack comment). Run `jjpr merge` again. If it keeps
failing, see the table below.

## The forge returned an error

| Status | Usually means | Do this |
|---|---|---|
| 401 | The token is missing, expired or revoked | `jjpr auth test`, then log in again |
| 403 | The token lacks a scope, or the organization requires SSO for it | Give it `repo` (GitHub, Forgejo) or `api` (GitLab), or authorize it for the organization |
| 404 | The token cannot see the repository, or the PR is gone | `jjpr auth test`, and check the remote URL |
| 405, 409 | The forge refused the merge: checks, reviews or a merge method the repository does not allow | Open the PR on the forge to see what it waits for |
| 422 | The forge rejected the change, often because a branch it names is gone | `jj git fetch`, then run the command again |
| 429 | Too many requests. jjpr waits out a limit that lifts within a minute | Wait until the limit lifts, then run again |
| 500–504 | The forge is having trouble | Run again in a minute |

## Watch gave up

`jjpr watch` stops after 10 errors in a row. The lines above that
message show the error. Fix it, then start `jjpr watch` again.
