- Posted on
- • Artificial Intelligence
Artificial Intelligence Merge Conflict Resolution
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Merge Conflict Resolution — A Bash-First Guide for Linux
You’ve just pulled main and your branch explodes into a sea of <<<<<<< HEAD markers. You’re juggling semantic changes, refactors, and rename noise across dozens of files. What if your merge tool could read the code, understand intent, and suggest a clean resolution — right on your Linux box — while you stay in control?
In this post, we’ll wire an AI-assisted merge driver into Git using plain Bash. You’ll learn why AI belongs in your merge strategy, how to install what you need via apt/dnf/zypper, and how to ship with guardrails so humans still make the final call.
Why AI for merge conflicts is valid (and useful)
Code merges are semantic. Traditional merge tools see lines; AI can infer intent, variable roles, and refactor patterns to propose coherent resolutions.
It doesn’t replace review. A good setup uses AI for first drafts and humans for verification, tests, and sign-off.
It can run locally. With a local LLM (e.g., via Ollama), you keep source private and avoid network latency or vendor lock-in.
It integrates into Git’s proven mechanisms. Using a merge driver, AI triggers only where you want it (specific file types), with fallbacks if anything fails.
What we’ll build
A hardened Git merge baseline (rerere, diff3, etc.).
A Bash merge driver that sends base/local/remote to an LLM and writes back a proposed merge.
Wiring via
.gitattributesso only certain file types use AI.Guardrails: tests/hooks and reproducibility tips.
1) Baseline your Git merge hygiene
Turn on settings that already reduce conflict pain:
git config --global rerere.enabled true
git config --global merge.conflictStyle diff3
git config --global pull.rebase false
git config --global rebase.autoStash true
git config --global merge.renames true
rerere remembers how you resolved similar conflicts.
diff3 includes the base version in conflict markers, which helps both you and the AI.
Optional: incremental merges for gnarly histories:
Install git-imerge:
- apt:
sudo apt update sudo apt install -y git-imerge- dnf:
sudo dnf install -y git-imerge- zypper:
sudo zypper refresh sudo zypper install -y git-imergeUse it like:
git imerge start main git imerge continue git imerge finish
2) Install prerequisites (Linux)
We’ll use curl and jq to talk to an LLM from Bash. Git, Python, and pip are handy for hooks/tests.
apt (Debian/Ubuntu):
sudo apt update sudo apt install -y git curl jq python3 python3-pipdnf (Fedora/RHEL/CentOS Stream):
sudo dnf install -y git curl jq python3 python3-pipzypper (openSUSE):
sudo zypper refresh sudo zypper install -y git curl jq python3 python3-pip
Option A: Local LLM via Ollama (recommended for privacy)
Install Ollama:
curl -fsSL https://ollama.com/install.sh | shStart it (if not already running) and pull a code-friendly model:
ollama serve # in a terminal, or ensure the systemd service is running ollama pull codellama:7b-instruct # Alternatives: # ollama pull llama3.1:8b # ollama pull qwen2.5-coder:7b
Option B: Hosted LLM (e.g., OpenAI) via API
You’ll need an API key in your environment:
export OPENAI_API_KEY="sk-..."No extra packages beyond curl/jq are required. This keeps your script portable but may send code to a third party — use only if policy allows.
3) Create a Bash AI merge driver
This driver reads Git’s base/current/other versions, asks the LLM for a deterministic merge, and writes the result back to the current file. It prefers Ollama by default, with an optional OpenAI path.
Create .git-tools/ai-merge.sh in your repo:
#!/usr/bin/env bash
set -Eeuo pipefail
# Usage (from Git merge driver):
# ai-merge.sh %O %A %B
# %O = ancestor (BASE), %A = ours (CURRENT), %B = theirs (OTHER)
BASE="$1"
CURRENT="$2"
OTHER="$3"
ENGINE="${AI_ENGINE:-ollama}" # "ollama" or "openai"
MODEL="${AI_MODEL:-codellama:7b-instruct}"
MAX_CHARS="${AI_MAX_CHARS:-120000}" # prevent sending enormous blobs
read_file() {
# Normalize newlines; trim if too large
local p="$1"
local s
s="$(sed 's/\r$//' "$p")"
if [ "${#s}" -gt "$MAX_CHARS" ]; then
echo "$s" | head -c "$MAX_CHARS"
echo -e "\n\n...[TRUNCATED]..."
else
echo "$s"
fi
}
BASE_TXT="$(read_file "$BASE")"
LOCAL_TXT="$(read_file "$CURRENT")"
REMOTE_TXT="$(read_file "$OTHER")"
PROMPT=$(cat <<'EOF'
You are a deterministic Git merge driver.
- Input: three labeled sections: BASE, LOCAL (ours), REMOTE (theirs).
- Goal: produce a single merged file that preserves both sides’ intended behavior.
- Rules:
* If both sides change different regions, keep both.
* If both edit the same logic, prefer clearer, more robust code while integrating critical fixes from both.
* Preserve imports/includes and public APIs unless both sides agree to change them.
* Do NOT include conflict markers, explanations, or code fences.
* Output ONLY the final merged file content.
Return only the merged file.
EOF
)
merge_with_ollama() {
local payload resp out
payload=$(jq -n \
--arg model "$MODEL" \
--arg prompt "$PROMPT" \
--arg base "$BASE_TXT" \
--arg local "$LOCAL_TXT" \
--arg remote "$REMOTE_TXT" \
'{
model: $model,
prompt:
($prompt + "\n\n<BASE>\n" + $base + "\n</BASE>\n<LOCAL>\n" + $local + "\n</LOCAL>\n<REMOTE>\n" + $remote + "\n</REMOTE>\n"),
temperature: 0.1,
stream: false
}')
resp="$(curl -fsS http://localhost:11434/api/generate -d "$payload")" || return 1
out="$(echo "$resp" | jq -r '.response // empty')"
[ -n "$out" ] || return 1
printf "%s" "$out" > "$CURRENT"
}
merge_with_openai() {
# Requires: export OPENAI_API_KEY=...
[ -n "${OPENAI_API_KEY:-}" ] || return 1
local messages resp out
messages=$(jq -n \
--arg system "You are a deterministic Git merge driver. Output only the final merged file with no commentary." \
--arg user "$PROMPT\n\n<BASE>\n$BASE_TXT\n</BASE>\n<LOCAL>\n$LOCAL_TXT\n</LOCAL>\n<REMOTE>\n$REMOTE_TXT\n</REMOTE>\n" \
'[ {role:"system", content:$system}, {role:"user", content:$user} ]')
resp="$(curl -fsS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --argjson msgs "$messages" '{model:"gpt-4o-mini", temperature:0.1, messages:$msgs}')" )" || return 1
out="$(echo "$resp" | jq -r '.choices[0].message.content // empty')"
# strip accidental code fences
out="${out#\`\`\`}"
out="${out%\`\`\`}"
[ -n "$out" ] || return 1
printf "%s" "$out" > "$CURRENT"
}
if [ "$ENGINE" = "ollama" ]; then
merge_with_ollama || exit 1
else
merge_with_openai || exit 1
fi
exit 0
Make it executable:
chmod +x .git-tools/ai-merge.sh
4) Wire the driver into Git (target only what you want)
Tell Git about the custom merge driver and restrict it to specific file types.
git config merge.ai.name "AI merge driver"
git config merge.ai.driver "./.git-tools/ai-merge.sh %O %A %B"
Add a .gitattributes at the repo root:
# Only run AI on languages where semantic merging helps
*.py merge=ai
*.js merge=ai
*.ts merge=ai
*.go merge=ai
# Never run AI on generated or vendor files
dist/** -merge
build/** -merge
vendor/** -merge
*.min.js -merge
Commit this:
git add .gitattributes .git-tools/ai-merge.sh
git commit -m "Add AI merge driver and attributes"
Tip: Use per-repo config (as above) first. Once you’re happy, you can promote it to global with git config --global ... and a global attributes file if you like.
5) Try it on a tiny real-world example
Create a demo repo:
mkdir ai-merge-demo && cd ai-merge-demo
git init
cat > greet.py <<'EOF'
def greet(name):
print("Hello, " + name + "!")
EOF
git add greet.py && git commit -m "init"
Create two branches that will conflict semantically:
git checkout -b feature-a
sed -i 's/print("Hello, " + name + "!")/print(f"Hello, {name}! 🎉")/' greet.py
git commit -am "feature-a: use f-string and emoji"
git checkout -b feature-b main
sed -i '1s/^/def sanitize(s):\n return s.strip().title()\n\n/' greet.py
sed -i 's/greet(name)/greet(name):\n name = sanitize(name)/' greet.py
git commit -am "feature-b: add sanitize() and call it"
Merge with the AI driver in place:
# ensure .gitattributes and driver are present as described
git checkout feature-a
git merge feature-b
What you’ll see:
Without the AI driver: a conflict that mixes refactor and new function.
With the AI driver: a synthesized
greet.pythat keepssanitize()and the f-string/emoji, integrating both changes. Always review the result and run tests.
6) Guardrails: keep humans in charge
Run tests automatically after merges. Example
post-mergehook:mkdir -p .git/hooks cat > .git/hooks/post-merge <<'EOF' #!/usr/bin/env bash set -e if command -v pytest >/dev/null 2>&1; then pytest -q || { echo "Tests failed after merge"; exit 1; } fi EOF chmod +x .git/hooks/post-mergeKeep rerere on. If you fix up an AI proposal, Git learns from it for similar future conflicts.
Log the AI intent. Consider adding a trailer to your merge commit:
git commit --amend -m "$(git log -1 --pretty=%B) AI-Merge: engine=$(echo ${AI_ENGINE:-ollama}) model=$(echo ${AI_MODEL:-codellama:7b-instruct}) "Limit scope via
.gitattributesand review binary/generated files manually.If the AI fails (network down, model missing), Git falls back to normal conflict markers. You can then resolve as usual.
Troubleshooting
Ollama not running:
curl -fsS http://localhost:11434/api/tags # If this fails, (re)start: ollama serveEmpty/garbled output: try a different model or lower temperature:
export AI_MODEL="llama3.1:8b" export AI_ENGINE="ollama"Large files: raise the cap or narrow patterns in
.gitattributes.No jq: install it with your package manager (see prerequisites).
Conclusion and next steps
AI-assisted merging won’t replace your judgment — and it shouldn’t. But it can turn tedious, mechanical conflict cleanup into a fast, reviewable proposal that preserves intent from both sides.
Your next steps: 1) Harden your Git merge settings (rerere, diff3). 2) Install prerequisites and a local model with Ollama, or configure a provider. 3) Drop in the Bash merge driver and target a few file types. 4) Trial on a feature branch. Measure saved time and error rates. 5) Add tests/hooks and expand gradually.
If this sped up your workflow or you’ve evolved the script (better prompts, per-language routing, CI integration), share it with your team — and consider upstreaming a general-purpose AI merge driver to your org’s templates.