How do I enable the ident string for a Git repository?

Solution 1:

Summary: The recommended way of embedding version information in a product is to use the build system for that; see below for details and alternate approaches.


In Git (and I think usually also in other VCS systems with atomic commits) there is no such thing like version of a single file.

Git does support on-demand expansion of $Id:$ keyword, but:

  1. It is done on request only. You have to specify (perhaps using globbing pattern) that a file (or a set of files) has an ident attribute set (in '.gitattributes' file in tree, or in '.git/info/attributes' for local repository settings).
  2. It expands to the SHA-1 of file contents (or to be more exact to $Id:<sha-1 of blob>$). The reason for this choice is that Git does not touch files that have not changed during branch switching or rewinding; if '$Id:$' expanded to revision info it would require to update every version-controlled file e.g. when switching branches.

Git supports quite a wide set of $Format:...$ placeholders which expands to commit information (e.g. $Format:%H$ replaced by a commit hash) but:

  1. Expansion is done only when running git archive, in its output file.
  2. It is done on request, controlled via export-subst attribute.

The recommended way of embedding version information is to do it via the build system (in a build stage); see for example Git Makefile and GIT-VERSION-GEN script used by Makefile in the Git web interface for the git.git repository.

You can however (ab)use a clean/smudge filter driver (via filter attribute) to get CVS-like keyword expansion, expanding keywords on checkout, and cleaning them up on entering contents to the repository.

Solution 2:

You can do this by adding a pattern for which files you want this functionality followed by ident in the .gitattributes file. This will replace $Id$ with $Id:<40-digit SHA>$ on checkout of the file. Notice though that it won't give you a revision number of the file as in CVS/SVN.

Example:

$ echo '*.txt ident' >> .gitattributes
$ echo '$Id$' > test.txt
$ git commit -a -m "test"

$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt

Link to gitattributes(5) Manual Page

Solution 3:

Git's ident does not do what $Id$ does in other versioning systems. As a kludge, use RCS along with git: RCS for individual file revisions and git to checkpoint the project as a whole. As I said, this is a kludge but it does kinda make sense (sometimes for some things).

Solution 4:

Jakub Narębski suggested in his answer (more than 10 years ago ):

You can however (ab)use a clean/smudge filter driver (via filter attribute) to get CVS-like keyword expansion, expanding keywords on checkout, and cleaning them up on entering contents to the repository.

The criticism is (by Arioch 'The in the comments)

filter driver would be really great if it actually had any power.
As of know it only seems to get only filename as a parameter: that is even less than ident filter, which gets the BLOB SHA1 as a parameter.

With Git 2.27 (Q2 2020), git content filters are powerless no more!

Git 2.27 provides more information (e.g. the object of the tree-ish in which the blob being converted appears, in addition to its path, which has already been given) to smudge/clean conversion filters.

See commit 0c0f8a7, commit 4cf76f6, commit 3f26785, commit dfc8cdc, commit 13e7ed6, commit c397aac, commit ab90eca (16 Mar 2020), and commit a860476 (10 Mar 2020) by brian m. carlson (``).
(Merged by Junio C Hamano -- gitster -- in commit 4e4baee, 27 Mar 2020)

convert: permit passing additional metadata to filter processes

Signed-off-by: brian m. carlson

There are a variety of situations where a filter process can make use of some additional metadata.

For example, some people find the ident filter too limiting and would like to include the commit or the branch in their smudged files.

This information isn't available during checkout as HEAD hasn't been updated at that point, and it wouldn't be available in archives either.

Let's add a way to pass this metadata down to the filter.

We pass the blob we're operating on, the treeish (preferring the commit over the tree if one exists), and the ref we're operating on.

Note that we won't pass this information in all cases, such as when renormalizing or when we're performing diffs, since it doesn't make sense in those cases.

The data we currently get from the filter process looks like the following:

command=smudge
pathname=git.c
0000

With this change, we'll get data more like this:

command=smudge
pathname=git.c
refname=refs/tags/v2.25.1
treeish=c522f061d551c9bb8684a7c3859b2ece4499b56b
blob=7be7ad34bd053884ec48923706e70c81719a8660
0000

There are a couple things to note about this approach.

For operations like checkout, treeish will always be a commit, since we cannot check out individual trees, but for other operations, like archive, we can end up operating on only a particular tree, so we'll provide only a tree as the treeish.

Similar comments apply for refname, since there are a variety of cases in which we won't have a ref.

And:

convert: provide additional metadata to filters

Signed-off-by: brian m. carlson

Now that we have the codebase wired up to pass any additional metadata to filters, let's collect the additional metadata that we'd like to pass.

The two main places we pass this metadata are checkouts and archives.
In these two situations, reading HEAD isn't a valid option, since HEAD isn't updated for checkouts until after the working tree is written and archives can accept an arbitrary tree.

In other situations, HEAD will usually reflect the refname of the branch in current use.

We pass a smaller amount of data in other cases, such as git cat-file, where we can really only logically know about the blob.