Recommended resources
The freely available resources this lesson will direct you to.
Welcome back. In the previous lesson, you created a focused feature branch, verified a backend change with Maven, pushed it, and opened a pull request. That workflow keeps proposed work separate from main until it is ready.
This lesson handles the situation that makes pull requests feel less automatic: someone else’s change reaches main while your PR is still open, and Git can no longer determine the correct combined version by itself. You will learn to resolve a simple conflict deliberately, verify that both intended changes remain, update the PR, and then merge it safely.
Plan for about 40 minutes: a short video demonstration, a careful walkthrough of the conflict markers, and one resolution workflow you can reproduce in your Java project.
What a merge conflict actually means
A merge conflict is not a failed build and it does not mean either developer made a mistake. It means Git has found two competing textual edits and cannot safely decide which final code you intended.
For example, Git can usually combine these independent edits automatically:
- One branch changes a validation message on line 20.
- Another branch adds a test on line 90.
But Git needs help when both branches modify the same line or nearby section of a file. It can also need help when one branch edits a file that another branch deletes. This lesson concentrates on the common competing line change conflict.
A pull request has two important sides:
| Term | In your workflow |
|---|---|
| Base branch | The branch receiving the change, usually main |
| Head branch | The source branch containing the PR, such as feature/validate-application-status |
Suppose your feature branch is based on Monday’s main. On Tuesday, another developer merges a change to the same service method into main. Your PR is still valid as a proposal, but GitHub may report that it cannot automatically merge. Before final PR merge, you must integrate the current main into your feature branch and choose the correct combined code.
This short GitHub video sets up precisely that situation and shows the terminal-based resolution route.
How to merge a pull request | Introduction to GitHub
Watch “How to merge a pull request” from GitHub. It gives a compact view of why a PR becomes conflicted and distinguishes resolving the conflict from finally merging the PR.
Watch the conflict concept for the relationship between pull requests and conflicts. Then watch the setup, where a second branch changes the same line and makes the earlier PR conflicted. Focus most closely on the terminal workflow: updating main, merging it into the feature branch, resolving, committing, and pushing. Finish with the final merge to see that the PR can be merged only after its conflict is cleared.
The key habit is this:
A conflict resolution is a design decision. Do not select a version merely because it is labelled “current,” “incoming,” “ours,” or “theirs.”
Those labels depend on which branch you currently have checked out and which operation you ran. Your question is always semantic: what behavior did each change intend, and what final code preserves the valid parts of both?
Read the markers before changing anything
When a merge stops with a conflict, Git writes markers into the affected file. Imagine that main improved status normalization, while your feature branch added business validation in the same method.
If you are on your feature branch and run git merge main, you might see:
<<<<<<< HEAD
String normalized = requestedStatus.trim().toUpperCase();
validateAllowed(normalized);
return normalized;
=======
String normalized = requestedStatus.strip().toUpperCase(Locale.ROOT);
return normalized;
>>>>>>> main
In this particular command context:
- The part after
<<<<<<< HEADis the version from the branch currently checked out: your feature branch. - The part after
=======is the incoming version frommain. >>>>>>> mainidentifies the end of the incoming version.
The markers are not Java syntax. Leaving even one in a .java file will cause compilation to fail.
The resolution should preserve both meaningful intentions:
String normalized = requestedStatus.strip().toUpperCase(Locale.ROOT);
validateAllowed(normalized);
return normalized;
Here, the final method retains:
- The normalization improvement from
main. - The status validation from the feature branch.
That is better than choosing an entire side blindly. It is also why tests matter: after combining changes, you must check that the resulting behavior still makes sense and compiles.
Before editing a conflict, inspect:
- the PR title and description, to recall what the feature branch was meant to deliver;
- the commits or diff that recently entered
main, to understand the other change; - the complete surrounding method, not only the marked lines;
- related tests, especially when a conflict occurs in service or validation code.
For a Spring Boot project, a conflict in a service method can easily affect its tests, API contract, or error behavior. Resolve the code first, then let ./mvnw clean verify tell you whether the combined change remains consistent.
The recommended local workflow
For a Java backend PR, resolve locally when possible. You can inspect the full project in your IDE and run Maven before updating the pull request.
First, make sure the working directory is clean. Do not begin a merge while unrelated edits are present.
git status -sb
Then update your local main, return to the feature branch, and merge the updated main into it:
git switch main
git pull --ff-only origin main
git switch feature/validate-application-status
git merge main
If Git can combine everything, it completes the merge without your intervention. If it cannot, it stops and prints a message similar to:
CONFLICT (content): Merge conflict in src/main/java/.../JobApplicationService.java
Automatic merge failed; fix conflicts and then commit the result.
At this point, do not keep running random Git commands. Check the exact state:
git status
Git lists the files under Unmerged paths. Open each listed file in your editor. VS Code will usually highlight the conflict and offer buttons such as “Accept Current Change,” “Accept Incoming Change,” and “Accept Both Changes.” These are conveniences, not decisions for you. Use them only when you understand the resulting code.
Resolve each conflict by editing the final content yourself:
- Identify what the feature branch intended.
- Identify what updated
mainintended. - Write the combined final version, keeping one side only when the other side is genuinely obsolete or incorrect.
- Remove all conflict markers.
- Read the surrounding code for duplicate statements, missing braces, repeated imports, or changed method behavior.
When the file looks correct, inspect the unstaged resolution:
git diff
Stage only the resolved files:
git add src/main/java/com/example/jobtracker/service/JobApplicationService.java
git add src/test/java/com/example/jobtracker/service/JobApplicationServiceTest.java
If only one file conflicted, stage only that file. Then inspect exactly what the merge commit will contain:
git diff --cached
Now verify the integrated project:
./mvnw clean verify
On Windows PowerShell:
.\mvnw.cmd clean verify
A successful build is especially important after a manual merge. It catches leftover markers, syntax errors, tests that now contradict the combined behavior, and other problems that visual inspection can miss.
Finally, create the merge-resolution commit and push it:
git commit -m "Resolve conflict with main status validation"
git push
The push updates the existing feature branch on GitHub. Because the pull request already points to that branch, the PR updates automatically. You do not open a second pull request.
If the merge was started by mistake or you discover that you need clarification before deciding, safely stop before staging and committing:
git merge --abort
This returns the branch to the state before git merge main. It is much safer than guessing at a code resolution.
Resolving a simple conflict in GitHub’s editor
GitHub’s conflict editor is appropriate for a small, obvious text conflict, particularly in a short documentation or configuration file. It is less suitable when you need to run tests, understand a larger refactor, or resolve several related Java files. In those cases, use the local workflow above.
Resolving a merge conflict on GitHub - GitHub Docs
Read GitHub Docs’ “Resolving a merge conflict on GitHub.” It explains the web editor flow and, importantly, what GitHub’s “Commit merge” button actually commits.
In the main article, begin with the scope note to distinguish simple browser-resolvable conflicts from cases better handled locally. In the instructions after the “Resolve conflicts” button, read the resolution decision; focus on constructing a final version rather than treating either branch as automatically correct. Finally, read the steps beginning “After you've resolved all the conflicts in the file” through the Commit merge explanation, including the following final PR merge step.
In GitHub, the process is:
- Open the affected pull request.
- Find the merge-conflict notice near the bottom of the Conversation tab.
- Select Resolve conflicts.
- For every conflicting file, edit the content into the desired final version and remove the markers.
- Select Mark as resolved for each file.
- Select Commit merge.

The phrase Commit merge is easy to misunderstand. It does not complete the pull request. It creates a commit on your feature branch that incorporates the base branch, such as main, and records your resolution. The pull request stays open.
Afterward, GitHub recalculates the PR. Review its Files changed tab again. You should see both:
- your original intended feature;
- the relevant change that had already entered
main.
Then wait for required checks and review, if your repository uses them. Only when the PR is ready should you select Merge pull request and confirm the merge.
If GitHub disables Resolve conflicts, do not try to work around it by editing arbitrary files in the web UI. Use a local Git client or the command line, where you can resolve and test the complete repository.
Final verification before merging the PR
A conflict-free PR is not automatically a correct PR. Before selecting the final merge button, perform this short review:
- No markers remain. Search the changed files for
<<<<<<<,=======, and>>>>>>>. - Both intentions survive. Compare the final method or file against the feature’s purpose and the current
mainchange. - No accidental duplication exists. Combining two versions can leave duplicate validation, repeated imports, or two contradictory return paths.
- The backend build passes. Run
./mvnw clean verifylocally when you used the local workflow; confirm the repository’s required checks are green on GitHub. - The PR diff is focused. The conflict-resolution commit should not include generated
target/files, local configuration, or unrelated formatting edits. - The PR is aimed correctly. Confirm that the base is
mainand the head is your feature branch.
After the PR has been merged, update your local main before beginning the next task:
git switch main
git pull --ff-only origin main
This prevents the next feature branch from starting from an outdated point.
Key takeaways
A merge conflict means Git cannot determine the intended final text; it does not determine that one branch is right and the other is wrong.
- First understand both changes and the behavior each was meant to produce.
- Resolve locally for Java changes when you need IDE support and Maven verification.
- Remove every conflict marker, stage the resolved files, inspect the staged diff, and run the build before committing.
- Push the merge-resolution commit to the same feature branch; the existing PR updates automatically.
- In GitHub’s web editor, Commit merge resolves the PR’s branch against
main; it does not merge the PR intomain. - Review checks and the final combined diff before selecting Merge pull request.
This completes the Java and developer-workflow refresh. The next module shifts to data structures and interview patterns, beginning with how to determine time and space complexity in short Java solutions.