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 .gitattributes so 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-imerge
    
  • Use 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-pip
    
  • dnf (Fedora/RHEL/CentOS Stream):

    sudo dnf install -y git curl jq python3 python3-pip
    
  • zypper (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 | sh
    
  • Start 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.py that keeps sanitize() 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-merge hook:

    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-merge
    
  • Keep 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 .gitattributes and 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 serve
    
  • Empty/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.