DEV Community

FetchSandbox
FetchSandbox

Posted on

How to test GitHub merge conflicts locally

The happy path for a pull request is boring. The merge conflict is where the integration usually gets real.

Most GitHub API demos stop too early.

They show how to open the pull request, fetch it, maybe list reviews, and then call it done.

That is useful for proving your auth works. It is not enough for proving your PR automation works.

The harder branch is the one where the pull request exists, the metadata looks fine, and then the merge step hits a conflict. That is the path that decides whether your bot, internal tool, or release workflow can recover sanely.

The small workflow that actually matters

For this kind of test, I care about a very small flow:

  1. open the pull request
  2. fetch the PR again to inspect its status
  3. try the merge step
  4. see what your system does when the PR is not cleanly mergeable

That starts with something like:

curl -X POST \
  "https://api.github.com/repos/acme-corp/api-gateway/pulls" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  -d '{
    "title": "fix: handle nil pointer in rate limiter",
    "body": "Fixes #142. Adds nil check before accessing connection pool.",
    "head": "fix/rate-limiter-nil",
    "base": "main"
  }'
Enter fullscreen mode Exit fullscreen mode

and then quickly becomes:

curl \
  "https://api.github.com/repos/acme-corp/api-gateway/pulls/<number>" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github+json"
Enter fullscreen mode Exit fullscreen mode

The useful part is not just that the PR exists. It is what your code does when the branch cannot merge cleanly after that.

Where teams usually get fooled

The trap is treating "pull request created" as proof that the automation is basically done.

But a lot of GitHub workflows only become interesting after creation:

  • the branch is stale
  • another change landed on main
  • the PR looks valid but is not mergeable
  • your bot tries to continue anyway
  • your UI says "ready" even though a human now has to intervene

That is the same pattern that shows up in a lot of async integrations: step 1 succeeds, so the app assumes the whole job is healthy.

What I would test before production

If I only had time for one narrow GitHub API test, I would test the merge-conflict branch.

I would check:

  • can my app detect that the PR exists but is not safely mergeable?
  • do I expose the right state to the user, instead of pretending it is still a normal merge path?
  • do retries keep hammering the same PR, or do we stop and surface the conflict clearly?
  • if there is a webhook or follow-up read in the flow, does the final state stay honest?

That last part matters more than the first API call.

A lot of automation bugs are not "could not create PR."

They are more like:

  • "we created it, then misread the later state"
  • "we retried the wrong step"
  • "we told the user merge was blocked too late"

Why this is a better test than another PR happy path

A happy-path pull request test mostly proves you can talk to GitHub.

A merge-conflict test tells you whether your integration can survive a normal repo state that changes under it.

That is a much better signal for:

  • release tooling
  • AI coding agents opening PRs
  • internal automation that tries to auto-merge fixes
  • any workflow that assumes the branch state will stay clean between creation and merge

It is also more realistic. Real repositories drift constantly. If your testing never touches the conflict branch, your merge logic is probably getting too much credit.

The part I think is underrated

The useful question is not just:

"can I create a PR through the GitHub API?"

It is:

"when the PR stops being mergeable, do I still know what happened and what to do next?"

That is the gap between endpoint testing and workflow testing.

The endpoint docs explain the request shape. The real integration job is deciding what your system should do after the branch state changes.

If you want to make this easier locally

I like testing this as a narrow workflow instead of a broad "GitHub API integration" check.

That is also why I keep a runnable GitHub docs portal on FetchSandbox around. The useful part is being able to open the PR, fetch it again, and inspect the later branch in one place instead of stopping at creation.

For me, the merge-conflict path is one of those cases where a small failure-mode test teaches more than a longer happy-path demo.

Curious how other people handle this. If your GitHub automation opens a PR and later hits a conflict, do you surface that as a first-class state, or does it still turn into log-diving and confused retries?

Top comments (0)