9

I have just discovered the --dirty option to git describe and it looks like it should do something very useful, i.e. append a suffix to the output of git describe when the working tree is dirty, however that doesn't seem to be the case on some of my repositories:

$ git status
# On branch 8.30
nothing to commit (working directory clean)
$ git describe --dirty
8.30rel-8-g9c1cbdb-dirty

I thought this might be because the working directory is dirty relative to the tag, but that doesn't seem to be the case either:

$ git status
# On branch 1.4
nothing to commit (working directory clean)
$ git describe --tags --dirty --long
1.4rel-0-gfedfe66-dirty

I used to make extensive use of hg id when I used to use Mercurial and liked the fact that it's default behaviour was to add a + suffix to any commit hash it reported for a dirty repository, so have been looking out for a git equivalent since, but git describe --dirty doesn't appear to do what I would expect given the documentation:

   --dirty[=<mark>]
       Describe the working tree. It means describe HEAD and appends
       <mark> (-dirty by default) if the working tree is dirty.

Am I misunderstanding what --dirty should do, or am I not using it correctly?

In case it makes any difference, all of the git repositories are deployed via buckminster so there are no submodules involved and the filesystem is an nfs share.


Update: I have discovered a work around, but I have absolutely no idea how this could possibly be making a difference.

If I run git diff --quiet HEAD on the repo then all of a sudden the git describe works as I expect:

$ git status
# On branch 8.30
nothing to commit (working directory clean)
$ git describe --dirty
8.30rel-8-g9c1cbdb-dirty
$ git diff --quiet HEAD
$ git describe --dirty
8.30rel-8-g9c1cbdb

I also spotted that when git describe was reporting the repository as dirty then gitk also showed "Local uncommitted changes, not checked in to index" and then listed each file in the working directory, but with no diffs against them, just the ---- filename ---- lines.


Further update: As this continued to be a problem, I eventually wrote a git-describe-dirty script, which starts by running git describe --dirty but if it finds the repository to be dirty, runs git update-index -q --refresh before trying again and taking the second result.

When iterating through hundreds of repositories, using git describe-dirty and only running the index update for a repository which initially indicates it is dirty saves a great deal of time compared to running git update-index -q --refresh ; git describe --dirty every time.

Community
  • 1
  • 1
Mark Booth
  • 7,605
  • 2
  • 68
  • 92
  • Do you have submodules in the project? – 1615903 Apr 16 '13 at 11:16
  • Note: with Git 2.13 (Q2 2016), you don't have `-dirty`, but also `-broken` to consider. See [my answer below](http://stackoverflow.com/a/43380087/6309) – VonC Apr 12 '17 at 21:39

3 Answers3

12

If you are running git 1.7.6 or earlier, you need to run git update-index --refresh before using git describe --dirty, because the index may be stale. Your workaround of using git diff --quiet HEAD works because "git diff" is a porcelain command, and probably updates the index itself.

The git commit that fixes this for git 1.7.7 describes the problem:

When running git describe --dirty the index should be refreshed. Previously the cached index would cause describe to think that the index was dirty when, in reality, it was just stale.

Note that the exact sequence of steps you describe shouldn't have this problem, because git status updates the index. But I still think you are seeing this same issue because the workaround you describe matches. Here is how I demonstrate the issue:

% git describe --tags --dirty
v1.0.0
% touch pom.xml
% git describe --tags --dirty
v1.0.0-dirty
% git status
# On branch dev
nothing to commit (working directory clean)
% git describe --tags --dirty
v1.0.0

Here running "git status" updates the index as a side effect and fixes "git describe" output, just as with your workaround. The proper plumbing fix for git 1.7.6 and earlier would be:

% touch pom.xml
% git describe --tags --dirty
v1.0.0-dirty
% git update-index --refresh
% git describe --tags --dirty
v1.0.0
jbyler
  • 7,200
  • 3
  • 34
  • 42
  • Here is a [test script](https://gist.github.com/peritus/936326) demonstrating the issue before it was fixed. Note that `describe` *is* porcelain, and has since been fixed as suggested by @peritus, despite the apparent resolution of the twitter discussion to the contrary. (See the [list of porcelain commands](http://schacon.github.io/git/git.html#_high_level_commands_porcelain)) – jbyler Jul 08 '13 at 23:02
  • More background info on stale indexes and why this is expected with plumbing-level commands in [this discussion](http://git.661346.n2.nabble.com/False-positives-in-git-diff-index-tp5869013p5891295.html). – jbyler Jul 08 '13 at 23:31
3

Beware that there was another bug fix regarding git describe --dirty, about a year ago: https://github.com/git/git/commit/a1e19004e11dcbc0ceebd92c425ceb1770e52d0b

"git --work-tree=$there --git-dir=$here describe --dirty" did not work correctly as it did not pay attention to the location of the worktree specified by the user

In case of this bug, the workarounds shown here didn't work, so we had to upgrade our installation. There seems to be no fix readily available for Debian buster as of today (2020-02-20), yet the Debian bullseye main git packages are compatible with buster right now.

Superlexx
  • 171
  • 1
  • 3
2

Git 2.13 (Q2 2017) improves a bit on that --dirty flag with a --broken one, since "git describe --dirty" dies when it cannot be determined if the state in the working tree matches that of HEAD (e.g. broken repository or broken submodule).

See commit b0176ce (21 Mar 2017) by Stefan Beller (stefanbeller).
(Merged by Junio C Hamano -- gitster -- in commit 844768a, 27 Mar 2017)

builtin/describe: introduce --broken flag

git-describe tells you the version number you're at, or errors out, e.g. when you run it outside of a repository, which may happen when downloading a tar ball instead of using git to obtain the source code.

To keep this property of only erroring out, when not in a repository, severe (submodule) errors must be downgraded to reporting them gently instead of having git-describe error out completely.

To achieve that a flag '--broken' is introduced, which is in the same vein as '--dirty' but uses an actual child process to check for dirtiness.
When that child dies unexpectedly, we'll append '-broken' instead of '-dirty'.


Note that with Git 2.41 (Q2 2023), "git describe --dirty"(man) learns to work better with sparse-index.

See commit 748b8d6 (03 Apr 2023) by Raghul Nanth A (NanthR).
(Merged by Junio C Hamano -- gitster -- in commit 7ac228c, 21 Apr 2023)

describe: enable sparse index for describe

Signed-off-by: Raghul Nanth A

git describe(man) compares the index with the working tree when (and only when) it is run with the "--dirty" flag.
This is done by the run_diff_index() function.
The function has been made aware of the sparse-index in the series that led to 8d2c373 ("Merge branch 'ld/sparse-diff-blame'", 2021-12-21, Git v2.35.0-rc0 -- merge listed in batch #4).
Hence we can just set the requires-full-index to false for "describe".

See "How to use git sparse-checkout in 2.27+" for more on sparse-index.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250