Skip to content

Branching & Merging

Branches let you experiment without breaking your main analysis. You can try a new normalization method, test a different statistical approach, or refactor your code. If it works, you merge it back. If not, you delete the branch and nothing was harmed.

A branch is a parallel line of development. Think of it as a copy of your project where you can make changes independently. Your main branch stays safe while you try something new on a separate branch.

Every Git repository starts with a single branch called main. When you create a new branch, Git makes a pointer to the current commit. Changes on the new branch do not affect main until you explicitly merge them.

main: A --- B --- C
\
new-analysis: D --- E

In this diagram, commits D and E exist only on the new-analysis branch. The main branch still points to commit C.

List all branches in your repository:

Terminal window
git branch

Output:

* main

The asterisk shows which branch you are on.

Create a new branch:

Terminal window
git branch new-analysis

Switch to it:

Terminal window
git switch new-analysis

Or create and switch in one command:

Terminal window
git switch -c new-analysis

Branch whenever you want to try something without risking your working code. Common scenarios in bioinformatics:

  • Testing a different normalization method for your RNA-seq data
  • Trying an alternative clustering resolution for single-cell analysis
  • Refactoring a long script into smaller functions
  • Adding a new analysis step that might not work out
  • Experimenting with different filtering thresholds

The key idea is simple. If the experiment succeeds, merge the branch back into main. If it fails, delete the branch.

Once you switch to a branch, all your normal Git commands work the same way. Edit files, stage them, and commit.

Terminal window
git switch -c try-combat-normalization
# Edit your scripts...
git add normalize.py
git commit -m "Replace quantile normalization with ComBat"
# Make more changes...
git add plot_pca.py
git commit -m "Update PCA plot to show batch correction results"

These commits only exist on the try-combat-normalization branch. If you switch back to main, your files revert to their state before you branched.

Terminal window
git switch main
# normalize.py is back to the original version

When your experiment works and you want to keep the changes, merge the branch into main.

First, switch to the branch you want to merge into:

Terminal window
git switch main

Then merge:

Terminal window
git merge try-combat-normalization

If nothing changed on main while you were working on your branch, Git performs a “fast-forward” merge. It simply moves the main pointer forward to include your new commits.

Before merge:
main: A --- B --- C
\
try-combat: D --- E
After fast-forward merge:
main: A --- B --- C --- D --- E

If main had new commits too, Git creates a merge commit that combines both lines of work.

Before merge:
main: A --- B --- C --- F
\
try-combat: D --- E
After merge:
main: A --- B --- C --- F --- M
\ /
try-combat: D --- E

Commit M is the merge commit. It has two parents: F and E.

Conflicts happen when two branches changed the same line in the same file. Git cannot decide which version to keep, so it asks you.

When a conflict occurs, Git marks the file like this:

<<<<<<< HEAD
result = normalize_quantile(counts)
=======
result = normalize_combat(counts, batch)
>>>>>>> try-combat-normalization

The section between <<<<<<< HEAD and ======= is the version on your current branch. The section between ======= and >>>>>>> is the version from the branch you are merging.

To resolve the conflict:

  1. Open the file and decide which version to keep. Or write a new version that combines both.
  2. Remove the conflict markers (<<<<<<<, =======, >>>>>>>).
  3. Stage the resolved file and commit.
Terminal window
# After editing the file to resolve the conflict
git add normalize.py
git commit -m "Merge combat normalization, resolve conflict in normalize.py"

After merging, you no longer need the branch. Delete it to keep things tidy:

Terminal window
git branch -d try-combat-normalization

The -d flag only works if the branch has been merged. This protects you from accidentally deleting unmerged work. If you are sure you want to delete an unmerged branch, use -D instead.

Sometimes you need to switch branches but you have uncommitted changes. Git will not let you switch if the changes would conflict with the other branch.

git stash saves your uncommitted changes temporarily:

Terminal window
# You're in the middle of editing, but need to check something on main
git stash
# Switch branches freely
git switch main
# ... do your work ...
git switch try-combat-normalization
# Bring your changes back
git stash pop

git stash works like a stack. You can stash multiple times. git stash list shows all stashed changes. git stash pop restores the most recent stash.

You do not need a complex branching strategy. This workflow covers most bioinformatics projects:

  1. Keep main for code that works. Every commit on main should be a version you could hand to a collaborator.
  2. Create a branch for each new experiment or analysis variation.
  3. Commit frequently on your branch as you develop.
  4. When the analysis works, merge it back into main.
  5. Delete the branch after merging.
Terminal window
# Start a new experiment
git switch -c test-different-clustering
# Work on it...
git add cluster_analysis.py
git commit -m "Try Leiden clustering with resolution 0.8"
git add cluster_analysis.py
git commit -m "Add cluster marker gene identification"
# It works! Merge it.
git switch main
git merge test-different-clustering
git branch -d test-different-clustering

This keeps your main branch clean and your experiments organized. If you ever need to go back to a previous approach, the commit history shows you exactly what changed and when.

  • Branches let you experiment without affecting your main code.
  • Use git switch -c branch-name to create and switch to a new branch.
  • Use git merge branch-name to bring changes back into main.
  • Merge conflicts happen when two branches edit the same line. Resolve them manually, then commit.
  • Use git stash to temporarily save uncommitted changes when switching branches.
  • Keep it simple: one branch per experiment, merge when it works, delete when done.