Branching & Merging in Git

16 min read·Jan 1, 2025

In Git, a branch is a lightweight and isolated line of development that allows developers to concurrently work on new features, bug fixes, or experiments without affecting the main codebase.

The master branch

The master branch, nowadays called the main branch, is the default main line of development in a Git repository.

It is automatically created by Git when initializing a new repository.

It contains the production-ready code that represents the latest stable released version of the software.

When a set of features or bug fixes are deemed ready for release, the temporary branches they were developed on are then merged (or integrated) into the main branch, so that the new code becomes part of the main branch's history.

This history provides a clear and organized record of the project's evolution over time, and represents the chronological history of stable releases and major changes in the project.

Note: While master and main are the two most common names for this branch, Git doesn't enforce a specific name.

Listing existing branches

To get the list of existing branches, you can use the git branch command without arguments:

$ git branch
  development
* master

Where the branch marked with an asterisk * represents the current branch in use, referred to as the checked out branch.

Creating a new branch

In essence, a branch is a type of "sandbox" environment to which you can make changes without affecting other branches.

A branch is always derived from another existing branch, and starts as a copy of this branch containing its entire commit history.

To create a new branch from the currently checked out branch, you can use the git branch command:

$ git branch <branch_name>

Where branch_name is the name of the new branch.

Example

For example, considering that the currently checked out branch is the main branch:

$ git branch
* main

This command will create a new branch named development, that contains the same files, directories, and commit history as the main branch:

$ git branch development
$ git branch
  development
* main

Creating a new branch off a specific commit

By default, each branch is created from the currently checked out branch, starting at the commit referenced by the HEAD pointer.

To create a new branch starting at specific commit instead, you can use the git branch command with the following syntax:

$ git branch <branch_name> <commit_hash>

Where:

  • branch_name is the name of the new branch.
  • commit_hash is the hash of the commit to branch from.

Example

For example, considering the following commit history:

$ git log --oneline
e97698a (HEAD -> master) third commit
cd2bbfe second commit
9e01fd9 first commit

This command will create a new branch named development starting at commit 9e01fd9:

$ git branch development 9e01fd9

Which means that this branch will only contain the first commit in its history.

Branch naming conventions

When creating branches, it is common to use descriptive names that help identify their purpose without having to look at their commit history.

Moreover, standardized branch names can be linked to project management tools, such as Github Issues or Jira, making it easier to connect code changes to specific tasks and tickets.

The most common naming convention for branches is the following one:

<type>/[reference/]<title>

Where:

  • type is a word describing the type of branch (e.g. feature).
  • reference is an optional identifier that references a ticket on a project management platform (e.g. issue-5132).
  • title is the name of the branch in kebab case (e.g. add-signup-form).

For example:

feature/add-signup-form
release/rc-3.2.1
bugfix/issue-5142/fix-database-encryption

Common branch types

Some of the most common types of branches include:

  • Release branches: they are prefixed with the release keyword and are used for preparing a new software release. They often regroup the commits of several branches.
  • Feature branches: they are prefixed with the feature keyword and are used for developing new software features or enhancements.
  • Bugfix branches: they are prefixed with the bugfix keyword and are used to address and fix specific bugs or issues in the software.
  • Hotfix branches: they are prefixed with the hotfix keyword and are used for emergency fixes to critical issues or bugs in the production code.

Switching between branches

As of Git version 2.23, to switch from the current branch to another branch, you can use the git switch command:

$ git switch <branch_name>

Where branch_name is the name of the branch you want to switch to.

Example

For example, the following command will switch from the master branch to the development branch:

$ git branch
  development
* master
$ git switch development
Switched to branch 'development'

Creating and switching to a new branch

To simultaneously create a new branch and switch to it, you can use the git switch command combined with the -c flag (short for --create):

$ git switch -c <branch_name>

Merging branches

In Git, merging a branch essentially consists in integrating the commits of a branch into another branch.

To merge a branch, you must first check out the branch you want to integrate the changes into using the git switch command:

$ git switch <target_branch>

Then integrate the commits of the desired branch using the git merge command:

$ git merge <source_branch>

Upon success, Git will output the list of changed files and append the commits of the source branch to the history of the target branch.

Example

Let's consider the feature/web-server branch, that contains 4 more commits than the main branch:

$ git branch
* feature/web-server
  main
$ git log --oneline
1f4f215 (HEAD -> feature/web-server) chore: add node_modules directory to gitignore file
f912db2 feat: create server loader module
ed13f9c feat: add express package
dc33745 feat: add app manifest
ae987a6 (main) feat: initial commit

To integrate the commits of the feature/web-server branch into the main branch, we must first switch to the main branch:

$ git switch main
Switched to branch 'main'

Then merge the commits of the feature/web-server branch, which will output the summary of files and changes:

$ git merge feature/web-server
Updating ae987a6..1f4f215
Fast-forward
 .gitignore        |   1 +
 index.js          |   4 +-
 package-lock.json | 711 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 package.json      |  14 +++
 server.js         |  13 +++
 5 files changed, 742 insertions(+), 1 deletion(-)
 create mode 100644 .gitignore
 create mode 100644 package-lock.json
 create mode 100644 package.json
 create mode 100644 server.js

Resulting in the HEAD pointer of the main branch being moved from the commit ae987a6 to the commit 1f4f215:

$ git log --oneline
1f4f215 (HEAD -> main, feature/web-server) chore: add node_modules directory to gitignore file
f912db2 feat: create server loader module
ed13f9c feat: add express package
dc33745 feat: add app manifest
ae987a6 feat: initial commit

Resolving merge conflicts

When attempting to merge two branches, it may happen that Git is unable to automatically reconcile the branches' changes.

These conflicts often happen because both branches have made changes to the same part of a file, making it unclear which changes should be kept.

Step 1: Merging branches

In this context, when executing the git merge command, Git will pause the merge process and prompt you to resolve the conflicts manually in the listed files.

$ git merge feature/web-server
Auto-merging index.js
CONFLICT (content): Merge conflict in index.js
Automatic merge failed; fix conflicts and then commit the result.

Step 2: Reviewing conflicts

Within these files, Git will mark the conflicting changes with markers, such as <<<<<<<, =======, and >>>>>>>:

$ cat index.js
const server = require('./server.js');

<<<<<<< HEAD
server({ port: 8000 });
=======
server({ port: 3000 });
>>>>>>> feature/web-server

Where:

  • The content between <<<<<<< and ======= represents your branch's changes.
  • The content between ======= and >>>>>>> represents the incoming changes from the other branch.

Step 3: Resolving conflicts

To resolve a conflict, you need to open the file pointed out by Git using your preferred text editor:

$ vim index.js

And decide which changes to keep by deleting the conflict markers and the unwanted lines, and saving the file:

const server = require('./server.js');

server({ port: 8000 });

Step 4: Committing changes

To indicate to Git that the conflicts have been resolved, stage and commit the file using the git add and git commit commands:

$ git add index.js
$ git commit -m "chore: resolve merge conflict on server port"
[main a359227] chore: resolve merge conflict on server port

Finally, if you were in the middle of a merge operation when conflicts occurred, you can continue the merge by running the git merge command with the --continue flag:

$ git merge --continue

Which will finalize the merge and create a merge commit.

Deleting branches

By nature, most branches are short-lived and meant to be deleted over time.

This could be because a feature has been completed and integrated into the main branch or because a feature has been discarded, among other things.

To delete a branch whose changes have been merged into another branch, you can use the git branch command with the -d flag (short for --delete):

$ git branch -d <branch_name>

Alternatively, to forcefully delete a branch with unmerged changes, you can use the -D flag, as Git will otherwise throw an error preventing you from deleting it:

$ git branch -D <branch_name>

Example

This command will delete the feature/web-server branch from the previous example, whose commits are already merged into the main branch:

$ git branch -d feature/web-server
Deleted branch feature/web-server (was e2e1613).
$ git branch
* main

Summary

Here's a summary of what you've learned in this lesson:

  • The master branch (or main) is the default main line of development in a Git repository.
  • The git branch command is used to list the existing branches.
  • The git branch <branch_name> command is used to create a new branch.
  • The git branch <branch_name> <commit_hash> command is used to create a new branch off a specific commit.
  • The <type>/[reference/]<title> is the most common branch naming convention.
  • The git switch <branch_name> command is used to check out an existing branch.
  • The git merge <source_branch> command is used to merge a branch into the currently checked out branch.
  • The git branch -d <branch_name> command is used to delete a branch with merged changes.
  • The git branch -D <branch_name> command is used to delete a branch with unmerged changes.

Enjoying the courses?

I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:

Support BackendBrewery
Branching & Merging in Git | Backend Brewery