Somewhat "advanced" tagging with git
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.