Complete Guide to Git Branching: Master Development Workflows
What is a Git Branch?
In Git, a branch is like a separate workspace where you can make changes and try new ideas without affecting the main project. Think of it as a "parallel universe" for your code.
Why is this concept important? Without branches, all developers would work directly on the same codebase, leading to conflicts, broken features, and difficulty in tracking changes. Branches provide isolation, allowing multiple people to work simultaneously on different features or fixes.
Simple Analogy: Imagine you're writing a book. The main branch is your published book that people are reading. A feature branch is like writing a new chapter in a separate notebook. You can edit, rewrite, and perfect it without affecting the published book. Only when it's perfect do you add it to the main book.
Why Use Branches?
Branches let you work on different parts of a project, like new features or bug fixes, without interfering with the main branch. Here are the key reasons:
- Isolation: Keep experimental or incomplete work separate from stable code
- Collaboration: Multiple developers can work on different features simultaneously
- Risk Management: Test new ideas without breaking production code
- Organization: Keep different types of work (features, bugs, experiments) organized
- Release Management: Prepare releases while continuing development
Common Reasons to Create a Branch
Understanding when to create branches helps in maintaining a clean workflow:
- Developing a new feature: Each feature gets its own branch
- Fixing a bug: Isolate bug fixes from feature development
- Experimenting with ideas: Try new approaches without risk
- Preparing a release: Stabilize code for deployment
- Refactoring code: Improve code structure without affecting functionality
Real-World Example: With and Without Git Branches
Let's say you have a large project, and you need to update the design on it. How would that work without and with Git:
Without Git Branches (The Messy Way)
Imagine you're working on a website redesign:
- Make copies of all the relevant files to avoid impacting the live version
- Start working with the design and find that code depends on code in other files, that also need to be changed!
- Make copies of the dependent files as well. Making sure that every file dependency references the correct file name
- EMERGENCY! There is an unrelated error somewhere else in the project that needs to be fixed ASAP!
- Save all your files, making a note of the names of the copies you were working on
- Work on the unrelated error and update the code to fix it
- Go back to the design, and finish the work there
- Copy the code or rename the files, so the updated design is on the live version
- Two weeks later: You realize that the unrelated error was not fixed in the new design version because you copied the files before the fix
With Git Branches (The Smart Way)
Now let's see how Git branches solve these problems:
- With a new branch called
new-design, edit the code directly without impacting the main branch - EMERGENCY! There is an unrelated error somewhere else in the project that needs to be fixed ASAP!
- Create a new branch from the main project called
small-error-fix - Fix the unrelated error and merge the
small-error-fixbranch with the main branch - You go back to the
new-designbranch, and finish the work there - Merge the
new-designbranch with main (getting alerted to the small error fix that you were missing)
Key Takeaway: Branches allow you to work on different parts of a project without impacting the main branch. When the work is complete, a branch can be merged with the main project. You can even switch between branches and work on different projects without them interfering with each other. Branching in Git is very lightweight and fast!
Creating a New Branch: Step-by-Step Guide
Let's say you want to add a new feature to your website. You can create a new branch for it.
Why create a branch? We are working in our local repository, and we do not want to disturb or possibly wreck the main project. Branches give us a safe space to experiment.
Basic Branch Creation
So we create a new branch:
git branch hello-world-images
What happens here? Git creates a new pointer (branch) called "hello-world-images" that points to the same commit you're currently on. No files are copied or moved - Git just creates a new reference.
Now we created a new branch called "hello-world-images", but we're still on our original branch. To start working in the new branch, we need to switch to it.
Listing All Branches: See What You Have
Let's confirm that we have created a new branch. To see all branches in your repository, use:
# Output will look like:
hello-world-images
* master
Understanding the output: We can see the new branch with the name "hello-world-images", but the * beside master specifies that we are currently on that branch. The asterisk (*) always indicates your current branch.
Switching Between Branches: Moving Workspaces
checkout is the command used to check out a branch. Moving us from the current branch, to the one specified at the end of the command:
git checkout hello-world-images
# Output:
Switched to branch 'hello-world-images'
What this does: Git updates your working directory to reflect the state of the hello-world-images branch. If there are files that exist in hello-world-images but not in master, they'll appear. If there are files in master that don't exist in hello-world-images, they'll disappear from your working directory (but they're still safe in the master branch).
Now you can work in your new branch without affecting the main branch. Any changes you make will only exist in the hello-world-images branch until you merge it.
Working in a Branch: Making Changes Safely
Now we have moved our current workspace from the master branch, to the new branch. Open your favourite editor and make some changes.
For this example, we added an image (img_hello_world.jpg) to the working folder and a line of code in the index.html file:
<html>
<head>
<title>Hello World!</title>
<link rel="stylesheet" href="bluestyle.css">
</head>
<body>
<h1>Hello world!</h1>
<div><img src="img_hello_world.jpg" alt="Hello World from Space"
style="width:100%;max-width:960px"></div>
<p>This is the first file in my new Git Repo.</p>
<p>A new line in our file!</p>
</body>
</html>
We have made changes to a file and added a new file in the working directory (same directory as the main branch). Now check the status of the current branch:
# Output:
On branch hello-world-images
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: index.html
Untracked files:
(use "git add ..." to include in what will be committed)
img_hello_world.jpg
no changes added to commit (use "git add" and/or "git commit -a")
Understanding the status: Git is telling us two important things:
- We modified index.html but haven't staged it for commit
- img_hello_world.jpg is a new file that Git isn't tracking yet
So we need to add both files to the Staging Environment for this branch:
git add --all
What --all does: Using --all instead of individual filenames will Stage all changed (new, modified, and deleted) files. This is convenient when you have multiple changes.
Check the status of the branch again to see what changed:
# Output:
On branch hello-world-images
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: img_hello_world.jpg
modified: index.html
Great! Now both files are staged and ready to be committed. We are happy with our changes. So we will commit them to the branch:
git commit -m "Added image to Hello World"
# Output:
[hello-world-images 0312c55] Added image to Hello World
2 files changed, 1 insertion(+)
create mode 100644 img_hello_world.jpg
What just happened: Git created a new commit in the hello-world-images branch with our changes. The commit has a unique hash (0312c55) that identifies it. Now we have a new branch that is different from the master branch.
Pro Tip: Using the -b option on checkout will create a new branch, and move to it, if it does not exist. This is a time-saver! Example: git checkout -b branch-name does both creation and switching in one command.
Switching Between Branches Example: Seeing Isolation in Action
Now let's see just how quick and easy it is to work with different branches, and how well it works.
We are currently on the branch hello-world-images. We added an image to this branch, so let's list the files in the current directory:
# Output:
README.md bluestyle.css img_hello_world.jpg index.html
We can see the new file img_hello_world.jpg, and if we open the html file, we can see the code has been altered. All is as it should be.
Now, let's see what happens when we change branch to master:
git checkout master
# Output:
Switched to branch 'master'
The new image is not a part of this branch. List the files in the current directory again:
# Output:
README.md bluestyle.css index.html
img_hello_world.jpg is no longer there! And if we open the html file, we can see the code reverted to what it was before the alteration.
Why this is powerful: See how easy it is to work with branches? And how this allows you to work on different things? You can have multiple versions of your project coexisting without interfering with each other.
Emergency Branch Example: Handling Urgent Fixes
Now imagine that we are not yet done with hello-world-images, but we need to fix an error on master.
The problem: I don't want to mess with master directly (that could break things), and I do not want to mess with hello-world-images (since it's not done yet and might have incomplete code).
The solution: So we create a new branch to deal with the emergency:
git checkout -b emergency-fix
# Output:
Switched to a new branch 'emergency-fix'
Now we have created a new branch from master, and changed to it. We can safely fix the error without disturbing the other branches.
Let's fix our imaginary error. We make changes in a file, and we need to get those changes to the master branch. First, check the status:
# Output:
On branch emergency-fix
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Stage the file, and commit:
git add index.html
# Commit the fix with a clear message
git commit -m "updated index.html with emergency fix"
# Output:
[emergency-fix dfa79db] updated index.html with emergency fix
1 file changed, 1 insertion(+), 1 deletion(-)
Now we have a fix ready for master. Later, we'll merge this branch into master to deploy the fix, and also merge it into hello-world-images so our design work includes the fix.
Deleting a Branch: Cleaning Up
When you're done with a branch, you can delete it to keep your repository clean:
git branch -d hello-world-images
Important: This deletes the branch named hello-world-images, but only if it's already merged into another branch (usually master). Git protects you from accidentally deleting unmerged work.
Best Practices for Working with Branches
Following these practices will make your Git workflow smooth and efficient:
- Use clear, descriptive branch names: Like
feature/login-pageorbugfix/header-crash. This helps everyone understand what the branch is for. - Keep each branch focused on a single purpose or feature: Don't mix unrelated changes in one branch.
- Regularly merge changes from the main branch: This keeps your branch up-to-date and reduces merge conflicts.
- Delete branches that are no longer needed: This keeps your repository clean and organized.
- Keep branches short-lived: Try to merge within a few days to avoid complex conflicts.
- Use pull requests for code review: Before merging to main, have someone review your changes.
Practical Command Reference
Create and Switch to New Branch
git checkout -b feature/new-feature
# What this does: Creates branch AND switches to it
# Equivalent to: git branch feature/new-feature && git checkout feature/new-feature
List All Branches
git branch
# See all branches including remote ones
git branch -a
# See branches with last commit message
git branch -v
Switch Between Branches
git checkout branch-name
# Modern alternative (Git 2.23+)
git switch branch-name
# Quick switch to previous branch
git checkout -
Delete Branches
git branch -d old-branch
# Force delete unmerged branch (use with caution!)
git branch -D experimental-branch
# Delete remote branch
git push origin --delete remote-branch
Other Useful Commands
git branch -m new-name
# See which branch you're currently on
git status
# See what changed between branches
git diff main..feature-branch
Troubleshooting Common Issues
Problem: "If you don't see your changes on the main branch, remember: changes in one branch stay there until you merge them."
Solution: You need to explicitly merge your feature branch into main. Changes don't automatically appear everywhere.
Problem: "When deleting a branch, make sure it's merged first. If you try to delete an unmerged branch, Git will prevent you from doing so."
Solution: Either merge the branch first, or use git branch -D to force delete (but be sure you won't need the work).
Problem: "To force delete an unmerged branch, use git branch -D branch-name."
Warning: This permanently deletes unmerged work. Use only when you're certain the branch content is no longer needed.
Frequently Asked Questions
git checkout is the traditional command that does multiple things: it can switch branches, restore files, or create new branches with the -b option. This versatility can sometimes be confusing for beginners.
git switch was introduced in Git 2.23 as a more focused command specifically for switching between branches. It only does branch switching, making it simpler and less error-prone.
Recommendation: If you have Git 2.23 or newer, use git switch for switching branches and git restore for restoring files. This separation of concerns makes commands more intuitive.
You can recover a deleted branch if you know its last commit hash. Here's how:
- Use
git reflogto see recent branch operations and find the commit hash of your deleted branch - Create a new branch from that commit:
git branch recovered-branch commit-hash
Alternatively, if the branch was pushed to a remote repository (like GitHub), you can fetch it back:
Prevention tip: Always double-check before deleting branches, especially with the -D flag.
Generally, no. Here's why:
- The main branch should represent stable, production-ready code
- Direct commits bypass important safeguards like code review and testing
- It increases the risk of breaking production code
- It makes it harder to track who made what changes and why
Best practice: Use feature branches for all development work. Merge them to main via pull requests after thorough review and testing. This maintains code quality and provides a clear audit trail of all changes.
Focus on one branch at a time for maximum productivity. Context switching between multiple branches can lead to confusion and errors.
However, in real-world scenarios, you might need to:
- Switch to review someone else's code
- Fix an urgent bug while working on a feature
- Help a teammate with their branch
Practical guideline: Try to have no more than 2-3 active branches per developer. Keep branches short-lived (merge within 1-2 days) to avoid complex conflicts and reduce mental overhead.
The best strategy depends on your team size and project:
- Small teams (2-5 people): GitHub Flow - simple feature branches merged via pull requests
- Medium teams (5-15 people): GitLab Flow - environment branches (dev, staging, prod) with upstream merging
- Large teams (15+ people): GitFlow - structured with develop, feature, release, and hotfix branches
- Advanced teams: Trunk-based development with feature flags
Start simple: Most teams begin with GitHub Flow and evolve as needed. The key is consistency - everyone should follow the same strategy.
For large features, break them into smaller, manageable pieces:
- Create a main feature branch (e.g.,
feature/ecommerce-redesign) - Create sub-branches for individual components (e.g.,
feature/ecommerce-cart,feature/ecommerce-checkout) - Merge sub-branches into the main feature branch as they're completed
- Regularly merge the main branch into your feature branch to stay updated
Advanced technique: Consider using feature flags to hide incomplete functionality while allowing incremental merging to main. This keeps the main branch updated and reduces merge conflicts.
Key principle: "Small, frequent merges are better than one big merge."