3

I have a project were I use a rather uncommon tag strategy. We end up with tags being leafs so our history looks like this:

  0.3.0                        0.3.0
|/                               |
| 0.2.0      rather than       0.2.0
|/                               |
| 0.1.0                        0.1.0
|/                               |

The reason why I do this is that our tags should include the dist output whereas during development we don't want to commit such files into version control. So when I run the build, the build tool automatically branches off, adds the build artifacts (dist folder) with a generated commit and then creates the tag there.

This workflow might look odd at first glance but so far I have found it to be quite convenient as we need to have the dist folder in our tags for a downstream deployment process.

Now the problem is, I want to generate release notes automatically, just the problem is, how do I figure out the previous tag in such a scenario? I'm aware of the answers given here but with tags being leafs, this doesn't work that way.

Community
  • 1
  • 1
Christoph
  • 26,519
  • 28
  • 95
  • 133

5 Answers5

2

(edit: original rewarded answer down below, here's a much simpler way that tags merge bases so git describe can find them.

doit () 
{ 
    cleanup="`mktemp -t`";
    git for-each-ref refs/tags/"$1" --format='
           echo git tag -d base/%(refname:short) >> '"$cleanup"'
           git tag base/%(refname:short) $(git merge-base HEAD %(refname:short))
        ' | sh -x;
    result=`git describe --tags`;
    sh -x "$cleanup";
    rm "$cleanup"
    echo ${result#base/}
}
doit "0.*"

)


Use latest.awk from this answer this way:

doit ()
{
        git rev-list --topo-order --first-parent --children --tags --format=%d \
        | awk -f path/to/latest.awk \
        | sed -n /$1"/,$ { /"$1"/! {p;q} }"
}

Testing:

~/sandbox/15$ git lgdo --topo-order --tags
* ecb363d (tag: tag4) tag4
* cd9f402 master
| * 26aa94b (tag: tag3) tag3
|/
* a1b6c1b master
| * 8866091(tag: tag2)tag2
|/
* b5d5283 master
| * 29a4e54(tag: tag1)tag1
|/
* cfcd7dc master
* 6120ab4 (tag: empty)
~/sandbox/15$ doit tag2
29a4e54debae973dfc3955d6663f14d6ade73df9 (tag: tag1)
~/sandbox/15$

(edit: and

~/sandbox/15$ git checkout tag4
HEAD is now at ecb363d...  tag4
~/sandbox/15$ git describe --tags
tag4
~/sandbox/15$ doit `git describe --tags`
26aa94bad1e37602791c354823cb4a84ff6fc437  (tag: tag3)
~/sandbox/15$ 

)


(git lgdo being an alias for git log --graph --decorate --oneline, and the "master"s in there being commit-message artifacts of a make-me-some-commits helper)

Community
  • 1
  • 1
jthill
  • 55,082
  • 5
  • 77
  • 137
  • I would still recommend keeping a proper structure (tag on the main branch in one repo, and release branches in another as in my answer. But your approach seems to precisely answer the OP's question, so +1. – VonC Nov 21 '13 at 04:52
  • Looks good, just a lot of vodoo for this kind of thing. I think I'll just keep what I'm using currently and wait until our deployment process will be a bit smarter so that I can do the proper fix with tags being back on the master branch. But yep, it answers the question so I think you should deserve the bounty ;-) – Christoph Nov 21 '13 at 12:51
  • Thanks, and yep, that's some rigmarole right there. I see the value in your tag structure, being able to fetch the full-media distribution for the tag you want and automatically get just the source history for the rest isn't something to sneeze at. ... heh. Lemme edit in a much simpler way. – jthill Nov 22 '13 at 01:09
1

You need a different approach, with multiple repos (after all, Git is a distributed VCS):

  • one as you describe
  • one with release tags combined with release branches:

Your release is whatever latest commit you have on the release branch created for each release:

0.3.0--(dist) 0.3.0-branch
  |
0.2.0--(dist) 0.2.0-branch
  |
0.1.0--(dist) 0.1.0-branch
  |

That allows you to:

  • keep the tag on the main branch
  • deduce the release notes by easily finding commits between tag (for instance, git describe actually works here)
  • have a release branch dedicating to store your dist folder, but also ready for hot-fixes
  • tag more commits on said release branch when those hot fixes appear (0.1.1, 0.1.2, ...)

All you need to do is to create a tag on the top of the release branch, and push that tag (and branch) on a release repo which follows what your deployment system is expecting:

 0.3.0
|/
| 0.2.0
|/
| 0.1.0
|/   
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • yep, I know that would be better. However, our deployment currently just monitors for tags and picks them as versions to be made available in a user visible admin area. Unfortunately the deployment system can't be told to only monitor for tags with a given naming convention . So if I would go that way then all the regular tags (or branches) without the `dist` folder would go into the admin page, too. That in turn would mean it lists a lot of version which won't work if the user tried them. I know, actually it's the deployment system that should be fixed. But I don't have access to that code. – Christoph Nov 16 '13 at 23:45
  • Just to further clarify. I also can't tell it to monitor on new branches. I'm very limited with a simple listener on new tags and all tags with be handled equally then. :-/ – Christoph Nov 16 '13 at 23:57
  • @Christoph no problem. I have edited my answer to address the specific needs of your deployment system. – VonC Nov 17 '13 at 14:52
0

This is the best thing I was able to come up so far:

git tag | sort -n -t. -k1,1 -k2,2 -k3,3 | tail -1

The sort is just to sort them in a sane way as described here.

However, that doesn't really take into account where your current HEAD is. So if you checkout 0.2.0 and then run it you would still get 0.3.0 as the previous tag which is not quite correct. However, that's not a big issue for my use case but I would still be interested if someone can come up with something that actually works correctly.

Community
  • 1
  • 1
Christoph
  • 26,519
  • 28
  • 95
  • 133
0

If the 'leaf' tag has only 1 commit from main branch:

git log --pretty=format:"%d %h %s" | grep '^ (' | grep -A2 '0.3.0' | tail -n1

You got the line include previous version tag like:

 (0.2.0) b5f4956 Commit message

Use awk to grab tag in () or sha1 after () to continue your job.

You man adjust grep -A2 to -A1 or -B2 according you scenario.

Fwolf
  • 671
  • 4
  • 8
0

If I understood correctly, when your current head is on 0.3.0, you want to get 0.2.0, and when on 0.2.0, you want to get 0.1.0, and so on. You can do this in two steps:

  1. Get the current tag using git describe --tags

  2. Get the list of tags sorted by date, the line after the current tag should be the previous tag

For example:

current=$(git describe --tags)
git for-each-ref --sort=-authordate refs/tags --format '%(refname)' | \
  cut -d/ -f3 | grep -A1 -F $current | tail -n 1

Let me know if this is still not what you're looking for.

janos
  • 120,954
  • 29
  • 226
  • 236
  • It says `cut command not found`. I just tried to copy and paste it on the console. Am I doing it wrong? – Christoph Nov 22 '13 at 15:10
  • Copy paste from browser window to console works just fine for me. Weird. You don't have the `cut` command? Which sounds extremely unlikely. But just in case, you can replace that `cut` command with awk instead: `awk -F/ '{print $3}'` – janos Nov 22 '13 at 16:06
  • Mmh, then it says `awk command not found` but I have both `cut` and `awk`. They work fine as single command. I must confess I'm not a bash yoda so it could be I'm doing something stupid. But basically I just copy and pasted it in. :-/ – Christoph Nov 22 '13 at 19:34