Branching and Merging Strategies

Mikey NicholsMikey Nichols
8 min read

I'll create a compelling and engaging draft for your article on Git branching and merging strategies. This will build upon the introduction to Git from the first article while focusing on Git's powerful branching capabilities.

The Magic of Parallel Development

Git's true superpower isn't just tracking changes—it's enabling parallel development through branching. Imagine your codebase as a tree growing upward through time. Each commit builds upon the last, forming the trunk of your development history. But what if you want to experiment with a new feature without risking the stability of your main code? That's where branches come in.

Branches allow developers to diverge from the main line of development, creating isolated environments where changes can be made without affecting the main codebase. This parallel development capability transforms how teams collaborate, enabling multiple features to be developed simultaneously while maintaining a stable production branch.

Understanding Branches: More Than Just a Name

What Branches Actually Are

At their core, branches in Git are simply lightweight, movable pointers to specific commits. When we create a branch, we're not actually duplicating code—we're just creating a new reference to a commit. This is why branching in Git is nearly instantaneous, regardless of project size.

Each branch name points to the commit at its tip, and as we add new commits to a branch, this pointer automatically moves forward. This elegant system allows Git to maintain multiple development lines with minimal overhead.

The HEAD Pointer: Your Current Location

In the Git universe, HEAD is a special pointer that indicates which branch (or commit) you're currently working with. Think of HEAD as "you are here" on a map—it's your current position in the repository's history.

When you checkout a branch, HEAD points to that branch, which in turn points to a commit. This is known as being in "attached HEAD" state. Sometimes, you might checkout a specific commit directly (rather than a branch), putting you in "detached HEAD" state—useful for exploring history but requiring care when making changes.

Local vs. Remote Branches

Git distinguishes between branches that exist on your machine (local branches) and those that exist on remote repositories (remote branches).

Local branches are yours to control—you can create, modify, and delete them freely without affecting others. Remote branches, like origin/main, represent the state of branches on remote repositories. These serve as bookmarks showing you where branches were the last time you communicated with the remote repository.

Changes to remote branches only happen when you explicitly sync with the remote using commands like push, fetch, or pull.

Branch Operations: Creating Your Development Paths

Creating Branches

Creating a branch in Git is straightforward and can be done in multiple ways:

bash# Traditional two-step approach
git branch feature-login    # Create branch
git checkout feature-login  # Switch to branch

# Shorthand approach (creates and switches in one command)
git checkout -b feature-login

# Modern approach (Git 2.23+)
git switch -c feature-login

Each approach creates a new branch pointer at your current commit. The -b or -c flag combines creation and switching into a single step.

Switching Between Branches

Moving between branches is like time travel in your codebase. When you switch, Git updates your working directory to match the snapshot stored in the target branch:

bash# Traditional approach
git checkout main

# Modern approach (Git 2.23+)
git switch main

When switching branches, Git tries to preserve uncommitted changes if possible. However, if those changes conflict with the target branch, Git prevents the switch to avoid losing work.

Listing Branches

To see what branches exist in your repository:

bash# List local branches
git branch

# List remote branches
git branch -r

# List all branches (local and remote)
git branch -a

The current branch is typically highlighted or marked with an asterisk (*).

Renaming and Deleting Branches

Branches can be renamed when their purpose evolves:

bash# Rename current branch
git branch -m new-branch-name

# Rename specific branch
git branch -m old-branch-name new-branch-name

Once a branch's changes have been merged, you'll often want to delete it:

bash# Safe delete (prevents deletion if branch has unmerged changes)
git branch -d feature-complete

# Force delete (use with caution!)
git branch -D abandoned-feature

Merging Fundamentals: Bringing Changes Together

The Concept of Merging

Merging is the process of integrating changes from one branch into another. When you've completed work on a feature branch, you typically merge it back into your main development branch.

Git's merge process:

  1. Identifies the common ancestor commit of the two branches

  2. Determines changes introduced since that common ancestor

  3. Applies those changes to the target branch

  4. Creates a new "merge commit" that combines both histories (in most cases)

Fast-Forward vs. Three-Way Merges

Git performs two types of merges depending on the branch structure:

Fast-forward merges occur when the target branch hasn't changed since your feature branch was created. In this case, Git simply moves the target branch pointer forward to match the feature branch—no new commit needed.

bash# Example of fast-forward merge
git checkout main
git merge feature-simple

Three-way merges happen when both branches have new commits since they diverged. Git creates a new "merge commit" with two parent commits (one from each branch).

bash# Three-way merge (creates a merge commit)
git checkout main
git merge feature-complex

You can force a merge commit even for fast-forward merges:

bashgit merge --no-ff feature-branch

This practice maintains explicit records of feature branches in your history.

Merge Conflicts and How to Resolve Them

Merge conflicts occur when Git can't automatically reconcile competing changes to the same part of a file. When this happens, Git pauses the merge process and marks the conflicted areas:

<<<<<<< HEAD
Code from the current branch
=======
Code from the branch being merged
>>>>>>> feature-branch

To resolve conflicts:

  1. Open conflicted files in your editor

  2. Decide which changes to keep (or combine them)

  3. Remove the conflict markers

  4. Stage the resolved files with git add

  5. Complete the merge with git commit

Modern IDEs and tools like VS Code offer visual conflict resolution tools that make this process more intuitive.

Rebasing vs. Merging: Different Paths to Integration

What Rebasing Does

Rebasing is an alternative to merging that rewrites history for a cleaner timeline. Instead of creating a merge commit, rebasing:

  1. Temporarily sets aside your branch's commits

  2. Moves your branch pointer to the tip of the target branch

  3. Replays your commits one by one on top of this new base

The result is a linear history that looks as if you had started your branch from the current state of the target branch.

bashgit checkout feature-branch
git rebase main

When to Use Rebase vs. Merge

Use merge when:

  • You want to preserve the exact history of branch development

  • You're collaborating on a branch that others are using

  • You want explicit documentation of where features were integrated

Use rebase when:

  • You want a clean, linear project history

  • You're working on a personal feature branch

  • You want to incorporate the latest changes from the main branch into your feature

A common workflow combines both: rebase your feature branch to incorporate updates from main, then merge (which will be fast-forward) when complete.

Dangers of Rewriting History

Rebasing changes commit history, creating new commits with different hashes even though the content changes may be identical. This can cause serious problems if:

  • You've pushed your branch to a remote repository

  • Others have based their work on your original commits

The golden rule: Never rebase commits that exist outside your local repository.

If you need to rebase a shared branch, coordinate with your team first and use git push --force-with-lease rather than --force for an extra layer of safety.

Branching Strategies: Frameworks for Collaboration

Feature Branching

The simplest branching strategy involves creating a separate branch for each feature or bugfix. This isolates changes and allows developers to work independently.

Basic feature branching workflow:

  1. Create a branch for your feature from main

  2. Develop and commit changes to your branch

  3. Merge the feature branch back to main when complete

  4. Delete the feature branch

This approach forms the foundation of most Git workflows.

Git Flow Overview

Git Flow is a comprehensive branching model designed for projects with scheduled releases. It defines specific branch types with strict rules:

  • main/master: Contains production-ready code

  • develop: The main development branch

  • feature/*: New features branched from and merged back to develop

  • release/*: Preparation for releases, branched from develop

  • hotfix/*: Urgent fixes for production, branched from main

Git Flow provides a structured approach but can be overly complex for some projects.

GitHub Flow Overview

GitHub Flow simplifies Git Flow into a more streamlined process:

  1. Branch from main

  2. Add commits

  3. Open a pull request

  4. Discuss and review changes

  5. Deploy to test

  6. Merge to main

This approach emphasizes continuous delivery and is well-suited to web applications where you can deploy frequently.

Choosing the Right Strategy for Your Project

The best branching strategy depends on your specific needs:

  • For continuous deployment: Consider GitHub Flow or Trunk-Based Development

  • For scheduled releases: Git Flow may be appropriate

  • For open-source projects: Fork & Pull Request workflows work well

  • For smaller teams: Simplified feature branching might be sufficient

Don't be afraid to adapt these strategies to your team's needs. The goal is to establish clear conventions that facilitate collaboration.

Conclusion: Mastering the Branch

Branching is what truly sets Git apart from older version control systems. By understanding how branches work and adopting thoughtful strategies for their use, your team can develop in parallel with confidence.

Remember these best practices:

  • Keep branches focused on a single task or feature

  • Branch often, merge often

  • Use meaningful branch names that reflect their purpose

  • Delete branches after they're merged

  • Establish clear team conventions for branching and stick to them


In our next article, we'll explore remote repositories and collaboration—how to share your branches with teammates and contribute to shared projects. We'll dive into pushing, pulling, and the mechanics of distributed development that make Git a truly powerful collaborative tool.

0
Subscribe to my newsletter

Read articles from Mikey Nichols directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mikey Nichols
Mikey Nichols

I am an aspiring web developer on a mission to kick down the door into tech. Join me as I take the essential steps toward this goal and hopefully inspire others to do the same!