orhun / git-cliff

A highly customizable Changelog Generator that follows Conventional Commit specifications ⛰️
https://git-cliff.org
Apache License 2.0
8.22k stars 165 forks source link

Include tags from other branches #56

Open hypervtechnics opened 2 years ago

hypervtechnics commented 2 years ago

Describe the bug Tags which are on release branches and not on the default branch are ignored and the commits are marked as Unreleased. We are migrating to release branches at the moment and therefore there are tags on the main branch.

To Reproduce Steps to reproduce the behavior:

  1. Create repository
  2. Create some commits and tags on the main branch
  3. Create release branch
  4. Do some changes on the release branch
  5. Set the tag on the release branch
  6. Execute git tags --list with the pattern used in the config file

Expected behavior The git tags are included as versions in the changelog. The diff for the commits is done by comparing 2.3.4...6.5.4

System:

orhun commented 2 years ago

Hi! If you want to include tags in a specific branch in your changelog, you can checkout to that branch. If this isn't what you want, can you clarify your use case with examples?

6. Execute git tags --list with the pattern used in the config file

Why this step is required for reproducing this problem?

The diff for the commits is done by comparing 2.3.4...6.5.4

I didn't get what you mean. What versions are you talking about?

hypervtechnics commented 2 years ago

I just wanted to tell that I am preparing an example to underline what I mean. I will post it in a couple days :) Thanks for your answer so far 👍🏼

hypervtechnics commented 2 years ago

Here is the sample:

# Setup
mkdir some-repository
cd some-repository
git init
git checkout -b main

# Create first commit
touch README.md
git add .
git commit -m "Initial commit"

# Init git-cliff
git checkout -b feature/git-cliff-init
git cliff --init
git add .
git commit -m "Use git cliff"

# Merge init git-cliff
git checkout main
git merge --squash --no-edit feature/git-cliff-init
git commit -m "chore: Init git cliff"
git branch -d feature/git-cliff-init

# Create first feature
git checkout -b feature/a
echo "# Feature A\n" >> README.md
git add .
git commit -m "Implement feature a"
echo "- Lorem ipsum\n\n" >> README.md
git add .
git commit -m "Implement feature a further"

# Merge first feature
git checkout main
git merge --squash --no-edit feature/a
git commit -m "feat: Section A in README"
git branch -d feature/a

# Create second feature
git checkout -b feature/b
echo "# Feature B\n" >> README.md
git add .
git commit -m "Implement feature b"
echo "- Ipsum lorem\n\n" >> README.md
git add .
git commit -m "Implement feature b further"

# Merge second feature
git checkout main
git merge --squash --no-edit feature/b
git commit -m "feat: Section B in README"
git branch -d feature/b

# Create release for 1.0.0
git checkout -b release/v1.0
git tag --annotate v1.0.0 --message "Release of version 1.0.0"

git checkout main

# Create first bugfix
git checkout -b bugfix/1
echo "> Bugfix.\n" >> README.md
git add .
git commit -m "Fix missing footer"

# Merge first bugfix
git checkout main
git merge --squash --no-edit bugfix/1
git commit -m "fix: add footer"
git branch -d bugfix/1

commitId=$(git rev-parse HEAD)

# Create release for 1.0.1
git checkout release/v1.0
git cherry-pick -x $commitId
git tag --annotate v1.0.1 --message "Release of version 1.0.1"

git checkout main

# Create third feature
git checkout -b feature/ab
echo "# Feature A/B\n" > README.md
git add .
git commit -m "Implement new feature combining previous ones"

# Merge third feature
git checkout main
git merge --squash --no-edit feature/ab
git commit -m "feat!: Section B is now combined with A"
git branch -d feature/ab

# Create release for 2.0.0
git checkout -b release/v2.0
git tag --annotate v2.0.0 --message "Release of version 2.0.0"
user@machine:/projects/git/some-repository$ git cliff
# Changelog

All notable changes to this project will be documented in this file.

## [2.0.0] - 2022-02-23

### Bug Fixes

- Add footer

### Features

- [**breaking**] Section B is now combined with A

## [1.0.0] - 2022-02-23

### Features

- Section A in README
- Section B in README

### Miscellaneous Tasks

- Init git cliff

<!-- generated by git-cliff -->

As you can see the tag v1.0.1 is not listed here.

user@machine:/projects/git/some-repository$ git log v1.0.0...v1.0.1
commit 3b4a83ebeafb8 (tag: v1.0.1, release/v1.0)
Author: me <me@me.com>
Date:   Wed Feb 23 18:17:08 2022 +0100

    fix: add footer

    (cherry picked from commit b713025b0b15)
user@machine:/projects/git/some-repository$ git log v1.0.1...v2.0.0
commit 6a5d3d6ad541 (HEAD -> release/v2.0, tag: v2.0.0, main)
Author: me <me@me.com>
Date:   Wed Feb 23 18:17:11 2022 +0100

    feat!: Section B is now combined with A

commit 3b4a83ebeafb8 (tag: v1.0.1, release/v1.0)
Author: me <me@me.com>
Date:   Wed Feb 23 18:17:08 2022 +0100

    fix: add footer

    (cherry picked from commit b713025b0b15)

commit b713025b0b15
Author: me <me@me.com>
Date:   Wed Feb 23 18:17:08 2022 +0100

    fix: add footer

grafik

orhun commented 2 years ago

git-cliff operates on a single branch. So in order to include tags from another branch, you should simply have them in the branch that you are working on. It works like the git log --simplify-by-decoration command while parsing the commit history:

--simplify-by-decoration: Commits that are referred by some branch or tag are selected.

When you run git-cliff on the main branch, only the commits that are in the main branch are used to create a common Release object. During this process, each tag is being evaluated for associating it with a commit. However, since your v1.0.1 tag is not on the main branch, it cannot be associated with a commit and it is simply ignored.

* 07da03a (HEAD -> release/v2.0, tag: v2.0.0, main) feat!: Section B is now combined with A
* 5a09ecf fix: add footer
| * a814f87 (tag: v1.0.1, release/v1.0) fix: add footer ---------------> tag is here
|/  
* 920934b (tag: v1.0.0) feat: Section B in README
* 59e6734 feat: Section A in README
* 65cd0fb chore: Init git cliff
* 7f21e6e Initial commit

In this case, I recommend creating a tag after you checkout to the main branch.

* 07da03a (HEAD -> release/v2.0, tag: v2.0.0, main) feat!: Section B is now combined with A
* 5a09ecf (tag: v1.0.1) fix: add footer ------------------------------> tag should be created here
| * a814f87 (release/v1.0) fix: add footer
|/  
* 920934b (tag: v1.0.0) feat: Section B in README
* 59e6734 feat: Section A in README
* 65cd0fb chore: Init git cliff
* 7f21e6e Initial commit

A possible question is, why can't git-cliff do this? Well, including tags from different branches mean including commits as well which will further complicate the usage and might have side effects due to combining n different histories. Maybe this could be done after #13 since that issue also requires combining histories.

gabyx commented 1 year ago

@orhun I completely agree, including other unvisible branchs from the main branch when going through the log, is pretty darn complicated. It will boil down to basically parsing the main branch then parsing all other refs to see if they merge into the main branch, and then include these sub-changelogs into the main one. Its not easy, but should if implemented be done in such a way. Its a hard and non-trivial task.

I came also across these issues with https://github.com/release-lab/whatchanged which does something, but actually not really correct. Meaning the above description.

friday commented 8 months ago

You can already do this now for some cases, but not in an optimal way.

Assuming you are maintaining a product with some form of support contract where you have to backport bug and security fixes for the supported branch and maintain that for a specific time period... That means that at one point you branched off the main branch to create the support branch, and you keep the main branch as the development branch, backport only fixes to the support branch. You eventually stop supporting it, but you never merge it back into the main branch.

Assuming these branches are named v1 and v2.

Then you can generate the release notes for v1 with git cliff v1... If you deleted the v1 branch you can use the latest tag from v1 instead.

Then you can generate the release notes for v2 with git cliff $(git merge-base v1 v2)..v2, which will specify the range since v2 branched off from v1 (or the other way around).

If you also have a v3 branch you can keep doing this git cliff $(git merge-base v2 v3)..v3

You can append/prepend these to the same output, but they will be individually sorted by date, so all the v2 releases before all the v1 releases :(

I would love if if we could support multiple commit ranges, like we now support multiple repositories, ex: git cliff v1.. $(git merge-base v1 v2)..v2 $(git merge-base v2 v3)..v3. Or better yet if git cliff could handle it for us git cliff --branches "v1,v2,v3"

Edit: Created this script for doing this(it's really inefficient/slow, but it works).

#!/bin/bash
VERSION_REGEX=^v[0-9]{1,}.[0-9]{1,}.[0-9]{1,}$ # semver stable release tags
OUTPUT_FILE=RELEASE_NOTES.md

echo "" > $OUTPUT_FILE

# get all tags by creation date, filtered by version regex
for VERSION in $(git --no-pager tag --sort=creatordate | grep -Eo $VERSION_REGEX); do
  # Get the closest ancestor tag that complies to the regex
  PARENT=$(git --no-pager log --pretty="%D" $VERSION^ | cut -c 6- | grep -Eo $VERSION_REGEX | head -1)
  if [ -z "$PARENT" ]; then
    # if there is no such tag, use the initial commit
    PARENT=$(git rev-list --max-parents=0 HEAD)
  fi
  git-cliff "$PARENT..$VERSION" --prepend $OUTPUT_FILE
done

# If your template had a header you have to add it later now.
echo -e "# Release Notes\n\n$(cat $OUTPUT_FILE)" > $OUTPUT_FILE