Merge Conflict Resolution Guide¶
How to resolve merge conflicts when bringing main into a long-running
feature branch. This guide provides general conflict resolution
principles (§1–§2), a catalog of known restructuring PRs with
their specific conflict patterns (§3), and universal verification
gates (§4).
When a new restructuring PR lands in main, add an entry to §3
following the template in §6.
1. General principles¶
1.1 Diagnose before acting¶
Every conflict traces back to one of: - Files moved or renamed on one branch, modified on the other. - Files added on one branch at paths that don't exist on the other. - Files deleted on one branch, modified on the other. - Source lists or config blocks replaced wholesale.
Identify which pattern applies before choosing a resolution strategy.
1.2 Resolve toward the forward-looking layout¶
When one side represents a restructuring and the other contains incremental changes, resolve toward the restructured layout and transplant the incremental changes into it. Do not revert restructuring to avoid conflicts.
1.3 Verification is part of resolution¶
Git reporting zero unmerged files is not enough. Run the universal verification gates in §4 after every merge. Add PR-specific gates when adding a new entry to §3.
2. Quick diagnosis¶
# What conflicted and what type?
git status --short | grep -E '^UU|^UA|^AU|^DU|^UD|^AA|^DD'
# Inspect the three merge stages (1=base, 2=ours, 3=theirs)
git ls-files -u
# Diff our side vs theirs for a specific file
git diff HEAD...MERGE_HEAD -- <file>
Stage codes:
| Code | Meaning | Typical action |
|---|---|---|
UU |
Both modified | Merge contents manually |
UA |
Added by main, absent here | git add after checking for path moves (§3) |
UD |
Deleted by main, modified here | git rm if the deletion is legitimate |
3. Known restructuring PRs¶
Each entry documents the layout changes, conflict patterns, and verification gates specific to one restructuring PR. When a new restructuring PR lands, add a new subsection here.
Template: see §6.
3.1 PR #88 — Buildsystem Maintenance¶
What changed:
| Change | Before | After |
|---|---|---|
| C++ tests moved | python/tests/cpp/test_*.cpp |
novaphy/tests/test_*.cpp |
| C++ test build config moved | python/tests/CMakeLists.txt |
novaphy/tests/CMakeLists.txt |
| Python tests flattened | python/tests/python/test_*.py |
python/tests/test_*.py |
| CMake compat moved | compat/ |
cmake/compat/ |
| Demos installed as subpackage | (no __init__.py) |
python/demos/__init__.py → novaphy.demos |
Conflict patterns:
Pattern A: CMakeLists.txt source list replaced¶
Cause: main replaced VBD sources (cpu/vbd_*.cpp + vbd_cuda/)
with a new structure (solver_vbd.cpp + rigid/ + soft/).
Symptom: UU conflict in novaphy/src/dynamics/vbd/CMakeLists.txt
with two completely different source lists.
Fix: Take main's version in full (it's a replacement, not an addition).
git checkout --theirs novaphy/src/dynamics/vbd/CMakeLists.txt
git add novaphy/src/dynamics/vbd/CMakeLists.txt
Pattern B: File renamed here + modified on main¶
Cause: We moved a file (e.g. python/tests/CMakeLists.txt →
novaphy/tests/CMakeLists.txt) and main modified the original.
Symptom: UU conflict with Git markers referencing two different
file paths and different internal conventions (e.g. cpp/ prefix).
Fix: Keep our file location. Port main's logical changes (new
files, new link deps) into our path convention.
Pattern C: Test path depth wrong (parents[N] off by one)¶
Cause: Main's tests compute REPO_ROOT via
Path(__file__).resolve().parents[3] — correct for the old
python/tests/python/ depth, overshooting after flattening.
Symptom: FileNotFoundError with paths like
/home/user/src/python/... (repo name missing).
Fix: Reduce parents[N] by 1 for every test file that was moved:
| Move | Old | New |
|---|---|---|
python/tests/python/ → python/tests/ (REPO_ROOT) |
parents[3] |
parents[2] |
python/tests/python/ → python/tests/ (module loader) |
parents[2] |
parents[1] |
Pattern D: Main added files at old paths¶
Cause: main added files under python/tests/cpp/ or compat/
— directories we moved or deleted.
Symptom: UA conflicts at old paths.
Fix: Move them:
| Main added at | Move to |
|---|---|
python/tests/cpp/test_new.cpp |
novaphy/tests/test_new.cpp |
python/tests/python/test_new.py |
python/tests/test_new.py |
compat/new_file.cmake |
cmake/compat/new_file.cmake |
PR-specific verification¶
In addition to the universal gates in §4, verify:
4. Universal verification gates¶
After resolving all git conflicts, run these checks. All must pass
before committing. These gates apply regardless of which PR caused
the conflicts.
Gate 1: Zero source tree injection in sys.path¶
novaphy._core is a compiled C extension that only exists at the
install location. Adding the source python/ directory to sys.path
shadows the installed package.
Expected: zero results, except python/novaphy/viewer/gl_backend.py
under if __name__ == "__main__" (acceptable).
Gate 2: Zero sys.modules replacement¶
Expected: zero results (same exception as Gate 1).
Gate 3: No unresolved git conflicts¶
Expected: no output.
5. Standard merge procedure¶
# 1. Start the merge
git checkout <your-branch>
git fetch origin main
git merge origin/main
# 2. Diagnose (see §2)
git status --short | grep -E '^[UAD]'
# 3. Identify which restructuring PR(s) caused the conflicts (see §3)
# Resolve each conflict using the patterns documented there.
# 4. Universal verification gates (see §4) — DO NOT SKIP
grep -rn 'sys\.path\.insert\|sys\.path\.append' python/
grep -rn 'sys\.modules\[.novaphy.\]' python/
git diff --name-only --diff-filter=U
# 5. PR-specific verification (see the PR entry in §3)
# 6. Commit
git add <resolved-files>
git commit -m "Merge branch 'main' into <your-branch>"
# 7. Verify (if environment supports it)
pytest python/tests/ -v
6. How to extend this document¶
When a new restructuring PR lands in main, add a subsection under §3
using this template:
### 3.X PR #NNN — <short title>
**What changed:**
| Change | Before | After |
|--------|--------|-------|
| <description> | `<old-path>` | `<new-path>` |
**Conflict patterns:**
#### Pattern A: <name>
**Cause:** <why this conflict happens due to this PR's changes>
**Symptom:** <what error or conflict marker the agent will see>
**Fix:** <concrete steps, with copyable commands>
#### Pattern B: ...
**PR-specific verification:**
```bash
<grep command>
# Expected: <what should happen>
Keep each pattern self-contained: an agent encountering it for the first time should be able to diagnose and fix it from this entry alone.
Prevention tips¶
- Alphabetize source lists in
CMakeLists.txt— reduces spurious conflicts when both sides append to the end. - Merge
mainbefore starting a large refactor; land it quickly. - Use
git mvfor renames — merge algorithms handle it better. - Call out moves in the PR description — a sentence like "this PR
moves C++ tests to
novaphy/tests/" saves the next merger hours. -
Never use
sys.pathto make the source tree importable. Install the package and import normally. -
Alphabetize source lists in
CMakeLists.txt— reduces spurious conflicts when both sides append to the end. - Merge
mainbefore starting a large refactor; land it quickly. - Use
git mvfor renames — merge algorithms handle it better. - Call out moves in the PR description — a sentence like "this PR
moves C++ tests to
novaphy/tests/" saves the next merger significant diagnosis time. - Never use
sys.pathto make the source tree importable. If a demo script needsnovaphy, install the package and import normally.