Branching & Merging in Git

You've now mastered the fundamental local workflow of Git: modifying files, staging them with git add, and saving snapshots with git commit. This is fantastic for tracking your own progress. But what happens when you want to work on a new feature or fix a bug without disrupting the stable, working version of your code?

Imagine your main code is a clean, finished highway. You wouldn't start major construction right in the middle of traffic! Instead, you'd build a separate off-ramp and construction road to do your work in isolation. In Git, this concept is called branching.

Branching is arguably Git's most powerful feature, and understanding it is key to working effectively on any modern software team. Let's learn how to create these safe work zones.

Tommy and Gina are looking at the conveyor belt with several pathways

What is a Branch – Your Coding Sandbox

A branch in Git is an independent line of development. Think of it as your own personal "sandbox" or a parallel universe for your project. When you create a new branch, you are creating a new pointer that starts from your current commit. As you make new commits on this new branch, that pointer moves forward, but the original branch you came from remains untouched.

The main branch in a repository is typically called main (or master in older projects). This branch is usually reserved for stable, tested, and releasable code.

The beauty of Git's branching model is that it's incredibly lightweight and fast. Creating a new branch doesn't involve copying all your files; it just creates a new small pointer. This encourages a common and highly effective workflow:

  • The main branch always contains the stable, production-ready code.
  • To work on anything new (a feature, a bug fix, an experiment), you create a new branch off of main (or develop – it depends on your project's Git workflow).
  • You do all your work and make all your commits on this new "feature branch".
  • If you make a mistake, you can simply discard the branch without ever harming the stable main branch.
  • Once your work is complete and tested, you merge your feature branch back into the main branch.

This workflow allows multiple developers to work on different features simultaneously without interfering with each other's work.

Essential Branching Commands in Git

Branching in Git revolves around just a few essential commands. In earlier Git versions, git checkout was the go-to for everything from switching branches to restoring files. But as of Git 2.23, two purpose-specific commands – git switch and git restore – were introduced to make these actions clearer and more intuitive.

Viewing Branches

To list all local branches and see which one you're currently on:

git branch

You'll see an asterisk * next to the active branch:

* main
development

Creating and Switching Branches

To work with branches more clearly, use git switch for navigating and creating branches, and reserve git restore for discarding changes. While git checkout still works, git switch is the recommended approach for branch-related tasks thanks to its clarity.

Two-Step Approach: Create a Branch, Then Switch to It

You can create a branch first and switch to it afterward.

  • Modern Way:
    # Step 1: Create the branch
    git branch login-feature
     
    # Step 2: Switch to it
    git switch login-feature
  • Legacy Way:
    # Step 1: Create the branch
    git branch login-feature
     
    # Step 2: Switch to it
    git checkout login-feature

One-Step Approach: Create and Switch

More commonly, you'll want to create and switch in a single command:

  • Modern Way:
    # -c stands for "create"
    git switch -c api-tests
  • Legacy Way:
    # -b stands for "branch"
    git checkout -b api-tests

What Happens When You Switch Branches?

Switching branches updates your working directory to match the last commit on that branch. That means files, folder structures, and code content can change instantly – giving you a clean, focused space to work on your task without affecting the rest of the project.

The Feature Branch Workflow in Action

Let's walk through a typical scenario. You have a stable project on the main branch, and you need to add a new login feature.

  1. First, ensure you are on the main branch. If you were on another branch, you would switch back to it.
    # Switch back to the 'main' branch
    git switch main
    (I'll cover pulling the latest changes from remotes in the next lesson).
  2. Create and switch to a new branch for your feature. It's good practice to name it descriptively.
    # The -c flag creates the new branch and immediately switches to it
    git switch -c feature/user-login
  3. Now you're in your safe sandbox. Do your work: create new files (e.g., LoginPage.cs, LoginTests.cs), modify existing files, etc.
  4. As you complete small, logical chunks of work, save them to your branch's history with add and commit.
    # Add all your changes to staging
    git add .
     
    # Commit the changes to your 'feature/user-login' branch
    git commit -m "Feat: Add LoginPage class and initial locators"

You can continue this cycle of modifying, adding, and committing as many times as you need. All of this history is being saved on the feature/user-login branch, while the main branch remains untouched and pristine.

Merging Your Work Back – git merge

Once your new feature is complete, tested, and ready to be incorporated into the main project, you need to merge your feature branch back into the main branch.

The merge process integrates the history from your feature branch into the target branch, combining your work.

The Merge Process:

  1. First, switch back to the branch you want to merge into. This is your target branch, which is typically main.
    # Switch to the target branch for the merge
    git switch main        
  2. Next, run the git merge command, specifying the name of the branch you want to merge from.
    # This command merges the history from 'feature/user-login' into 'main'
    git merge feature/user-login        

Git will perform the merge by finding the common ancestor commit between the two branches and creating a new "merge commit" that combines the histories of both. The main branch now contains all the work from your feature branch!

When Worlds Collide – Understanding Merge Conflicts

Sometimes, a merge isn't automatic. If you worked on a feature branch, and in the meantime, another developer made changes to the exact same lines of code in the main branch, Git won't know which version to keep. It can't read your mind!

When this happens, Git will pause the merge process and report a merge conflict. It will mark the conflicting files with special markers (like <<<<<<< HEAD, =======, and >>>>>>>) to show you exactly where the two different versions clash.

Don't Fear Conflicts!

Seeing a merge conflict for the first time can be intimidating, but they are a normal and expected part of collaborative development. They are not an error; they are simply Git asking you, the human, to make a decision.

Your job is to open the conflicted file(s), look at the marked sections, delete the markers, and edit the file to be the correct final version (which might be a combination of both changes). Once you've resolved the conflicts, you git add the fixed file(s) and run git commit to finalize the merge.

Branch Housekeeping – Renaming & Deleting

Keeping your repository tidy involves more than just merging branches. You might want to rename an active branch to fix a typo or clarify its purpose before it's merged. And after a branch has been successfully merged, it's good practice to delete it to prevent clutter and keep your branch list manageable.

Renaming a Branch

If you're currently on the branch you want to rename, you can use the -m flag (for "move"):

# Assume you are currently on a branch named 'feture/user-login' (with a typo)
# This command renames your current branch to 'feature/user-login'
git branch -m feature/user-login    

If you want to rename a different branch while you are on another one, you provide both the old name and the new name:

# Renames 'old-branch-name' to 'new-branch-name' even if you're not on it
git branch -m old-branch-name new-branch-name    

Deleting a Branch

Once a feature branch has been successfully merged into your main branch, it's no longer needed and should be deleted to keep your repository clean. You use the -d (lowercase "d" for "delete") flag for this. This is a "safe" delete operation; Git will prevent you from deleting a branch that has unmerged changes.

# First, merge your feature branch into main
git switch main
git merge feature/user-login
 
# Then, safely delete the feature branch
git branch -d feature/user-login    

In rare cases where you want to force the deletion of a branch and discard all its changes (e.g., you made a mistake and want to abandon the work), you can use a capital -D flag. Be very careful with this command, as it can lead to losing work permanently!

# Force delete a branch and all its commits that haven't been merged
git branch -D abandoned-feature    

Keeping your local branch list tidy by deleting merged branches is a great habit to get into.

Key Takeaways

  • Branching is Git's core feature for working on different lines of development in parallel, providing a safe sandbox for new features and bug fixes.
  • The primary branch is usually named main and should contain your stable, production-ready code.
  • Use git switch -c <branch-name> to create and switch to a new branch, which is the modern and recommended approach.
  • Use git switch <branch-name> to switch between existing branches.
  • After completing your work on a feature branch, you switch back to the main branch and use git merge <feature-name> to integrate your changes.
  • It's good practice to clean up merged branches using git branch -d <branch-name> for safe deletion.
  • A merge conflict occurs when Git cannot automatically combine changes because the same lines were edited on both branches; this requires manual resolution.

Branching Out Your Knowledge

What's Next?

You now understand the local Git workflow: committing your work and using branches to manage different features safely. This is fantastic! But so far, all our work has been on our own computer. The true power of Git is unlocked when we collaborate with others.

In our next lesson, we'll learn how to do just that by Working with Remote Repositories using GitHub.