Skip to main content

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
SectionMeaning
<<<<<<< HEAD to ||||||| baseYour current branch version
||||||| base to =======The common ancestor (shown with --diff3)
======= to >>>>>>> feature/JIRA-123The 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

PracticeHow it helps
Short-lived branchesLess divergence = fewer conflicts
Pull/rebase frequentlyStay close to main; conflicts are smaller
Small, focused commitsEasier to reason about when conflicts do occur
Team ownership of filesAvoid two people editing the same file
Feature flagsMerge incomplete features to main early behind a flag

Resolving Conflicts in Rebase

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.