Git is a very powerful tool and It has a long list of capabilities available at your disposition. Some commands provide slightly similar results and it is worth understanding and comparing them in order to choose the one that satisfies your needs and improves your workflow. This post is going to define and compare the following commands: revert, checkout, reset, merge, and rebase.
These tools allow you to undo and manipulate changes in your repository. The problem is that they are very similar, therefore it is really easy to get them confused. We will define each of them in detail and list their similarities and differences.
This is an unconventional and safe way to revoke an operation as it prevents you from losing history. Git revert inverts the changes that were added in a commit and adds a new commit with the inverted content. This command is especially helpful when you have already pushed your changes to a remote repository as it keeps your original commit intact.
To revert using commit hashes:
git revert [SHA]
To revert using ranges:
git revert HEAD~[num-of-commits-back]
This is a versatile tool, it allows you to switch in between branches, inspect older commits, and undo local uncommitted changes by moving HEAD and updating your working directory accordingly.
To switch in between branches:
git checkout [branch-name]
To inspect an older commit using commit hashes:
git checkout [SHA]
To inspect an older commit using ranges:
git checkout HEAD~[num-of-commits-back]
- When moving HEAD to a different branch or commit, you are going to need to commit or stash any uncommitted changes that could be lost.
- You will be in a detached HEAD state by switching to an old commit, which is great for inspection but not for modifications. In other words, you are not in your branch anymore so don’t add any commits on a detached HEAD because you are going to lose them when you switch to another branch. If you are planning to commit changes on a detached HEAD, make sure to create a new branch from it.
To undo all local uncommitted changes:
git checkout .
To undo a specific local uncommitted change:
git checkout -- [file-name]
This is a powerful but complex way to undo an operation. There are three available arguments:
This is the default mode so
git reset --mixed is the same as
git reset. By using any of these commands you will move HEAD to the latest commit and all the changes that were added after that commit will be available as untracked changes in your working directory.
This operation is similar to the one executed using the --mixed argument. HEAD is moved to the latest commit, however, the changes that were added after that commit are left staged.
Use the command
git reset --hard only when you know what you are doing as it is very dangerous. By resetting the hard way, you will move HEAD to the latest commit and destroy the changes that were added after. This cannot be undone so use it wisely.
You should not use
git reset when you have already pushed to a remote repository. Removing a commit that other team members have continued building upon is hard and it would disrupt your team member’s workflow.
Bonus action: Use
git reset -- [file-name] to unstage a file that hasn't been committed yet.
When multiple people are working in a project you will need to combine your code at some point. Git rebase and merge have different approaches to consolidate the changes from one branch into another one.
It uses a non-destructive operation to combine two branches’ histories without changing them. In addition, it creates a new “merge commit” every time it’s used. This is a great way to consolidate changes, however your commit history could get several merge commits depending on how active your master branch is, which can get cumbersome.
To combine the latest changes from master into your branch:
git checkout [branch-name]
git merge master
This is a destructive operation as it moves an entire branch’s history on top of another one by rewriting the project history with new commits.
This command is useful if you prefer a clean and linear project history. However, it is not safe to rebase on changes that have been pushed to a remote repository master branch because you would be changing the master branch’s history while your team members continue working on a diverged version of it.
In addition, Git will not allow you to easily push a rebased branch back to a remote repository. You are going to need to force it by using
git push --force, which overwrites the remote branch and could cause issues with other collaborators. In other words, stop yourself from confusing your team members and creating tedious conflicts by only using rebase to cleanup in-progress features and not the master branch.
I hope you found these comparisons helpful and easy to follow. I explore, define, and explain git concepts every Saturday!
If you are new to git, checkout my previous post Git Explained: The Basics.