Skip to content
ExponaBLOG
GIT & GITHUB FOR PROFESSIONAL DEVELOPERS · DAY 008 / 100

Git branching basics: a Git & GitHub guide for developers

Create, switch and merge branches.

Expona Admin7 min read

By day 8 of your Git and GitHub journey, you've committed code and pushed it upstream. Now it's time to work like the professionals do: creating isolated branches, collaborating without stepping on each other's toes, and merging changes back in a controlled way. Git branching basics: a Git & GitHub guide for developers covers the everyday commands that let teams ship features, fix bugs, and experiment safely on the same repository.

Why branches matter in professional workflows

Imagine your team's main branch is the single source of truth for production code. If every developer pushed directly to main, you'd have half-finished features, broken tests, and deploy disasters every single day. Branches solve this by letting each person work in isolation. A feature branch for login flows, another for payment integration, another for database migrations—all running in parallel, all protected from main until they're ready.

In real projects, branches also enforce code review discipline. On platforms like GitHub, you can't merge a branch to main without approval. Your CI pipeline runs tests. Your team reviews the code. Only then does it land in production. That's the professional model, and it starts with understanding how to create and manage branches locally.

Creating and switching branches with git switch

The modern way to create and switch to a new branch is git switch -c (the -c flag stands for "create"). This is clearer than the older git checkout -b syntax, though both work.

Let's say you're starting work on a login feature. You'd run:

bash
git switch -c feature/login

This command does two things: it creates a new branch called feature/login and immediately switches you onto it. Your HEAD now points to this branch, and any commits you make will be recorded here, not on main.

A few naming conventions help keep teams organized:

  • feature/ for new features (e.g., feature/payment-gateway)
  • bugfix/ for bug fixes (e.g., bugfix/cart-crash-on-mobile)
  • hotfix/ for urgent production fixes (e.g., hotfix/database-connection-leak)
  • refactor/ for code improvements without behavior changes (e.g., refactor/auth-service)
  • docs/ for documentation updates

Using prefixes makes branch history readable and helps automation tools route branches to the right reviewers or deploy pipelines.

If you already have a branch and just need to switch to it (without creating), use git switch without the -c flag:

bash
git switch feature/login

You can only switch if your working directory is clean (no uncommitted changes that would conflict). If you have uncommitted work, either commit it, stash it with git stash, or discard it with git restore.

Listing branches and tracking information

After a few weeks of development, you'll have many branches. The git branch command lists them. The basic form shows local branches:

bash
git branch

Output might look like:

bash
  bugfix/old-bug
  feature/login
  feature/payments
* main
  refactor/database

The asterisk marks your current branch. But this doesn't show you which branches are tracking upstream (on GitHub or your Git server). That's where -vv comes in:

bash
git branch -vv

Output:

bash
  bugfix/old-bug                    a1b2c3d old commit message
  feature/login                     f4e5d6c [origin/feature/login] latest commit message
  feature/payments                  g7h8i9j [origin/feature/payments: ahead 3] commit here
* main                              k0l1m2n [origin/main] up to date
  refactor/database                 o3p4q5r [origin/refactor/database: behind 2] earlier commit

The -vv output tells you:

  • The latest commit hash on each branch
  • The commit message
  • Which remote branch it's tracking (if any)
  • Whether you're ahead, behind, or in sync with the remote

This is invaluable when coordinating with your team. If feature/login shows it's tracking origin/feature/login, your local and remote are in sync. If feature/payments is "ahead 3," you have 3 commits not yet pushed.

To remove branches you no longer need from your view, use git branch -d branch-name (more on this below).

Merging branches to bring changes into main

Once your feature is complete, tested, and approved, you merge it back into main. The standard workflow is:

  1. Switch to main
  2. Pull the latest version from GitHub
  3. Merge your feature branch
  4. Push the result back to GitHub

Here's the concrete sequence:

bash
git switch main
git pull origin main
git merge feature/login
git push origin main

After step 2, your local main is up to date with GitHub. Step 3 applies all commits from feature/login into your current branch (main). If there are no conflicts, Git creates a merge commit that records the integration. Step 4 pushes main back to GitHub.

Fast-forward vs. three-way merges

Git uses different merge strategies depending on history. If your feature branch was created from an older commit on main and main has moved ahead, Git performs a three-way merge: it finds the common ancestor, compares both branches, and creates a new merge commit. If your feature branch is directly ahead of main (no intervening commits on main since you branched), Git performs a fast-forward merge: it just moves the main pointer forward to your feature commit. Both are safe; the difference is in the resulting history.

Some teams prefer to always create an explicit merge commit, even on fast-forward merges, for clearer history:

bash
git merge --no-ff feature/login

The --no-ff flag (no fast-forward) forces Git to create a merge commit regardless.

Handling merge conflicts

If you and a colleague both edited the same lines in the same file, Git can't automatically decide which changes to keep. It stops and asks you to resolve the conflict.

Git marks conflicts in your files like this:

bash
<<<<<<< HEAD
  password: hashedPassword,
=======
  password: password,
>>>>>>> feature/login

You edit the file, keeping the version you want (or both, restructured), then:

bash
git add conflicted-file.js
git commit -m "Resolve merge conflict in auth service"

This completes the merge. In larger projects, conflicts are often resolved in a pull request on GitHub, where both versions are visible side-by-side.

Deleting merged branches

Once a branch is merged, it's typically no longer needed. Leaving old branches around clutters your branch list and can confuse team members. Delete them with:

bash
git branch -d feature/login

The -d flag (delete) only works if the branch is fully merged into your current branch. If you try to delete an unmerged branch, Git refuses:

bash
error: The branch 'feature/login' is not fully merged.

This protection prevents accidental loss of work. If you're absolutely sure you want to discard an unmerged branch, use -D (uppercase):

bash
git branch -D feature/login

You should also delete the remote branch on GitHub so the repository stays clean:

bash
git push origin --delete feature/login

Or, if you've already deleted the local branch and the remote branch is still there (stale tracking branch), prune it with:

bash
git fetch --prune

This removes local references to branches that no longer exist on the remote.

Common mistakes

Merging into the wrong branch

You meant to merge into main but accidentally merged into develop. You were on develop and ran git merge feature/login, and now develop has commits it shouldn't. Use git reset to undo:

bash
git reset --hard HEAD~1

This rewinds develop by one commit (the merge commit). Make sure you're on the right branch before merging next time. A good habit: always confirm your current branch before running a merge.

Deleting a branch that wasn't pushed

You created a branch, made commits, then deleted it locally without pushing to GitHub. The commits still exist in Git's reflog for a while, but the branch is gone from your daily view. Always push feature branches to GitHub before deletion, or at least confirm they're merged into main first.

Switching branches with uncommitted changes

You're on feature/login, you made edits, and you switch to main without committing or stashing. Git either refuses (if there's a conflict) or silently carries your changes to main (which is confusing). Always commit or stash before switching. If you accidentally switched, use git stash to capture your work, switch back to the correct branch, and re-apply it:

bash
git stash
git switch feature/login
git stash pop

Force-pushing a shared branch

Never use git push --force on a branch that others are working on. Force-push rewrites history and can destroy your teammates' commits. It's okay to force-push your own feature branch before the first code review, but once others have branched from it or are reviewing, don't rewrite it.

Key takeaways

  • Use git switch -c branch-name to create and move to a new branch in one command. Follow naming conventions like feature/, bugfix/, and refactor/ for clarity.
  • Run git branch -vv to see which branches track remote versions and whether you're ahead or behind.
  • Merge branches with git switch main, git pull origin main, git merge feature-branch, and git push origin main. Watch for merge conflicts and resolve them by editing the marked sections.
  • Delete merged branches locally with git branch -d and remotely with git push origin --delete to keep the repository tidy.
  • Always commit or stash changes before switching branches. Always confirm your current branch before merging. Never force-push shared branches.
  • Branches isolate work, enable parallel development, and enforce code review discipline—they're the foundation of professional Git workflows.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles