A different approach to ident-style strings in Git?

676 views Asked by At

I'm planning my company's transition from CVS to Git, and certain engineers are wishing we still had use of CVS keyword strings such as $Id: $. I have read all about implementing this with the ident setting .gitattributes, and I understand why it's undesirable, though maybe possible for smaller source trees. (Our source is huge, so I think it would be impossible.)

Our engineers don't particularly care about having the SHA1 hash in the file. They just want to know:

  1. the date of last modification
  2. the name of the committer
  3. possibly the commit message

They find it extremely handy to see this info in file headers when browsing code, and I can't argue with that.

What I want to know is:

Is there any way to info-stamp all the staged files at the moment before git commit? In other words, to run a perl command -- that replaces $Id: $ with a block of desired info -- on the working copies of files being committed?

This wouldn't require any .gitattributes actions at all. Git would just need to know how to merge two such blocks of info, ideally by choosing the later one. The stamped info would be just one more file change in the newly created version.

I looked at doing this in the pre-commit hook, but it seems to be intended differently -- not for editing files, just for checking them. Am I right about that?

And has nobody tried this approach? It seems simpler to me than trying to filter all source files every time git changes versions, which it what it sounds like .gitattributes does.

Much thanks for any advice/warnings/pointers .

3

There are 3 answers

3
vonbrand On

RCS (and thus CVS) does the expanding of $Id:$ and such on checkout, those aren't in the saved files. And they can't really be, somebody might come around and rename version 1.8.2-rc10 to plain 1.8.2. If somebody wants to know where file comes from, git log file answers that nicely, with more details than RCS could ever give. And it is a local command, no trip to the CVS server (and thus available everywhere where git is, and instantaneous).

3
Klas Mellbourn On

The Keyword Expansion section of the git documentation explains how to make clean keyword expansion.

A ruby script for expanding what you want would be something like this (not tested)

#! /usr/bin/env ruby
data = STDIN.read
last_info = `git log --pretty=format:"%ad %cn %s" -1`
puts data.gsub('$Last$', '$Last: ' + last_info.to_s + '$')

setup filter

$ git config filter.last_expander.smudge expand_last_info
$ git config filter.last_expander.clean 'perl -pe "s/\\\$Last[^\\\$]*\\\$/\\\$Last\\\$/"'

setup .gitattributes

echo '*.txt filter=last_expander' >> .gitattributes

Note: (just as vonbrand says) what this gives you, and what you in all likelyhood want, is field expansion on checkout and removal of the fields on commit. But the effect is that your engineers will be able to read the field in the checked out files in their working directory. Isn't that what they want? And this will not mess up the actually versioned content with any redundant metadata.

0
src committer On

This is how you solve this:

  1. Add the following pre-commit hook:

    #!/bin/sh
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 .filters/keywords --
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    
  2. Add the following commit-msg hook:

    #!/bin/sh
    awk '!/^[[:space:]]*(#|$)/{exit f++}END{exit !f}' "$1" && exit
    # NOTREACHED unless commit was aborted
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 .filters/keywords -d --
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    
  3. Download ".filters/fraubsd-keywords" but rename file to "keywords":

    https://raw.githubusercontent.com/freebsdfrau/FrauBSD/master/.filters/fraubsd-keywords

  4. Edit "keywords", changing in the CONFIGURATION section at top:

    • FrauBSD to Header
    • _FrauBSD to _Header

After which, each time you do a git commit the text $Header$ and/or $Header: ... $ will be translated into $Header: file YYYY-MM-DD HH:MM:SS GMTOFFSET committer $

NOTE: You may have to modify a small section of the "keywords" script to operate on more-or-less types of files. At the time of this writing it only operates on files that are "ASCII text" or "shell scripts".