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'. Signed-off-by: Stefan Beller <sbeller@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Beller committed Mar 21, 2017 at 15:57 UTC b0176ce6b5d954a747dc4d0c5a8593ed576714c4
3 files changed +66 -12
Documentation/git-describe.txt
+8 -3
@@ -30,9 +30,14 @@ OPTIONS
30 Commit-ish object names to describe. Defaults to HEAD if omitted.
31
32 --dirty[=<mark>]::
33 - Describe the working tree.
34 - It means describe HEAD and appends <mark> (`-dirty` by
35 - default) if the working tree is dirty.
33 +--broken[=<mark>]::
34 + Describe the state of the working tree. When the working
35 + tree matches HEAD, the output is the same as "git describe
36 + HEAD". If the working tree has local modification "-dirty"
37 + is appended to it. If a repository is corrupt and Git
38 + cannot determine if there is local modification, Git will
39 + error out, unless `--broken' is given, which appends
40 + the suffix "-broken" instead.
41
42 --all::
43 Instead of using only the annotated tags, use any ref
builtin/describe.c
+38 -9
@@ -9,6 +9,7 @@
9 #include "diff.h"
10 #include "hashmap.h"
11 #include "argv-array.h"
12 +#include "run-command.h"
13
14 #define SEEN (1u << 0)
15 #define MAX_TAGS (FLAG_BITS - 1)
@@ -31,7 +32,7 @@ static int have_util;
32 static struct string_list patterns = STRING_LIST_INIT_NODUP;
33 static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
34 static int always;
34 -static const char *dirty;
35 +static const char *suffix, *dirty, *broken;
36
37 /* diff-index command arguments to check if working tree is dirty. */
38 static const char *diff_index_args[] = {
@@ -292,8 +293,8 @@ static void describe(const char *arg, int last_one)
293 display_name(n);
294 if (longformat)
295 show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
295 - if (dirty)
296 - printf("%s", dirty);
296 + if (suffix)
297 + printf("%s", suffix);
298 printf("\n");
299 return;
300 }
@@ -369,8 +370,8 @@ static void describe(const char *arg, int last_one)
370 struct object_id *oid = &cmit->object.oid;
371 if (always) {
372 printf("%s", find_unique_abbrev(oid->hash, abbrev));
372 - if (dirty)
373 - printf("%s", dirty);
373 + if (suffix)
374 + printf("%s", suffix);
375 printf("\n");
376 return;
377 }
@@ -413,8 +414,8 @@ static void describe(const char *arg, int last_one)
414 display_name(all_matches[0].name);
415 if (abbrev)
416 show_suffix(all_matches[0].depth, &cmit->object.oid);
416 - if (dirty)
417 - printf("%s", dirty);
417 + if (suffix)
418 + printf("%s", suffix);
419 printf("\n");
420
421 if (!last_one)
@@ -445,6 +446,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
446 {OPTION_STRING, 0, "dirty", &dirty, N_("mark"),
447 N_("append <mark> on dirty working tree (default: \"-dirty\")"),
448 PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
449 + {OPTION_STRING, 0, "broken", &broken, N_("mark"),
450 + N_("append <mark> on broken working tree (default: \"-broken\")"),
451 + PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
452 OPT_END(),
453 };
454
@@ -493,7 +497,28 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
497 die(_("No names found, cannot describe anything."));
498
499 if (argc == 0) {
496 - if (dirty) {
500 + if (broken) {
501 + struct child_process cp = CHILD_PROCESS_INIT;
502 + argv_array_pushv(&cp.args, diff_index_args);
503 + cp.git_cmd = 1;
504 + cp.no_stdin = 1;
505 + cp.no_stdout = 1;
506 +
507 + if (!dirty)
508 + dirty = "-dirty";
509 +
510 + switch (run_command(&cp)) {
511 + case 0:
512 + suffix = NULL;
513 + break;
514 + case 1:
515 + suffix = dirty;
516 + break;
517 + default:
518 + /* diff-index aborted abnormally */
519 + suffix = broken;
520 + }
521 + } else if (dirty) {
522 static struct lock_file index_lock;
523 int fd;
524
@@ -506,11 +531,15 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
531
532 if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
533 diff_index_args, prefix))
509 - dirty = NULL;
534 + suffix = NULL;
535 + else
536 + suffix = dirty;
537 }
538 describe("HEAD", 1);
539 } else if (dirty) {
540 die(_("--dirty is incompatible with commit-ishes"));
541 + } else if (broken) {
542 + die(_("--broken is incompatible with commit-ishes"));
543 } else {
544 while (argc-- > 0)
545 describe(*argv++, argc == 0);
t/t6120-describe.sh
+20
@@ -233,4 +233,24 @@ test_expect_success 'describe --contains and --no-match' '
233 test_cmp expect actual
234 '
235
236 +test_expect_success 'setup and absorb a submodule' '
237 + test_create_repo sub1 &&
238 + test_commit -C sub1 initial &&
239 + git submodule add ./sub1 &&
240 + git submodule absorbgitdirs &&
241 + git commit -a -m "add submodule" &&
242 + git describe --dirty >expect &&
243 + git describe --broken >out &&
244 + test_cmp expect out
245 +'
246 +
247 +test_expect_success 'describe chokes on severly broken submodules' '
248 + mv .git/modules/sub1/ .git/modules/sub_moved &&
249 + test_must_fail git describe --dirty
250 +'
251 +test_expect_success 'describe ignoring a borken submodule' '
252 + git describe --broken >out &&
253 + grep broken out
254 +'
255 +
256 test_done