If you publish code for others to use, it's very beneficial to also publish a Changelog. I won't rehash what's already written in Keep a Changelog. But I will share my conflict-free method for maintaining such a log.
The Method
Let's assume your changelog is in CHANGELOG.md
. Instead of accumulating unreleased changes at the top of that file in an "Unreleased" section, put each entry in a separate file:
- Create a
./changes/
directory - For any change worth noting, commit a new file in
./changes
namedCHANGETYPE-decription-of-change.md
. For example:fix-issue-2345.md
orbreaking-no-longer-support-cobol.md
- When it's time to make a new release, gather up the files and add them to the top of
CHANGELOG.md
, grouped and labeled by change type. - Then delete the files.
By using this method you don't have to fix conflicts in CHANGELOG.md
, either when merging or when backing out a change.
Other Options
Instead of using markdown snippets, you could use a more structured format if you want more structure (e.g. TOML, YAML, XML, JSON, etc...). I haven't needed this yet.
Example Script
It's fairly simple to automate this in your project's language. Here's an example of doing this with Nim.
import os
import strutils
import strformat
import times
import tables
if paramCount() < 1:
let prog = getAppFilename().splitFile.name
echo &"usage: {prog} NEWVERSION"
quit(1)
let today = now().format("yyyy-MM-dd")
let newversion = &"[{paramStr(1)}] - {today}"
let changedir = "changes"
# Option: You could make the script look in CHANGELOG.md and
# guess the next version based on the presence of fixes, breaking
# changes, or new features.
var changes = initOrderedTable[string, seq[string]]()
changes["breaking"] = @[]
changes["fix"] = @[]
changes["new"] = @[]
changes["doc"] = @[]
changes["misc"] = @[]
for file in changedir.walkDir:
let changetype = file.path.splitFile.name.split("-")[0]
if changes.hasKey(changetype):
var prefix = ""
case changetype
of "breaking": prefix = "**BREAKING CHANGE:** "
of "fix": prefix = "**FIX:** "
of "new": prefix = "**NEW:** "
changes[changetype].add("- " & prefix & file.path.readFile.strip)
else:
changes["misc"].add("- " & file.path.readFile.strip)
# Option: You could make this handle multi-line entries
echo &"## {newversion}"
echo ""
for entries in changes.values:
for line in entries:
echo line
# Option: You could make this update CHANGELOG.md in place
What the sample change snippets look like:
$ grep "" changes/*
changes/breaking-foo.md:I broke a thing
changes/doc-heyo.md:Fixed documentation
changes/fix-foo.md:Another thing
changes/fix-something.md:I did something
Running it:
$ nim c -r changelog.nim 1.2.0
## [1.2.0] - 2020-09-15
- **BREAKING CHANGE:** I broke a thing
- **FIX:** Another thing
- **FIX:** I did something
- Fixed documentation
Top comments (0)