views:

692

answers:

5

Let's say, a team of programmers is doing a web application in Perl and uses git to host their code. Now they have a small problem with versioning their modules:

  • Perl::Critic and PBP both recommend a RCS-backed $VERSION variable in code
  • git explicitly recommends against using replaceable revision numbers in code (with good reasoning)

I understand why git doesn't do keyword expansion. However, I can perfectly understand the need for revision numbers for a bit of code:

  • You do need a separate versioning for each module, since you might want to use versioned use
  • You probably don't want to change those version numbers for rapidly-changing module manually

A global product version for packaging and testing can be easily implemented with tags and git describe, but I still don't see a way to introduce automatic versioning for single modules.

Do you have any solution for me?

+3  A: 

I'd go for manually bumping versions when introducing incompatible changes, because, certainly not every change justifies the bump.

You may also want to checkout the 'gitattributes' manpage for the filter attribute — perhaps you will want to introduce your own filter for doing the substitutions based on 'git describe' output automatically.

Michael Krelin - hacker
Are you sure that a filter would help in that case? git doesn't have anyhing like revision numbers. So what would a filter do when you tell it to insert a version number somewhere?
innaM
Manni, filter would implement versioning based on tags and `git describe` output.
Michael Krelin - hacker
Basically, `git describe` output, which is nice when based on tags.
Michael Krelin - hacker
Thanks for the clarification.
innaM
@hacker: wouldn't you version every module with the same version number then? This is "global versioning", no problem with `git describe`. Module versioning is trickier than that -- and I wouldn't want to sacrifice git's performance...
rassie
You're welcome, I'll edit my answer to elaborate on that.
Michael Krelin - hacker
My gripe with a solution like that is that it is too manual. Maybe you are far more disiplined than I am, but I'd simply forget to tag individual modules.
innaM
rassie, it would. That doesn't make much sense, but it doesn't make much less sense that bumping version on each modification either. Of course you can add some module-related base to this versioning. But, anyway, you obviously will *have* to sarifice git's performance to some certain extent if you want automatic versioning. As I've said in the very beginning of my answer — I would avoid it.
Michael Krelin - hacker
Manni and you can't even tag individual modules unless they're in separate repositories. If they are, though, that shouldn't be a problem, because you would only have to tag relatively major releases and `git describe` will go from that. Like producing `1.0-12-babbababba` for the 12th commit after the 1.0 tag.
Michael Krelin - hacker
Again, thanks for the clarification. I only glanced at the git-tag man page.
innaM
@Manni: Use something like Module::Release to manage your development. Automate this stuff outside of source control so you don't forget a step.
brian d foy
A: 

How about something really lazy that is only a bit messy:

You could write a tiny wrapper for git-add. Before actually calling git-add the wrapper would nudge a version number somewhere in the module where it can be found with a simply regex.

Update:

I am not currently using something like that myself, but I'm seriously thinking about it.

My current reasoning:

  • Unlike CVS or subversion, git doesn't know anything about version or revision numbers.
  • Filter drivers would need to have a different source of information to be able to insert a version number and this source would need to be avaiable on the machine of any of the developers involved.
  • Thus the best source for a version number is the versioned file itself.
  • And for the file itself to be usable as a source for the next version number, the version number needs to be part of its corresponding git blob.

Benefits:

  • This is as automatic as it gets, there is no way to forget to update your version number.
  • Smooth transition from CVS/SVN to git. Every commit introduces a new version.

Drawbacks:

  • The lazy developer who always does git add . will nudge version numbers of files he didn't touch.
  • There doesn't seem to be a way to enforce the use of the wrapper script.
innaM
No need for a wrapper--this is what filter drivers do.
William Pursell
No. That's absolutely not what they do. A filter driver will smudge a file upon checkout and clean it upon add. What I am suggesting is a version number that actually makes it into the blob and thus the revision list.
innaM
That is, indeed, not the purpose of the filter, but `pre-commit` hook may do better than wrapper around `add`.
Michael Krelin - hacker
I thought that a pre-commit hook will only give you the opportunity to abort the commit and not change what is about to be commited?
innaM
Well, to tell you the truth, I don't know. But what would happen if it modifies file and stages modification?
Michael Krelin - hacker
@ the anonymous downvoter: I need some feedback here, but simply down-voting isn't that helpful.
innaM
+1 to the anonymous downvoter plea ;-) Want some feedback regarding where I went wrong too. It's clearer for your answer, for you say it's messy yourself and you know it is. Not worth a downvote IMO, but opinions differ.
Michael Krelin - hacker
BTw, `git add .` drawback can safely be dismissed, since the wrapper can be smart enough to know there were no changes. Besides, a developer doing `git add .` is a drawback itself ;-) And if you keep `git add .` there, then add `git commit -a` which is even worse.
Michael Krelin - hacker
+2  A: 

What do you use for building your Perl module?

The solution used by Linux kernel and by Git project itself is to replace "++VERSION++" (or "++PROJECT_VERSION++", or "@@PROJECT_VERSION@@") placeholders by the build system, in that case by Makefile. (In files in C it is done by defining "PROJECT_VERSION" preprocessor constant, via '-DPROJECT_VERSION=$(PROJECT_VERSION)' option passed to compiler). In your case that would probably be something like Module::Install, Module::Build, or ExtUtils::MakeMaker.

Both Git ans Linux kernel uses for that GIT-VERSION-GEN script which:

  1. uses git describe if project is built from within git repository,
  2. falls back on version (or VERSION, or GIT-VERSION-FILE) file if build is done from snapshot (this file is created when creating snapshot / archive),
  3. and finally falls back on pre-defined version number, which number is updated in commit which marks new released version (e.g. cb572206 commit in git repository, aka commit v1.6.4.4).

I hope that would help you.

Jakub Narębski
+14  A: 

Forget what Perl Best Practices says. It's not the bible, and it merely suggests using RCS keywords because at the time it was written no one was thinking about other source control systems. Your goal should never be compliance with PBP's particular implementation, but adapting the ideas in PBP to your own situation. Remember to read the first chapter of that book.

First, let's fix your assumptions:

  1. You don't need a separate version for each module in a distribution. You only need to give each module file a version that is different from that in previous distributions. Every module in the distro can have the same version, and when they do, they can all still be greater than the version from the last distro.

  2. Why not change the versions of a rapidly changing module manually? You should have defined points where your code becomes something that people can use. At those points, you do something to say that you've made the decision that your work product should be distributed, whether as a test or stable release. You change the versions as a way to tell people something about your development. When you let the source control system do that merely because you committing, you're losing your chance to denote cycles in your development. For instance, I typically use two place minor releases. That means I get 100 releases before the collating goes out of wack and I need to bump the major version to restore a proper sort order. That's not enough version space if I let the VCS handle this for me.

I used to use RCS keywords to link the versions of my modules to their checkin or revision number, but I never really liked that. I make many commits to a file before it's ready to be the next version, and I don't need $VERSION changing merely because I fixed a documentation typo. There would be big jumps in version numbers because I had made a lot of little changes.

Now I just change the versions of all of my module files when I'm ready to release a new distribution. I use the ppi_version to change all the versions at once:

ppi_version change 1.23 1.24

All of my module files get the same $VERSION. I don't need to use $VERSION to tell them apart because I use normal source control features to do that. I don't need $VERSION to tie it to a specific commit.

If I'm working toward a new distribution from version 1.23, I start making development versions 1.23_01, 1.23_02, and so on, but only when I'm ready to let people try those versions. I change the version at the start of the cycle, not the end. All of my commits leading up to the next release already have their next version. I also make notes about what I want that cycle to accomplish.

When I think it's the start of a new cycle, I bump the version again. When I think I have a stable release, I change the development $VERSION to a stable one, like 1.23_04 to 1.24. Whenever I release something, I tag it in source control too. I can see where my major points of development line up with source control quite easily.

Everything is much easier this way for me. Nothing is tied to the source control that I decide to use, so I don't have to redo everything if I change what I use.

brian d foy
Just to clarify: in my team I'm mostly the one who cries "F**k PBP!" ;-) I rather wanted to find out whether there is a better method than what I'm planning to do. ppi_version is just one such tip I hoped for, so thank you very much!
rassie
Thanks for the tip about ppi_version! http://search.cpan.org/dist/PPI-PowerToys/lib/PPI/PowerToys.pm
Drew Stephens
+4  A: 
  1. Forget having the value be managed by git, derived from git, or anything else.
  2. Use Dist::Zilla to manage your releases.
  3. Use dzil's PkgVersion plugin (or more likely, the Classic plugin bundle which includes it) to automatically set the $VERSION in all of your packages.
  4. Use the AutoVersion or BumpVersion dzil plugins to insure that the version number for each release is greater than the version number for all of the previous releases.
hobbs