245

I've been using Git for a while now, and I recently started using it to tag my releases so that I could more easily keep track of changes and be able to see which version each of our clients is running (unfortunately the code currently mandates that each client have their own copy of the PHP site; I'm changing this, but it's slow-going).

In any case, we're starting to build some momentum, and I thought it would be really good to be able to show people what has changed since the last release. The problem is, I haven't been maintaining a changelog, because I don't have a good idea of how to go about it. For this particular time, I can run through the log and manually create one, but that will get tiring very quickly.

I tried googling "git changelog" and "git manage changelog", but I didn't find anything that really talked about the workflow of code changes and how that coincides with the changelog. We're currently following Rein Henrichs' development workflow and I would love something that went along with that.

Is there a standard approach that I am missing, or is this an area where everybody does their own thing?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Topher Fangio
  • 20,372
  • 15
  • 61
  • 94

11 Answers11

202

This was in 2015, but for the sake of future searchers, it's now possible to generate gorgeous logs with:

git log --oneline --decorate

Or, if you want it even prettier (with color for the terminal):

git log --oneline --decorate --color

Piping that output to ChangeLog is what I currently use in all my projects, and it's simply amazing.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 6
    Another useful tag is `--graph`, which visually shows you which branches the commits are on. – Eruant Jul 21 '14 at 16:39
  • @Eruant I find this form to be more appropriate for a changelog, specifically. Since it does exactly that. Graph should be used more by users to find specific things. –  Jul 22 '14 at 01:32
  • 56
    I would strongly advise against using gift log diffs as a CHANGELOG: http://keepachangelog.com – Olivier Lacan Aug 30 '14 at 22:34
  • @OlivierLacan in this case, it gives the revisions numbers (so one can revert to them) and the commit messages (which in theory should effectively be a changelog) –  Sep 09 '14 at 16:53
  • 7
    copying ``git log`` output to changelog has no sense. You need to do a filtering and editing work to have a readable changelog, otherwise, why would you even need a changelog ? I think you can automate the generation of a changelog but please don't do raw copy of ``git log`` ! – vaab Mar 11 '15 at 03:44
  • 2
    `git log` is readable, people upvoting here have shown. Moreover it's not raw, `--oneline` and `--decorate` are options. It makes perfect sense to use it for software that has releases, where people download a tarball that does not include the .git directory and `git log` is thus unavailable. Please be objective with criticism. –  Mar 12 '15 at 04:22
  • 28
    The problem with this is that, even assuming that every contributor to your project writes clear and readable commit messages, you'll still be generating a "changelog" containing TONS of noise. Changelogs should be written with the goal of explaining to the *users* of your project the *notable* changes *relevant to them* that occurred between releases, whereas commit messages should be focused on explaining to *developers* what improvements your commit makes *to the code*. Sometimes there's overlap there, but not always. – Ajedi32 Jan 11 '16 at 17:29
  • 8
    Or, to make that a bit more concrete, this method will create a "change log" containing lots of entries like "Fixed spelling of fooMethod in ZModule" and "Refactor XModule to use new version of XYLibarary". Your users *don't care* about that. They want to know what changes were made from *their* perspective as users, not *your* perspective as a developer. And that's even ignoring stuff like "Merge PR #123 from xdev/foo" and "Opps, fixed newFeature so it actually works" type things that are likely to exist in any real-world repo. – Ajedi32 Jan 11 '16 at 17:41
  • 1
    Also, even commits that are relevant to a change log probably aren't phrased in a way that users can easily understand. E.g. "Fix YModule to correctly foo the bar" vs "Fix bug causing incorrect layout of blah blah for some users". git log entries *aren't* a viable substitute for a changelog. I guess answers like this are to be expected given the way the question was phrased though... – Ajedi32 Jan 11 '16 at 17:42
  • @OlivierLacan what’s described in keepachangelog.com is a NEWS file. But instead of saying “there’s the GNU standard, let’s build on it”, the site re-invents the next standard which contradicts the GNU standard (that requires a very strict format for ChangeLog files, focussing on developers, but has a loose format for NEWS which could have easily been used as a base for keepachangelog.com). – Arne Babenhauserheide Jun 04 '16 at 21:54
  • 2
    Just to add to this conversation, as the OP I guess I should have stated that I was looking for a _starting point_ for managing the changelog. Something that I could go through and edit out to make it more human readable, but something where I could easily see all of the changes. This method worked great for me because I keep fairly good commit messages, and I can obviously change or edit out the ones I don't care to include. – Topher Fangio Jun 21 '18 at 15:07
67

You can use some flavor of git log to help you out:

git log --pretty=%s                 # Only prints the subject

If you name your branches nicely, so that a merge to master shows up as something like "Merged branch feature-foobar", you can shorten things by only showing that message, and not all the little commits that you merged, which together form the feature:

git log --pretty=%s --first-parent  # Only follow the first parent of merges

You might be able to augment this with a script of your own, which could do things like strip out the "Merged branch" bits, normalize formatting, etc. At some point you have to write it yourself though, of course.

Then you could create a new section for the changelog once per version:

git log [opts] vX.X.X..vX.X.Y | helper-script > changelogs/X.X.Y

And commit that in your version release commit.

If your problem is that those commit subjects aren't anything like what you'd want to put in a changelog, you pretty much have two options: keep doing everything manually (and try to keep up with it more regularly instead of playing catch-up at release time), or fix up your commit message style.

One option, if the subjects aren't going to do it for you, would be to place lines like "change: added feature foobar" in the bodies of your commit messages, so that later you could do something like git log --pretty=%B | grep ^change: to grab only those super-important bits of the messages.

I'm not entirely sure how much more than that Git could really help you create your changelogs. Maybe I've misinterpreted what you mean by "manage"?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cascabel
  • 479,068
  • 72
  • 370
  • 318
66

TL;DR: You might want to check gitchangelog's own changelog or the ASCII output that generated the previous.

If you want to generate a changelog from your Git history, you'll probably have to consider:

  • the output format. (Pure custom ASCII, Debian changelog type, Markdown, REST, etc.)
  • some commit filtering (you probably don't want to see all the typos or cosmetic changes getting in your changelog)
  • some commit text wrangling before being included in the changelog. (Ensuring normalization of messages as having a first letter uppercase or a final dot, but it could be removing some special markup in the summary also.)
  • is your Git history compatible?. Merging and tagging is not always so easily supported by most of the tools. It depends on how you manage your history.

Optionally, you might want some categorization (new things, changes, bugfixes, etc.).

With all this in mind, I created and used gitchangelog. It's meant to leverage a Git commit message convention to achieve all of the previous goals.

Having a commit message convention is mandatory to create a nice changelog (with or without using gitchangelog).

Commit message convention

The following are suggestions to what might be useful to think about adding in your commit messages.

You might want to separate roughly your commits into big sections:

  • by intent (for example: new, fix, change, etc.)
  • by object (for example: doc, packaging, code, etc.)
  • by audience (for example: dev, tester, users, etc.)

Additionally, you could want to tag some commits:

  • as "minor" commits that shouldn't get outputted to your changelog (cosmetic changes, a small typo in comments, etc.)
  • as "refactor" if you don't really have any significant feature changes. Thus this should not also be part of the changelog displayed to final user for instance, but it might be of some interest if you have a developer changelog.
  • you could tag also with "API" to mark API changes or new API stuff...
  • ...etc...

Try to write your commit message by targeting users (functionality) as often as you can.

Example

This is the standard git log --oneline to show how this information could be stored::

* 5a39f73 fix: encoding issues with non-ASCII characters.
* a60d77a new: pkg: added ``.travis.yml`` for automated tests.
* 57129ba new: much greater performance on big repository by issuing only one shell command for all the commits. (fixes #7)
* 6b4b267 chg: dev: refactored out the formatting characters from Git.
* 197b069 new: dev: reverse ``natural`` order to get reverse chronological order by default. !refactor
* 6b891bc new: add UTF-8 encoding declaration !minor

So if you've noticed, the format I chose is:

{new|chg|fix}: [{dev|pkg}:] COMMIT_MESSAGE [!{minor|refactor} ... ]

To see an actual output result, you could look at the end of the PyPI page of gitchangelog.

To see a full documentation of my commit message convention, you can see the reference file gitchangelog.rc.reference.

How to generate an exquisite changelog from this

Then, it's quite easy to make a complete changelog. You could make your own script quite quickly, or use gitchangelog.

gitchangelog will generate a full changelog (with sectioning support as New, Fix...) and is reasonably configurable to your own committing conventions. It supports any type of output thanks to templating through Mustache, Mako templating, and has a default legacy engine written in raw Python; all current three engines have examples of how to use them and can output changelogs as the one displayed on the PyPI page of gitchangelog.

I'm sure you know that there are plenty of other git log to changelog tools out there also.

DISCLAIMER: I'm the author of gitchangelog of which I'll be speaking in the following.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vaab
  • 9,685
  • 7
  • 55
  • 60
30

A more to-the-point changelog:

git log --since=1/11/2011 --until=28/11/2011 --no-merges --format=%B
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harshniket Seta
  • 654
  • 7
  • 7
  • This kinda does it: I would add other parameters to `--format`, as explained in https://git-scm.com/docs/git-log#_pretty_formats – bluesmonk Jan 26 '18 at 15:52
25

The gitlog-to-changelog script comes in handy to generate a GNU-style ChangeLog.

As shown by gitlog-to-changelog --help, you may select the commits used to generate a ChangeLog file using either the option --since:

gitlog-to-changelog --since=2008-01-01 > ChangeLog

or by passing additional arguments after --, which will be passed to git-log (called internally by gitlog-to-changelog):

gitlog-to-changelog -- -n 5 foo > last-5-commits-to-branch-foo

For instance, I am using the following rule in the top-level Makefile.am of one of my projects:

.PHONY: update-ChangeLog
update-ChangeLog:
    if test -d $(srcdir)/.git; then                         \
       $(srcdir)/build-aux/gitlog-to-changelog              \
          --format='%s%n%n%b%n' --no-cluster                \
          --strip-tab --strip-cherry-pick                   \
          -- $$(cat $(srcdir)/.last-cl-gen)..               \
        >ChangeLog.tmp                                      \
      && git rev-list -n 1 HEAD >.last-cl-gen.tmp           \
      && (echo; cat $(srcdir)/ChangeLog) >>ChangeLog.tmp    \
      && mv -f ChangeLog.tmp $(srcdir)/ChangeLog            \
      && mv -f .last-cl-gen.tmp $(srcdir)/.last-cl-gen      \
      && rm -f ChangeLog.tmp;                               \
    fi

EXTRA_DIST += .last-cl-gen

This rule is used at release time to update ChangeLog with the latest not-yet-recorded commit messages. The file .last-cl-gen contains the SHA-1 identifier of the latest commit recorded in ChangeLog and is stored in the Git repository. ChangeLog is also recorded in the repository, so that it can be edited (e.g. to correct typos) without altering the commit messages.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
24

Since creating a tag per version is the best practice, you may want to partition your changelog per version. In that case, this command could help you:

git log YOUR_LAST_VERSION_TAG..HEAD --no-merges --format=%B
bithavoc
  • 1,539
  • 15
  • 20
11

I also made a library for this. It is fully configurable with a Mustache template. That can:

I also made:

More details are on GitHub: https://github.com/tomasbjerre/git-changelog-lib

From the command line:

npx git-changelog-command-line -std -tec "
# Changelog

Changelog for {{ownerName}} {{repoName}}.

{{#tags}}
## {{name}}
 {{#issues}}
  {{#hasIssue}}
   {{#hasLink}}
### {{name}} [{{issue}}]({{link}}) {{title}} {{#hasIssueType}} *{{issueType}}* {{/hasIssueType}} {{#hasLabels}} {{#labels}} *{{.}}* {{/labels}} {{/hasLabels}}
   {{/hasLink}}
   {{^hasLink}}
### {{name}} {{issue}} {{title}} {{#hasIssueType}} *{{issueType}}* {{/hasIssueType}} {{#hasLabels}} {{#labels}} *{{.}}* {{/labels}} {{/hasLabels}}
   {{/hasLink}}
  {{/hasIssue}}
  {{^hasIssue}}
### {{name}}
  {{/hasIssue}}

  {{#commits}}
**{{{messageTitle}}}**

{{#messageBodyItems}}
 * {{.}}
{{/messageBodyItems}}

[{{hash}}](https://github.com/{{ownerName}}/{{repoName}}/commit/{{hash}}) {{authorName}} *{{commitTime}}*

  {{/commits}}

 {{/issues}}
{{/tags}}
"

Or in Jenkins:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tomas Bjerre
  • 3,270
  • 22
  • 27
4

GNU style changelog

For a GNU style changelog, I've cooked the function:

gnuc() {
  {
    printf "$(date "+%Y-%m-%d")  John Doe  <john.doe@gmail.com>\n\n"
    git diff-tree --no-commit-id --name-only -r HEAD | sed 's/^/\t* /'
  } | tee /dev/tty | xsel -b
}

With this:

  • I commit my changes periodically to backup and rebase them before doing the final edit to the ChangeLog
  • then run: gnuc

and now my clipboard contains something like:

2015-07-24  John Doe  <john.doe@gmail.com>

        * gdb/python/py-linetable.c (): .
        * gdb/python/py-symtab.c (): .

Then I use the clipboard as a starting point to update the ChangeLog.

It is not perfect (e.g., files should be relative to their ChangeLog path, so python/py-symtab.c without gdb/ since I will edit the gdb/ChangeLog), but it is a good starting point.

More advanced scripts:

I have to agree with Tromey though: duplicating Git commit data in the ChangeLog is useless.

If you are going to make a changelog, make it a good summary of what is going on, possibly as specified at Keep a Changelog.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
3
git log --oneline --no-merges `git describe --abbrev=0 --tags`..HEAD | cut -c 9- | sort

Is what I like to use. It gets all commits since the last tag. cut gets rid of the commit hash. If you use ticket numbers at the beginning of your commit messages, they are grouped with sort. Sorting also helps if you prefix certain commits with fix, typo, etc.

orkoden
  • 18,946
  • 4
  • 59
  • 50
3

Based on bithavoc, the following lists the last tag until HEAD. But I hope to list the logs between two tags.

// Two or three dots between `YOUR_LAST_VERSION_TAG` and `HEAD`
git log YOUR_LAST_VERSION_TAG..HEAD --no-merges --format=%B

List logs between two tags:

// Two or three dots between two tags
git log FROM_TAG...TO_TAG

For example, this will list logs from v1.0.0 to v1.0.1:

git log v1.0.0...v1.0.1 --oneline --decorate

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
AechoLiu
  • 17,522
  • 9
  • 100
  • 118
3

I let the CI server pipe the following into a file named CHANGELOG for each new release with the date set in the release-filename:

git log --graph --all --date=relative --pretty=format:"%x09 %ad %d %s (%aN)"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lorenz Lo Sauer
  • 23,698
  • 16
  • 85
  • 87