انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-046-L08

عرض عملي: إنشاء تعارض دمج وحلّه

  • الدرس 8 من 8
  • 10 دقيقة قراءة
  • مبتدئ
في هذا الدرس 11 موضوعات

هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.

Lesson Overview

Reading about a conflict is not the same as meeting one. In this hands-on lesson you will create a merge conflict on purpose in the Bank Accounts project, resolve it by hand, and see the result in the history. You will also make a second planned mistake: committing the conflict markers while they are still in the file. Seeing the build break once, in a safe practice repository, is the best way to remember to check.

This lesson is the second half of the module lab (BEG-046-LAB1). It continues the project from the previous lesson, which already has a .gitignore and a few commits. If you did not do that lesson, any repository with a Java file and at least one commit will do. Your file contents will differ, so follow the steps, not the exact lines. Commands are for a terminal, because editor and IDE menus differ. All outputs below were produced with Git 2.44 and JDK 21; hashes will differ for you.

Learning Objectives

By the end of this lesson you will be able to:

  • Create a conflict deliberately by changing the same line on two branches.
  • Recognise that a conflicted merge is paused, and read git status and the conflict markers.
  • Resolve the conflict by editing the file to the intended final text.
  • Explain what happens when markers, or both versions of a line, are left in.
  • Finish a merge with git add and git commit and verify with a compile.
  • Read git log --oneline --graph to see a merge.

Written Explanation

The plan

A conflict needs two branches that change the same line differently. The recipe:

  1. Start from a clean main with a line that both branches will edit.
  2. On a branch, change that line and commit.
  3. On main, change the same line differently and commit.
  4. Merge the branch into main.

We will use a monthly fee in Account.java. The sentence to remember: a conflict is two people deciding different things about the same line.

Step 0: A shared starting point

Check you are on main with nothing pending:

bash
git status

Add one line to Account (below the balance field) and commit it, so that both branches start from it:

java
    private double monthlyFee = 5.0;
bash
git commit -am "Add monthly fee field"

(-a stages changes to already tracked files, then commits. It does not add new files.)

Step 1: Change the line on a branch

bash
git switch -c add-fee-waiver

Edit the line to private double monthlyFee = 0.0; (the fee is waived), then:

bash
git commit -am "Waive monthly fee"

Step 2: Change the same line on main

bash
git switch main

Edit the same line to private double monthlyFee = 7.5;, then:

bash
git commit -am "Raise monthly fee to 7.5"

Both branches now contain a different decision about one line. The two lines of history have diverged, as in lesson L04.

Step 3: Merge and meet the conflict

bash
git merge add-fee-waiver
text
Auto-merging src/Account.java
CONFLICT (content): Merge conflict in src/Account.java
Automatic merge failed; fix conflicts and then commit the result.

Git did not give up and did not lose anything; it paused. git status -s shows:

text
UU src/Account.java

UU means the file is unmerged, modified on both sides. Open the file:

text
public class Account {
    private final String owner;
    private double balance;
<<<<<<< HEAD
    private double monthlyFee = 7.5;
=======
    private double monthlyFee = 0.0;
>>>>>>> add-fee-waiver

    public Account(String owner, double balance) {

Above ======= is the current branch (main: 7.5). Below is add-fee-waiver (0.0). You can still stop here and run git merge --abort to go back to before the merge, if you want to start over.

Step 4: The planned mistake, commit with markers

A hurried developer sees the conflict, runs:

bash
git add src/Account.java
git commit -m "Merge add-fee-waiver"
text
[main 5d9be22] Merge add-fee-waiver

Git accepted it. Git marks a conflict as resolved when you git add the file; it does not read the file to see whether the markers are gone. But the project no longer builds:

bash
javac -d out src/*.java
text
src\Account.java:4: error: illegal start of type
<<<<<<< HEAD
^
src\Account.java:4: error: > expected
<<<<<<< HEAD
            ^

(The path separator appears as \ on Windows and / elsewhere. There are further error lines.) The compiler messages point at the first marker line. That is your clue: "illegal start of type" on a line starting with <<<<<<< means unresolved markers.

Step 5: Fix it, and a second trap

The quick-fix a beginner tries is to delete only the three marker lines. That leaves both versions of the line:

text
    private double monthlyFee = 7.5;
    private double monthlyFee = 0.0;

and the compiler says:

text
src\Account.java:5: error: variable monthlyFee is already defined in class Account

This shows what resolving means: you choose the final text. Both lines cannot stay. The team decides the fee is waived, so keep 0.0 and delete the 7.5 line. The file reads:

java
    private double monthlyFee = 0.0;

Check, then record the fix as a normal commit:

bash
javac -d out src/*.java
git diff --stat
git commit -am "Resolve fee conflict: waive monthly fee, remove leftover markers"
text
 src/Account.java | 4 ----
 1 file changed, 4 deletions(-)

Confirm no markers remain anywhere:

bash
git grep -n "<<<<<<<"

It prints nothing (exit status 1 means no match). The earlier bad merge commit stays in history, with the fix right after it. That is honest and good enough for this stage. Rewriting history to hide the slip is an advanced topic, and for commits that other people already have it can cause trouble, so we do not do it here.

Step 6: See the merge

bash
git log --oneline --graph
text
* 115b825 Resolve fee conflict: waive monthly fee, remove leftover markers
*   5d9be22 Merge add-fee-waiver
|\  
| * ed81b2e Waive monthly fee
* | 7f31231 Raise monthly fee to 7.5
|/  
* 6683578 Add monthly fee field
* ab0fa5e Add deposit and withdraw
* 2948ea1 Add Main that creates one account
* 833d77b Add Account class with owner and balance

You can see the shared commit at the bottom, the two diverging commits, the merge commit that joins them, and the fix on top. You can now delete the merged branch:

bash
git branch -d add-fee-waiver

The correct order, summarised

When you do it carefully the sequence is shorter, with no broken commit:

  1. git merge branch (conflict reported).
  2. git status (which files?).
  3. Edit each file to the final text, with all markers removed.
  4. Compile (and run tests, when you have them).
  5. git add file.
  6. git commit.

Examples

Example 1: Abort and retry (simple)

After the conflict in Step 3, you change your mind:

bash
git merge --abort
git status -s

The status is clean and both branches are as before. You can retry the merge whenever you like. This is the safest way to look at a conflict: try, look, abort.

Example 2: Combining both sides (practical)

Suppose instead of one number, both branches added a different method at the same place in Account.java. Git marks both as conflicting. The right resolution is not to choose; it is to keep both methods and delete only the marker lines. Then compile, git add, and git commit. Resolving does not mean "pick a winner"; it means "write the correct final file".

Example 3: A conflict in a comment, not code (realistic)

A conflict can happen in any text file: a README line, a configuration file, a comment. The same markers appear, but the compiler will not warn you if you leave them in a .txt or .md file. For non-code files, rely on the habit of searching for <<<<<<< before committing: git grep -n "<<<<<<<" lists every tracked file that still contains the marker, including files the compiler never reads.

Backend Insight

Conflicts in backend projects often land in configuration (for example, a database setting or a port number) and in central classes that everyone touches. A wrong resolution in a configuration file may compile fine and only fail when the service starts in an environment you did not test. The compile check you used in this demo is the minimum; real teams add automated tests and review (BEG-047 and JNR-053) so a bad merge is caught before it reaches production.

Remember also that a conflict is often a decision, such as the fee above. Developers resolve it by talking, not by guessing. The commit message of the resolution is a good place to record the outcome ("waive monthly fee"), so the history explains why.

Common Mistakes

Mistake 1: Committing the markers

  • Incorrect: git add and git commit right after the conflict message.
  • Correct: edit first, compile, then git add and git commit.

Mistake 2: Deleting only the marker lines

Leaving both versions of a conflicted line often causes a compile error (as in Step 5) or, worse, a silent logic error. Decide the final text.

Mistake 3: Resolving in the wrong place

Edit the file in your working folder, not in .git. Never edit anything inside .git.

Mistake 4: Panicking and deleting the repository

Nothing is lost during a conflict. git merge --abort goes back, and git status tells you what is left to do.

Exercises

Level A: Foundation

A1. Name the four steps needed to create a conflict on purpose.

أظهر الحل A1

A1. Start from a shared commit; change a line on a branch and commit; change the same line differently on main and commit; merge the branch into main.

A2. What does UU in git status -s mean?

أظهر الحل A2

A2. The file is unmerged: both sides modified it and the conflict is not resolved yet.

A3. Which command abandons a conflicted merge?

أظهر الحل A3

A3. git merge --abort.

Level B: Application

B1. In a practice repository, create a conflict on one line of a file and resolve it by choosing the version from the branch you are merging in. List your commands.

أظهر الحل B1

B1.

bash
git switch -c feature
# edit line 3, then
git commit -am "Change line 3 on feature"
git switch main
# edit line 3 differently, then
git commit -am "Change line 3 on main"
git merge feature
# edit the file: keep the feature text, remove all marker lines
git add File.java
git commit -m "Merge feature: keep feature wording"

B2. After resolving, how do you check that no marker lines remain in any tracked file? Name two checks (one command and one tool).

أظهر الحل B2

B2. git grep -n "<<<<<<<" (and likewise for >>>>>>>), which prints nothing when clean; and javac on the Java files, which fails if markers remain. Searching in your editor works too.

B3. Explain why the commit that Git creates after git add of a file with markers is "valid" for Git but not for Java.

أظهر الحل B3

B3. Git only needs git add to mark the file resolved; it does not parse the content. The Java compiler parses the content, and <<<<<<< is not Java syntax.

Level C: Challenge

C1. Both branches added a different method to the same class at the same spot. Describe the conflict you expect, the resolved file, and the commit message you would write.

أظهر الحل C1

C1. Git shows both methods inside one conflict block, one above ======= and one below. Resolved file: both methods kept, markers deleted, formatting tidy. Message example: Merge add-fee-waiver: keep both fee methods.

C2. You committed a merge with markers and pushed on to two more commits before noticing. Describe how you repair it without rewriting history, and why that is acceptable.

أظهر الحل C2

C2. Open the file in the latest commit, remove the markers (and keep the right text), compile, and commit the fix. The history keeps the bad commit but shows the repair. This is acceptable because rewriting history that others have already received can cause them problems; fixing forward is safer.

Mini Challenge

On your Bank Accounts repository, run the whole recipe on a different line from the one in the demo (for example, an interest rate or a maximum withdrawal). Do it twice. The first time make the planned mistake (commit with markers, watch the compiler complain) and repair it. The second time, in a fresh branch pair, do it correctly: abort the merge once to practise, then merge again, edit, compile, git add, git commit. Save the final git log --oneline --graph output.

Check your work: the graph shows the merge commit; git grep -n "<<<<<<<" prints nothing; the project compiles; your resolution message says what was decided.

Summary & Next Lesson

Key takeaways

  • A conflict needs two branches changing the same line differently.
  • The merge pauses; git status shows UU, and git merge --abort goes back.
  • Resolve by writing the final text, with all marker lines removed and no duplicate lines.
  • Git does not check for markers; compile and search before committing.
  • A repair commit is a normal way to fix a bad merge commit.
  • git log --oneline --graph shows where the lines joined.

Next lesson

Next, in BEG-047, "Remotes, push, and pull" takes the history you built here and shares it between computers, starting with GitHub.

Self-check

  1. What do you need to make a conflict?
  2. Why can git commit succeed with markers in the file?
  3. What error is typical when markers are left in a Java file?
  4. What does it mean to "resolve" a conflict?
  5. How do you see the merge in the history?
أظهر الإجابات
  1. Two branches that change the same line in different ways, then a merge.
  2. Git only checks that you staged the file; it does not read its content.
  3. illegal start of type at a line starting with <<<<<<<.
  4. To edit the file into the correct final text, with the markers removed, then git add and git commit.
  5. git log --oneline --graph.

References

  • Pro Git book, "Basic Branching and Merging". It supports the conflict marker layout, the resolve steps (edit, git add, git commit), and deleting merged branches with git branch -d.
  • Git documentation, git log. It supports --graph and --oneline.
  • Not verified from an official page: that Git does not check for leftover markers when you git add (observed by running it), the behaviour of git merge --abort, and the advice about rewriting shared history. The demo and every output, including the compiler messages, come from a real run with Git 2.44 and JDK 21.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.