Skip to content

Git - Managing Branches

Branches in Git are incredibly lightweight and fundamental to modern development workflows. A branch represents an independent line of development. You can use branches to work on new features, bug fixes, or experiments without affecting the main codebase (e.g., the main branch). Once work on a branch is complete, it can be merged back into the main branch.

For example, if you’re releasing version 1.0 of your software from the main branch, you might create a develop branch for version 2.0 features, and separate feature branches off develop. Meanwhile, bug fixes for version 1.0 can be made on a hotfix branch created from main.

Tom wants to create a new branch named new-feature. He can use the git branch <branch-name> command. This creates the new branch pointing to the current commit (HEAD), but doesn’t switch to it.

[tom@dev-machine project/src]$ git branch new-feature
[tom@dev-machine project/src]$ git branch
* main
new-feature

The git branch command lists all local branches. The asterisk * indicates the currently checked-out branch. Visually, if main was at commit C3: ... -> C1 -> C2 -> C3 (HEAD -> main). After git branch new-feature, it becomes: ... -> C1 -> C2 -> C3 (HEAD -> main, new-feature).

Jerry uses the git switch <branch-name> command (or the older git checkout <branch-name>) to switch to an existing branch. This updates the files in the working directory to match the snapshot of the commit the branch points to, and updates HEAD to point to this branch.

[jerry@dev-machine project/src]$ git switch new-feature
Switched to branch 'new-feature'
[jerry@dev-machine project/src]$ git branch
main
* new-feature

Now, HEAD points to new-feature.

A common workflow is to create a new branch and immediately switch to it. You can do this in one step using git switch -c <branch-name> or git checkout -b <branch-name>.

[jerry@dev-machine project/src]$ git switch -c test-branch
Switched to a new branch 'test-branch'
[jerry@dev-machine project/src]$ git branch
main
new-feature
* test-branch

Once a feature branch has been merged and is no longer needed, you can delete it using git branch -d <branch-name>. The -d option will only delete the branch if its changes have been merged. Use -D to force delete an unmerged branch (use with caution).

Jerry is currently on test-branch and wants to delete it. He first switches to another branch (e.g., main).

[jerry@dev-machine project/src]$ git branch
main
new-feature
* test-branch
[jerry@dev-machine project/src]$ git switch main
Switched to branch 'main'
[jerry@dev-machine project/src]$ git branch -d test-branch
Deleted branch test-branch (was 5776472).

Now, git branch will show only main and new-feature.

To rename a branch, use git branch -m <old-name> <new-name>. If you are currently on the branch you want to rename, you can use git branch -m <new-name>.

[jerry@dev-machine project/src]$ git branch
* main
new-feature
[jerry@dev-machine project/src]$ git branch -m new-feature feature/wchar-support
[jerry@dev-machine project/src]$ git branch
* main
feature/wchar-support

Merging takes the changes from one branch and integrates them into another. Typically, you merge a feature branch back into your main development branch (e.g., main or develop).

Jerry has been working on the feature/wchar-support branch and implemented a w_strlen function. After committing his changes to this branch, the history might look like: ...C2 -> C3 (main) -> C4 -> C5 (HEAD -> feature/wchar-support) where C4 and C5 are commits on the feature branch.

He commits his changes on feature/wchar-support:

[jerry@dev-machine src]$ git switch feature/wchar-support
[jerry@dev-machine src]$ # ... makes changes to string_operations.c ...
[jerry@dev-machine src]$ git add string_operations.c
[jerry@dev-machine src]$ git commit -m "Added w_strlen for wide char support"
[feature/wchar-support 64192f9] Added w_strlen for wide char support
1 file changed, 10 insertions(+)
[jerry@dev-machine src]$ git push origin feature/wchar-support

Tom wants to integrate Jerry’s w_strlen function into the main branch. First, Tom ensures his main branch is up-to-date and then checks out main. Then he uses git merge.

[tom@dev-machine project]$ git switch main
Switched to branch 'main'
[tom@dev-machine project]$ git pull origin main # Ensure main is up-to-date
[tom@dev-machine project]$ git fetch origin # Fetch latest changes from remote, including Jerry's branch
[tom@dev-machine project]$ git merge origin/feature/wchar-support
Updating 5776472..64192f9
Fast-forward
src/string_operations.c | 10 ++++++++++
1 file changed, 10 insertions(+)

In this case, Git performed a ‘fast-forward’ merge. This happens when the main branch hasn’t diverged from the feature/wchar-support branch since it was created. main simply moves its pointer forward to the latest commit of feature/wchar-support. The history becomes linear: ...C2 -> C3 -> C4 -> C5 (HEAD -> main, origin/feature/wchar-support).

If main had new commits not present in feature/wchar-support, Git would create a new ‘merge commit’ to tie the two histories together. This commit has two parents.

After testing, Tom pushes the updated main branch to the remote:

[tom@dev-machine project]$ git push origin main

git rebase is another way to integrate changes from one branch to another. Instead of creating a merge commit, rebase rewrites the commit history by taking all commits from your current branch and replaying them, one by one, on top of another branch (the base).

Imagine your main branch has commits A-B-C. You branched off main at C to create my-feature and added commits X-Y. So, my-feature is A-B-C-X-Y. Meanwhile, main has advanced with new commits D-E, so main is A-B-C-D-E.

If you run git rebase main while on my-feature, Git will take your commits X-Y, and re-apply them on top of main’s latest commit E. Your my-feature branch will now look like A-B-C-D-E-X’-Y’ (X’ and Y’ are new commits with the same changes but different IDs because their base changed). This results in a linear history, which some teams prefer.

Example: [jerry@dev-machine project]$ git switch my-feature then [jerry@dev-machine project]$ git rebase main.

Warning: Rebasing rewrites history. Never rebase commits that have already been pushed to a shared remote branch and are being used by others, as it will cause inconsistencies and problems for them. It’s generally safe to rebase your own local branches that haven’t been shared, or to rebase a feature branch onto an updated main/develop branch before merging (often done via git pull --rebase).