Advanced Git commands you should know

Kelvin Salton do Prado
8 min readJun 18, 2022
Photo by Yancy Min on Unsplash

Every software developer should have a good understanding of git, at least the basic commands like pull, push, add, commit, checkout, reset, status, diff and branch. However, only a few people know some specific and more advanced commands like revert, stash, rebase, squash and cherry-pick. In this article, you’ll learn some advanced git commands and how to use them in practice.

git revert

Let’s start with the simpler one: git revert

The git revert command is similar to an ‘undo’ operation however, instead of removing the commit from git history, it will create a new commit reverting the changes introduced in the previous one, preventing git from losing history information.

To illustrate the git revert usage, create a new git project and add some files and commits to it, for example:

$ mkdir learning-git
$ cd learning-git
$ git init
$ git checkout -b main
$ touch README.md
$ git add README.md
$ git commit -m "Add README"
$ echo "HELLO" > README.md
$ git commit -am "Change README"
$ touch CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING"

You can use git log to see the commit logs:

$ git log --oneline55d4942 (HEAD -> main) Add CONTRIBUTING
4ec43a9 Change README
92ee1e4 Add README

Now, suppose you want to revert the changes introduced in the commit 4ec43a9. You can simply run the following command:

$ git revert 4ec43a9

If you run git log again you will see that a new commit 0ce4338 has been created, which reverts the commit 4ec43a9:

$ git log --oneline0ce4338 (HEAD -> main) Revert "Change README"
55d4942 Add CONTRIBUTING
4ec43a9 Change README
92ee1e4 Add README

If you don’t want changes to be committed automatically, it is possible to use the --no-commit flag, for example:

$ git revert 4ec43a9 --no-commit

This flag is useful when you want to create a single commit that reverts multiple ones or want to manually edit the reverted changes.

git stash

The git stash command temporarily stashes changes from your working copy so you can work on other things and come back later re-applying the changes. It will become clearer with the following example.

To understand how it works, create a new git project with a README.md file:

$ mkdir learning-git
$ cd learning-git
$ git init
$ git checkout -b main
$ touch README.md
$ git add README.md
$ git commit -m "Add README"

Now, suppose you are editing this file:

$ echo "Learning Git" > README.md

But in the middle of editing the README.md file a new demand has arrived and now you need to create a CONTRIBUTING.md file. You need to stop editing the README.md file to create the CONTRIBUTING.md but you’re not ready to commit your changes. What can you do? You can use the stash command as follows:

$ git stash
Saved working directory and index state WIP on main: aed0b08 Add README

If you run git stash list you’ll see the stash entries you currently have:

$ git stash list
stash@{0}: WIP on main: aed0b08 Add README

Now you’re ready to work on the CONTRIBUTING.md file:

$ touch CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING"

Once you finished creating the CONTRIBUTING.md file you can reapply previously stashed changes:

$ git stash pop

git stash pop will remove the changes from the stash and reapplies it to the working copy. Alternatively it is possible to use git stash apply to reapply the changes to the working copy without removing it from the stash.

Now, finish editing the README.md file, commit it and see how the commit logs will look like:

$ echo "The end" >> README.md
$ git add README.md
$ git commit -m "Finish README"
$ git log --oneline53ad28b (HEAD -> main) Finish README
9c086c0 Add CONTRIBUTING
aed0b08 Add README

Note that git stash will not stash new files that have not yet been staged or files that have been ignored.

As you can see git stash is very useful when you need to quickly switch context to work on something else but you’re not ready to commit you current changes.

For more information about git stash, visit the Atlassian: Git Stash tutorial.

git rebase

git rebase is one of the two ways of integrating changes from one branch to another. The other way is using git merge.

Rebasing is the process of moving or combining a sequence of commits to a new base commit.

git rebase is very useful when working with feature branches. It consists of changing the base of your branch from one commit to another, making it appear that you have created your branch from a different commit. You will understand it better with a practical example.

Supposing you have a new git project:

$ mkdir learning-git
$ cd learning-git
$ git init
$ git checkout -b main
$ touch README.md
$ git add README.md
$ git commit -m "Add README"

Now, create a new branch to work on a feature X:

$ git checkout -b feature-x$ touch feature-x.txt
$ git add feature-x.txt
$ git commit -m "Add Feature X"

Now, consider a situation where the main branch has progressed since you started working on feature-x branch:

$ git checkout main$ echo "My Awesome Project" > README.md
$ git add README.md
$ git commit -m "Improve README"
$ git log --onelinea22391f (HEAD -> main) Improve README
c31375f Add README

Going back to the feature-x branch, you can see it is not up to date with the main branch. You could apply a git merge to update it but you want to keep your history clean, so it appears as if you’ve been working off the latest commit from the main branch. To accomplish that you can use git rebase:

$ git checkout feature-x
$ git log --oneline
6f89d7c (HEAD -> feature-x) Add Feature X
c31375f Add README
$ git rebase main
$ git log --oneline
d11727a (HEAD -> feature-x) Add Feature X
a22391f (main) Improve README
c31375f Add README

⚠️️ But be careful:

you should never rebase commits once they’ve been pushed to a public repository. The rebase would replace the old commits with new ones and it would look like that part of your project history abruptly vanished.

For more details about the git rebase command, I recommend taking a look at the Atlassian: Git Rebase tutorial.

squash

Sometimes, when working on a new feature, you create a lot of commits to keep track of your changes and be able to go back and forth in your changes as needed, so you end up with commits like these:

  • Add new section “Examples” to the README
  • Add new example to the “Examples” section
  • Fix typo in the “Examples” section
  • Change “Examples” section to “Usage”

Then you need to merge your changes (via pull request) to the main branch of the project. A good practice would be to squash these commits into a single one, for example:

  • Add new section “Usage” to the README

This simple habit will keep your project history clean and your commits and changes easier to find.

To demonstrate how it works, create a new git project:

$ mkdir learning-git
$ cd learning-git
$ git init
$ git checkout -b main
$ touch README.md
$ git add README.md
$ git commit -m "Add README"
$ echo "Hello" > README.md
$ git commit -am "Add Hello to README"
$ echo "World" >> README.md
$ git commit -am "Add World to README"
$ git log --oneline05e88b3 (HEAD -> main) Add World to README
71ada17 Add Hello to README
aeaec9d Add README

As you can see there are 3 commits that could be squashed to a single one. To squash these commits use the following command:

$ git rebase -i --root

Also, if you want to squash only the last 2 commits you can use this command:

git rebase -i HEAD~2

After running git rebase -i — root, it will open your default text editor so you will be able to choose which commits you want to squash, for example:

pick aeaec9d Add README
pick 71ada17 Add Hello to README
pick 05e88b3 Add World to README

# Rebase 05e88b3 onto 4ca655b (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
...

You can squash all commits into the first commit as shown below:

pick aeaec9d Add README
squash 71ada17 Add Hello to README
squash 05e88b3 Add World to README

When you save the file, git will open your commit message to edit but you can just save it. Now, if you run git log you will see something like this:

$ git logcommit 6dfbbc7d965cf777edbff602fde090b52a7c02e4 (HEAD -> main)
Author: Kelvin <xxx>
Date: Sat Oct 12 15:00:12 2042 -0300
Add READMEAdd Hello to READMEAdd World to README

As you can see, the 3 commits were squashed into a single one.

git cherry-pick

git cherry-pick is a powerful git command that allows you to pick specific commits from a branch and apply it to another branch.

It is very useful when you accidentally made a commit to the wrong branch, so you can switch to the correct branch and cherry-pick the commit.

Suppose you have the following git project and commits:

$ mkdir learning-git
$ cd learning-git
$ git init
$ git checkout -b main
$ echo "Hello Wolrd" > README.md
$ git add README.md
$ git commit -m "Add README"

Then your coworker starts working on feature 1 and notices that there is a typo in the README file:

$ git checkout -b feature1$ echo "Contrib" > CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING"
$ echo "Hello World" > README.md
$ git commit -am "Fix typo in the README"
$ echo "Contributing" > CONTRIBUTING.md
$ git commit -am "Change CONTRIBUTING text"

If you take a look into this branch log you will see the following commits:

$ git log --onelineb9c57a2 (HEAD -> feature1) Change CONTRIBUTING text
3b50155 Fix typo in the README
c89ec40 Add CONTRIBUTING
cc7fb72 (main) Add README

But meanwhile you started working on another branch called feature2:

$ git checkout main
$ git checkout -b feature2
$ echo "print('Hello World')" > main.py
$ git add main.py
$ git commit -m "Create main.py"

And for some reason you want to get the commit 3b50155 from your coworker branch (feature1). You can do that using git cherry-pick:

$ git cherry-pick 3b50155

Then, if you run git log again you will see something like this:

$ git log --onelinee318bc6 (HEAD -> feature2) Fix typo in the README
7ae0fd7 Create main.py
cc7fb72 (main) Add README

As you can see, it picked up the changes from commit 3b50155 (branch feature1) and created a new commit (e318bc6) in the branch feature2.

That’s it for today. I hope this article have helped you to better understand these advanced git commands. If you liked it, please give it a clap. 👏

References

If you want to go deeper into these commands, I really recommend taking a look at the following references:

--

--