History visualization
TECHNIQUE 30 Listing commits with different formatting
# git show HEAD^
commit 06b5eb58b62ba8bbbeee258705c636ca7ac20b49 Author: Mike McQuaid <[email protected]> Date: Thu Nov 28 15:49:30 2013 +0000
Remove unfavourable review file.
diff --git a/GitInPracticeReviews.tmp b/GitInPracticeReviews.tmp deleted file mode 100644
index ebb69c3..0000000
--- a/GitInPracticeReviews.tmp +++ /dev/null
@@ -1 +0,0 @@ -Git Sandwich
B
shows all the same information expected in git log output, but it only ever shows a single commit.C
shows the changes made in that commit. It’s the equivalent of typing git diff HEAD^^..HEAD^—the difference between the previous commit and the one before it. The git show HEAD^ output is equivalent to git log --max-count=1 --patch HEAD^.Technique 30
Listing commits with different formatting
The default git log output format is helpful, but it takes a minimum of six lines of output to display each commit. It displays the commit SHA-1, author name and email, commit date, and the full commit message (each additional line of which adds a line to the git log output). Sometimes you’ll want to display more information, and some- times you’ll want to display less. You may even have a personal preference about how the output is presented that doesn’t match how it currently is.
Fortunately, git log has some powerful formatting features with varied, sensible supplied options that give you the ability to completely customize the output to meet your needs.
WHY ARE COMMITS STRUCTURED LIKE EMAILS? Remember in technique 4 I mentioned that commits are structured like emails? This is because Git was initially created for use by the Linux kernel project, which has a high-traffic mailing list. People frequently send commits (known as patches) to the mail- ing list. Previously there was an implicit format that people used to turn a requested change into an email for the mailing list, but Git can convert com- mits to and from an email format to facilitate this. Commands such as git format-patch, git send-mail, and git am (an abbreviation for “apply mail- box”) can work directly with email files to convert them to/from Git commits. This is particularly useful for open source projects where everyone can access the Git repository but fewer people have write access to it. In this case, some- one could send me an email that contains all the metadata of a commit using one of these commands. Nowadays, typically this is done with a GitHub pull request instead (which we’ll cover in chapter 11).
Listing 4.2 Output: showing a commit
Commit information
B
C
Let’s display some commits in an email-style format.
Problem
You want to list the last two commits in an email format with the oldest displayed first.
Solution
1 Change to the directory containing your repository; for example,
cd /Users/mike/GitInPracticeRedux/.
2 Run git log --format=email --reverse --max-count 2' and, if necessary, q to
exit. The output should resemble the following.
# git log --format=email --reverse --max-count 2
From 06b5eb58b62ba8bbbeee258705c636ca7ac20b49 Mon Sep 17 00:00:00 2001 From: Mike McQuaid <[email protected]>
Date: Thu, 28 Nov 2013 15:49:30 +0000
Subject: [PATCH] Remove unfavourable review file.
From 36640a59af951a26e0793f8eb0f4cc8e4c030167 Mon Sep 17 00:00:00 2001 From: Mike McQuaid <[email protected]>
Date: Thu, 28 Nov 2013 15:57:43 +0000 Subject: [PATCH] Ignore .tmp files.
B
can be safely ignored. The first part is the SHA-1 hash for the commit. The log out- put is generated in the Unix mbox (short for mailbox) format. The second, date part is not affected by the commit date or contents but is a special value used to indicate that this was outputted from Git rather than taken from the real Unix mbox.C
is the author of the commit. This is one of the reasons Git stores a name and an email address for authors and in commits; it eases the transition to email format. A commit can be seen as an email sent by the author of the commit requesting that a change be made.D
is the date on which the commit was made. This also sets the date for the email in its headers.E
is the first line of the commit message prefixed with [PATCH]. This is another rea- son to structure your commit messages like emails (as mentioned in technique 4). If there’s more than one line in a commit message, the other lines are shown as the message body. Remember, if you use the --patch (or -p) argument, then the git log output will also include the changes made in the commit. With this argument provided, each outputted git log entry will contain the commit and all the metadata necessary to convert it to or from an email.Listing 4.3 Output: email-formatted log
B
Unix mailbox date Commit
author
C
Commit73
TECHNIQUE 30 Listing commits with different formatting
Discussion
If you specify the --patch (or -p) flag to git log, you can also format the diff output by specifying flags for git diff. Recall the discussion of word diffs in section 1.7. git log --patch --word-diff shows the word diff (rather than the unified diff) for each log entry.
git log can take a --date flag, which takes various parameters to display the out- put dates in different formats. For example, --date=relative displays all dates rela- tive to the current date; 6 weeks ago and --date-short display only the date, such as 2013-11-28. iso (or iso8601), rfc (or rfc2822), raw, local, and default formats are also available, but I won’t detail them in this book.
The --format (or --pretty) flag can take various parameters, such as email, which you’ve seen in this example; medium, which is the default if no format was speci- fied; and oneline, short, full, fuller, and raw. I won’t show every format in this book, but please compare and contrast them on your local machine. Different formats are better used in different situations depending on how much of their displayed information you care about at that time.
You may have noticed that the full output contains details about an author and a committer, and the fuller output additionally contains details of the author date and commit date.
# git log --format=fuller
commit 334181a038e812050051776b69f0a80187abbeed
Author: BrewTestBot <[email protected]> AuthorDate: Thu Jan 9 23:48:16 2014 +0000
Commit: Mike McQuaid <[email protected]> CommitDate: Fri Jan 10 08:19:50 2014 +0000
rust: add 0.9 bottle. ...
This snippet shows a single commit from Homebrew, an open-source project accessi- ble at https://github.com/Homebrew/homebrew. This was used because in the Git- InPracticeRedux repository, all the previous commits will have the same author and committer, author date, and commit date.
WHY DO COMMITS HAVE AN AUTHOR AND A COMMITTER? The fuller commit out- put shows that for a commit, there are two recorded actions: the original author of the commit and the committer (the person who added this commit to the repository). These two attributes are both set at git commit time. If they’re both set at once, then why are they separate values? Remember, you’ve seen repeatedly that commits are like emails and can be formatted as emails and sent to others. If I have a public repository on GitHub, other users can clone my repository but can’t commit to it.
In these cases they may send me commits through a pull request (discussed later in section 10.1) or by email. If I want to include these in my repository, the separation between committing and authoring means I can then include these commits, and Git stores the person who, for example, made the code changes and the person who added these changes to the repository (hope- fully after reviewing them). This means you can keep the original attribution for the person who did the work but still record the person who added the commit to the repository and (hopefully) reviewed it. This is particularly use- ful in open source software; with other tools, such as Subversion, if you don’t have commit access to a repository, the best attribution you could hope for would be something like “Thanks to Mike McQuaid for this commit!” in the commit message.
In Subversion the equivalent git blame command is svn blame. It also has an alias called svn praise. In Git there’s no such alias by default (but technique 50 will later show you how to create one yourself). I’m sure there’s a joke to be made about the fact that Subversion offers praise and blame equally but Git offers only blame!
4.2
Custom output format
If none of the git log output formats meets your needs, you can create your own cus- tom format using a format string. The format string uses placeholders to fill in various attributes per commit.
Let’s create a more prose-like format for git log.
# git log --format="%ar %an did: %s"
6 weeks ago Mike McQuaid did: Ignore .tmp files.
6 weeks ago Mike McQuaid did: Remove unfavourable review file. 6 weeks ago Mike McQuaid did: Add first review temporary file. 6 weeks ago Mike McQuaid did: Rename book file to first part file. 9 weeks ago Mike McQuaid did: Start Chapter 2.
3 months ago Mike McQuaid did: Joke rejected by editor! 3 months ago Mike McQuaid did: Improve joke comic timing. 3 months ago Mike McQuaid did: Add opening joke. Funny? 3 months ago Mike McQuaid did: Initial commit of book.
Here I’ve specified the format string with %ar %an did: %s. In this format string %ar is the relative format date on which the commit was authored. %an is the name of the author of the commit.
did: is text that’s displayed the same in every commit and isn’t a placeholder. %s is the commit message subject (the first line).
You can see the complete list of these placeholders in git log --help. There are too many for me to detail them all in this book. The large number of placeholders should mean you can customize git log output into almost any format.
75
The ultimate log output
4.3
Releasing logs: git shortlog
git shortlog shows the output of git log in a format that’s typically used for open source software-release announcements. It displays commits grouped by author with one commit subject per line.
# git shortlog HEAD~6..HEAD Mike McQuaid (9):
Joke rejected by editor! Start Chapter 2.
Rename book file to first part file. Add first review temporary file. Remove unfavourable review file. Ignore .tmp files.
B
shows the name of the author of the following commits and how many commits they’ve made.C
shows the first line of the commit message.The commit range (HEAD~6..HEAD) is optional, but typically you’d want to use one to create a software-release announcement for any version after the first.
4.4
The ultimate log output
As mentioned previously, often the git log output is too verbose or doesn’t display all the information you wish to query in a compact format. It’s also not obvious from the output how local or remote branches relate to the output.
I have a selection of format options I refer to as my “ultimate log output.” Let’s look at the output with these options.
# git log --oneline --graph --decorate
* 36640a5 (HEAD, origin/master, origin/HEAD, master) Ignore .tmp files. * 06b5eb5 Remove unfavourable review file.
* fcd8f6e Add first review temporary file. * c6eed66 Rename book file to first part file. * ac14a50 Start Chapter 2.
* 07fc4c3 Joke rejected by editor! * 85a5db1 Improve joke comic timing. * 6b437c7 Add opening joke. Funny? * 6576b68 Initial commit of book.
This output format displays each commit on a single line. The line begins with a branch graph indicator (which I’ll explain shortly) followed by the short SHA-1
Listing 4.6 Output: short log
Listing 4.7 Output: graph log
Commit author
B
Commit message
(which is useful for quickly copying and past- ing), the branches, tags (introduced in tech- nique 36), and HEAD (which points to this commit in parentheses), and ends with the commit subject.
As you may have noticed, this format is similar to that of the first two columns of GitX (see figure 4.1). The GitInPracticeRedux repository doesn’t currently have any merge commits. Let’s see what the graph log output looks like with some of them.
# git log --oneline --graph --decorate
* 129cce6 (origin/master, origin/HEAD, master) Merge branch 'testing' |\
| * a86067a (origin/testing, testing) testing branch commit * | 1a36bd6 master branch commit
...
Here you can see the branch graph indicator becoming more useful. Like the graphi- cal tools you saw in technique 4, this displays branch merges and the commits on dif- ferent branches, using ASCII symbols to draw lines. The * means a commit that was made. Each line follows a single branch. Reading from the bottom up, you can see from the preceding listing that a commit was made on the master branch, a commit was made on the testing branch, and then the testing branch was merged into master. Both the testing and master branches remain (haven’t been deleted), and both have been pushed to their respective remote branches. All this from just three lines of ASCII output. Hopefully you can see why I love this presentation. Typing git log --oneline --graph --decorate is unwieldy, so you’ll see later in technique 50 how to shorten this to something like git l by using an alias.