JavaRush /Courses /Kotlin SELF /Safe experiments: working with branches

Safe experiments: working with branches

Kotlin SELF
Level 18, Lesson 2
Available

1. What are branches, and why are they needed?

Working with branches in Git is one of the key aspects of version control that lets you run several lines of development in parallel within a single repository. Branching makes Git a powerful tool for collaboration, experimentation, and managing different versions of a project.

            gitGraph
            commit id: "Initial setup"
            commit id: "Add base features"
            branch feature/new-idea
            checkout feature/new-idea
            commit id: "Implement new logic"
            commit id: "Refactor the logic"
            checkout main
            commit id: "Urgent bugfix on main"
            merge feature/new-idea
            commit id: "Prepare for release"
        
A new branch, feature/new-idea, is created off the main branch main for safe development. After the work is finished, it is merged back into main.

Imagine you want to seriously refactor something in your project or run a risky experiment. What would you do without Git? Most likely, you would copy the whole project into a new folder and work there. If you like the result, you would move it into the main folder. If not, you would simply delete the copy.

Branches in Git work on the same principle, but much more elegantly. Let’s look at an example of writing a book:

  1. You have a finished manuscript (this is your main branch main).
  2. You want to write an alternative ending (you create a new branch, for example feature/new-idea).
  3. You write the new ending without touching the main text of the manuscript (you work in the new branch).
  4. If the new ending turns out better, you replace the old one with it (you merge branches — merge).
  5. The old draft with the unnecessary ending can be deleted (you delete the branch).

2. Creating a new branch and working in it

Step 1. Open the branch management menu.

On the main toolbar, next to the project name, there is a widget showing the name of the current branch (by default — main). Click it and select + New Branch.

Branches widget in the upper-left corner

Step 2. Name the new branch.

A good practice is to name branches according to the task you are working on. For example, feature/add-usage-examples.

New branch creation window

After creating the branch, the IDE will automatically switch to it. You will see the new name in the same widget.

Step 3. Make and commit changes.

Now you are in your “sandbox”. Let’s add a new section with usage examples to our README.md file. Make the changes and create a commit, just as you learned in the previous lecture.

Commit in a new branch

3. Switching between branches

Your changes with usage examples are now safely stored in the feature/add-usage-examples branch. Let’s go back to the main branch main and see what it looks like there.

Step 1. Click the widget with the current branch name in the upper-left corner again.

Step 2. In the Local or Recent list, select the main branch, and in the submenu that appears, click Checkout.

Switching to the main branch

Step 3. Check the result.

As soon as you switch, open the README.md file. You will see that it does not contain the usage examples section! It stayed in the other branch. This way, you can work on new functionality without affecting the stable version in the main branch.

File without changes from the other branch

4. Merging branches

The merge command takes all commits from the feature/add-usage-examples branch (in this case, commit C3) and combines them with the current main branch, creating a new merge commit.

            gitGraph
            commit id: "C1"
            commit id: "C2"
            branch feature/add-examples
            checkout feature/add-examples
            commit id: "C3: Add new section"
            checkout main
            merge feature/add-examples
        

So, you have finished working on your task in the feature/add-usage-examples branch and want to add these changes to the main project.

Step 1. Switch to the target branch.

Make sure you are on the branch INTO WHICH you want to add the changes. In our case, that is main.

Step 2. Perform the merge.

Click the branch management widget again. In the list, select the branch FROM WHICH you want to take the changes (feature/add-usage-examples), and in the submenu select Merge 'feature/add-usage-examples' into 'main'.

Merging branches

Step 3. Check the result.

Now, in the README.md file on the main branch, your new section with examples has appeared. You have successfully merged your work into the main version of the project!

Successful merge

5. Merge conflicts

For beginners, the word “conflict” often sounds scary, but in team development it is absolutely normal. A conflict happens when the same lines in the same file were changed in both branches being merged. Git cannot decide on its own which version is correct and asks for your help.

This is where the full power of IntelliJ IDEA shows up. Resolving conflicts in the terminal is not much fun, but the IDE’s built-in 3-way merge tool turns this task into a clear and simple process.

            gitGraph
            commit id: "C1: Common base"
            branch feature/new-title
            checkout main
            commit id: "C2: Change in main"
            checkout feature/new-title
            commit id: "C3: Change in feature"
        
Both branches, main and feature/new-title, have new commits (C2 and C3) based on a common ancestor (C1). This is guaranteed to lead to a conflict during merging.

Let’s simulate a conflict:

  1. Make sure you are on the main branch, and you have no uncommitted changes in the Commit window.
  2. Create a new branch feature/new-title, but do not switch to it yet. Make sure the Checkout branch checkbox is cleared.
  3. Now, while on the main branch, change the first line in README.md to "My Awesome Project" and make a commit.
  4. Switch to the feature/new-title branch. You will see that the first line in README.md is still the old one there: this is the state of the file at the moment the branch was created. Change that same line to "My Super Project" and make a commit.
  5. Go back to the main branch and perform a merge with feature/new-title.

Now Git will see that both histories were changed in the same place. It will show you a window for resolving the conflict (Merge Conflicts).

Conflict detection window

Click the Merge button to open the conflict resolution tool. Here is what you see:

  • On the left (Your changes): the version of the file from your current branch (the one you are merging into — main).
  • On the right (Changes from branch): the version of the file from the branch you are merging.
  • In the center (Result): the final version of the file that you must assemble manually.
3-way conflict resolution window

You can click the arrows >> or << (or the X icons to discard) to accept one version or the other in full. You can also simply type the correct text in the center pane.

When you are satisfied with the result in the center pane and all conflicts are resolved, click Apply (or Save Changes and Finish). The IDE will create a special merge commit itself, and the conflict will be resolved.

Why does a conflict sometimes not occur?

A situation may occur where you followed all the steps, but no conflict happened. Most often, this is because Git was able to perform a fast-forward merge (a fast-forward merge). This happens when the history of one branch simply continues the history of another, and they did not diverge in parallel.

Example:

  1. You have some commit in main (say, C1).
  2. You make a new commit on the main branch with the text "My Awesome Project". The main branch now points to commit C2 (main -> C1 -> C2).
  3. You create the feature/new-title branch from the current position of the main branch. This means the new branch also starts from commit C2.
  4. You make a commit on feature/new-title with the text "My Super Project". This branch “moves forward” and now points to commit C3 (feature/new-title -> C1 -> C2 -> C3).
  5. You return to main (which is still at commit C2) and issue the command to merge feature/new-title.

Git looks at the situation and sees that the main branch is a direct ancestor of the feature/new-title branch. There were no new commits in the main history while you were working in the other branch. Git thinks: "Oh, then I just need to fast-forward main to commit C3. There are no contradictions." And it simply moves the main pointer to commit C3 without conflicts.

            gitGraph
            commit id: "C1"
            commit id: "C2"
            branch feature/new-title
            checkout feature/new-title
            commit id: "C3"
            checkout main
            merge feature/new-title
        

6. Viewing change history

To better understand what is happening in your project, it is useful to look at its history.

Open the Git tool window (usually an icon on the left sidebar or at the bottom of the IDE window) and select the Log tab or Show Git Log on the Git toolbar. You will see a graphical representation of all your branches and commits. This will help you visually track which branch split off from which and where they were merged.

Git log window

Here you can click any commit to see what changes it includes, who made it, and when. It is a real time machine for your code!

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION