Review

log

Filter and sort

$ git log [-n COUNT]

lists the commit history of the current branch.

-n

limits the number of commits to the specified number.

$ git log [--after="YYYY-MM-DD"] [--before="YYYY-MM-DD"]

Commit history filtered by date.

Relative specifications such as 1 week ago or yesterday are also permitted.

$ git log --author="VEIT"

filters the commit history by author.

It is also possible to search for several authors at the same time, for example:

$ git log --author="VEIT|VSC"

$ git log --grep="TERM" [-i]

filters the commit history for regular expressions in the commit message.

-i

ignores upper and lower case.

$ git log -S"FOO" [-i]

filters commits for specific lines in the source code.

-i

ignores upper and lower case.

$ git log -G"BA*"

filters commits for regular expressions in the source code.

$ git log -- PATH

filters the commit history for specific files.

$ git log MAIN..FEATURE

filters for different commits in different branches, in our case between the MAIN and FEATURE branches.

However, this is not the same as git log FEATURE..MAIN. Let’s take the following example:

A - B main
 \
  C - D feature
$ git log MAIN..FEATURE

shows changes in FEATURE that are not contained in MAIN, that is, commits C and D.

$ git log FEATURE..MAIN

shows changes in MAIN that are not contained in FEATURE, that is, commit B.

$ git log MAIN...FEATURE

shows the changes on both sides, that is, commits B, C and D.

$ git log --follow PATH/TO/FILE

This ensures that the log shows changes to a single file, even if it has been renamed or moved.

You can activate --follow for individual file calls by default by activating the log.follow option in your global configuration:

$ git config --global log.follow true

Then you no longer have to enter --follow, but only the file name.

$ git log -L LINE_START_INT|LINE_START_REGEX,LINE_END_INT|LINE_END_REGEX:PATH/TO/FILE
$ git log -L :FUNCNAME_REGEX:PATH/TO/FILE

With the -L option, you can perform a refined search by checking the log of only part of a file. This function allows you to thoroughly search through the history of a single function, class or other code block. It is ideal for finding out when something was created and how it was changed so that you can correct, refactor or delete it with confidence.

For more comprehensive investigations, you can also track multiple blocks. You can use multiple -L options at once.

$ git log --reverse

The log usually displays the latest commit first. You can reverse this with --reverse. This is particularly useful if you are analysing with the -S and -G options already mentioned. By reversing the order of the commits, you can quickly find the first commit that added a specific string to the codebase.

View

$ git log --stat --patch|-p
--stat

A summary of the number of changed lines per file is added to the usual metadata.

--patch|-p

adds the complete commit diff to the output.

$ git log --oneline --decorate --graph --all|FEATURE

display the history graph with references, one commit per line.

--oneline

One commit per line.

--decorate

The prefixes refs/heads/, refs/tags/ and refs/remotes/ are not output.

--graph

The log usually smoothes historical branches and displays commits one after the other. This hides the parallel structure of the history when merging branches. --graph displays the history of the branches in ASCII format.

--all|FEATURE

--all shows the log for all branches; FEATURE only shows the commits of this branch.

reflog

With git reflog, your Git repository is not checked a second time. Instead, it displays the reference log, a record of all commits made. The reflog not only tracks changes to a branch, it also records changes to the current commit, branch changes, rebasing, etc. You can use it to find all unreachable commits, even those on deleted branches. This allows you to undo many otherwise destructive actions.

Let’s look at the basics of using reflog and some typical use cases.

Warning

The reflog is only part of your local repository. If you delete a repository and clone it again, the new clone will have a fresh, empty reflog.

Show the reflog for HEAD

$ git reflog

If no options are specified, the command displays the reflog for HEAD by default. It is short for git reflog show HEAD. git reflog has other subcommands to manage the log, but show is the default command if no subcommand is passed.

1$ git reflog
212bc4d4 (HEAD -> main, my-feature-branch) HEAD@{0}: merge my-feature-branch: Fast-forward
3900844a HEAD@{1}: checkout: moving from my-feature-branch to main
412bc4d4 (HEAD -> main, my-feature-branch) HEAD@{2}: commit (amend): Add my feature and more
5982d93a HEAD@{3}: commit: Add my feature
6900844a HEAD@{4}: checkout: moving from main to my-feature-branch
7900844a HEAD@{5}: commit (initial): Initial commit
  • The output is quite dense.

  • Each line is a reflog entry, the most recent first.

  • The lines start with the abbreviated SHA of the corresponding commit, for example 12bc4d4.

  • The first entry is what HEAD currently refers to: (HEAD -> main, my-feature).

  • The names HEAD@\{N} are alternative references for the specified commits. N is the number of returning reflog entries.

  • remaining text describes the change. Above you can see several types of entries:

    • commit: MESSAGE for commits

    • commit (amend): MESSAGE for a commit change

    • checkout: moving from SRC TO DST for a branch change

There are many other possible types of entries. The text should be descriptive enough that you can understand the process without looking it up in the documentation. In most cases, you will want to look through such reflog entries to find the corresponding commit SHA.

Show the reflog for a branch

You can focus on entries for a single branch by using the explicit subcommand show and the branch name:

$ git reflog show my-feature-branch
12bc4d4 (HEAD -> main, my-feature-branch) my-feature-branch@{0}: commit (amend): Add my feature and more
982d93a my-feature-branch@{1}: commit: Add my feature
900844a my-feature-branch@{2}: branch: Created from HEAD

Show timestamps of the entries

If you need to distinguish between similarly titled changes, the timestamps can help. For relative timestamps you can use --date=relative:

$ git reflog --date=relative
12bc4d4 (HEAD -> main, my-feature) HEAD@{vor 37 Minuten}: merge my-feature-branch: Fast-forward
900844a HEAD@{vor 37 Minuten}: checkout: moving from my-feature-branch to main
12bc4d4 (HEAD -> main, my-feature-branch) HEAD@{vor 37 Minuten}: commit (amend): Add my feature and more
982d93a HEAD@{vor 38 Minuten}: commit: Add my feature
900844a HEAD@{vor 39 Minuten}: checkout: moving from main to my-feature-branch
900844a HEAD@{vor 40 Minuten}: commit (initial): Initial commit

And for absolute timestamps you can also use --date=iso:

$ git reflog --date=iso
12bc4d4 (HEAD -> main, my-feature) HEAD@{2024-01-11 15:26:53 +0100}: merge my-feature-branch: Fast-forward
900844a HEAD@{2024-01-11 15:26:47 +0100}: checkout: moving from my-feature-branch to main
12bc4d4 (HEAD -> main, my-feature-branch) HEAD@{2024-01-11 15:26:11 +0100}: commit (amend): Add my feature and more
982d93a HEAD@{2024-01-11 15:25:38 +0100}: commit: Add my feature
900844a HEAD@{2024-01-11 15:24:37 +0100}: checkout: moving from main to my-feature-branch
900844a HEAD@{2024-01-11 15:23:56 +0100}: commit (initial): Initial commit

Passes all options that git log supports

git reflog show has the same options as git log. For example, you can use --grep to search for commit messages that mention my feature without case-sensitivity:

$ git reflog -i --grep 'my feature'
12bc4d4 (HEAD -> main, my-feature-branch) HEAD@{0}: merge my-feature: Fast-forward
12bc4d4 (HEAD -> main, my-feature-branch) HEAD@{2}: commit (amend): Add my feature and more
982d93a HEAD@{3}: commit: Add my feature

Note the expiry of entries

Reflog entries expire after a certain time when Git runs the automatic gc process for your repository. This expiration time is controlled by two gc.* options:

gc.reflogExpire

The general expiration time, which is set to 90 days by default.

gc.reflogExpireUnreachable

The expiry time for entries relating to commits that can no longer be reached is set to 30 days by default.

You can increase these options to a longer time frame, but this is rarely useful.