David
David

Reputation: 10738

generate changelog from git log including the current commit message

I'm trying to use git hooks to auto-generate a changelog from the commits. I have this in the commit-msg hook

#!/bin/sh

git log --oneline > ./CHANGELOG.txt
cat ./CHANGELOG.txt | while read line; do echo "* $line"; done > ./CHANGELOG.md
MSG=$(cat $1)
sed -i "1i* [CURRENT] $MSG" ./CHANGELOG.md
sed -i '1i# CHANGELOG\n' ./CHANGELOG.md
git add ./CHANGELOG.md
rm -rf ./CHANGELOG.txt

The problem is that the change doesn't get included in the current commit. After a commit, my working tree is immediately dirty because the CHANGELOG.md file change didn't get included with the last commit.

I initially had this in the pre-commit hook but didn't have access to the current commit message. Is this even possible?

Upvotes: 1

Views: 2393

Answers (3)

Andrei Vukolov
Andrei Vukolov

Reputation: 564

This is quite an old question, but to address it again, I have just created a soft and tiny helper utility, mkchangelog, for the simple CI pipeline. It is written in pure Bash, so there are no additional dependencies. The sources are, of course, open, under an MIT license.

https://github.com/twdragon/mkchangelog

The building routine may look like:

TAG_STRING=$(git describe --tags --abbrev=0)
COMMIT_LAST=$(git rev-parse --short HEAD)
CLEAR_VERSION_STRING=$(echo -e "${TAG_STRING}" | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+')
VERSION_STRING="${CLEAR_VERSION_STRING}+git${COMMIT_LAST}"
LAST_MESSAGES=$(git log ${TAG_STRING}..HEAD --pretty=format:"  * %s")
    tee "${TEMPLOG}" > /dev/null << EOM
${PACKAGE_CODENAME} (${VERSION_STRING}) ${DISTRO_CODENAME}; urgency=${PACKAGE_URGENCY}

  [ ${PACKAGE_VENDOR_DESCRIPTION} ]
EOM
echo "${LAST_MESSAGES}" | sed '/^$/d' | sed -e 's/^-\ \+/\ \ \*\ /;s/^\ \+-\ \+/\ \ \ \ /' | sed -E 's/\(*\[(.*)\]\((.*)\)./ \2\ \1/g' | fmt -w 80 -s | tee --append "${TEMPLOG}" > /dev/null
tee --append "${TEMPLOG}" > /dev/null << EOM
  * END OF UNRELEASED CHANGES
EOM
tee --append "${TEMPLOG}" > /dev/null << EOM

 -- ${PACKAGE_VENDOR_NAME} <${PACKAGE_VENDOR_EMAIL}>  ${TIMESTAMP}

EOM
PREV_TAG=$(git describe --tags --abbrev=0 "${TAG_STRING}^")
LAST_MESSAGES=$(git log ${PREV_TAG}..${TAG_STRING} --pretty=format:"  * %s")
tee --append "${TEMPLOG}" > /dev/null << EOM
${PACKAGE_CODENAME} (${CLEAR_VERSION_STRING}) ${DISTRO_CODENAME}; urgency=${PACKAGE_URGENCY}

  [ ${PACKAGE_VENDOR_DESCRIPTION} ]
EOM
echo "${LAST_MESSAGES}" | sed '/^$/d' | sed -e 's/^-\ \+/\ \ \*\ /;s/^\ \+-\ \+/\ \ \ \ /' | sed -E 's/\(*\[(.*)\]\((.*)\)./ \2\ \1/g' | fmt -w 80 -s | tee --append "${TEMPLOG}" > /dev/null
TAG_CREATED=$(git log -1 --format=%ct "${TAG_STRING}")
TAG_TIMESTAMP=$(date -d "@${TAG_CREATED}" +"%a, %d %b %Y %H:%M:%S %z")
tee --append "${TEMPLOG}" > /dev/null << EOM

 -- ${PACKAGE_VENDOR_NAME} <${PACKAGE_VENDOR_EMAIL}>  ${TIMESTAMP}

EOM

Upvotes: -1

Evandro Coan
Evandro Coan

Reputation: 9428

This is very easy, but requires a complex/trick algorithm. It is well explained and pointed out already on this answer on the other question: https://stackoverflow.com/a/38494680/4934640

Copy:

Yes, you can add generated files automatically on the commit using git hooks! But it requires a tricky script.

Here you can find the problem solved. There, it is updating the file version on every commit, adding a new modified file and amending the commit as you need it to. It is fully working: https://github.com/addonszz/Galileo/tree/master/githooks

Then you just replace the 'Version File Replacement' algorithm on the file 'updateVersion.sh', by your algorithm. Maybe you need to change a few things like, remove the branch limitation, because there, the script only runs if you are on the 'develop' branch.

Also, it will only change the specified file, if is staged. If the file is not staged, then it will do nothing than the normal/usual commit. More precisely, it print out what it is doing on every step.

I am going to explain, that trick. It is quite tricky. On the prepare-commit-msg-hook, it detects whether the desired file is being staged and committed. After that, it creates a flag file, and stops the prepare-commit-msg-hook. Later on the post-commit-hook, it checks whether the flag file exists. If yes, it amends the files on the commit.

Attention, it would create a infinity loop because it would call again the prepare-commit-msg-hook (as we are amending). But it does not happen because of the flag file. When the prepare-commit-msg-hook runs and find the flag file, it "knows" what is happening. Then is just deletes the flag file and do not create it again. Doing it, it will block the post-commit-hook from amending again the commits.

Upvotes: -1

pherris
pherris

Reputation: 17703

It looks like you need to use a combination of commit-msg hook and post-commit. In the commit-msg hook you write a file that tells the post-commit hook to take the last commit message and add it to the log. The post-commit hook then commits the change log with the --no-verify flag to skip the commit-msg hook so you don't get into an infinite loop:

commit-msg

touch .commit

post-commit

#!/bin/sh
if [ -a .commit ]
    then
    rm .commit
    git log -1 HEAD > tmpfile && cat COMMIT.md >> tmpfile && mv tmpfile COMMIT.md
    git add COMMIT.md
    git commit --amend -C HEAD --no-verify
fi
exit

I'm not sure how this would work in a headless state, but it should help. This question has most of what you are looking for: https://stackoverflow.com/a/12802592/1861459

Upvotes: 1

Related Questions