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'.

Signed-off-by: Stefan Beller <sbel...@google.com>
---
 Documentation/git-describe.txt | 11 +++++++---
 builtin/describe.c             | 47 ++++++++++++++++++++++++++++++++++--------
 t/t6120-describe.sh            | 20 ++++++++++++++++++
 3 files changed, 66 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 8755f3af7b..26f19d3b07 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -30,9 +30,14 @@ OPTIONS
        Commit-ish object names to describe.  Defaults to HEAD if omitted.
 
 --dirty[=<mark>]::
-       Describe the working tree.
-       It means describe HEAD and appends <mark> (`-dirty` by
-       default) if the working tree is dirty.
+--broken[=<mark>]::
+       Describe the state of the working tree.  When the working
+       tree matches HEAD, the output is the same as "git describe
+       HEAD".  If the working tree has local modification "-dirty"
+       is appended to it.  If a repository is corrupt and Git
+       cannot determine if there is local modification, Git will
+       error out, unless `--broken' is given, which appends
+       the suffix "-broken" instead.
 
 --all::
        Instead of using only the annotated tags, use any ref
diff --git a/builtin/describe.c b/builtin/describe.c
index 76c18059bf..45adbf67d5 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -9,6 +9,7 @@
 #include "diff.h"
 #include "hashmap.h"
 #include "argv-array.h"
+#include "run-command.h"
 
 #define SEEN           (1u << 0)
 #define MAX_TAGS       (FLAG_BITS - 1)
@@ -31,7 +32,7 @@ static int have_util;
 static struct string_list patterns = STRING_LIST_INIT_NODUP;
 static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
 static int always;
-static const char *dirty;
+static const char *suffix, *dirty, *broken;
 
 /* diff-index command arguments to check if working tree is dirty. */
 static const char *diff_index_args[] = {
@@ -292,8 +293,8 @@ static void describe(const char *arg, int last_one)
                display_name(n);
                if (longformat)
                        show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
-               if (dirty)
-                       printf("%s", dirty);
+               if (suffix)
+                       printf("%s", suffix);
                printf("\n");
                return;
        }
@@ -369,8 +370,8 @@ static void describe(const char *arg, int last_one)
                struct object_id *oid = &cmit->object.oid;
                if (always) {
                        printf("%s", find_unique_abbrev(oid->hash, abbrev));
-                       if (dirty)
-                               printf("%s", dirty);
+                       if (suffix)
+                               printf("%s", suffix);
                        printf("\n");
                        return;
                }
@@ -413,8 +414,8 @@ static void describe(const char *arg, int last_one)
        display_name(all_matches[0].name);
        if (abbrev)
                show_suffix(all_matches[0].depth, &cmit->object.oid);
-       if (dirty)
-               printf("%s", dirty);
+       if (suffix)
+               printf("%s", suffix);
        printf("\n");
 
        if (!last_one)
@@ -445,6 +446,9 @@ int cmd_describe(int argc, const char **argv, const char 
*prefix)
                {OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
                        N_("append <mark> on dirty working tree (default: 
\"-dirty\")"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+               {OPTION_STRING, 0, "broken",  &broken, N_("mark"),
+                       N_("append <mark> on broken working tree (default: 
\"-broken\")"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
                OPT_END(),
        };
 
@@ -493,7 +497,28 @@ int cmd_describe(int argc, const char **argv, const char 
*prefix)
                die(_("No names found, cannot describe anything."));
 
        if (argc == 0) {
-               if (dirty) {
+               if (broken) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       argv_array_pushv(&cp.args, diff_index_args);
+                       cp.git_cmd = 1;
+                       cp.no_stdin = 1;
+                       cp.no_stdout = 1;
+
+                       if (!dirty)
+                               dirty = "-dirty";
+
+                       switch (run_command(&cp)) {
+                       case 0:
+                               suffix = NULL;
+                               break;
+                       case 1:
+                               suffix = dirty;
+                               break;
+                       default:
+                               /* diff-index aborted abnormally */
+                               suffix = broken;
+                       }
+               } else if (dirty) {
                        static struct lock_file index_lock;
                        int fd;
 
@@ -506,11 +531,15 @@ int cmd_describe(int argc, const char **argv, const char 
*prefix)
 
                        if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
                                            diff_index_args, prefix))
-                               dirty = NULL;
+                               suffix = NULL;
+                       else
+                               suffix = dirty;
                }
                describe("HEAD", 1);
        } else if (dirty) {
                die(_("--dirty is incompatible with commit-ishes"));
+       } else if (broken) {
+               die(_("--broken is incompatible with commit-ishes"));
        } else {
                while (argc-- > 0)
                        describe(*argv++, argc == 0);
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 167491fd5b..16952e44fc 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -233,4 +233,24 @@ test_expect_success 'describe --contains and --no-match' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup and absorb a submodule' '
+       test_create_repo sub1 &&
+       test_commit -C sub1 initial &&
+       git submodule add ./sub1 &&
+       git submodule absorbgitdirs &&
+       git commit -a -m "add submodule" &&
+       git describe --dirty >expect &&
+       git describe --broken >out &&
+       test_cmp expect out
+'
+
+test_expect_success 'describe chokes on severly broken submodules' '
+       mv .git/modules/sub1/ .git/modules/sub_moved &&
+       test_must_fail git describe --dirty
+'
+test_expect_success 'describe ignoring a borken submodule' '
+       git describe --broken >out &&
+       grep broken out
+'
+
 test_done
-- 
2.12.1.385.gd4d53dfa89.dirty

Reply via email to