# Resurrecting Gitrev

**URL:** <https://discourse.haskell.org/t/resurrecting-gitrev/11933>\
**Category:** Show and Tell\
**Created:** [April 27, 2025, 11:16pm UTC](https://discourse.haskell.org/t/resurrecting-gitrev/11933 "2025-04-27T23:16:13Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![tbidne](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tbidne/32/2949_2.png) [@tbidne](https://discourse.haskell.org/u/tbidne)\
**Post date:** [April 27, 2025, 11:16pm UTC](https://discourse.haskell.org/t/resurrecting-gitrev/11933/1 "2025-04-27T23:16:14Z")

</div>

Hello!

[gitrev](https://hackage.haskell.org/package/gitrev) is a package for embedding git info in haskell programs via TH e.g.

```haskell
import Development.GitRev (gitHash)

myVersion :: String
myVersion = "Version 1.0 (" ++ $(gitHash) ++ ")"

```

I used to be a regular user, but I mostly stopped because `gitrev` does not handle “out-of-tree” builds, which afflicts both `nix` and `cabal install` (`stack install`, interestingly, seems okay).

Recently, I decided to try my hand at implementing workrounds for this situation on my fork [gitrev-typed](https://github.com/tbidne/gitrev-typed). To get right down to it, here is an example:

### Example

```haskell
import Development.GitRev.Typed qualified as GRT

myHashEnv :: Code Q String
myHashEnv = toCode gitHash
  where
    toCode :: Q (Either (Exceptions GitRevError) String) -> Code Q String
    toCode = GRT.qToCode . GRT.projectError

    gitHash :: Q (Either (Exceptions GitRevError) String)
    gitHash =
      GRT.firstSuccessQ
        [ GRT.embedGitError GRT.gitHashQ,
          GRT.embedEnvError $ GRT.envValQ "EXAMPLE_HASH",
          GRT.runGitInEnvDirQ "EXAMPLE_HOME" GRT.gitHashQ
        ]

```

#### Explanation

This tries (in order, stopping after the first success):

1. `GRT.embedGitError GRT.gitHashQ`: This is the normal `gitHash` logic, which will work for ordinary `cabal build`, but will fail for `nix` and `cabal install`.

2. `GRT.embedEnvError $ GRT.envValQ "EXAMPLE_HASH"`: This will look up the environment variable `EXAMPLE_HASH`, and, if it exists, return its value. This can work with nix if you inject the hash yourself e.g.

3. `GRT.runGitInEnvDirQ "EXAMPLE_HOME" GRT.gitHashQ`: This will look up the environment variable `EXAMPLE_HOME`, and, if it exists, run the ordinary `gitHashQ` in the directory pointed to by the env var. For example:

`GRT.projectError` will cause a compilation failure if all methods fail. The previous behavior – which would return the string `UNKNOWN` – is still available via `GRT.projectStringUnknown`. Futhermore, because this is all typed in `Q`, you can define whatever behavior you want.

Finally, `GRT.qToCode` lifts the result to `Code`, where it can be used with typed TH i.e. `$$myHashEnv`.

### Other features

In addition to the new `Development.GitRev.Typed` (and corresponding `Development.GitRev.Typed.OsString`) interfaces, a number of the open issues/PRs from the original repo are potentially resolved e.g.

1. The new interface uses typed TH, and the typed error is returned, so users can choose how to respond:

2. “Out-of-tree” workarounds:

3. Locale bug possibly fixed:

4. Outstanding git commands

I also chose to expose

```haskell
runGit :: [String] -> IndexUsed -> Q (Either GitError String)

```

so users can define their own git actions, without having to open a PR for every possibility.

The full API is available on the package candidate [gitrev-typed](https://hackage.haskell.org/package/gitrev-typed-0.1/candidate).

### Why not update gitrev?

The original [repo](https://github.com/acfoltzer/gitrev) has been archived, with an explicit message from the author noting its unmaintained status. I reached out about potentially taking over the `gitrev` namespace on hackage, but the author preferred to keep it as-is, citing concerns for backwards compatibility and supply-chain attacks. Fair enough.

Even if the author consented, I now think the API (and some internal details) have diverged enough to warrant a new package.

### Why not githash?

To be honest, when I set out to do this I completely forgot about the other fork, [githash](https://hackage.haskell.org/package/githash). Having revisited it now, I think forking off `gitrev` was the right call.

`githash`’s strategy seems to be collecting everything someone might want up front, then returning whatever subset the user asks for. `gitrev`, on the other hand, only takes what is directly requested via the passed arguments.

I prefer `gitrev`’s flexibility here, especially given that it allows users to easily specify custom commands. I am not very familiar with `githash`’s internals, however, so please do let me know if I am missing something.

### Next steps

This seems to work for my use-cases, though I am eager to hear what the community thinks. Is this useful? Does the API/documentation seem reasonable? Other thoughts/suggestions?

Assuming others find this useful, I will publish the candidate after some time.

Thanks!

---

<div class="post-metadata">

**Author:** ![tbidne](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tbidne/32/2949_2.png) [@tbidne](https://discourse.haskell.org/u/tbidne)\
**Post date:** [May 7, 2025, 12:43am UTC](https://discourse.haskell.org/t/resurrecting-gitrev/11933/2 "2025-05-07T00:43:50Z")

</div>

> **[gitrev-typed](https://hackage.haskell.org/package/gitrev-typed-0.1)**
>
> Compile git revision info into Haskell projects
