I’ve been collecting some shell tricks. The list got long enough to warrant a few posts, so this is part one of a few. I’m going to add them to the “Bash Fun” series rather than creating a new one, so that’s the place if you want to track them.

I should mention that serial posts on my blog don’t have a permanent address, but you can find them all on the Topics page, in the “Series” tab. Every post in a series gets a box at the bottom linking to all the other posts in the series.

The first trick I want to share is how to generate a changelog from all of the commits to a Git repository since the last release. It should be noted that the git-extras package (available through brew) has similar functionality already built in. I just wanted something I could customize.

The first thing you need to do is find the date of the last annotated tag, which git-flow will create when you do a git flow release. If you use the git-extras and do a git release this is also available. Barring all of that, you’ll need to do your own annotated tags at the time of a release for this to work.

To get the last annotated tag:

git rev-list --tags --max-count=1

To list the date of the tag incorporate that command into a git show command by executing it inline:

git show -s --format=%ad `git rev-list --tags --max-count=1`

You can use that command to put the date into a shell variable:

lastdate=$(git show -s --format=%ad `git rev-list --tags --max-count=1`)

And then use that to limit the git log command to commits “since” the last tag, displaying just their date and subject lines:

git log --oneline --pretty=format:"%ad: %s" --date=short --since="$lastdate"

For my changelog, I don’t need the date, but I’d like to include any additional info in the commit body and a Markdown list prefix in the format string:

git log --pretty=format:"- %s%n%b" --since="$lastdate"

You can add a date back in using %cd (committer date) or %ad (author date) in the format string. You’d probably want to include the --date=short flag with that as well.

Putting it all together into one command, you get:

git log --pretty=format:"- %s%n%b" --since="$(git show -s --format=%ad `git rev-list --tags --max-count=1`)"

The Markdown formatting isn’t perfect off the bat if you include the full commit body, as I haven’t found a way to have multiline messages automatically indented without a lot of sed/awk work. It just takes a little editing, though.

Now just redirect the output to a file or pipe it to pbcopy and you have a changelog ready for editing and adding to your release. You can make a shell function for that, or include it as a git alias. I like the shell function approach for this one just because it does allow me to do extra formatting and processing of the output more conveniently.

Good luck, and may generating your release notes be as painless as possible.