Investigating Git History
Table of Contents
A version control system like Git is frequently recommended as a way to keep track of the history of a project. Using such a system, you can do things like jump back to a specific point in time, integrate work from multiple developers and undo and redo changes as you see fit. Before you do any of that, however, it's often useful to just be able to look at the project history. In this article we cover how to figure out who did what when, using command-line Git as well as our graphical Git client Tower.
Git can tell us what changes have been done lately in a repository, what changes have been done to a specific file, and what changes are introduced by a certain branch.
One of the most important commands for investigating the history is git log
. We'll look at this command first.
Viewing History
By itself, git log
lists the history of the branch you're currently on. It shows info like the commit hash and message, date and author for each commit on the branch, starting from the latest one. For a more compact view, add the --oneline
option: this will restrict the output to the first line of the commit message and remove most other info except the beginning of the commit hash.
$ git log --oneline
Things get interesting when we start providing options to git log
to constrain the list of commits displayed. You can show only commits from the last two days using git log --since="2 days ago"
. You can also show only the last two commits by the command git log -2
.
You can look for commits by a specific author using the --author
option (the option takes a regular expression value). You can, for example, look for commits authored by someone whose name includes "Alex":
$ git log --author Alex
If you're looking for commits related to some specific functionality, searching commit messages can be very useful. This is done using the --grep
option to git log
(like --author
, this option takes a regular expression). Adding the -i
option makes the patterns provided to both --author
and --grep
case-insensitive.
$ git log --oneline --grep caching -i
Another often useful strategy is looking at commits that change a specific file. To do this, just add the the filename to the end of the command:
git log config/environments/production.rb
If there's a branch with the same name as the file you're interested in, add two dashes before the filename to distinguish it from other options to the command (the same goes for other commands with a filename at the end):
git log -- config/environments/production.rb
Needless to say, there are more options than these, and parameters like the date for --since
can be specified in many different ways. Take a look at the git-log
man page for more details. Also, the options can be combined.
Say we want a compact look at all changes related to caching made to the file config/environments/production.rb
by someone called Alex in the last two years. No problem:
git log --oneline --grep caching -i --author Alex --since '2 years ago' config/environments/production.rb
You start to see how git log
can be very useful in drilling down into the history of your repository in very specific ways!
Inspecting Particular Commits
This is only half the battle, though. Once we've found the commits we want, there's a good chance we're interested in knowing what changes they introduce. There are a couple of ways to go about that:
A Git commit is identified by its hash, which is listed in the git log
output (partly, if using the --oneline
option). When referring to a commit, you don't have to use the whole hash, just enough characters to make it unique. While one character is probably not going to be enough, six usually are. Passing this hash to the git show
command will show you the basic info of the commit along with its diff.
$ git show abc123
If you want to see the diffs for several commits at once, you can do so directly using git log
. Just take whatever command you put together to find the relevant commits, and add the --patch
(or -p
) option, like so:
$ git log --since="2 days ago" --patch
In Tower, our graphical Git client, you can view the history for any branch or tag by selecting it in the sidebar on the left. You can also use the history view to get a combined history for all branches and tags, or Command-click multiple branches and tags to get a combined history of those. Clicking any of the commits in the history gives you more info, including diffs showing exactly what each commit changed.
Just like in Git, you can filter the history by commit author, date range, files affected, commit message and more. Click the dropdown in the search box in the upper right corner to select your first criteria to filter by (1).
After entering a value and hitting Enter, the filter shows up above the commit list, where you can use the plus and minus buttons to add more criteria as needed (2)!
For more on searching in Tower, see our Help page on the subject.
For viewing changes to a specific file, Tower has the File History view. One way to reach it is by right-clicking a file in the Working Copy and selecting "Show File History...". However, it might be faster to use Quick Actions: hit ⌘ + ⇧ + a and type enough of the filename to find the file. Hit Tab to show more actions, then select "Show File History".
This shows a list of only those commits that touched the file in question and you can search the history by, for example, commit message or author as before.
File History
Let's jump from git log
to another command. What if, instead of the commits that touch a specific file, you're interested in the history of specific lines in a file? This is where git blame
comes in.
git blame
will show the author, date and partial commit hash of the latest commit touching every line in the file.
git blame config/environments/production.rb
It's not uncommon to be interested in earlier commits touching a specific line as well. You can check the hash of the latest commit modifying the line you're interested in and then limit git blame
to search commits up to the parent of that commit like this: git blame abc123^ config/environments/production.rb
. This will show you the previous commit changing that line, and you can repeat the process to get the previous one etc.
Once we know the relevant commit hash, we can pass it to git show
to view the commit message, diff and more information.
The process of finding earlier commits touching a specific line, outlined above, is quite cumbersome. There is a potentially nicer way. With git log
, not only can you search through commit messages for a specific pattern, you can search through the actual changes introduced by the commits. In other words, you can search for every commit where a specific piece of code is either added to or removed from the codebase. This option, -S
, is called the "pickaxe", supposedly because the symbols resemble a pickaxe. As before, you can add the --patch
option to view the diff. You can also add a filename to limit the search, but if the file has been renamed along the way, you risk not getting the results you're looking for.
git log -S config.cache_classes --patch
Naturally, Tower has a blame view as well (more on this in our Help). One way to reach it is by right-clicking a file in the Working Copy and selecting "Show Blame...".
Another is to use Quick Actions: hit ⌘ + ⇧ + a and type enough of the filename to find the file, then hit Tab to show more actions, then select "Show Blame". In the blame view, you can double-click a commit on the right side to go to that commit.
If you're looking for older changes to a file, the File History view is useful: you can right-click any commit here and select "Show Blame" to show the blame view only including commits up to that point in history.
Comparing Branches
Another aspect of the version control history that's often interesting is the difference between branches. For example, given a feature branch, we might want to know which commits on that feature branch are not on the main branch, and how the feature branch differs from the main branch.
git log
helps us answer the first question. git log --oneline main..feature/my-feature-branch
shows all the commits in the feature/my-feature-branch
branch not on the main branch. Mind you, the main branch can also have commits of it's own not present in the feature branch — commits added after the feature branched forked off.
The natural question, then, is how these branches differ from each other. We could add the --patch
option for git log
, which would show the changes introduced by each commit. However, to just view the current diff between the tips of the branches, we use git diff
:
git diff main feature/my-feature-branch
The above command will show us all changes between the files in these branches. We can also limit our interest to a specific file, and see how that differs between the two branches:
git diff main feature/my-feature-branch config/environments/production.rb
Another interesting question might be "what changes has this branch introduced since it branched off?". Referring to our example above, instead of the difference between the tip of the current feature branch and the tip of the main
branch, we're interested in the difference between the tip of the feature branch and the state of the main branch when we forked off the feature branch. Another way to put it is that we're looking for the difference between our feature branch and the last common ancestor of the feature branch and the main branch. git diff
has a "three-dot operator" available for exactly this. A command like this will show the diff between feature/my-feature-branch
and the last common ancestor of that branch and the main
branch:
git diff main...feature/my-feature-branch
Tower has a dedicated feature allowing you to view the commits on one branch since it forked off from another branch. After selecting the branch you're interested in in the sidebar to see its history, click the "Compare to another branch" button in the upper right corner of the commit list. You can use the dropdown menu to the left to select the desired branch as basis for comparison.
Tower shows you per-commit diffs. If you want to view diffs between branches, you can use an external tool such as Kaleidoscope. After setting up Kaleidoscope or any of the other supported diff tools in Tower, you can select any two branches or tags in Tower, right-click, and select "Compare..." to compare them in your diff tool of choice. This also works for a pair of commits selected in the commit list!
Final Words
Commands such as git log
, git blame
, and git diff
can be incredibly useful for obtaining valuable insights into the timeline of your project's development. As we have learned in this deep dive, Git provides a wide range of options for efficiently accessing the changes we need to examine, whether in the Command Line or in Tower.
So, don't hesitate to dive into your Git history. It's not just about knowing where you are; it's about understanding how you got there and where you're headed.
We hope you found this post helpful! For more programming tips, don't forget to sign up for our newsletter below and follow Tower on Twitter and LinkedIn! ✌️
Join Over 100,000 Developers & Designers
Be the first to know about new content from the Tower blog as well as giveaways and freebies via email.