JavaRush /Courses /Go SELF /Safe Experiments: Working with Branches

Safe Experiments: Working with Branches

Go SELF
Level 32, 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 allows you to conduct multiple 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, branches off from the main main branch for safe development. After the work is completed, it is merged back into main.

Imagine you want to seriously rework something in your project or conduct a risky experiment. How would you do it without Git? Most likely, you would copy the entire project to a new folder and work in it. If you like the result, you would move it to 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 affecting the main text of the manuscript (you work in the new branch).
  4. If the new ending turns out to be better, you replace the old one with it (you merge the 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 displaying the name of the current branch (by default — main). Click on it and select + New Branch.

Branch widget in the upper left corner

Step 2. Name the new branch.

It is good practice to name branches according to the task you are solving. 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 commit them, as you learned in the previous lecture.

Commit in a new branch

3. Switching between branches

Your changes with usage examples are now safely saved in the feature/add-usage-examples branch. Let's return to the main main branch and see what's there.

Step 1. Click on 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 the section with usage examples is missing! It remained 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 another 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 in the branch WHERE you want to add the changes. In our case, this is main.

Step 2. Perform the merge.

Click on 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'.

Performing a branch merge

Step 3. Check the result.

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

Successful merge

5. Merge conflicts

For beginners, the word "conflict" often sounds intimidating, but in team development, it is absolutely normal. A conflict occurs when the exact same lines in the same file have been changed in both branches being merged. Git cannot decide on its own which version is more correct and asks for your help.

And here the full power of IntelliJ IDEA (and GoLand) manifests itself. Resolving conflicts in the terminal is quite an "enjoyable" experience, but the built-in 3-way merge tool in the IDE 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) that are based on a common ancestor (C1). This is guaranteed to result in a merge conflict.

Let's simulate a conflict:

  1. Ensure you are on the main branch, and you have no unsaved 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 unchecked.
  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 here remains old: this is the state of the file at the time the branch was created. Change this same line to "My Super Project" and make a commit.
  5. Return to the main branch and merge it with feature/new-title.

Now Git will see that both histories have been changed in the exact same place. It will show you a window to resolve the conflict (Merge Conflicts).

Conflict detection window

Click the Merge button to open the conflict resolution tool. Here's what you see here:

  • Left (Your changes): the version of the file from your current branch (where you are merging to — main).
  • Right (Changes from branch): the version of the file from the branch you are merging.
  • Center (Result): the final version of the file that you have to assemble manually.
Three-way conflict resolution window

You can click on the >> or << arrows (or the crosses to cancel) to accept one or the other option entirely. You can also simply write the correct text in the central panel.

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

Why does a conflict sometimes not occur?

A situation may arise where you have completed all the steps, and a conflict has not occurred. Most often, this is because Git was able to perform a fast-forward merge. This happens when the history of one branch simply continued the history of another, and they did not diverge in parallel.

Example:

  1. Make sure you are in main (say, C1).
  2. You make a new commit in the main branch with the text "My Awesome Project". The main branch now points to commit C2 (main -> C1 -> C2).
  3. You create a feature/new-title branch from the current position of the main branch. This means that the new branch also starts from commit C2.
  4. You make a commit in 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 on commit C2) and give 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 history of main while you were working in the other branch. Git thinks: "Ah, so here you 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 the history of changes

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 or Show Git Log tab in the Git toolbar. You will see a graphical representation of all your branches and commits. This will help visually track which branch separated from which and where they were merged.

Git log window

Here you can click on any commit to see what changes were included in it, who made it, and when. This is a real time machine for your code!

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