Summary

A tag which is made on a git branch can be taken along with a merge, or left on the branch. The technique to "keep the tag on the branch it was originally set on" is to use --no-fast-forward when merging this branch. With --no-ff, a merge changeset is created to represent the received changes, and only that merge changeset ends up in the destination branch. This technique can be useful when there are two active lines of development, e.g. "stable" and "next", and it's necessary to be able to tag both lines independently.


Now I'll try to explain things in a more orderly fashion.


Git has a useful feature - git describe. It produces a compact, unique and readable description of a commit (Git calls 'commit' what in Bazaar is called 'changeset', a committed revision). In Tarantool, we use git describe when building a release, to produce a compact version identifier:
kostja@shmita:~/work/tarantool$ git describe
1.4.0-20-gc4c17ac
In the above output, "1.4.0" is the tag most recently set on the branch:


kostja@shmita:~/work/tarantool$ git tag | tail -1
1.4.0
"20" stands for the number of changesets (commits) on the branch after the tag, and "gc4c17ac" uniquely identifies the last commit.


A version identifier built in this way is excellent for troubleshooting or when the product has many special-purpose forks: 'describe' output can be used to produce an identical binary whenever it becomes necessary, regardless of the subsequent changes or merges.


It was to much of my disappointment, when I discovered that git describe "broke" when I introduced into the product a new line of development: 1.4.


Previously, Tarantool had only one "main" line of development: 1.3 release series. All features were developed on branches, and merged into master when ready. A number of tags existed on the master:


kostja@shmita:~/work/tarantool-old$ git tag
1.3
1.3.1
1.3.2
With release of Tarantool 1.3.4, we decided to "freeze" the current development series for new features, and start the new series - 1.4.


Thus, 1.3 series was moved on an own branch, "master-stable", where it was tagged 1.3.4, and the master branch was tagged 1.4.0.


And, the point of this post, git describe broke. For both branches, it would display the same tag value - the one that was set last.


The thing is, git tags do not belong to branches. Even though annotated tags are fully fledged objects, all tags are "global" in a repository, and are attached to the commits they are set on. If the commit, to which the tag is attached, belongs to the "trunk" of a branch, git describe believes that the tag belongs to the branch and uses it. Worse yet, since I intended to keep a patch flow going from master-stable (1.3) to master (1.4), any tag set on 1.3 (1.3.5, 1.3.6), was bound to end up in 1.4. Yuck.


The solution turned out to be very easy. To make sure that a tag doesn't end up in the destination branch, it was only necessary to have the commit, to which the tag is attached, "stay on the original branch". A merge with disabled "fast-forward" does exactly that -- creates a "merge" commit and adds it to both branches.
Here's how it may look like:


kostja@shmita:~/work/tarantool$ git checkout master
Already on 'master'
kostja@shmita:~/work/tarantool$ git tag -a 1.4.1 -m "Next development"
kostja@shmita:~/work/tarantool$ git describe
1.4.1
kostja@shmita:~/work/tarantool$ git checkout master-stable
Switched to branch 'master-stable'
kostja@shmita:~/work/tarantool$ git tag -a 1.3.5 -m "Next stable" 
kostja@shmita:~/work/tarantool$ git describe
1.3.5
kostja@shmita:~/work/tarantool$ git checkout master
Switched to branch 'master'
kostja@shmita:~/work/tarantool$ git describe
1.4.1
kostja@shmita:~/work/tarantool$ git merge --no-ff master-stable
Auto-merging CMakeLists.txt
Merge made by recursive.
 CMakeLists.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
kostja@shmita:~/work/tarantool$ git describe
1.4.1-2-g0a98576
PS Thanks to fr0sty from #git channel on irc.freenode.net for helping me understand the gist of what was going on.