Moving from CVS to Git: $Id:$ equivalent?

2020-01-24 18:51发布

I read through a bunch of questions asking about simple source code control tools and Git seemed like a reasonable choice. I have it up and running, and it works well so far. One aspect that I like about CVS is the automatic incrementation of a version number.

I understand that this makes less sense in a distributed repository, but as a developer I want/need something like this. Let me explain why:

I use Emacs. Periodically I go through and look for new versions of the Lisp source files for third-party packages. Say I've got a file, foo.el, which, according to the header, is version 1.3; if I look up the latest version and see it's 1.143 or 2.6 or whatever, I know I'm pretty far behind.

If instead I see a couple of 40-character hashes, I won't know which is later or get any idea of how much later it is. I would absolutely hate it if I had to manually check ChangeLogs just to get an idea of how out of date I am.

As a developer, I want to extend this courtesy, as I see it, to the people that use my output (and maybe I'm kidding myself that anyone is, but let's leave that aside for a moment). I don't want to have to remember to increment the damn number myself every time, or a timestamp or something like that. That's a real PITA, and I know that from experience.

So what alternatives do I have? If I can't get an $Id:$ equivalent, how else can I provide what I'm looking for?

I should mention that my expectation is that the end user will NOT have Git installed and even if they do, will not have a local repository (indeed, I expect not to make it available that way).

17条回答
forever°为你锁心
2楼-- · 2020-01-24 19:38

The SHA is just one representation of a version (albeit canonical). The git describe command offers others and does so quite well.

For example, when I run git describe in my master branch of my Java memcached client source, I get this:

2.2-16-gc0cd61a

That says two important things:

  1. There have been exactly 16 commits in this tree since 2.2
  2. The exact source tree can be displayed on anyone else's clone.

Let's say, for example, you packaged a version file with the source (or even rewrote all the content for distribution) to show that number. Let's say that packaged version was 2.2-12-g6c4ae7a (not a release, but a valid version).

You can now see exactly how far behind you are (4 commits), and you can see exactly which 4 commits:

# The RHS of the .. can be origin/master or empty, or whatever you want.
% git log --pretty=format:"%h %an %s" 2.2-12-g6c4ae7a..2.2-16-gc0cd61a
c0cd61a Dustin Sallings More tries to get a timeout.
8c489ff Dustin Sallings Made the timeout test run on every protocol on every bui
fb326d5 Dustin Sallings Added a test for bug 35.
fba04e9 Valeri Felberg Support passing an expiration date into CAS operations.
查看更多
够拽才男人
3楼-- · 2020-01-24 19:38

This isn't an unreasonable request from the OP.

My use-case is:

  1. I use Git for my own personal code, therefore no collaboration with others.
  2. I keep system Bash scripts in there which might go into /usr/local/bin when they are ready.

I use three separate machines with the same Git repository on it. It would be nice to know what "version" of the file I have currently in /usr/local/bin without having to do a manual "diff -u <repo version> <version in /usr/local/bin>".

To those of you being negative, remember there are other use cases out there. Not everyone uses Git for collaborative work with the files in the Git repository being their "final" location.

Anyway, the way I did it was to create an attributes file in the repository like this:

cat .git/info/attributes
# see man gitattributes
*.sh ident
*.pl ident
*.cgi ident

Then put $Id$ somewhere in the file (I like to put it after the shebang).

The commit. Note that this doesn't automatically do the expansion like I expected. You have to re-co the file, for example,

git commit foo.sh
rm foo.sh
git co foo.sh

And then you will see the expansion, for example:

$ head foo.sh
#!/bin/sh

# $Id: e184834e6757aac77fd0f71344934b1cd774e6d4 $

Some good information is in How do I enable the ident string for a Git repository?.

查看更多
该账号已被封号
4楼-- · 2020-01-24 19:38

Something that is done with Git repositories is to use the tag object. This can be used to tag a commit with any kind of string and can be used to mark versions. You can see that tags in a repository with the git tag command, which returns all the tags.

It's easy to check out a tag. For example, if there is a tag v1.1 you can check that tag out to a branch like this:

git checkout -b v1.1

As it's a top level object, you'll see the whole history to that commit, as well as be able to run diffs, make changes, and merges.

Not only that, but a tag persists, even if the branch that it was on has been deleted without being merged back into the main line.

查看更多
劳资没心,怎么记你
5楼-- · 2020-01-24 19:38

If you're just wanting people to be able to get an idea how far out of date they are, Git can inform them of that in several fairly easy ways. They compare the dates of the last commit on their trunk and your trunk, for example. They can use git cherry to see how many commits have occurred in your trunk that are not present in theirs.

If that's all you want this for, I'd look for a way to provide it without a version number.

Also, I wouldn't bother extending the courtesy to anyone unless you're sure they want it. :)

查看更多
聊天终结者
6楼-- · 2020-01-24 19:39

If you want the git commit information accessible into your code, then you have to do a pre-build step to get it there. In bash for C/C++ it might look something like this:

prebuild.sh

#!/bin/bash
commit=$(git rev-parse HEAD)
tag=$(git describe --tags --always ${commit})
cat <<EOF >version.c
#include "version.h"
const char* git_tag="${tag}";
const char* git_commit="${commit}";
EOF

with version.h looking like:

#pragma once
const char* git_tag;
const char* git_commit;

Then, wherever you need it in your code #include "version.h" and reference git_tag or git_commit as needed.

And your Makefile might have something like this:

all: package
version:
  ./prebuild.sh
package: version
  # the normal build stuff for your project

This has the benefit of:

  • getting the currently correct values for this build regardless of branching, merging cherry-picking and such.

This implementation of prepublish.sh has the drawbacks of:

  • forcing a recompile even if the git_tag/git_commit didn't change.
  • it does not take into account local modified files that have not been committed but effect the build.
    • use git describe --tags --always --dirty to catch that use-case.
  • pollutes the global namespace.

A fancier prebuild.sh that could avoid these issues is left as an exercise for the reader.

查看更多
登录 后发表回答