0

I have a post-commit hook that edits a specific source file (specifically, writes commit hash to a string in c# class). Script is:

#!/bin/sh
hashStr=`git log --pretty=format:'%H' -n 1`
str="public static class GitHash { public static readonly string CommitHash = \"$hashStr\"; }"
echo $str > MyApp\\MyNamespace\\gitHash.cs

The same script is in my post-rebase, post merge, etc hooks. It was working fine and was showing up as modified in git until I added GitHash.cs file to skip-worktree:

git update-index --skip-worktree MyApp/MyNamespace/GitHash.cs

Now the file isn't being modified at all and stays in the state it was when I added it to skip-worktree. If I remove it from skip-worktree, hook works again (file is modified). If I am understanding skip-worktree correctly, it is ok to use it when I want to modify files locally, but don't want to accidentally commit them and don't want to see those files when i do git diff. However, it seems that somehow it's blocking the hook from editing this file?

So, if I want to have the file modified by hooks AND not showing up on git diff, what should I do?

defaultUsernameN
  • 365
  • 5
  • 14

2 Answers2

3

I realize it's a different approach than the path you've set out on, but:

Why store the file in source control at all if it's purely a generated file? If you remove it from the commit (and possibly at it to .gitignore) and make generating it part of your build process instead of a hook, then you should get the behaviors you're describing.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • 1
    I do want some version of it to be in source control, because I want the project to be buildable for a new developer. If I were to remove it completely, the fresh cloned copy of the repo will be unbuildable because of missing reference to that file, won't it? Or is there a way to somehow set it up to be generated post-cloning of the repo? – defaultUsernameN Mar 17 '20 at 18:52
  • Also, I actually remembered the reason I didn't do it that way in the first place. It's a Unity project and I want this string (commit hash) to be there immediately after the commit, because we are taking screenshots in unity editors' play mode and this string may be visible there. So, the build has not happened yet but the string should already reflect the last commit. – defaultUsernameN Mar 17 '20 at 19:04
  • You can generate it as part of the build process _and_ in a hook (perhaps using the same script). This is the right way to do it, since [the Git documenation](https://git-scm.com/docs/git-update-index#_notes) specifically says not to use skip-worktree or assume-unchanged for this. – bk2204 Mar 18 '20 at 00:37
0

Technically, the skip-worktree bit exists as a helper for sparse checkout. Using it the way you are is an abuse of the bit. However, the other bit, assume-unchanged, is a helper for slow lstat system calls, and while you can use it the same way, that's also a technical abuse. So you're kind of out of luck either way.

It's generally best not to do this at all, if at all possible. For instance, if your build system can just create the appropriate information—writing it to a file that never gets committed—that's usually the way to go. (This is what Mark Adelsberger said.) You can, however, have your post-commit hook write to the file, yet have one or both of these bits set, which is what you are doing. It just means that the file doesn't go into a new commit: instead, the index hangs on to the old copy of that file.

To get the updated file into a new commit, you must copy the file into Git's index before you make the new commit. But remember, every commit you make has a new, unique hash ID. This means you must store the hash ID before you know it.

In other words, the hash ID you can store in a file that is committed is never the hash ID of the commit you can make that contains that file. You can get the one just before that. Perhaps that is sufficient: you could, before making the commit, store the hash ID of the current commit, after making sure it matches everything except the new-hash-ID-file:

bit=skip-worktree
# or assume-unchanged: either will work, though both are technically abuse

git commit                    # make sure we commit what we have
hash=$(git rev-parse HEAD)    # get the hash ID of the current commit
echo "built from stuff that mostly matches $hash" > file.ext
git update-index --no-$bit file.ext
git add file.ext
git update-index --$bit file.ext
git commit -m "update build info"

But you definitely can't store the hash ID of the commit that has the correct hash ID in the file, because you can't predict what it will be: storing the future hash ID in the file changes the hash ID of the future commit.1


1The one possible exception here is if you manage to find a fixed point in the hash function.

torek
  • 448,244
  • 59
  • 642
  • 775