generate-changelog turns conventional commits into a release changelog
Generate a changelog from git commits.
At a glance
- What is it?
- This small MIT licensed npm module reads your commit history, groups the commits by their conventional commit type, and writes the entries into CHANGELOG.md, with the release scripts ordered so the changelog commit lands inside the version tag. It is the tool Lob uses to release its own packages.
- Who is it for?
- Adopt generate-changelog when your commits already follow the conventional commit format and you want a changelog that is written before the version tag is cut, so the tag contains the changelog commit. Do not adopt it if your commit messages are free-form, since the grouping depends entirely on the `type(category): description` shape and unknown types fall under `other`.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 74 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A changelog that is committed before the tag
The module's purpose is one line: generate a changelog from git commits. What makes it worth reading is the second half of that README paragraph, which explains the ordering constraint it is designed around.
The intent is that for every patch, minor or major version, you update the changelog prior to running `npm version`, so that the git tag contains the commit that updated both the changelog and the version.
That is a specific and slightly unusual design decision. The common alternative is to generate the changelog as part of or after the release, which means the tag points at a commit where the changelog is stale or absent. Here the release script writes the changelog, commits it, and only then runs `npm version`, so the tag contains both changes and a reader who checks out the tag sees the notes for that release in the tree.
The mechanism is conventional commits. The commit messages have to be in the format `type(category): description [flags]`, and the type has to be one of `breaking`, `build`, `ci`, `chore`, `docs`, `feat`, `fix`, `other`, `perf`, `refactor`, `revert`, `style` or `test`. The category can be anything you choose. The flag list has one entry, `breaking`, which alters the type to mark a breaking change, and it must be surrounded in square brackets.
One fallback is built in: if you use a type that is not in the list but still follows the same format of the message, the commit is grouped under `other`. There is also a `--allow-unknown` flag on the CLI to change that behaviour, which suggests the default is deliberately conservative.
The project is JavaScript, MIT licensed, at version 1.8.0 in the package manifest, with the last push on 2026-08-11 and no GitHub releases published.
Installing it as a dependency or globally
There are two installation routes, and the README gives both:
$ npm i generate-changelog -D # install it as a dev dependency
# OR
$ npm i generate-changelog -g # install it globallyThe first is the dev dependency route, which is what the recommended usage assumes, because the tool is referenced from npm scripts in your package.json rather than run by hand. The second installs it globally so it is available to every repository on your machine.
The reason the global route exists is the CLI, which writes to a file in the current working directory and therefore has to be run from the repository root. A globally installed binary works from anywhere, while a dev dependency works only after you have run `npm install` in the project.
The package manifest registers two binary names for the same entry point, `generate-changelog` and `changelog`, both pointing at `./bin/generate`, and the library entry point is `./lib/index.js`. The repository layout follows that split, with a `bin/` directory, a `lib/` directory and a `test/` directory, alongside `CHANGELOG.md` and `CONTRIBUTING.md`.
Two smaller details in the manifest are worth a look. The dependencies are just three: `bluebird` for promises, `commander` for the CLI option parsing, and `github-url-from-git`, which is how the tool discovers the repository URL. That last one explains the `-u` option's default behaviour, since the fallback is to derive the URL from the git remote rather than from the package.json.
The `keywords` array lists changelog, cli, npm, version, git and semver, and the author is Lob at [email protected], which matches the README's statement that the tool is used to release this module itself.
The CLI options, and what each one changes
The CLI section of the README prints the full help output, which is the quickest way to see the surface:
$ changelog -h
Usage: generate [options]
Generate a changelog from git commits.
Options:
-h, --help output usage information
-V, --version output the version number
-p, --patch create a patch changelog
-m, --minor create a minor changelog
-M, --major create a major changelog
-t, --tag <range> generate from specific tag or range (e.g. v1.2.3 or v1.2.3..v1.2.4)
-x, --exclude <types> exclude selected commit types (comma separated)
-f, --file [file] file to write to, defaults to ./CHANGELOG.md, use - for stdout
-u, --repo-url [url] specify the repo URL for commit links, defaults to checking the package.json
-a, --allow-unknown allow unknown commit typesThe three release flags select which commits are included: a patch changelog takes the commits since the last patch, a minor takes the new features, and a major takes the breaking changes. That is the whole point of the conventional commit typing, since the tool can tell a `feat` from a `fix` and sort them accordingly.
The `-t` option takes a specific range rather than deriving one, and the help text gives both forms, a single tag such as `v1.2.3` or a range such as `v1.2.3..v1.2.4`. The README shows that used directly on a commit range and on a pair of release branches:
generate-changelog 420c945...2a83752generate-changelog release/3.1.2822...release/3.1.2858The second example is interesting because it shows the tool working with branch names as the range endpoints, which is what you would use when a release is cut from a release branch rather than from the default branch.
Two options change the output rather than the input. `-f` chooses the file, defaulting to `./CHANGELOG.md`, and the value `-` writes to stdout instead, which is the option that makes the tool composable with other scripts. `-u` sets the repository URL used for the commit links, and the default is to check the package.json.
And `-x` excludes commit types by a comma separated list, which is how you keep `chore` and `style` noise out of a release note.
The release scripts that do the whole dance
The recommended section gives three npm scripts, and they are the actual integration you would copy into your own package.json:
"release:major": "changelog -M && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version major && git push origin && git push origin --tags",
"release:minor": "changelog -m && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version minor && git push origin && git push origin --tags",
"release:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version patch && git push origin && git push origin --tags",Read the order carefully, because it is the whole design. The changelog is generated first, then the file is staged and committed with the message `updated CHANGELOG.md`, then `npm version` runs, which bumps the version and creates the tag pointing at that commit, and only after that does the script push the branch and then the tags.
So the tag contains both the version bump and the changelog entry, which is what the README's opening paragraph promised. And the push of the branch happens before the push of the tags, which is the correct order, since a tag that exists on the remote before its commit does would be unreachable.
The package manifest in this repository carries the same three scripts, with one difference: they invoke `./bin/generate` directly rather than the installed `changelog` binary, because the project releases itself with the local checkout rather than with the published package.
The other scripts in the manifest show the development setup. `lint` runs eslint, `test` runs `NODE_ENV=test istanbul cover _mocha -- test --recursive --timeout 30000`, and `test-no-cover` runs the same mocha suite without the istanbul coverage wrapper, which is the faster loop while you are writing a test. The coverage report is then piped to coveralls.
So the tool is small but the project around it is a complete one: eslint with an `.eslintrc` and an `.eslintignore`, an `.editorconfig`, a `.npmignore` for what goes into the published package, and a `.travis.yml` for continuous integration.
Calling the generate function directly
There is a third route, and it is the one to reach for if you want to do something with the entries other than write them to a file.
You can write a script that calls the `generate` function and does whatever you want with the new logs:
var Changelog = require('generate-changelog');
var Fs = require('fs');
return Changelog.generate({ patch: true, repoUrl: 'https://github.com/lob/generate-changelog' })
.then(function (changelog) {
Fs.writeFileSync('./CHANGELOG.md', changelog);
});The options object mirrors the CLI flags, with `patch` selecting the release level and `repoUrl` supplying the link base. The return value is a promise, which is why the promise library is a dependency, and the resolved value is the changelog text itself.
That is what makes the module composable. You can write the output somewhere other than a markdown file, you can post it to a release page, you can split it by category, or you can run it over several ranges and concatenate. The CLI is a thin wrapper over this function.
The one thing to note is that the generated text is what the function resolves to, so any formatting decisions, including how the categories are laid out and how commit links are rendered, are made inside the library rather than in your script. If you need a different shape, the function gives you the string and you post-process it.
The README also mentions a GitHub Actions route, using the `generate-changelog-action` action from the GitHub marketplace, for running the same generation inside a workflow.
The limits of a commit-message convention
The main limitation is not a bug but a dependency, and it should be checked before you install anything.
The tool parses your commit history. If your team does not already write conventional commits, the output is either empty or a single `other` bucket, and no flag fixes that, because the information is not in the history. Adopting this tool is really adopting the commit format, and the format is a team agreement rather than a configuration.
The second limitation is the same from the other direction. The type list is fixed at thirteen values, and a commit with an unknown type is grouped under `other` unless you pass `--allow-unknown`. So a team that invents a type, `deps` or `refactor` variants beyond the list, will find those commits either hidden in `other` or only visible when the flag is on. That is a deliberate default, since an unrecognised type probably means a typo, but it means the flag needs to be a decision rather than an afterthought.
There is also a limit on what a changelog can say. The entries come from commit subjects, so they are as good or as bad as your commit writing. A changelog generated from well written conventional commits reads well; the same tool over a history of `fix` and `wip` produces a wall of noise that nobody reads. The `-x` exclusion option exists for exactly that, since `chore` and `style` commits are usually noise in a release note.
The alternative approaches are worth naming. The current generation of release tooling has moved toward conventional commits as a standard, and several ecosystems now ship their own changelog generators, so the real comparison is usually between this module and a generator for your ecosystem rather than between this module and a hand-written changelog. A hand-written changelog is more accurate and more work, and it is still the right answer for a project with a small number of releases a year. A service that derives notes from your issue tracker rather than your commits is a third option, and it produces better prose for users who are not developers.
For a JavaScript package released several times a month, where the release notes exist mainly for the people installing from npm, this module is a good fit precisely because it costs nothing to keep current. That value disappears if you are not releasing often.
Editorial conclusion
Adopt generate-changelog when your commits already follow the conventional commit format and you want a changelog that is written before the version tag is cut, so the tag contains the changelog commit. Do not adopt it if your commit messages are free-form, since the grouping depends entirely on the `type(category): description` shape and unknown types fall under `other`. Verify first that your history can be parsed, that the `-u` repo URL is correct for your project, since the default reads the package.json, and that the release scripts you copy match your branching model, since the ones published here push straight to origin with tags.
Frequently asked questions
What does generate-changelog do?
It generates a changelog from git commits. Your commit messages must follow the conventional commit format `type(category): description [flags]`, and the tool groups the commits by type to build the changelog entries.
How do I install and run generate-changelog?
Install it as a dev dependency with `npm i generate-changelog -D` or globally with `npm i generate-changelog -g`. The CLI exposes two binary names, `generate-changelog` and `changelog`, and writes to ./CHANGELOG.md by default, or to stdout when the file option is `-`.
What commit types does generate-changelog understand?
The listed types are breaking, build, ci, chore, docs, feat, fix, other, perf, refactor, revert, style and test. A commit that follows the format but uses an unrecognised type is grouped under `other`, unless you pass the `--allow-unknown` flag.
How should I wire generate-changelog into my release?
As npm scripts. The README's release:patch script runs `changelog -p`, stages and commits CHANGELOG.md with the message 'updated CHANGELOG.md', then runs `npm version patch`, then pushes the branch and the tags, so the tag contains both the changelog update and the version bump.
Can I use generate-changelog from a script instead of the CLI?
Yes. Require the module and call `Changelog.generate({ patch: true, repoUrl: '...' })`, which returns a promise resolving to the changelog text, and then write it wherever you want. The CLI is a thin wrapper over that function.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lob-generate-changelog)Was this useful?