best way to versionize different git branches
We have the following scenario: We have several base versions of our game OpenLieroX; right now 0.57, 0.58 and 0.59. For each base version, we have a seperate branch. Each such base version has several releases (like 0.57 beta1-beta8 and rc1, 0.58 beta1-beta9).
When we are working on new stuff, we are working in the highest base version branch (right now that is 0.59). When we are fixing some reported bugs, we do that in the earliest version (mos开发者_运维百科tly 0.58) where that occured. From time to time, we always merge all changes in 0.58 into 0.59 (as long as we will still maintain and do changes on the old branch).
This all works really fine until it comes to some changes which we want to have only in 0.58 but not in 0.59. This happend only for one case so far: The version number. We have some Version.cpp file (and also some other files) which contains the version number. So, when we want to push a new release for 0.58, we change the versionstring in there to "0.58 beta10" (or whatever). Now, when we do the usual merging from 0.58 into 0.59, this change will also be applied. We fix such cases at the moment by just overwriting it again with the right version number (or in cases for other bad commits, probably a revert).
This detail about such unwanted changes seems to be a bit ugly to me. Is the way we manage this in general bad/uncommon? How would be the easiest way to do this to get the same result? Cherry-picking all commits of 0.58 in 0.59 would be much more work.
There is also one further detail which probably makes it more complicated: While working on the code, I have to set already the upcoming version number. This is because we have a network engine and we may have introduced some new functionality and there are checks in the code like 'if(client->version() >= Version(X,Y,Z)) ...'. Now, when we introduce something new, usually it means at some points also such checks. (But we are trying to avoid these changes in older branches.)
Another problem is that we don't just count the version up (like 0.58.1, 0.58.2, ...) but we count like this: 0.58 beta1, 0.58 beta2, ..., 0.58 betaX, 0.58 rc1, ..., 0.58, 0.58.1, 0.58.2, ... This is because we want to mark it as experimental for the beginning (beta stage) and then as mostly stable or stable. In some rare cases, there may be serious changes (maybe network protocol) even between two different beta releases (of course, we try to avoid them, but sometimes it's not possible without).
With Extra Branches
After 0.59 diverges from 0.58, you could use a separate “release” branch for the version number changes in 0.58. Each version (except the most recent) would have its own “release” branch that only contains merges from the base branch and changes that update the external version number. The branch structure might look something like this:
A--------o--B 0.58-release
/ /
...--o--o--o--o--o--o--o--o 0.58
\ \ \ \
\ \ \ \ C 0.59-release
\ \ \ \ /
o--o--o--o--o--o--o--o--o--o--o--o 0.59
\ \
o--o--o 0.60
- A labels the software “0.58 beta9”
- B labels the software “0.58 rc1”
- 0.58 has changes that have not yet been released
- C labels the software “0.59 beta1”
- 0.59 has changes that have not yet been released
- 0.60 is not yet fully up to date with 0.59
Or, if you are very strict in only making changes at A, B, C, etc. that change the external version number (no significant code changes, those belong on the ‘base’ branches: 0.58, 0.59, etc.), then you could do without the “release” branches. Instead you could use a detached HEAD or temporary branch (deleted after the release is versioned) to make the external-version-update commit and save it in a tag.
A B
/ /
...--o--o--o--o--o--o--o--o 0.58
\ \ \ \
\ \ \ \ C
\ \ \ \ /
o--o--o--o--o--o--o--o--o--o--o--o 0.59
\ \
o--o--o 0.60
- A labels the software “0.58 beta9” and is tag 0.58-beta9
- B labels the software “0.58 rc1” and is tag 0.58-rc1
- C labels the software “0.59 beta1” and is tag 0.59-beta1
Like Git Does It
You also might look into the way Git does its own versioning.
For builds done out of a Git working tree, the version number is generated from the output of git describe HEAD
. The Makefile knows which files need to be recompiled/rebuilt if the version number changes and it always runs the GIT-VERSION-GEN script to make sure it has the most recent version number. The version number is available in the Makefile by inclusion of the generated version file. It is passed to C files by a argument to the compiler (-DGIT_VERSION=…
) and it is substituted into scripts by using sed.
There are some provisions for overriding the version number “burned into” a build, but they are generally there only for builds done outside a working tree (e.g. a build done from a tree extracted from a tar file).
Mixing Version-String Changes With Other Changes
In your addendum to your question, you state that you need to adjust the version number while doing development. First, I think that the “0.58-release” branch scheme that seh and I have described can still work for you. It will just take more discipline to separate your changes. If you think about the *-release branches as “released for internal testing” and not just “released to customers (or for external testing)”, then it still makes sense. Always do your development on the base branch (e.g. “0.58”), and always merge the base branch into the release branch (e.g. “0.58-release”) before doing a build that will require a specific version number (always build from such a merged release branch).
If you insist on putting version-number changes and (non-merge) code changes in the same line of history, then is seems to me that you will have little choice but to deal with the conflict when merging (unless you go with git cherry-pick
(per Damien Wilson, or an automated edit script aimed at git rebase -i
).
If your “version files” only contain versioning information, you might be able to ease the conflict resolution by using .gitattributes
to mark your Version.cpp
file as unmergable.
.gitattributes in the dir that contains Version.cpp
/Version.cpp -merge
Marking it like this (same as merge=binary
) will always cause a conflict if the file is different between merged branches. The post-merge working-tree version will default to the version from the branch you have checked out (not the one from the branch(es) you are merging), so you can just git add Version.cpp && git commit
to finish the merge (assuming all other conflicts are also resolved).
It looks like what you're doing is the right way (for this result). But maybe you should reconsider your version system.
You could add a build version, 0.58.1, 0.58.2, etc. that way you can add as much stuff as you want, and you'll still collectively call it "0.58", even though it has a build number to it.
Upon release you could add beta, RC, whatever to it, but that isn't something the versioning system should care about.
The release "0.58.3 (beta1)" just means version 0.58.3. The beta1 part is human information and not a versioning system concern.
If I understand the problem, it sounds like you want to include a certain subset of commits from different branches. If that's the case, you may just need to run git cherry-pick
and only apply the commits you want.
More information on the command in the git docs and on Git Ready.
The tricky part is isolating the version designation from the underlying code.
You can float a version-designating branch on top of the released code branch, such that you can safely merge all of that code from your released branch (0.58) to your main branch (0.59) at any point, without mingling the conflicting version designations. That version-designating branch would never be merged back down to the released branch; you will merely rebase it on top of the released branch when you want to include new code in a versioned release.
You can arrange this easily with the following entry in your .git/config file:
[branch "0.58-release"]
remote = .
merge = refs/heads/0.58
rebase = true
With that, your release branch is called "0.58", and you'd make your versioned builds using the "0.58-release" branch. When it's time to make a release build, you'd issue the following commands:
git checkout 0.58-release
git pull
# Edit the version-designating file.
git commit -a -m'Updated version.'
All the changes to the version-designating files live only on the "58-release" branch, and can be moved forward safely with git rebase
. Alternately, if you want each change to the version-designating file to stick with a state of the code from the "0.58" branch, you could merge the "0.58" branch into the "0.58-release" branch rather than rebasing "0.58-release" on top of "0.58".
精彩评论