If you've ever browsed the git manual (or run man git
), then you'll have noticed there's a whole lot more to git than what most of us use on a daily basis. A lot of these commands are incredibly powerful and can make your life a lot easier (others are a bit niche, but still good to know).
This post outlines 20 of my favourite under-used git features, which you can use to level up your development process, impress your colleagues, help you answer git interview questions, and most importantly - have fun with!
Contents (and TL;DR)
- Git Web - Open builtin GUI
- Git Notes - Attach extra info to commits
- Git Bisect - Debug like a pro
- Git Grep - Search for anything
- Git Archive - Compress project for sharing
- Git Submodules - Import other repos into yours
- Git Bugreport - Compile bug report with system info
- Git Fsck - Verify and recover unreachable objects
- Git Stripspace - Remove trailing whitespaces
- Git Diff - Compare changes between two files
- Git Hooks - Execute script when a git command is run
- Git Blame - Show who wrote a given line
- Git Large File Storage - Store big files in git
- Git Garbage Collection - Optimize your git repo
- Git Show - Easily inspect any git object
- Git Describe - Give readable name based on last tag
- Git Tag - Create version tags at specific points
- Git Reflog - List all git actions made on a repo
- Git Log - View commit log, and branch diagrams
- Git Cherry Pick - Pull a feature into your branch
- Git Switch - Quickly jump between branches
- Bonus - Extend git with more commands!
Git Web
Run
git instaweb
to instantly browse your working repository in gitweb
Git has a built-in web-based visualiser for browsing local repositories, which lets you view and manage your repo via a GUI in the browser. It's packed with useful features, including:
- Browsing and stepping through revisions and inspecting diffs, file contents and metadata
- Viewing commit logs, branches, directories, file history, and attached data visually
- Generating RSS or Atom feeds of commits and repository activity logs
- Searching commits, files, changes and diffs
To open it, just run git instaweb
from within your repo. Your browser should pop open, and load http://localhost:1234
. If you don't have Lighttpd installed, you can specify an alternative web server with the -d
flag. Other options can be configured either via flags (like -p
for port, -b
for the browser to open, etc), or under the [instaweb]
block in your git config.
There's also the git gui
command, which can open up a GUI-based git app
Git Notes
Use
git notes
to add extra info to commits
Sometimes you need to attach additional data to a git commit (beyond just the changes, message, date-time and author info).
The notes are stored within .git/refs/notes
, and since that's separate from the commit object data, you can modify the notes associated with a commit at anytime, without the SHA-1 hash changing.
You can view notes with git log
, using most git GUI apps, or with the git notes show
command. Some git hosts also show notes in the commit view (although GH no longer displays notes).
Git Bisect
With
git bisect
you can find the commit that introduced a bug using binary search
This is one of the most powerful, yet easy to use git commands- bisect is an absolute life saver when it comes to debugging. After starting a bisect, it checks out commits for you, and you tell it weather that commit is good
(no bug), or bad
(bug introduced), which lets you narrow down the the earliest commit where the bug is present.
To get started, run git bisect start
then pass it a known good commit with git bisect good <commit-hash>
and a known bad one (defaults to current) with git bisect bad <optional-hash>
. It will then checkout the commit in-between the good and bad commits, then you specify weather the bug is present with either git bisect good
or git bisect bad
. It will then repeat the process, checking out a commit in the center of the bad and good, all the way until you've found the exact commit that introduced the bug. Cancel anytime with git bisect reset
.
There's much more to the bisect command, including replays, viewing commits, skipping, so it's worth checking out the docs next time your debugging.
Git Grep
Use
git grep
to search for code, files, commits or anything else across your repo
Ever find yourself needing to search for a string anywhere within a git project? With git grep you can easily search for any string, or RegEx throughout your project, and across branches (like a more powerful Ctrl + F!).
git grep <regexp> <ref>
It includes plenty of options to narrow down your search, or specify results format. For example, use -l
to only return file names, -c
to specify number of matches per file to return, -e
to exclude results matching a condition, --and
to specify multiple conditions, -n
to search with line number.
Since git grep is regex-compatible, you can get much more advanced with the string your searching for.
You can also use it to specify a file extension, like git grep 'console.log' *.js
which will show all console.logs from within JavaScript files
The second parameter is a ref, and can be a branch name, commit, range of commits, or anything else. E.g. git grep "foo" HEAD~1
will search the previous commit.
Git Archive
Use
git archive
to combine an entire repo into a single file
When sharing or backing up a repository, it's often preferred to store it as a single file. Using git archive will include all repo history, so it can be easily extracted back to it's original form. The command also includes a lot of additional options, so you can customise exactly what files are and aren't included in the archive.
git archive --format=tar --output=./my-archive HEAD
Git Submodules
Use
git submodule
to pull any other repository into yours
In git, submodules let you mount one repo into another, and is commonly used for core dependencies or splitting components into separate repositories. For more info, see this post.
Running the following command will pull a module into the specified location, and also create a .gitmodules
file so that it's always downloaded when the repo is cloned. Use the --recursive
flag to include sub-modules when cloning the repo.
git submodule add https://github.com/<user>/<repo> <path/to/save/at>
There's also git subtree
, which does a similar thing, but without the need for metadata files.
Git Bug Report
Use
git bugreport
to compose a bug ticket, including git and system info
This command will capture system info, and then open up a standard bug template (steps to reproduce, actual + expected output, etc). The completed file should be a very complete bug report, with all necessary info captured.
This is very handy if your a maintainer for an open source package and asking a user (developer) to raise a bug report, as it ensures all necessary data is included.
And if you are raising a bug report to the core git system, you can also run the git diagnose
command, and then raise your issue here.
Git Fsck
Use
git fsck
to check all objects, or recover unreachable ones
Although not often needed, sometimes you may have to verify the objects stored by git. This is where fsck (or File System ChecK) comes in, it tests the object database and verifies the SHA-1 ID of all objects and the connections they make.
It can also be used alongside the --unreachable
flag to find objects that are no longer reachable from any named reference (since unlike other commands, it includes everything in .git/objects
).
Git Stripspace
Use
git stripspace
to format whitespaces within a given file
Best practice is to avoid trailing whitespaces at the end of lines, avoid having multiple consecutive blank lines, avoid empty lines from beginning and end of an input, and end each file with a new line. There's plenty of language-specific tools which do this automatically for you (like prettier), but Git also has this functionality builtin.
It's intended for metadata (commit messages, tags, branch descriptions, etc), but also works if you pipe a file to it, then pipe the response back to a file. E.g. cat ./path-to-file.txt | git stripspace
or git stripspace < dirty-file.txt > clean-file.txt
You can also use this to remove comments (with --strip-comments
), or even comment out lines (with --comment-lines
).
Git Diff
With
git diff
you can compare the difference between 2 sets of code
You're probably aware that you you can run git diff
to show all changes since the last commit, or use git diff <commit-sha>
to compare either 2 commits, or 1 commit to the HEAD. But there's much more you can do with the diff command.
You can also use it to compare any two arbitrary files, with diff file-1.txt file-2.txt
(no more visiting diffchecker.com!)
Or compare 2 branches, or refs with each other, using git diff branch1..branch2
Note that a double dot (..
) is the same as a space and indicates the diff input should be the tip of the branches, but you can also use a triple dot (...
) to convert the first parameter into a ref of the shared common ancestor commit between the two diff inputs - very useful! If you want to only compare a single file across branches, just pass the files name in as the third argument.
You may want to see all changes made within a given date range, for this use git diff HEAD@{7.day.ago} HEAD@{0}
(for the last week), again this can be paired up with a filename, branch names, specific commits or any other ref.
There's also the git range-diff
command, which provides a simple interface for comparing commit ranges.
There's much more to the git diff tool (as well as the option of using your own diff checker), so I recommend checking out the docs.
Git Hooks
Use
hooks
to execute command or run scripts when a given get action occurs
Hooks let you automate pretty much anything. For example: ensuring standards are met (commit message, branch name, patch size), code quality (tests, lint), appending additional info to a commit (user, device, ticket ID), calling a webhook to record an event or run a pipeline, etc.
There's pre and post hooks available for most git events, like commit, rebase, merge, push, update, applypatch, etc.
Hooks are stored in .git/hooks
(unless you configure them elsewhere with git config core.hooksPath
), and can be tested with the git hook
command. Since they're just shell files, they can be used to run any command.
Hooks aren't pushed to the remote repository, so to share and manage them across your team, you'll need to use a hook manager, like lefthook or husky. There's also several 3rd-party tools, which make managing hooks easier, I recommend overcommit.
Remember, hooks can always be skipped (with the --no-verify
flag), so never rely purely on hooks, especially for anything security related.
Git Blame
Use
git blame
to show author info for a specific revision and line
A classic, quickly find out who wrote a specific line of code (aka which of your co-workers to blame for the bug!). But it's also useful to determine at which point in time something changed and inspect that commit and associated metadata.
For example, to view author and commit info for line 400 to 420 of index.rs, you'd run:
git blame -L 400,420 index.rs
Git LFS
Store large files using
git lfs
to not bog down your repo
Often your project will contain larger files (such as databases, binary assets, archives or media files), which would slow down the git workflow and max out usage limits. That's where Large File Storage comes in - it enables you to store these large assets elsewhere, while keeping them trackable with git and maintaining the same access controls/ permissions. LFS works by replacing these larger files with text pointers that are tracked within git.
To use it, just run git lfs track <file glob>
, which will update your .gitattributes
file. You can specify files by their extension (e.g. *.psd
), directory, or individually. Running git lfs ls-files
to view a list of tracked LFS files.
Git GC
Use
git gc
to optimize your repository
Over time git repos accumulate various types of garbage, which take up disk space, and slow down actions. That's where the built-in garbage collector comes in. Running git gc
will remove orphaned and inaccessible commits (with git prune
), compress file revisions and stored git objects, as well as some other general house keeping tasks like packing refs, pruning reflog, revere metadata or stale working trees and updating indexes.
Adding the --aggressive
flag will aggressively optimize the repository, throwing away any existing deltas and re-computing them, this takes much longer to run but may be needed if you've got a large repository.
Git Show
Use
git show
to easily inspect any git object
Outputs objects (blobs, treees, tags or commits) in an easily readable form. To use, just run git show <object>
. You'll likely also want to append the --pretty
flag, for a clearer output, but there's many other options available to customize the output (with --format
), so this command can be extremely powerful for displaying exactly what you need.
An instance that this is very useful for, is previewing a file in another branch, without switching branches. Just run git show branch:file
Git Describe
Use
git describe
to find the latest tag reachable from a commit, and give it a human-readable name
Run git describe
and you'll see a human-readable string made from combining the last tag name, with the current commit, to generate a string. You can also pass a specific tag to it,
Note that you must have created tags for this to work, unless you append the --all
flag. Git describe will also only use annotated tags by default, so you must specify the --tags
flag to make it use lightweight tags as well.
Git Tag
Tag a specific point in your repo's history using
git tag
It's often useful to be able to tag specific, important points in a repository’s history most commonly used to denote versions of releases. Creating a tag is as simple as git tag <tagname>
, or you can tag a historical commit with git tag -a v4.2.0 <commit sha>
. Like with commits, you can include a message alongside a tag, using -m
.
Don't forget to push your tag to remote, with git push origin <tagname>
.
To list all tags, just run git tag
, and optionally use -l
for a wildcard search.
You'll then be able to checkout a specific tag, with git checkout <tagname>
Git Reflog
List all updates made on your repo using
git reflog
Git keeps track of updates to the tip of branches using a mechanism called reference logs, or "reflogs". Various events are tracked, including: clone, pull, push, commit, checkout and merge. It's often useful to be able to find an events reference, as many commands accept a ref as a parameter. Just run git reflog
to view recent events on HEAD
.
One thing that reflog is really useful for is recovering lost commits. Git never really loses anything, even when rewriting history (like rebasing or commit amending). Reflog allows you to go back to commits even though they are not referenced by any branch or tag.
By default reflog uses HEAD
(your current branch), but you can run reflog on any ref. For example git reflog show <branch name>
, or to see stashed changes with git reflog stash
. Or show all references with git reflog show --all
Git Log
Use
git log
to view a list of commits
You're likely already familiar with running git log
to view a list of recent commits on your current branch. But there's a few things more you can do with git log.
Using git log --graph --decorate --oneline
will show a nice neat commit graph along with ref pointers.
You also often need to be able to filter logs based on various parameters, the most useful of which are:
-
git log --search="<anything>"
- Search logs for specific code changes -
git log --author="<pattern>"
- Show log only for specific author(s) -
git log --grep="<pattern>"
- Filter log using search term or regex -
git log <since>..<until>
- Show all commits between two references -
git log -- <file>
- Show all commits made only to a specific file
Or, just run git shortlog
for a summerized list of commits.
Git Cherry Pick
Use
git cherry-pick
to pick specified commit(s) by reference and append them to the working HEAD
Sometimes you need to pull a specific commit from elsewhere, into your current branch. This can be very useful for applying hot fixes, undoing changes, restoring lost commits and in certain team collaboration settings. Note that often traditional merges are better practice, since cherry picking commits can cause duplicate commits in the log.
Usage is straightforward, just run git cherry-pick <commit-hash>
. This will pull the specified commit into your current branch.
Git Switch
Use
git switch
Moving between branches is something that we do often, the switch
command is like a simplified version of git checkout
, it can be used to create and navigate between branches, but unlike checkout does not copy modified files when you move between branches.
Similar to checkout -b
, with the switch command you can append the -c
flag to create a new branch, and jump strait into it, e.g. git switch -c <new branch>
. And running git switch -
will discard any experimental changes you've made, and return you to your previous branch.
Git Standup
Use
git standup
to recall what you did on the last working day , based on git commits
I've put this one at the end, as it's not included with most git clients, but you can easily install it either with your systems package manager, using a 1-line curl script, or by building from source.
If your boss requires you do a daily standup, to give updates on yesterdays work, but you can never remember what you actually did - this one is for you! It'll show a nicely formatted list of everything done in a given time frame. Usage is simple, just run git standup
, or use these options to specify what data should be shown (author, timeframe, branches, etc.
Bonus
Git can be easily extended with add-ons, to add extra commands that do useful tasks. One of the most complete extensions is git-extras by @tj. It gives you 70+ extra commands to automate common git jobs.
For a list of more useful git add-ons, see stevemao/awesome-git-addons.
If you're working with GitHub repos, then the GitHub CLI let's you do common tasks (managing PRs, issues, code reviews, etc) from the command line.