{"thread":{"id":"22655","subject":"[PATCH/RFC] git diff --submodule: Show detailed dirty status of submodules","startedAt":"2010-02-14T21:29:40Z","lastAt":"2010-02-14T21:29:40Z","messageCount":1,"participants":["Jens Lehmann"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"134573","messageId":"4B786B44.7070103@web.de","threadId":"22655","inReplyTo":null,"subject":"[PATCH/RFC] git diff --submodule: Show detailed dirty status of submodules","fromName":"Jens Lehmann","fromEmail":"jens.lehmann@web.de","sentAt":"2010-02-14T21:29:40Z","receivedAt":"2010-02-14T21:29:40Z","isPatch":true,"sender":{"key":"jens.lehmann@web.de","avatar":"https://avatars.githubusercontent.com/u/135220?v=4"},"body":"When encountering a dirty submodule while doing \"git diff --submodule\"\nprint an extra line for new untracked content and another for modified\nbut already tracked content. And if the HEAD of the submodule is equal\nto the ref diffed against in the superproject, drop the output which\nwould just show the same SHA1s and no commit message headlines.\n\nTo achieve that, the dirty_submodule bitfield is expanded to two bits.\nThe output of \"git status\" inside the submodule is parsed to set the\naccording bits.\n\nSigned-off-by: Jens Lehmann <Jens.Lehmann@web.de>\n---\n\n\nI think it makes sense to provide a bit more information about the\nkind of dirtiness of a submodule. So here is a patch doing that for\n\"git diff --submodule\", it changes the output from\n\nSubmodule sub 5431f52..3f35670-dirty:\n  > first line of commit message\n\nto\n\nSubmodule sub contains untracked content\nSubmodule sub contains modified content\nSubmodule sub 5431f52..3f35670:\n  > first line of commit message\n\n\nThe first two lines will only show up when the condition they describe\nis met in the submodule, the third line will be dropped when the hashes\nare identical and thus no commit headlines follow (I used the term\n\"content\" instead of \"files\" in the output for new/untracked and\nmodified entries because a submodule can not only contain files but\nother submodules too).\n\nI think it also makes sense to provide the same information in the\nstatus information present in the comment lines when using \"git commit\"\nand in the output of \"git status\" too, but that will be another patch.\n\nWhat do you think?\n\n\n diff-lib.c                |   16 ++++++++--------\n diffcore.h                |    4 +++-\n submodule.c               |   39 ++++++++++++++++++++++++++++++++++-----\n submodule.h               |    2 +-\n t/t4041-diff-submodule.sh |   17 +++++++++++------\n 5 files changed, 57 insertions(+), 21 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex d7e13cb..15ca7cd 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -180,10 +180,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\tchanged = ce_match_stat(ce, &st, ce_option);\n \t\tif (S_ISGITLINK(ce->ce_mode)\n \t\t    && !DIFF_OPT_TST(&revs->diffopt, IGNORE_SUBMODULES)\n-\t\t    && (!changed || (revs->diffopt.output_format & DIFF_FORMAT_PATCH))\n-\t\t    && is_submodule_modified(ce->name)) {\n-\t\t\tchanged = 1;\n-\t\t\tdirty_submodule = 1;\n+\t\t    && (!changed || (revs->diffopt.output_format & DIFF_FORMAT_PATCH))) {\n+\t\t\tdirty_submodule = is_submodule_modified(ce->name);\n+\t\t\tif (dirty_submodule)\n+\t\t\t\tchanged = 1;\n \t\t}\n \t\tif (!changed) {\n \t\t\tce_mark_uptodate(ce);\n@@ -243,10 +243,10 @@ static int get_stat_data(struct cache_entry *ce,\n \t\tchanged = ce_match_stat(ce, &st, 0);\n \t\tif (S_ISGITLINK(ce->ce_mode)\n \t\t    && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)\n-\t\t    && (!changed || (diffopt->output_format & DIFF_FORMAT_PATCH))\n-\t\t    && is_submodule_modified(ce->name)) {\n-\t\t\tchanged = 1;\n-\t\t\t*dirty_submodule = 1;\n+\t\t    && (!changed || (diffopt->output_format & DIFF_FORMAT_PATCH))) {\n+\t\t\t*dirty_submodule = is_submodule_modified(ce->name);\n+\t\t\tif (*dirty_submodule)\n+\t\t\t\tchanged = 1;\n \t\t}\n \t\tif (changed) {\n \t\t\tmode = ce_mode_from_stat(ce, st.st_mode);\ndiff --git a/diffcore.h b/diffcore.h\nindex 66687c3..fcd00bf 100644\n--- a/diffcore.h\n+++ b/diffcore.h\n@@ -42,7 +42,9 @@ struct diff_filespec {\n #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)\n \tunsigned should_free : 1; /* data should be free()'ed */\n \tunsigned should_munmap : 1; /* data should be munmap()'ed */\n-\tunsigned dirty_submodule : 1;  /* For submodules: its work tree is dirty */\n+\tunsigned dirty_submodule : 2;  /* For submodules: its work tree is dirty */\n+#define DIRTY_SUBMODULE_UNTRACKED 1\n+#define DIRTY_SUBMODULE_MODIFIED  2\n\n \tstruct userdiff_driver *driver;\n \t/* data should be considered \"binary\"; -1 means \"don't know yet\" */\ndiff --git a/submodule.c b/submodule.c\nindex 7d70c4f..47734ed 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -5,6 +5,7 @@\n #include \"commit.h\"\n #include \"revision.h\"\n #include \"run-command.h\"\n+#include \"diffcore.h\"\n\n static int add_submodule_odb(const char *path)\n {\n@@ -85,13 +86,21 @@ void show_submodule_summary(FILE *f, const char *path,\n \t\t\tmessage = \"(revision walker failed)\";\n \t}\n\n+\tif (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)\n+\t\tfprintf(f, \"Submodule %s contains untracked content\\n\", path);\n+\tif (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)\n+\t\tfprintf(f, \"Submodule %s contains modified content\\n\", path);\n+\n+\tif (!hashcmp(one, two)) {\n+\t\tstrbuf_release(&sb);\n+\t\treturn;\n+\t}\n+\n \tstrbuf_addf(&sb, \"Submodule %s %s..\", path,\n \t\t\tfind_unique_abbrev(one, DEFAULT_ABBREV));\n \tif (!fast_backward && !fast_forward)\n \t\tstrbuf_addch(&sb, '.');\n \tstrbuf_addf(&sb, \"%s\", find_unique_abbrev(two, DEFAULT_ABBREV));\n-\tif (dirty_submodule)\n-\t\tstrbuf_add(&sb, \"-dirty\", 6);\n \tif (message)\n \t\tstrbuf_addf(&sb, \" %s\\n\", message);\n \telse\n@@ -121,9 +130,9 @@ void show_submodule_summary(FILE *f, const char *path,\n \tstrbuf_release(&sb);\n }\n\n-int is_submodule_modified(const char *path)\n+unsigned is_submodule_modified(const char *path)\n {\n-\tint len;\n+\tssize_t len;\n \tstruct child_process cp;\n \tconst char *argv[] = {\n \t\t\"status\",\n@@ -132,6 +141,8 @@ int is_submodule_modified(const char *path)\n \t};\n \tchar *env[4];\n \tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned dirty_submodule = 0;\n+\tconst char *line, *next_line;\n\n \tstrbuf_addf(&buf, \"%s/.git/\", path);\n \tif (!is_directory(buf.buf)) {\n@@ -160,6 +171,24 @@ int is_submodule_modified(const char *path)\n \t\tdie(\"Could not run git status --porcelain\");\n\n \tlen = strbuf_read(&buf, cp.out, 1024);\n+\tline = buf.buf;\n+\twhile (len > 2) {\n+\t\tif ((line[0] == '?') && (line[1] == '?')) {\n+\t\t\tdirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;\n+\t\t\tif (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)\n+\t\t\t\tbreak;\n+\t\t} else {\n+\t\t\tdirty_submodule |= DIRTY_SUBMODULE_MODIFIED;\n+\t\t\tif (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tnext_line = strchr(line, '\\n');\n+\t\tif (!next_line)\n+\t\t\tbreak;\n+\t\tnext_line++;\n+\t\tlen -= (next_line - line);\n+\t\tline = next_line;\n+\t}\n \tclose(cp.out);\n\n \tif (finish_command(&cp))\n@@ -169,5 +198,5 @@ int is_submodule_modified(const char *path)\n \tfree(env[1]);\n \tfree(env[2]);\n \tstrbuf_release(&buf);\n-\treturn len != 0;\n+\treturn dirty_submodule;\n }\ndiff --git a/submodule.h b/submodule.h\nindex 2336965..267881c 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -5,6 +5,6 @@ void show_submodule_summary(FILE *f, const char *path,\n \t\tunsigned char one[20], unsigned char two[20],\n \t\tunsigned dirty_submodule,\n \t\tconst char *del, const char *add, const char *reset);\n-int is_submodule_modified(const char *path);\n+unsigned is_submodule_modified(const char *path);\n\n #endif\ndiff --git a/t/t4041-diff-submodule.sh b/t/t4041-diff-submodule.sh\nindex 4643054..11b1997 100755\n--- a/t/t4041-diff-submodule.sh\n+++ b/t/t4041-diff-submodule.sh\n@@ -201,7 +201,7 @@ test_expect_success 'submodule contains untracked content' \"\n \techo new > sm1/new-file &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head6-dirty:\n+Submodule sm1 contains untracked content\n EOF\n \"\n\n@@ -209,7 +209,8 @@ test_expect_success 'submodule contains untracked and modifed content' \"\n \techo new > sm1/foo6 &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head6-dirty:\n+Submodule sm1 contains untracked content\n+Submodule sm1 contains modified content\n EOF\n \"\n\n@@ -217,7 +218,7 @@ test_expect_success 'submodule contains modifed content' \"\n \trm -f sm1/new-file &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head6-dirty:\n+Submodule sm1 contains modified content\n EOF\n \"\n\n@@ -235,7 +236,8 @@ test_expect_success 'modified submodule contains untracked content' \"\n \techo new > sm1/new-file &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head8-dirty:\n+Submodule sm1 contains untracked content\n+Submodule sm1 $head6..$head8:\n   > change\n EOF\n \"\n@@ -244,7 +246,9 @@ test_expect_success 'modified submodule contains untracked and modifed content'\n \techo modification >> sm1/foo6 &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head8-dirty:\n+Submodule sm1 contains untracked content\n+Submodule sm1 contains modified content\n+Submodule sm1 $head6..$head8:\n   > change\n EOF\n \"\n@@ -253,7 +257,8 @@ test_expect_success 'modified submodule contains modifed content' \"\n \trm -f sm1/new-file &&\n \tgit diff-index -p --submodule=log HEAD >actual &&\n \tdiff actual - <<-EOF\n-Submodule sm1 $head6..$head8-dirty:\n+Submodule sm1 contains modified content\n+Submodule sm1 $head6..$head8:\n   > change\n EOF\n \"\n-- \n1.7.0.173.g684e9\n"}]}