git log: where am I coming from, where can I go?By Delicious Insights • Published on Jun 18, 2019
Last updated on January 31, 2023, 04:04pm
This post is also available in French.
The log: what for?
- Display the project’s history to better grasp the context
- limit the depth/number of displayed commits
- narrow scope to specific files and directories
- view the changes introduced by each commit
- See the recent history of the branches
- target a specific branch
- target branches by pattern (
- Check completed tasks
- view merged branches
- list commits between 2 versions
- in commit metadata: dates, authors, messages…
- in commit changes:
- additions and deletions of searched text
- changes around a known text (on the same line)
- in a specific file, the evolution of a fragment (typically a function/method body)
- Get stats for the projet
This is by no means a comprehensive list, and there are many display and filtering rules that we will not dive into here.
Note that most of the options described can be combined.
Configuration: setup an optimal default behavior
Before we dive into this Git command, we need to configure two important things:
- Tracking file renames by default through history.
- An alias for a “graphical” log, essential for a pleasant use in the terminal.
History across renames
You will sometimes need to track a file’s history regardless of any renames or moves it may have undergone. This can be achieved with the
git log --follow option, but you may as well configure it once and forget about it:
git config --global log.follow true
A pleasant, handy display:
The default display of the log is rather confusing and lack many useful bits. This is an adoption barrier to using Git from the command line.
Let’s define a
git lg alias that we’ll use most of the time instead of the standard
git log. It displays a graph view with a concise set of data, for easier history browsing:
git config --global alias.lg "log --graph --date=relative --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'"
Here’s the result! 🤩
Alternative without an alias
If you prefer redefining the default display behavior of the log, you can set the following configuration keys/values:
git config --global format.pretty 'tformat:%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset' git config --global log.date relative
However, this will not spare you from specifying the `--graph' option. That's why I think the alias is still the better way to go.
Now that we have a clean, beautiful log we can tackle the useful aspects of the command.
Display the project’s history to better grasp the context
The default log behavior displays the history of the current reference since its very first commit. We’ll often want to restrict that to the latest X commits. Here is an example that shows only the 10 latest commits from the current position of HEAD:
git lg -10 # or `-n 10` or `--max-count=10`
It is common to only want commits that touch specific files or directories:
git lg my-file my-directory another-directory
It even works with globs 🤘:
git lg *.html *.js
You can view the actual changes in each listed commit:
git log -p # or `--patch`
That display may feel cumbersome because it provides surrounding file context for each change. Fortunately, the
log command shares display options with its
diff cousin. For instance, you can:
- display only the names of the files:
--name-statusto see whether it was an addition/modification/deletion).
- display changes on the same line (rather than two lines) with
- filter on modifications (
M), additions (
A), deletions (
- tone down context lines by adding the
0here is the number of context lines, so none).
Protip for displaying patches/diffs: install diff-so-fancy. It provides a concise, convenient display.
What about branches?
The default log behavior only displays the history of the current reference (
HEAD). It is naturally possible to target specific branches by using their names:
git lg my-branch [other-branch] [and-another-branch]
However, this syntax doesn't allow for globbing. To display a whole category of branches we can use any of the following:
--branchesoption to display local branches ;
--alloption to display local and remote branches, tags, stashes and in general any known named reference of the repository.
We can go further with the first option and choose to display the branches whose names match the desired pattern:
git lg --branches='*demo*'.
Check completed tasks
View merged branches
If you want to verify that you merged some branches, evenafter removing their labels, you just have to do this:
git lg --merges
We can see in this example that the branches
feat/second-demo were merged in 🤝.
Walk a specific interval of commits
Sometimes you need to display commits between two specific points in time. Here are some concrete examples.
Say we want to display the commits on a
dev branch since its sprouting off
master, to know the current state of its work:
git lg master..dev
Note: one might think that a
git lg dev would have worked the same. In fact it would have also displayed all the history of
master prior to the sprouting of
dev, because the part of
master is also part of the history of
We may want the opposite of the previous situation: the new commits on
master since the creation of
master has since merged a feature branch that is necessary for us to finish our work on
dev: we would then know that it’s probably a good idea to update (rebase)
dev to make it start off the latest
git lg dev..master
If you use tags to "version" your project, you will sometimes want to analyze the new features from one version to another, just like in a changelog (still, prefer a real changelog):
git lg v1.0.0..v2.0.0
Search the log
Many filtering options are available. Not all of them are useful on a daily basis, so we’ll only see a curated selection.
Filtering on dates
We may want to search our log for work done since a given date, or up to that date.
Say I want to display the commits of my current branch for the past 24 hours; I could write:
git lg --since='1 day ago'
A team leader who would like to know the work shared by his team over the past 7 days would do:
git lg --since='1 week ago' --all
The date can be specified in plain English (e. g.
5 minutes ago,
1 month 2 weeks 3 days 1 hour 1 second ago) or in ISO8601 format (
This analysis can be further refined by using a bounded interval with the
Now if our team leader wants to see the work done over the previous week, they could use this:
git lg --since='3 weeks ago' --until='2 week ago' --all
Aliases are available for these options:
Filtering by commit author
Let's say one of your coworkers went on holidays without being able to tell you what tasks she has completed. You could analyze her recent history to determine what she shared/pushed:
git lg -20 --all --author='Anna'
Be careful, this option searches for authors whose names contain the given pattern. So if I have several Annas in my project, I will have to refine my search.
Similarly, we can filter on committers:
git lg -20 --all --committer='Christophe'
It is possible that the author and the committer are different, e.g. during a rebase, or when we do peer-programming and assign a commit explicitly:
git commit --author='Maxime'
There is an interesting alternative to the log that groups recent commits by author:
Filtering by commit message
If you write useful commit messages (which I hope you do 😅), you may want to find some of them. Let's say you use a syntax such as GitHub's or GitLab to reference your tasks (issues). We could thus list all the commits related to task 42:
git log --branches --grep='#42'
This option accepts a regular expression that can be an extended expression when the
-E option is added. It can also be told to ignore case with the
-i option. Here is a more complete example:
git log --grep='(clos(e[sd]?|ing)|fix(e[sd]?|ing)?)\s*#42' -E -i
You probably noticed that I didn't use the
lg alias there, but went instead with
log. The reason is simple: I want to display the messages in their entirety, because my search is performed on the entire message, not just its first line. Displaying full messages allows me to view the text occurrence I'm looking for.
One more tip: if your display is paginated by a tool like
less, you can type
/ (slash) to search in displayed text.
Searching for changes introduced by commits
The focus here is on content changes. There are two takes on this:
- we’re looking for commits that added or modified/deleted a specific text, or
- we’re looking for changes that occurred on the same line as a specific text.
Say I have a file
my-module.js that contains a function that makes a
console.log(...). This call was introduced when the module was created and several changes were made to improve the displayed message.
Now, if I'm looking for the
console.log call itself, I should only hit on one commit (its creation). But if I'd like to track changes to the logged message (what occurs within that
console.log(...)), I should hit on several commits.
Here is the current log of all the commits related to the file:
Searching for additions or deletions
git lg -S 'searched text' my-module.js
-S option takes a literal text. However, you can extend its behaviour to use a regular expression with a complementary
--pickaxe-regex option (not an easy name to remember 😨).
Back in our use case, to retrieve all additions/deletions of
console.log in that file, I can do:
git lg -p -S 'console.log' my-module.js
We get only one commit here: the one that created the module.
Searching for changes on lines matching a given pattern
We may want to list all the commits that introduced changes on the lines matching a given pattern.
Say I want to view changes to the message displayed by my
console.log. I’ll assume my
console.log stays on the same line as the message, so I can use it as a search pattern:
git lg -p -G 'console\.log' my-module.js
The result will provide us with both surrounding changes in theis line and additions/deletions of other occurrences.
Note that this
-G option takes a regular expression without needing an additional option (hence the escaping of the period:
Tracing changes to a fragment / code block
That's my favorite option! It allows us to track the evolution of a fragment of a file, commit by commit. Imagine that you have a function and that overnight a bug appears. How would you go about it?
git lg -L 1,99:file-path
This option searches within a single file. The idea is simple: we define the interval to follow through the commits and Git will show us only changes to this block. We recommend you stay with a line-number interval syntax (comma-separated start line and end line; e.g.
3,99). Note that these are the line numbers in the most recent version of the file, and Git will automatically adjust these as it moves backwards in time through changes, according to the surrounding changes of the file (shifts due to insertions above, addition/deletion of lines in the block).
Let's take the following
my-module.js file’s current version:
![It has a `logUppercased' function that starts at line 5 and ends at line 11](/assets/images/articles/git-log-L-file.png)
We want to track changes to the
logUppercased function, so between lines 5 and 11, because a bug appeared (the function no longer displays in upper case) and we want to trace the origin of the problem:
git lg -L 5,11:my-module.js
Here comes magic ✨…
The result shows us that the commit
58462c4 test(log-l): add test for 'logUppercased' replaced the line doing the display with another that does not convert to upper case 😱. We can conclude that this is a mistake and not an intentional choice, so we can fix it!
A word about
blame command is often used for, well… blaming purposes when an error occurs in a project. Keep in mind that this command only displays the current status of the file, line by line. It only shows the last person to have touched each line, perhaps by removing trailing whitespace or re-indenting. It does not provide conclusive proof that the listed person actually introduced the error.
I think this command should be prohibited for two reasons:
- it carries aggressive meaning;
- it seldom provides truly useful information: prefer
git logfor a sharp analysis!
The log features the same stats options as the
stat: stats by file (addition/deletion ratio);
shortstat: single-line overall commit stats;
dirstat: % changed (in terms of code line counts), per directory;
numstat: equivalent to
statbut with a raw tab-separated output, ideal for scripting the result or exporting it to a spreadsheet.
git diff will produce consolidated stats whilst
git log will display stats on a per-commit basis. It is therefore very likely that you will lean more towards
git diff for analysis.
Git services (GitHub, GitLab etc.) also provide advanced graphical stats that go beyond what can be obtained from the command line.
status commands are the two commands I use most to analyze my current and past work. The log is a valuable way to remind me of context (assuming of course that my commit messages are meaningful).
There are many options not listed here, some of which are also
reflog options. I ecnourage you to remain curious and follow any updates to this article.
Want to know more and become a true Git master? Check out our kickass 360° Git training!
Check out our video courses! 🖥