I use Git every day in my work on an enterprise-level ecommerce codebase. Some people view Git as a source control file manager, but Git doesn’t care about your files. It does everything based on SHAs (unique 40 character identifiers). Branch names are really just aliases for SHAs. Files, branches, and commits are all SHAs.
When I realized that Git just works on SHAs, my workflow changed. It cleared up a lot of confusion I had about where things were, how they were interacting, and whether I needed to panic when I ran a git reset --hard
when I shouldn’t have. Here are a few basic Git commands for managing SHAs that I use to make my life a little easier.
Git Log
In our codebase, history is always changing as we add new commits and update our master branch. git log
helps me keep up with a changing commit history and is one of my most frequently used Git commands. It shows a log of all the commits in my current branch:
commit 7a2733be4e9cfd057cc34c55673a578a9a021863
Author: Melissa Thompson <melissa@example.com>
Date: Thu Dec 12 11:44:35 2019 -0500
feat: add photo carousel
commit 672121a43d9e8dd298c5437a5a52bb5b4b9c318c
Author: Nate Jacobs <nate@example.com>
Date: Thu Dec 12 09:51:31 2019 -0500
fix: add image load handling
I usually only use this command for a couple of tasks:
Looking at previous commit messages
Getting commit SHAs for various purposes
The basic git log
command includes a lot of details that I’m not always interested in. As you can see from the example and the list of things I use the command for, I usually don’t need to know who authored the commit or the date of the commit. To condense the information I get from the log, I add the --oneline
flag. This flag is actually a combination of two others.
The first is --pretty=oneline
, which condenses the commit logs into a single line and removes the author and date details:
7a2733be4e9cfd057cc34c55673a578a9a021863 feat: add photo carousel
The second is --abbrev-commit
, which shows a shorthand version of the commit SHA:
commit 7a2733b
Author: Melissa Thompson <melissa@example.com>
Date: Thu Dec 12 11:44:35 2019 -0500
feat: add photo carousel
Using git log --oneline
combines these flags for a super-condensed, need-to-know version of the commit log:
7a2733b feat: add photo carousel
Git Reset
The git reset
command is another one that’s useful in many scenarios.
Imagine you’re reviewing a pull request for someone, and they’ll need to make some changes. If part of their branch is broken, help them out by suggesting a fix instead of just pointing out the issue. To do this, I’ll sometimes pull down the branch and make changes to figure out what a good suggestion would be. After that’s done, to keep my local environment clean, I’ll use git reset origin/branch --hard
to reset my local branch to the SHA that is on the server. The --hard
flag tells it to reset the branch no matter what, so it’s one that I use sparingly. You can learn more about the --hard
and --soft
flags in Catherine’s article, I Screwed Up Git; How Do I Fix It?
Another way I use git reset
is to unstage a file. For example, sometimes changing my code causes an existing unit test to fail. When that happens, I need to know what’s happening in that unit to be able to fix the test. I make comments as I go through it so I can piece together how it works, and then I have the knowledge I need to fix the test file. Then I want to stage the test file but not the unit file. I could just say git add test-file.js
, but if instead I run git add .
, I’ll have added the test file and the unit file:
Changes to be committed:
modified: test-file.js
modified: unit-file.js
At this point, before I commit, I run git reset HEAD unit-file.js
to unstage the unit file:
Changes to be committed:
modified: test-file.js
Changes not staged for commit:
modified: unit-file.js
You can also reset to a specific SHA by running git reset <SHA>
Git Reflog
While git log
shows commit logs, git reflog
(short for reference logs) shows the history of all Git actions I’ve taken. It shows when branches and other references were updated.
4419c39 (HEAD -> master, origin/master, origin/hero-cube-position, origin/HEAD, hero-cube-position) HEAD@{0}: merge hero-cube-position: Fast-forward
c25a76a (origin/new-hero-text-style, new-hero-text-style) HEAD@{1}: checkout: moving from hero-cube-position to master
4419c39 (HEAD -> master, origin/master, origin/hero-cube-position, origin/HEAD, hero-cube-position) HEAD@{2}: checkout: moving from master to hero-cube-position
c25a76a (origin/new-hero-text-style, new-hero-text-style) HEAD@{3}: rebase finished: returning to refs/heads/new-hero-text-style
c25a76a (origin/new-hero-text-style, new-hero-text-style) HEAD@{4}: pull --rebase: checkout c25a76a7ea9e1b99dc7a4e5e4acec6840513eae1
9f84e75 (origin/melissa-remove-auth, melissa-remove-auth) HEAD@{5}: commit: remove auth for prod
41f47d1 HEAD@{6}: checkout: moving from master to melissa-remove-auth
41f47d1 HEAD@{7}: reset: moving to HEAD
41f47d1 HEAD@{8}: pull --rebase: Fast-forward
Remember that --hard
flag you can attach to a git reset
? The one that discards all changes to files you’re tracking? This tool is really handy if you accidentally run that and lose your work. You can use git reflog
to get the SHA (that seven-digit code at the beginning of each line) from before you ran the reset and go back to that place in your history.
Git Cherry-pick
Sparkboxers love a clean commit history, which means we avoid those messy merge commits that muddy it up. In his article How to Not Dread Rebases When Managing Long-Lived Feature Branches, Adam talks about how and why we avoid merge commits and what we do instead. One of the commands he calls out is git cherry-pick
. It’s a great way to copy and move around commit SHAs.
For example, maybe I’ve accidentally made some commits to master locally. This is inadvisable because our master branch should always be in a state where it could be pushed to production. So I need to move those commits into a branch. I can make a new branch git branch fix--IE-flexbox-bug
. I still need to know what to put in that branch, though. At this point I would use git log --oneline
to grab those commit SHAs:
9965ebd (HEAD -> master) fix: update tests
3a980fc fix: flexbox bug in IE
Now I can checkout my new branch git checkout fix--IE-flexbox-bug
and run a cherry-pick git cherry-pick 3a980fc 9965ebd
. This will copy and move those commits into my new branch. I can check that I’ve cherry-picked in the correct order by running another git log --oneline
to see that my commit messages are in the order I want:
341252c (HEAD -> test) fix: update tests
895b713 fix: flexbox bug in IE
The commit messages are in the order I made the changes, which means I should have a clean cherry-pick with no conflicts. Notice that once I’ve cherry-picked those SHAs onto a new branch, the SHAs change. That’s because the history and timestamp of the commit is different, so Git adjusts the SHA to accommodate that.
SHA Management
For all the commands I’ve listed here, there’s a pattern. Whether I’m running git log
, git reset
, git reflog
, or git cherry-pick
, I’m always interacting with SHAs. The primary way I use Git is as a SHA management tool. Since my goal in using Git is to manage SHAs, I can use the commands above to navigate the codebase with ease, always finding what I need and relatively easily remedying any mistakes I’ve made.