CHANGELOG.md

I’ve seen some teams and projects having really nice CHANGELOG.md and release notes but knowing from experience, writing release notes by hand is a pain. Scrolling through git log, guessing which commits count as features or fixes, and hoping you don’t forget something important isn’t exactly fun.

In DevOps we often talk about removing toil and automating the boring things and I wanted to see if tbdflow could help with that. Since the tool already encourages Conventional Commits, we’ve got a clean, structured Git history ready to be turned into a CHANGELOG. So, I added a new command: changelog.

Generating a CHANGELOG in one command

The changelog command turns the commit history into a clear, readable, and nice-looking CHANGELOG automatically. I took inspiration of the formatting by release-please, and because I have used tbdflowto commit changes to tbdflow I had the advantage of having a pretty clean commit history.

I categorised the allowed types in sections:

/// Returns the section header based on the commit type.
fn get_section_header(commit_type: &str) -> &'static str {
    match commit_type {
        "feat" => "### ✨ Features",
        "fix" => "### 🐛 Bug Fixes",
        "perf" => "### 🚀 Performance Improvements",
        "refactor" => "### 🔨 Code Refactoring",
        "build" | "chore" | "ci" | "docs" | "style" | "test" => "### ⚙️ Maintenance",
        _ => "### Miscellaneous",
    }
}

Then I looped thru my git log, adding a header and links and printing it to stdout.

The changelog command

Let’s take a look at the two options we can use. The first one is looking at last release up until HEAD:

tbdflow changelog --unreleased

This will generate a nice CHANGELOG of unreleased changes which could be good to have as tbdflow sync only prints git log --graph --oneline -n 15 (you can see all Git commands tbdflow is using by adding --verbose before any other command, like tbdflow --verbose sync)

After a release we can run the changelog command with a range, to show all commits that happened within that span.

tbdflow changelog --from v0.13.0 --to v0.14.0

It groups commits by category and outputs something like this:

# [0.13.0](https://github.com/cladam/tbdflow/releases/tag/v0.13.0) (2025-08-20)

### 🔨 Code Refactoring
- **(commit):** moved commit handler to seperate file to keep main clean-er [`8975588`](https://github.com/cladam/tbdflow/commit/89755887be8322a6cd0739772731432278098a47)
- updated init command to handle populated remote repo [`87e9e5e`](https://github.com/cladam/tbdflow/commit/87e9e5ee25798f677f552506dfcef4cfa25247df)

### ⚙️ Maintenance
- **(release):** bump version and publish to GH Releases and crates.io [`428628c`](https://github.com/cladam/tbdflow/commit/428628c394cce08017e6e2a434799f900e8d198a)

Updating CHANGELOG.md

Most projects keep the latest release at the top of the changelog. Prepending text manually can be tricky as >> append is the default, but here’s the one-liner I use in tbdflow itself:

{ tbdflow changelog --from v0.12.0 --to v0.13.0; echo; cat CHANGELOG.md; } > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md

It generates the new section, adds a blank line, and then appends the old content; all in one step.

Wrapping Up

The new changelog command is another step in tbdflow’s mission to be a “friendly assistant” for your TBD workflow. It takes one more repetitive task off your plate, so you can spend more time writing code.

Take a look at tbdflow’s CHANGELOG, generated by the changelog command 🙂