[2026] Git Merge Conflict Resolution Case Study | Merging a 3-Month Refactor Branch

[2026] Git Merge Conflict Resolution Case Study | Merging a 3-Month Refactor Branch

이 글의 핵심

Resolving hundreds of merge conflicts when merging a long-lived refactor into main: strategies, categorization, automation, tests, and safe integration.

Introduction

After three months on refactor/new-architecture, merging into main caused conflicts in hundreds of files. This post explains how we resolved them systematically.

What you will learn

  • Strategies for large branch merges
  • How to reduce conflict volume
  • Phased resolution techniques
  • Testing strategy after integration

Table of contents

  1. Context: three-month refactor branch
  2. First merge attempt fails
  3. Strategy: phased integration
  4. Step 1: merge main into the refactor branch
  5. Step 2: classify conflicts
  6. Step 3: auto-resolvable conflicts
  7. Step 4: manual conflicts
  8. Step 5: test and verify
  9. Step 6: merge to main
  10. Lessons: conflict minimization
  11. Closing thoughts

1. Context

Refactor scope

  • Branch: refactor/new-architecture
  • Duration: Dec 2025 – Mar 2026 (~3 months)
  • Changes:
    • Directory layout (src/app/)
    • Renames (UserManagerUserService)
    • Dependency injection
    • Tests rewritten

main kept moving

  • ~20 new features
  • ~50 bug fixes
  • ~10 dependency bumps

2. First merge attempt

아래 코드는 bash를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

$ git checkout refactor/new-architecture
$ git merge main
CONFLICT (content): Merge conflict in src/user_manager.cpp
...
Automatic merge failed; fix conflicts and then commit the result.
$ git status | grep "both modified" | wc -l
247

Problem: resolving 247 files in one sitting is not realistic.

3. Strategy

  1. Merge main into the refactor branch first (not the reverse on a dirty main)
  2. Categorize conflicts
  3. Trivial / mechanical first
  4. Logic conflicts one by one
  5. Green tests before merging to main

Why merge into the feature branch?

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Preferred: on refactor branch
$ git checkout refactor/new-architecture
$ git merge main
# Fix here; main stays healthy until the end
# Risky: merge huge branch straight into main first
$ git checkout main
$ git merge refactor/new-architecture
# main can be broken for a long time during resolution

4. Step 1: merge main in

$ git checkout refactor/new-architecture
$ git merge main --no-commit --no-ff
$ git status > conflicts.txt

Counts

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

$ grep "both modified" conflicts.txt | wc -l
189
$ grep "deleted by us" conflicts.txt | wc -l
34
$ grep "added by them" conflicts.txt | wc -l
24

5. Step 2: prioritize

Example classifier: 다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import subprocess
conflicts = subprocess.check_output(
    ['git', 'diff', '--name-only', '--diff-filter=U']
).decode().splitlines()
categories = {
    'rename': [],
    'trivial': [],
    'logic': [],
    'delete': [],
}
for file in conflicts:
    if 'test' in file:
        categories['trivial'].append(file)
    elif file.endswith('.h') or file.endswith('.hpp'):
        categories['rename'].append(file)
    else:
        categories['logic'].append(file)

Rough outcome: renames, trivial (imports/tests), logic, delete/modify.

6. Step 3: mechanical fixes

Import path conflicts

Prefer the new layout from the refactor:

$ git checkout --ours src/some_file.cpp

Tests fully rewritten on refactor

$ git checkout --ours tests/*.cpp

Batch

다음은 간단한 bash 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

$ for file in $(cat trivial_conflicts.txt); do
    git checkout --ours "$file"
    git add "$file"
done

7. Step 4: manual merges

Both sides changed behavior

Merge refactor structure + main features (e.g. caching): 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class UserService {
    std::shared_ptr<Database> db_;
    std::unordered_map<int, User> cache_;
    
public:
    User getUser(int id) {
        if (auto it = cache_.find(id); it != cache_.end()) {
            return it->second;
        }
        auto user = db_->query("SELECT * FROM users WHERE id = ?", id);
        cache_[id] = user;
        return user;
    }
};

8. Step 5: verify

다음은 간단한 bash 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

$ cmake --build build
$ cd build && ctest
$ ./integration_tests.sh
$ ./benchmark.sh

Commit the integration

$ git add .
$ git commit -m "Merge branch 'main' into refactor/new-architecture
Resolved conflicts; preserved main features; tests green."

9. Step 6: merge to main

Open PR, review conflict resolutions, then:

$ git checkout main
$ git merge refactor/new-architecture --no-ff
$ git push origin main

10. Lessons

Takeaways

  1. Sync often—merge main weekly (or rebase if policy allows)
  2. Split work—multiple smaller PRs when possible
  3. Classify conflicts (trivial vs logic)
  4. Never skip tests after resolution

Long-running branches

$ git checkout refactor/new-architecture
$ git merge main
# small, frequent integrations beat rare huge ones

Tips

  • Separate rename-only commits from logic commits
  • .gitattributes for lockfiles/generated assets
  • git diff --check and grep '<<<<<<< HEAD' before commit

11. Conflict patterns

Same function, both edited

Combine validation from main with new names/types from refactor.

File moved on refactor, edited on main

다음은 간단한 bash 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

$ git show main:src/user_manager.cpp > /tmp/main_version.cpp
# Port new methods into app/user_service.cpp
$ git rm src/user_manager.cpp
$ git add app/user_service.cpp

Include paths

Unify on new paths; re-home any new headers from main.

12. Tools

VS Code merge editor, vimdiff, merge.conflictstyle diff3.

Closing thoughts

  1. Merge main into the feature branch to protect main
  2. Classify for throughput
  3. Resolve in phases to reduce mistakes
  4. Test to prevent regressions
    Don’t try to resolve everything in one undifferentiated batch.

FAQ

Q1. merge vs rebase on long branches? Often merge for shared long branches; rebase rewrites history. Q2. Too many conflicts? Split the branch: structure first, renames next, behavior last—multiple PRs. Q3. New files on main? Port them into the new layout on the refactor branch.


Checklists

Large merge

  • Backup branch
  • Count conflicts
  • Classify
  • Trivial first
  • Manual one by one
  • Build
  • Tests
  • Review
  • Merge
  • Monitor

Per-file resolution

  • No conflict markers left
  • Both sides’ intent preserved where needed
  • Build + targeted tests
  • Final git diff sanity check

Keywords

Git, merge conflict, refactoring, large merge, branch strategy, rebase, collaboration, case study, code review

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3