DEV Community

Cover image for I kept seeing the same Git complaint on Reddit, so I wrote a small CLI for it
Ikrame Ibn Hayoun
Ikrame Ibn Hayoun

Posted on

I kept seeing the same Git complaint on Reddit, so I wrote a small CLI for it

I was reading Reddit threads about Git annoyances — not big architecture debates, just the little things that waste time. One kept coming up: merge conflicts that are only import lines at the top of a Python file.

Someone on one branch adds import os. Someone else adds import math. Same file, same place. Git treats it like a real conflict, and you end up deleting conflict markers by hand even though you just want both imports.

The cover image is a quick sketch I made in Excalidraw while figuring out the flow: conflict in → one command → clean imports out.

What I looked at

Before writing anything, I checked how this is usually handled:

  • GitHub's official guide walks you through opening the file and fixing markers by hand. That is the right approach for real logic conflicts. It is just slow when the conflict is only imports.
  • Common Stack Overflow answers push git checkout --ours or --theirs. That keeps one side and throws away the other — so you can delete an import your teammate still needs.
  • Formatters like isort, ruff, and Black help keep imports tidy, but they fail while conflict markers are still in the file.
  • There are heavier tools too (structural merge drivers, LLM helpers) if you want that trade-off.

I could not find a small local command that only handles this case, so I wrote one.

The tool

pip install import-resolve-cli
import-resolve-cli
Enter fullscreen mode Exit fullscreen mode

Before (Git still conflicted):

import json
<<<<<<< HEAD
import os
=======
import math
>>>>>>> feature

print(json.dumps({"ok": True}))
Enter fullscreen mode Exit fullscreen mode

After (both imports kept, markers gone):

import json
import math
import os

print(json.dumps({"ok": True}))
Enter fullscreen mode Exit fullscreen mode

If you run it with no file paths, it asks Git for conflicted .py files. There is also --dry-run, --check, and an optional --install-hook that registers a merge driver for this repo only (not your global Git config).

Limits

It only rewrites a conflict block when every non-blank line on both sides is a single-line import / from or a comment. Anything else is skipped with a reason — including multiline imports, indented imports, and logic mixed into the same block. Conflicts further down the file stay for you.

That is intentional. I wanted help with the mechanical part, not a tool that tries to merge real code for me.

Out of scope Why
Business-logic conflicts Needs human intervention
Multiline import blocks Too easy to corrupt; skipped with a warning
Full isort/ruff style Run your formatter after; sorting here is simple
JS/TS imports Possible later
Config files No config by design

Links

import-resolve-cli

CI PyPI Python License: MIT

Resolve Git merge conflicts that only touch Python import lines.

$ git merge feature-branch
CONFLICT (content): Merge conflict in app.py
$ import-resolve-cli
[ok] app.py: resolved 1 import conflict

Install

pip install import-resolve-cli
Enter fullscreen mode Exit fullscreen mode

Python 3.9+, no runtime dependencies.

Example

Two branches each added an import. Git left this:

import json
<<<<<<< HEAD
import os
=======
import math
>>>>>>> feature-a

print(json.dumps({'ok': True}))
Enter fullscreen mode Exit fullscreen mode

After import-resolve-cli:

import json
import math
import os

print(json.dumps({'ok': True}))
Enter fullscreen mode Exit fullscreen mode

Same example as files in docs/examples/.

Usage

import-resolve-cli                 # every conflicted .py file reported by git
import-resolve-cli app.py utils.py # specific files
import-resolve-cli --dry-run       # print the diff, write nothing
import-resolve-cli --check         # exit 1 if conflict markers remain
Enter fullscreen mode Exit fullscreen mode

Exit codes: 0 done, 1 conflicts left for you, 2 usage…

Python 3.9+, no runtime dependencies. If you try it on a real conflict and something looks off, I would be glad to hear about it in an issue.

If this saves you a few minutes, a star on the repo helps others find it!

Top comments (0)