Git Secrets: 7 Commands You Might Not Know
As a full-stack developer, Git is an essential part of your toolkit. While most developers are familiar with basic commands like git commit, git push and git pull, Git has a lot more to offer. Knowing some of Git‘s more advanced capabilities can help you work more efficiently and give you better control over your code and development workflow.
In this article, we‘ll look at 7 lesser-known Git commands that can really come in handy. These commands go beyond the basics to provide powerful version control features. If you‘ve never used them before, they might seem a bit mysterious at first. But once you get to know them, you‘ll realize they are real gems that can make you a more productive Git user.
Let‘s dive in and uncover some Git secrets!
1. git bisect
Have you ever been in the situation where a bug suddenly appears in your code, but you‘re not sure which commit caused it? Tracking down the offending change can be time-consuming, especially if there are a lot of commits to sort through. This is where git bisect comes to the rescue.
The git bisect command does a binary search through your commit history to help you identify as quickly as possible which commit introduced a bug. To start, you tell bisect the last known "good" and "bad" commits. It then splits the range of commits between those two points in half and checks out a commit in the middle for you to test. You tell bisect whether the commit it checked out is good or bad, and it continues narrowing down the range by halves until the bad commit is found.
Here‘s how you use it:
$ git bisect start
$ git bisect bad # Current commit is the first known bad commit
$ git bisect good 7869731 # The last known good commit
Bisecting: 3 revisions left to test after this (roughly 2 steps)
…
Git will then check out a commit halfway between the bad and good commits. You test this commit, and if it‘s bad you run:
$ git bisect bad
If it‘s good:
$ git bisect good
Git will then present you with another commit to test, narrowing down the range each time until it pinpoints the commit that caused the problem. When you‘re finished, run:
$ git bisect reset
This will return you to the branch you were on before you started the bisect.
Being able to quickly find the commit that broke something is a real timesaver and can help you deliver fixes faster. Instead of manually checking out commits and testing them one by one, let git bisect do the hard work for you!
2. git reflog
Sometimes you may find yourself in the position where you accidentally deleted a commit, a branch, or a tag and you need to get it back. Normally, once you delete something in Git, it‘s gone forever. But there is a way you can recover lost commits – by using the git reflog command.
Git keeps a record of updates to the tip of branches using a mechanism called reference logs, or "reflogs". You can view the reflog for the local branch you‘re currently on by running:
$ git reflog
7869731 (HEAD -> master) HEAD@{0}: reset: moving to HEAD~1
986237e HEAD@{1}: commit: more changes
7869731 (HEAD -> master) HEAD@{2}: checkout: moving from my-feature to master
…
The reflog contains a list of all the times the tip of your branch was updated, whether by a commit, a reset, a merge, or any other operation that moves the tip. Each entry has a commit hash and message associated with it.
So if you accidentally reset or delete something, you can find the commit hash for the state you want to return to in the reflog, then use git reset to restore your branch to that state. For example, to restore the branch in the previous example to the "more changes" commit, you would run:
$ git reset –hard 986237e
The git reflog command gives you a safety net in case you mess up your repository and need to recover lost commits. It‘s a good idea to check your reflog before doing any potentially destructive operations just in case!
3. git cherry-pick
Sometimes you may have a situation where you want to grab a single commit from one branch and apply it to another. Maybe there‘s a bug fix on a maintenance branch that you need in your feature branch, or a commit in a teammate‘s branch that you want to build on. In these cases, git cherry-pick is just the thing.
The git cherry-pick command "copies" a commit from somewhere else, and then adds it to the current working branch as a new commit. The new commit will have a different hash than the original, but the same changes and commit message.
To use cherry-pick, simply provide the hash of the commit you want to copy:
$ git cherry-pick 986237e
This will take the changes from commit 986237e and apply them to your current branch as a new commit.
You can also cherry-pick a range of commits by providing the starting and ending hashes:
$ git cherry-pick 7869731..986237e
One thing to keep in mind is that cherry-picking can sometimes cause merge conflicts if the code you‘re applying the commit to has diverged from the original. You‘ll need to resolve any conflicts and complete the cherry-pick with git cherry-pick –continue.
Cherry-picking is a convenient way to share small fixes and features between branches without having to do a full merge. It lets you be surgical about which changes you bring in. Just be careful not to overuse it, as having duplicate commits can get confusing!
4. git revert
What if you push a commit, then realize it has a bug or breaks something? Normally you would fix the problem in a new commit. But sometimes you may want to undo the commit entirely, as if it never happened. For this, you can use git revert.
The git revert command creates a new commit that undoes the changes from a previous commit. It does this by applying the inverse of the changes from the bad commit, effectively cancelling them out.
To revert a commit, use the hash of the commit you want to undo:
$ git revert 986237e
This will create a new commit that removes the changes made in commit 986237e. Your repo will be in the state it was in before that commit, but the bad commit will still be in your history.
One key thing to note is that reverting a merge commit is a bit trickier. If you want to revert a merge, you need to specify the -m parent-number option to specify which parent branch should be treated as the mainline.
Revert is a safe way to undo a commit, because it doesn‘t rewrite history. The original commit you revert is still there, and you can always recover it if needed. This is in contrast to git reset, which can destructively change history and loses commits entirely. For undoing shared public commits, revert is usually the right option.
5. git blame
When you‘re puzzling over how a piece of code ended up the way it is, figuring out its history can provide valuable clues. The git blame command is a great forensic tool for this detective work.
Git blame shows you who last modified each line of a file, and in which commit. To use it, simply provide the file path:
$ git blame index.js
^d0b752c (John Smith 2019-02-25 22:04:44 -0500 1) const express = require(‘express‘);
7869731d (Jane Doe 2019-03-11 15:32:05 -0400 2) const app = express();
7869731d (Jane Doe 2019-03-11 15:32:05 -0400 3)
986237e0 (Bob Jones 2019-04-02 10:15:53 -0400 4) app.get(‘/‘, (req, res) => {
986237e0 (Bob Jones 2019-04-02 10:15:53 -0400 5) res.send(‘Hello, world!‘);
986237e0 (Bob Jones 2019-04-02 10:15:53 -0400 6) });
The output shows the commit hash, author, date, and line number for each line in the file. This can help you track down when a change was introduced, and by whom. You can even pass a specific revision to see what the file looked like at that point:
$ git blame 7869731 index.js
Blame output can get a bit noisy for a large file. To make it more manageable, Git provides a few flags you can use to customize the output:
- -L start,end: only show lines between the given line numbers
- -w: ignore whitespace changes
- -M: detect moved or copied lines within the same file
- -C: detect lines moved or copied from other files
Using these flags can make the output easier to scan through, and help you zero in on the changes that matter.
The git blame command is a great way to explore the evolution of a file and gain a better understanding of your codebase. It can help you find out why a piece of code was changed, and connect changes to bug reports or feature requests. Just remember to use your blame powers for good, not evil! The goal is to understand the code, not to point fingers.
6. git stash
Have you ever been halfway through working on something when you suddenly need to jump to another branch to check something? Or maybe your co-worker needs you to review their pull request right away? In these situations, you need a way to quickly get back to a clean working state without losing your work. Git stash to the rescue!
The git stash command takes your uncommitted changes (both staged and unstaged), saves them away for later use, and then reverts your working copy to match the HEAD commit. To save your changes, simply run:
$ git stash
This will take all your changes and stash them, leaving you with a clean working directory. To see a list of all your stashed changes, run:
$ git stash list
stash@{0}: WIP on master: 7869731 initial commit
stash@{1}: WIP on master: 986237e added search endpoint
Each stash is given a name like stash@{0} so you can reference it later. To apply your most recent stash and remove it from the list, run:
$ git stash pop
If you want to apply a specific stash and keep it in the list, use git stash apply instead:
$ git stash apply stash@{1}
You can also create a branch from a stash, which is handy if the changes in the stash ended up being more involved than you originally thought:
$ git stash branch my-feature stash@{1}
This creates a new branch named "my-feature", checks out the commit you were on when you stashed your work, and then pops your stashed changes onto it.
Git stash is a lifesaver when you need to context-switch quickly. It gives you a clean working copy without forcing you to make half-done commits. Just be careful not to let your stash list get too long – it‘s usually best to pop or apply your stashes as soon as you can to avoid conflicts!
7. git submodule
Sometimes your project might depend on code from another repository. Maybe it‘s a library your app uses, or a set of shared scripts used by several projects. You could just copy the code into your repository, but then you‘d have to manually keep it updated. A better solution is to use Git submodules.
A Git submodule lets you embed one Git repository inside another. The submodule has its own history and can be updated independently, but is tied to a specific revision in the parent repository.
To add a submodule to your project, use the git submodule add command:
$ git submodule add https://github.com/your-name/some-library.git
This will clone the submodule repository into a new directory and create a .gitmodules file that stores the mapping between the parent project and the submodule.
When someone clones your repository, they will get the submodule directory, but it will be empty. To populate it, they need to run:
$ git submodule init
$ git submodule update
To update a submodule to the latest commit, go into the submodule directory and pull like normal:
$ cd some-library
$ git pull
Then commit the change in the parent repository to reference the new submodule commit.
Submodules take some getting used to, but they can be very helpful for organizing code and reusing components between projects. Just be aware that they add some complexity to your repository, so make sure your team agrees on how to work with them.
Unlocking Git‘s Full Potential
I hope this tour through some of Git‘s lesser-known commands has given you a sense of how much power is hiding under the surface. Mastering advanced features like bisect, reflog, cherry-pick, revert, blame, stash and submodule can really step up your Git game.
But this is just the tip of the iceberg – Git has many more tricks up its sleeve! I encourage you to dive into the documentation and explore. The more you learn about Git‘s capabilities, the more you can bend it to your will.
Here are some great resources for going deeper with Git:
- Pro Git – an in-depth free book on Git
- Oh Shit, Git!?! – a guide to getting out of Git messes
- Learn Git Branching – an interactive tutorial on branching
Remember, with great power comes great responsibility. Use your newfound Git knowledge wisely, and happy committing!