Conflict Resolution
What is a Merge Conflict?
A conflict occurs when two branches have made different changes to the same line(s) of the same file, and Git cannot automatically determine which version to keep. Conflicts can happen during merge, rebase, cherry-pick, revert, and stash pop.
Conflicts are normal. They are not errors — they are Git asking you to make a decision.
Anatomy of a Conflict Marker
<<<<<<< HEAD
public Page<TransactionDto> findTransactions(UUID userId, Pageable pageable) {
return transactionRepository.findByUserId(userId, pageable)
||||||| base (with --diff3)
public Page<TransactionDto> findTransactions(UUID userId, Pageable pageable) {
return transactionRepository.findAll(pageable)
=======
public Page<TransactionDto> findTransactions(
UUID userId, LocalDate fromDate, LocalDate toDate, Pageable pageable) {
validateDateRange(fromDate, toDate);
return transactionRepository.findByUserIdAndCreatedAtBetween(
userId, toInstant(fromDate), toInstant(toDate), pageable)
>>>>>>> feature/JIRA-123
| Section | Meaning |
|---|---|
<<<<<<< HEAD to ||||||| base | Your current branch version |
||||||| base to ======= | The common ancestor (shown with --diff3) |
======= to >>>>>>> feature/JIRA-123 | The incoming branch version |
Enable the diff3 style globally — the base section is invaluable for understanding why each side changed what it did:
git config --global merge.conflictstyle diff3
Step-by-Step Resolution
Step 1 — Identify all conflicted files
git status
# Both modified: src/main/java/com/example/TransactionService.java
# Both modified: src/main/resources/application.yml
Step 2 — Open each conflicted file and resolve
Edit the file to what the final correct version should be. Remove all conflict markers (<<<<<<<, |||||||, =======, >>>>>>>):
// Resolved version — keeps the new signature with date range filter
public Page<TransactionDto> findTransactions(
UUID userId, LocalDate fromDate, LocalDate toDate, Pageable pageable) {
validateDateRange(fromDate, toDate);
return transactionRepository.findByUserIdAndCreatedAtBetween(
userId, toInstant(fromDate), toInstant(toDate), pageable);
}
Step 3 — Stage the resolved file
git add src/main/java/com/example/TransactionService.java
git add src/main/resources/application.yml
Step 4 — Complete the operation
# After a merge:
git merge --continue
# or: git commit
# After a rebase:
git rebase --continue
# After a cherry-pick:
git cherry-pick --continue
Abort and start over
git merge --abort
git rebase --abort
git cherry-pick --abort
Using a Merge Tool
Configure IntelliJ IDEA as the merge tool
git config --global merge.tool intellij
git config --global mergetool.intellij.cmd \
'idea merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"'
git config --global mergetool.intellij.trustExitCode true
# Launch the tool for all conflicted files
git mergetool
VS Code
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd \
'code --wait "$MERGED"'
Prefer one side entirely
# Accept all changes from the incoming branch (theirs)
git checkout --theirs src/main/resources/application.yml
git add src/main/resources/application.yml
# Accept all changes from the current branch (ours)
git checkout --ours src/main/resources/application.yml
git add src/main/resources/application.yml
Preventing Conflicts
| Practice | How it helps |
|---|---|
| Short-lived branches | Less divergence = fewer conflicts |
| Pull/rebase frequently | Stay close to main; conflicts are smaller |
| Small, focused commits | Easier to reason about when conflicts do occur |
| Team ownership of files | Avoid two people editing the same file |
| Feature flags | Merge incomplete features to main early behind a flag |
During a rebase, Git replays commits one at a time. You may need to resolve the same logical conflict multiple times across different commits. If this happens frequently, consider squashing your commits first (git rebase -i), then rebasing the single resulting commit — you will only need to resolve the conflict once.