{"thread":{"id":"54468","subject":"[PATCH] userdiff: support Bash","startedAt":"2020-10-20T07:10:59Z","lastAt":"2020-10-22T17:30:29Z","messageCount":9,"participants":["Victor Engmark","Junio C Hamano","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"407963","messageId":"373640ea4d95f3b279b9d460d9a8889b4030b4e9.camel@engmark.name","threadId":"54468","inReplyTo":null,"subject":"[PATCH] userdiff: support Bash","fromName":"Victor Engmark","fromEmail":"victor@engmark.name","sentAt":"2020-10-20T07:10:49Z","receivedAt":"2020-10-20T07:10:59Z","isPatch":true,"sender":{"key":"victor@engmark.name","avatar":"https://avatars.githubusercontent.com/u/168301?v=4"},"body":"Supports POSIX, bashism and mixed function declarations, all four compound\ncommand types, trailing comments and mixed whitespace.\n\nUses the POSIX.1-2017 definition of allowed characters in names\n<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_235>\nsince actual allowed characters in Bash function names are locale\ndependent <https://unix.stackexchange.com/a/245336/3645>.\n\nUses the default `IFS` characters to define words.\n\nSigned-off-by: Victor Engmark <victor@engmark.name>\n---\n Documentation/gitattributes.txt     | 2 ++\n t/t4018-diff-funcname.sh            | 1 +\n t/t4018/bash-arithmetic-function    | 4 ++++\n t/t4018/bash-bashism-style-function | 4 ++++\n t/t4018/bash-conditional-function   | 4 ++++\n t/t4018/bash-mixed-style-function   | 4 ++++\n t/t4018/bash-other-characters       | 4 ++++\n t/t4018/bash-posix-style-function   | 4 ++++\n t/t4018/bash-subshell-function      | 4 ++++\n t/t4018/bash-trailing-comment       | 4 ++++\n t/t4018/bash-whitespace             | 4 ++++\n userdiff.c                          | 6 ++++++\n 12 files changed, 45 insertions(+)\n create mode 100644 t/t4018/bash-arithmetic-function\n create mode 100644 t/t4018/bash-bashism-style-function\n create mode 100644 t/t4018/bash-conditional-function\n create mode 100644 t/t4018/bash-mixed-style-function\n create mode 100644 t/t4018/bash-other-characters\n create mode 100644 t/t4018/bash-posix-style-function\n create mode 100644 t/t4018/bash-subshell-function\n create mode 100644 t/t4018/bash-trailing-comment\n create mode 100644 t/t4018/bash-whitespace\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 2d0a03715b..8a15ff6bdf 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -802,6 +802,8 @@ patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n+- `bash` suitable for source code in the Bourne-Again SHell language.\n+\n - `bibtex` suitable for files with BibTeX coded references.\n \n - `cpp` suitable for source code in the C and C++ languages.\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9d07797579..9675bc17db 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -27,6 +27,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \tada\n+\tbash\n \tbibtex\n \tcpp\n \tcsharp\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\nnew file mode 100644\nindex 0000000000..c0b276cb50\n--- /dev/null\n+++ b/t/t4018/bash-arithmetic-function\n@@ -0,0 +1,4 @@\n+RIGHT() ((\n+\n+    ChangeMe = \"$x\" + \"$y\"\n+))\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\nnew file mode 100644\nindex 0000000000..f1de4fa831\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT {\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\nnew file mode 100644\nindex 0000000000..c5949e829b\n--- /dev/null\n+++ b/t/t4018/bash-conditional-function\n@@ -0,0 +1,4 @@\n+RIGHT() [[ \\\n+\n+    \"$a\" > \"$ChangeMe\"\n+]]\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\nnew file mode 100644\nindex 0000000000..555f9b2466\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\nnew file mode 100644\nindex 0000000000..a3f390d525\n--- /dev/null\n+++ b/t/t4018/bash-other-characters\n@@ -0,0 +1,4 @@\n+_RIGHT_0n() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\nnew file mode 100644\nindex 0000000000..a4d144856e\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-function\n@@ -0,0 +1,4 @@\n+RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\nnew file mode 100644\nindex 0000000000..80baa09484\n--- /dev/null\n+++ b/t/t4018/bash-subshell-function\n@@ -0,0 +1,4 @@\n+RIGHT() (\n+\n+    ChangeMe=2\n+)\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\nnew file mode 100644\nindex 0000000000..f1edbeda31\n--- /dev/null\n+++ b/t/t4018/bash-trailing-comment\n@@ -0,0 +1,4 @@\n+RIGHT() { # Comment\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-whitespace b/t/t4018/bash-whitespace\nnew file mode 100644\nindex 0000000000..62e7d1f25c\n--- /dev/null\n+++ b/t/t4018/bash-whitespace\n@@ -0,0 +1,4 @@\n+\t function \tRIGHT() \t{\n+\n+\t    ChangeMe\n+\t }\ndiff --git a/userdiff.c b/userdiff.c\nindex fde02f225b..9de0497007 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -23,6 +23,12 @@ IPATTERN(\"ada\",\n \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n+PATTERNS(\"bash\",\n+\t /* POSIX & bashism form; all four compound command types */\n+\t \"^[ \\t]*((function[ \\t]*)?[a-zA-Z_][a-zA-Z0-9_]*(\\\\(\\\\))?[ \\t]*(\\\\{|\\\\(\\\\(?|\\\\[\\\\[))\",\n+\t /* -- */\n+\t /* Characters not in the default $IFS value */\n+\t \"[^ \\t]+\"),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n\n"},{"id":"408034","messageId":"xmqqk0vk8o20.fsf@gitster.c.googlers.com","threadId":"54468","inReplyTo":"373640ea4d95f3b279b9d460d9a8889b4030b4e9.camel@engmark.name","subject":"Re: [PATCH] userdiff: support Bash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-10-20T23:39:35Z","receivedAt":"2020-10-20T23:39:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Victor Engmark <victor@engmark.name> writes:\n\nIt's a bit troubling not to see the most basic form, i.e.\n\n\tRIGHT () {\n\t\t: ChangeMe\n\t}\n\nin the set of tests.\n\n> diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\n> new file mode 100644\n> index 0000000000..f1edbeda31\n> --- /dev/null\n> +++ b/t/t4018/bash-trailing-comment\n> @@ -0,0 +1,4 @@\n> +RIGHT() { # Comment\n> +\n> +    ChangeMe\n> +}\n\nThis is the closest, but it tests \"# with comment\" at the same time.\n\n> diff --git a/userdiff.c b/userdiff.c\n> index fde02f225b..9de0497007 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -23,6 +23,12 @@ IPATTERN(\"ada\",\n>  \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n>  \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n>  \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n> +PATTERNS(\"bash\",\n> +\t /* POSIX & bashism form; all four compound command types */\n> +\t \"^[ \\t]*((function[ \\t]*)?[a-zA-Z_][a-zA-Z0-9_]*(\\\\(\\\\))?[ \\t]*(\\\\{|\\\\(\\\\(?|\\\\[\\\\[))\",\n\nWe allow an optional leading indent, an optional noiseword\n\"function\" plus a run of whitespaces, and then an identifier\nfollowed by \"line noise\".\n\nThe pattern makes the run of whitespaces after \"function\" entirely\noptional, which makes \"functionabc\" be taken as a single identifier\nwithout noiseword \"function\", but it's not like we are parsing and\npainting only the identifer in a color different from the keyword,\nso it is OK.  Using [ \\t]+ instead of [ \\t]* after \"function\" would\nprobably make the result more clear, even though it does not make a\npractical difference.\n\nThen the \"line noise\" part.  I am not sure if I follow this part\ncorrectly:\n\n\t\"(\\\\(\\\\))?[ \\t]*(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n\nis what follows the identifier that is the function name.  We may\nhave 0 or 1 \"()\", followed by an optional run of whitespaces.  And\nthen one of '{', '(', '((', '[[' would come.\n\nDid I read it correctly?\n\nEven though it may be unusual to write open and close parentheses\nnot directly next to each other, or with a space after the name of\nthe function, i.e.\n\n\tRIGHT ( ) {\n\nwould also be a valid header, no?  The way I read the pattern in the\nabove, it should not hit, as the pattern does not allow anything but\n\"()\" as the \"the previous identifier is the name of the function we\nare defining\" sign, and it does not allow any whitespace between the\nidentifier and \"()\".\n\nBut it does, and the reason why this most basic form happens to work\nis because it relies on the support for \"bash-ism\" forms.  Even if\nthe \"()\" after identifier is missing, you'll match as long as the\nidentifier is followed by an optional run of whitespace and then an\nopen paren, brace or bracket:\n\n\t\"[ \\t]*(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n\nAnd I do not like code or pattern that happens to appear to work by\naccident.  Can we tighten it a bit?\n\n\t\"^[ \\t]*\"\t\t     /* (op) leading indent */\n\t\"(\"\t\t\t     /* here comes the whole thing */\n\t\"(function[ \\t]+)?\"\t     /* (op) noiseword \"function\" */\n\t\"[a-zA-Z_][a-zA-Z0-9_]*\"     /* identifier - function name */\n\t\"[ \\t]*(\\\\([ \\t]*\\\\))?\"      /* (op) start of func () */\n\t\"[ \\t]*(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\" /* various \"opening\" of body */\n\t\")\",\n\nis my attempt to break it down to make it readable and more correct.\n\n> +\t /* -- */\n> +\t /* Characters not in the default $IFS value */\n> +\t \"[^ \\t]+\"),\n>  PATTERNS(\"dts\",\n>  \t \"!;\\n\"\n>  \t \"!=\\n\"\n\nThanks.\n"},{"id":"408035","messageId":"1442e85cfbe70665890a79a5054ee07c9c16b7c6.camel@engmark.name","threadId":"54468","inReplyTo":"xmqqk0vk8o20.fsf@gitster.c.googlers.com","subject":"[PATCH v2] userdiff: support Bash","fromName":"Victor Engmark","fromEmail":"victor@engmark.name","sentAt":"2020-10-21T03:00:24Z","receivedAt":"2020-10-21T03:00:35Z","isPatch":true,"sender":{"key":"victor@engmark.name","avatar":"https://avatars.githubusercontent.com/u/168301?v=4"},"body":"Supports POSIX, bashism and mixed function declarations, all four compound\ncommand types, trailing comments and mixed whitespace.\n\nUses the POSIX.1-2017 definition of allowed characters in names\n<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_235>\nsince actual allowed characters in Bash function names are locale\ndependent <https://unix.stackexchange.com/a/245336/3645>.\n\nUses the default `IFS` characters to define words.\n\nAdds testing functionality to verify non-matches by including the\nliteral string \"non-match\" somewhere in the test file. To verify that\nonly the matching files are syntactically valid:\n\nfor file in t/t4018/bash*\ndo\n    echo \"$file\"\n    if grep non-match \"$file\"\n    then\n        . \"$file\" && echo FAILED\n    else\n        . \"$file\" || echo FAILED\n    fi\ndone 2>/dev/null | grep FAILED\n\nSigned-off-by: Victor Engmark <victor@engmark.name>\n---\nFixes issues found by Junio C Hamano.\n\nSupports more valid function declaration formats.\n\nVerifies non-matching code which looks similar to function\ndeclarations.\n\nDoes *not* include the following suggested test:\n\n> \tRIGHT () {\n> \t\t: ChangeMe\n> \t}\n\nQuoting t/t4018/README:\n\n    Insert the word \"ChangeMe\" (exactly this form) at a distance of\n    at least two lines from the line that must appear in the hunk\n    header.\n\nt/t4018-diff-funcname.sh verifies the hunk header as part of `git diff\n-U1`, so there's one line of context. In your example \"RIGHT\" is on the\nline directly above \"ChangeMe\", so it's part of the context. I assume\nit is intentional that no part of the context is duplicated in the hunk\nheader. The other tests, for example t/t4018/python-def, are\nimplemented in the same way, with at least one line of content between\n\"RIGHT\" and \"ChangeMe\".\n\n> > diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-\n> > comment\n> > new file mode 100644\n> > index 0000000000..f1edbeda31\n> > --- /dev/null\n> > +++ b/t/t4018/bash-trailing-comment\n> > @@ -0,0 +1,4 @@\n> > +RIGHT() { # Comment\n> > +\n> > +    ChangeMe\n> > +}\n> \n> This is the closest, but it tests \"# with comment\" at the same time.\n\nIt's meant to test only the comment part, so that is intentional.\nt/t4018/bash-trailing-comment is identical to t/t4018/bash-posix-style-\nfunction except for the comment.\n---\n Documentation/gitattributes.txt       |  2 ++\n t/t4018-diff-funcname.sh              | 18 ++++++++++++++----\n t/t4018/README                        |  3 +++\n t/t4018/bash-arithmetic-function      |  4 ++++\n t/t4018/bash-bashism-style-compact    |  4 ++++\n t/t4018/bash-bashism-style-function   |  4 ++++\n t/t4018/bash-bashism-style-whitespace |  4 ++++\n t/t4018/bash-conditional-function     |  4 ++++\n t/t4018/bash-missing-parentheses      |  4 ++++\n t/t4018/bash-mixed-style-compact      |  4 ++++\n t/t4018/bash-mixed-style-function     |  4 ++++\n t/t4018/bash-other-characters         |  4 ++++\n t/t4018/bash-posix-style-compact      |  4 ++++\n t/t4018/bash-posix-style-function     |  4 ++++\n t/t4018/bash-posix-style-whitespace   |  4 ++++\n t/t4018/bash-subshell-function        |  4 ++++\n t/t4018/bash-trailing-comment         |  4 ++++\n userdiff.c                            | 22 ++++++++++++++++++++++\n 18 files changed, 97 insertions(+), 4 deletions(-)\n create mode 100644 t/t4018/bash-arithmetic-function\n create mode 100644 t/t4018/bash-bashism-style-compact\n create mode 100644 t/t4018/bash-bashism-style-function\n create mode 100644 t/t4018/bash-bashism-style-whitespace\n create mode 100644 t/t4018/bash-conditional-function\n create mode 100644 t/t4018/bash-missing-parentheses\n create mode 100644 t/t4018/bash-mixed-style-compact\n create mode 100644 t/t4018/bash-mixed-style-function\n create mode 100644 t/t4018/bash-other-characters\n create mode 100644 t/t4018/bash-posix-style-compact\n create mode 100644 t/t4018/bash-posix-style-function\n create mode 100644 t/t4018/bash-posix-style-whitespace\n create mode 100644 t/t4018/bash-subshell-function\n create mode 100644 t/t4018/bash-trailing-comment\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 2d0a03715b..8a15ff6bdf 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -802,6 +802,8 @@ patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n+- `bash` suitable for source code in the Bourne-Again SHell language.\n+\n - `bibtex` suitable for files with BibTeX coded references.\n \n - `cpp` suitable for source code in the C and C++ languages.\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9d07797579..d18c4669d2 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -27,6 +27,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \tada\n+\tbash\n \tbibtex\n \tcpp\n \tcsharp\n@@ -106,10 +107,19 @@ do\n \telse\n \t\tresult=success\n \tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >actual &&\n-\t\tgrep '@@ .* @@.*RIGHT' actual\n-\t\"\n+\n+\tif grep non-match \"$i\" >/dev/null 2>&1\n+\tthen\n+\t\ttest_expect_$result \"hunk header non-match: $i\" \"\n+\t\t\tgit diff -U1 $i >actual &&\n+\t\t\t! grep '@@ .* @@.*RIGHT' actual\n+\t\t\"\n+\telse\n+\t\ttest_expect_$result \"hunk header: $i\" \"\n+\t\t\tgit diff -U1 $i >actual &&\n+\t\t\tgrep '@@ .* @@.*RIGHT' actual\n+\t\t\"\n+\tfi\n done\n \n test_done\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1..cfe2b86ee7 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -10,6 +10,9 @@ The text that must appear in the hunk header must contain the word\n To mark a test case that highlights a malfunction, insert the word\n BROKEN in all lower-case somewhere in the file.\n \n+To mark a test case that verifies some text is not matched, insert the\n+word NON-MATCH in all lower-case somewhere in the file.\n+\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\nnew file mode 100644\nindex 0000000000..c0b276cb50\n--- /dev/null\n+++ b/t/t4018/bash-arithmetic-function\n@@ -0,0 +1,4 @@\n+RIGHT() ((\n+\n+    ChangeMe = \"$x\" + \"$y\"\n+))\ndiff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\nnew file mode 100644\nindex 0000000000..9a06bb08fd\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-compact\n@@ -0,0 +1,4 @@\n+function RIGHT{ # non-match\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\nnew file mode 100644\nindex 0000000000..f1de4fa831\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT {\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace\nnew file mode 100644\nindex 0000000000..ade85dd3a5\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-whitespace\n@@ -0,0 +1,4 @@\n+\t function \tRIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\nnew file mode 100644\nindex 0000000000..c5949e829b\n--- /dev/null\n+++ b/t/t4018/bash-conditional-function\n@@ -0,0 +1,4 @@\n+RIGHT() [[ \\\n+\n+    \"$a\" > \"$ChangeMe\"\n+]]\ndiff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\nnew file mode 100644\nindex 0000000000..d112761300\n--- /dev/null\n+++ b/t/t4018/bash-missing-parentheses\n@@ -0,0 +1,4 @@\n+functionRIGHT { # non-match\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact\nnew file mode 100644\nindex 0000000000..d9364cba67\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-compact\n@@ -0,0 +1,4 @@\n+function RIGHT(){\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\nnew file mode 100644\nindex 0000000000..555f9b2466\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\nnew file mode 100644\nindex 0000000000..a3f390d525\n--- /dev/null\n+++ b/t/t4018/bash-other-characters\n@@ -0,0 +1,4 @@\n+_RIGHT_0n() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\nnew file mode 100644\nindex 0000000000..045bd2029b\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-compact\n@@ -0,0 +1,4 @@\n+RIGHT(){\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\nnew file mode 100644\nindex 0000000000..a4d144856e\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-function\n@@ -0,0 +1,4 @@\n+RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\nnew file mode 100644\nindex 0000000000..4d984f0aa4\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-whitespace\n@@ -0,0 +1,4 @@\n+\t RIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\nnew file mode 100644\nindex 0000000000..80baa09484\n--- /dev/null\n+++ b/t/t4018/bash-subshell-function\n@@ -0,0 +1,4 @@\n+RIGHT() (\n+\n+    ChangeMe=2\n+)\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\nnew file mode 100644\nindex 0000000000..f1edbeda31\n--- /dev/null\n+++ b/t/t4018/bash-trailing-comment\n@@ -0,0 +1,4 @@\n+RIGHT() { # Comment\n+\n+    ChangeMe\n+}\ndiff --git a/userdiff.c b/userdiff.c\nindex fde02f225b..8830019f05 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -23,6 +23,28 @@ IPATTERN(\"ada\",\n \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n+PATTERNS(\"bash\",\n+\t /* Optional leading indentation */\n+\t \"^[ \\t]*\"\n+\t /* Start of function definition */\n+\t \"(\"\n+\t /* Start of POSIX/Bashism grouping */\n+\t \"(\"\n+\t /* POSIX identifier with mandatory parentheses */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*[ \\t]*\\\\([ \\t]*\\\\))\"\n+\t /* Bashism identifier with optional parentheses */\n+\t \"|(function[ \\t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \\t]*\\\\([ \\t]*\\\\))|([ \\t]+))\"\n+\t /* End of POSIX/Bashism grouping */\n+\t \")\"\n+\t /* Optional whitespace */\n+\t \"[ \\t]*\"\n+\t /* Compound command starting with `{`, `(`, `((` or `[[` */\n+\t \"(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n+\t /* End of function definition */\n+\t \")\",\n+\t /* -- */\n+\t /* Characters not in the default $IFS value */\n+\t \"[^ \\t]+\"),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n\n"},{"id":"408039","messageId":"a07042af-d16c-1975-c0d1-f22f4fec5827@kdbg.org","threadId":"54468","inReplyTo":"1442e85cfbe70665890a79a5054ee07c9c16b7c6.camel@engmark.name","subject":"Re: [PATCH v2] userdiff: support Bash","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2020-10-21T07:07:12Z","receivedAt":"2020-10-21T07:07:21Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 21.10.20 um 05:00 schrieb Victor Engmark:\n> Supports POSIX, bashism and mixed function declarations, all four compound\n> command types, trailing comments and mixed whitespace.\n> \n> Uses the POSIX.1-2017 definition of allowed characters in names\n> <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_235>\n> since actual allowed characters in Bash function names are locale\n> dependent <https://unix.stackexchange.com/a/245336/3645>.\n\nSo, this is more like\n\n   Even though bash allows locale-dependet characters in function names,\n   only the allowed characters per the POSIX.1-2017 definition are\n   implemented for simplicity. We can expect that this catches the vast\n   majority of use-cases.\n\n> \n> Uses the default `IFS` characters to define words.\n\nWe could do better than this, I think. At a minimum, the equal sign,\nsingle quote, double quote, parentheses, and braces should also\ndelineate words. $(, ${, $((, ((, )), [[, ]], should be words. I would\nexclude single brackets because they could only occur in globs, IIRC,\nand they need not be broken into words at brackets. $var should be a\nsingle word, IMO.\n\nThat said, this can be presented as a patch on top of this one.\n\n> \n> Adds testing functionality to verify non-matches by including the\n> literal string \"non-match\" somewhere in the test file. To verify that\n> only the matching files are syntactically valid:\n> \n> for file in t/t4018/bash*\n> do\n>     echo \"$file\"\n>     if grep non-match \"$file\"\n>     then\n>         . \"$file\" && echo FAILED\n>     else\n>         . \"$file\" || echo FAILED\n>     fi\n> done 2>/dev/null | grep FAILED\n\nThis complication is not necessary. See below for an example how to do\nnegative tests.\n\nWhile speaking of that: it is very refreshing to see negative tests!\n\n> \n> Signed-off-by: Victor Engmark <victor@engmark.name>\n> ---\n\nWhen you write a commit message, please always answer the question WHY\nthe change should be made. For example, the notice \"use IFS characters\"\nalone does not add value; that much can be seen in the patch text. How\nabout:\n\n   Since a word pattern has to be specified, but there is no easy way\n   to request the default word pattern, use the standard IFS characters\n   for a starter. A later patch can improve this.\n\nIn general, a justification of why something should be added, should\nalso be answered. But in the case of \"bash pattern for userdiff\" the\nanswer is too obvious and trivial, that an exception is warranted.\n\nPlease write the commit message in imperative mood. \"Support\" instead of\n\"Supports\", etc.\n\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 2d0a03715b..8a15ff6bdf 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -802,6 +802,8 @@ patterns are available:\n>  \n>  - `ada` suitable for source code in the Ada language.\n>  \n> +- `bash` suitable for source code in the Bourne-Again SHell language.\n\nCan we mention POSIX shell language as well?\n\n> diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\n> new file mode 100644\n> index 0000000000..d112761300\n> --- /dev/null\n> +++ b/t/t4018/bash-missing-parentheses\n> @@ -0,0 +1,4 @@\n> +functionRIGHT { # non-match\n> +    :\n> +    echo 'ChangeMe'\n> +}\n\nTo check for a non-match, you write the test like this:\n\n\tfunction RIGHT () {\n\t}\n\t# the following must not be picked up:\n\tfunctionWrong {\n\t\t:\n\t\tChangeMe\n\t}\n\nThat is, you present a positive match, and then expect that the\nsubsequent negative match is not picked up.\n\n> diff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\n> new file mode 100644\n> index 0000000000..045bd2029b\n> --- /dev/null\n> +++ b/t/t4018/bash-posix-style-compact\n> @@ -0,0 +1,4 @@\n> +RIGHT(){\n> +\n> +    ChangeMe\n> +}\n> diff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\n> new file mode 100644\n> index 0000000000..a4d144856e\n> --- /dev/null\n> +++ b/t/t4018/bash-posix-style-function\n> @@ -0,0 +1,4 @@\n> +RIGHT() {\n> +\n> +    ChangeMe\n> +}\n> diff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\n> new file mode 100644\n> index 0000000000..4d984f0aa4\n> --- /dev/null\n> +++ b/t/t4018/bash-posix-style-whitespace\n> @@ -0,0 +1,4 @@\n> +\t RIGHT \t( \t) \t{\n> +\n> +\t    ChangeMe\n> +\t }\n\nGood to see POSIX-style function tests.\n\n> diff --git a/userdiff.c b/userdiff.c\n> index fde02f225b..8830019f05 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -23,6 +23,28 @@ IPATTERN(\"ada\",\n>  \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n>  \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n>  \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n> +PATTERNS(\"bash\",\n> +\t /* Optional leading indentation */\n> +\t \"^[ \\t]*\"\n> +\t /* Start of function definition */\n> +\t \"(\"\n\nThe purpose of this outer-most pair of parentheses is actually to mark\nthe captured text, not so much \"the function definition\".\n\n> +\t /* Start of POSIX/Bashism grouping */\n> +\t \"(\"\n\nYou could omit the comment if you indent the parts that are inside the\nparentheses:\n\n\t\"(\"\n\t\t\"...\"\n\t\"|\"\n\t\t\"...\"\n\t\")\"\n\n(But perhaps don't indent between the outer-most parentheses; it would\nget us too far to the right. But judge yourself.)\n\n> +\t /* POSIX identifier with mandatory parentheses */\n> +\t \"[a-zA-Z_][a-zA-Z0-9_]*[ \\t]*\\\\([ \\t]*\\\\))\"\n> +\t /* Bashism identifier with optional parentheses */\n> +\t \"|(function[ \\t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \\t]*\\\\([ \\t]*\\\\))|([ \\t]+))\"\n> +\t /* End of POSIX/Bashism grouping */\n> +\t \")\"\n> +\t /* Optional whitespace */\n> +\t \"[ \\t]*\"\n> +\t /* Compound command starting with `{`, `(`, `((` or `[[` */\n> +\t \"(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n> +\t /* End of function definition */\n> +\t \")\",\n> +\t /* -- */\n> +\t /* Characters not in the default $IFS value */\n> +\t \"[^ \\t]+\"),\n"},{"id":"408082","messageId":"xmqq8sbz8lvb.fsf@gitster.c.googlers.com","threadId":"54468","inReplyTo":"a07042af-d16c-1975-c0d1-f22f4fec5827@kdbg.org","subject":"Re: [PATCH v2] userdiff: support Bash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-10-21T18:39:04Z","receivedAt":"2020-10-21T18:39:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n>> diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\n>> new file mode 100644\n>> index 0000000000..d112761300\n>> --- /dev/null\n>> +++ b/t/t4018/bash-missing-parentheses\n>> @@ -0,0 +1,4 @@\n>> +functionRIGHT { # non-match\n>> +    :\n>> +    echo 'ChangeMe'\n>> +}\n>\n> To check for a non-match, you write the test like this:\n>\n> \tfunction RIGHT () {\n> \t}\n> \t# the following must not be picked up:\n> \tfunctionWrong {\n> \t\t:\n> \t\tChangeMe\n> \t}\n>\n> That is, you present a positive match, and then expect that the\n> subsequent negative match is not picked up.\n\nAll good suggestions, but I especially appreciate this one ;-).\n\n>> diff --git a/userdiff.c b/userdiff.c\n>> index fde02f225b..8830019f05 100644\n>> --- a/userdiff.c\n>> +++ b/userdiff.c\n>> @@ -23,6 +23,28 @@ IPATTERN(\"ada\",\n>>  \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n>>  \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n>>  \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n>> +PATTERNS(\"bash\",\n>> +\t /* Optional leading indentation */\n>> +\t \"^[ \\t]*\"\n>> +\t /* Start of function definition */\n>> +\t \"(\"\n>\n> The purpose of this outer-most pair of parentheses is actually to mark\n> the captured text, not so much \"the function definition\".\n\nThis, too (I called it \"here comes the whole thing\" in my suggested\nversion ).\n\n>> +\t /* Start of POSIX/Bashism grouping */\n>> +\t \"(\"\n>\n> You could omit the comment if you indent the parts that are inside the\n> parentheses:\n>\n> \t\"(\"\n> \t\t\"...\"\n> \t\"|\"\n> \t\t\"...\"\n> \t\")\"\n>\n\nAn excellent readability suggestion.\n\nThanks for a review.  Especially the parts that mine didn't touch\n(i.e. the proposed log message).\n\n> (But perhaps don't indent between the outer-most parentheses; it would\n> get us too far to the right. But judge yourself.)\n>\n>> +\t /* POSIX identifier with mandatory parentheses */\n>> +\t \"[a-zA-Z_][a-zA-Z0-9_]*[ \\t]*\\\\([ \\t]*\\\\))\"\n>> +\t /* Bashism identifier with optional parentheses */\n>> +\t \"|(function[ \\t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \\t]*\\\\([ \\t]*\\\\))|([ \\t]+))\"\n>> +\t /* End of POSIX/Bashism grouping */\n>> +\t \")\"\n>> +\t /* Optional whitespace */\n>> +\t \"[ \\t]*\"\n>> +\t /* Compound command starting with `{`, `(`, `((` or `[[` */\n>> +\t \"(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n>> +\t /* End of function definition */\n>> +\t \")\",\n>> +\t /* -- */\n>> +\t /* Characters not in the default $IFS value */\n>> +\t \"[^ \\t]+\"),\n"},{"id":"408122","messageId":"f56cff1c617de3fa0ce6ebff106dab629565e1fa.camel@engmark.name","threadId":"54468","inReplyTo":"a07042af-d16c-1975-c0d1-f22f4fec5827@kdbg.org","subject":"Re: [PATCH v2] userdiff: support Bash","fromName":"Victor Engmark","fromEmail":"victor@engmark.name","sentAt":"2020-10-21T22:48:09Z","receivedAt":"2020-10-21T22:48:22Z","isPatch":true,"sender":{"key":"victor@engmark.name","avatar":"https://avatars.githubusercontent.com/u/168301?v=4"},"body":"On Wed, 2020-10-21 at 09:07 +0200, Johannes Sixt wrote:\n> Am 21.10.20 um 05:00 schrieb Victor Engmark:\n> > Uses the default `IFS` characters to define words.\n> \n> We could do better than this, I think. At a minimum, the equal sign,\n> single quote, double quote, parentheses, and braces should also\n> delineate words. $(, ${, $((, ((, )), [[, ]], should be words. I\n> would\n> exclude single brackets because they could only occur in globs, IIRC,\n> and they need not be broken into words at brackets. $var should be a\n> single word, IMO.\n> \n> That said, this can be presented as a patch on top of this one.\n\nI can't tell where this word definition is used, and I can't seem to\nfind any tests for the word regexes for the other languages. Is it used\nfor word diffs somehow? I'll leave it for now, but if you have some\npointers I could look into that later.\n\nThank you very much for the comments!\n\n"},{"id":"408128","messageId":"6c6b5ed2166ec2c308c53bf87c78b422fdc5084f.camel@engmark.name","threadId":"54468","inReplyTo":"a07042af-d16c-1975-c0d1-f22f4fec5827@kdbg.org","subject":"[PATCH v3] userdiff: support Bash","fromName":"Victor Engmark","fromEmail":"victor@engmark.name","sentAt":"2020-10-21T23:45:08Z","receivedAt":"2020-10-21T23:45:19Z","isPatch":true,"sender":{"key":"victor@engmark.name","avatar":"https://avatars.githubusercontent.com/u/168301?v=4"},"body":"Support POSIX, bashism and mixed function declarations, all four\ncompound command types, trailing comments and mixed whitespace.\n\nEven though Bash allows locale-dependent characters in function names\n<https://unix.stackexchange.com/a/245336/3645>, only detect function\nnames with characters allowed by POSIX.1-2017\n<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_235>\nfor simplicity. This should cover the vast majority of use cases, and\nproduces system-agnostic results.\n\nSince a word pattern has to be specified, but there is no easy way to\nknow the default word pattern, use the default `IFS` characters for a\nstarter. A later patch can improve this.\n\nSigned-off-by: Victor Engmark <victor@engmark.name>\n---\nIncludes suggestions by Johannes Sixt <j6t@kdbg.org>.\n---\n Documentation/gitattributes.txt       |  3 +++\n t/t4018-diff-funcname.sh              |  1 +\n t/t4018/bash-arithmetic-function      |  4 ++++\n t/t4018/bash-bashism-style-compact    |  6 ++++++\n t/t4018/bash-bashism-style-function   |  4 ++++\n t/t4018/bash-bashism-style-whitespace |  4 ++++\n t/t4018/bash-conditional-function     |  4 ++++\n t/t4018/bash-missing-parentheses      |  6 ++++++\n t/t4018/bash-mixed-style-compact      |  4 ++++\n t/t4018/bash-mixed-style-function     |  4 ++++\n t/t4018/bash-nested-functions         |  6 ++++++\n t/t4018/bash-other-characters         |  4 ++++\n t/t4018/bash-posix-style-compact      |  4 ++++\n t/t4018/bash-posix-style-function     |  4 ++++\n t/t4018/bash-posix-style-whitespace   |  4 ++++\n t/t4018/bash-subshell-function        |  4 ++++\n t/t4018/bash-trailing-comment         |  4 ++++\n userdiff.c                            | 21 +++++++++++++++++++++\n 18 files changed, 91 insertions(+)\n create mode 100644 t/t4018/bash-arithmetic-function\n create mode 100644 t/t4018/bash-bashism-style-compact\n create mode 100644 t/t4018/bash-bashism-style-function\n create mode 100644 t/t4018/bash-bashism-style-whitespace\n create mode 100644 t/t4018/bash-conditional-function\n create mode 100644 t/t4018/bash-missing-parentheses\n create mode 100644 t/t4018/bash-mixed-style-compact\n create mode 100644 t/t4018/bash-mixed-style-function\n create mode 100644 t/t4018/bash-nested-functions\n create mode 100644 t/t4018/bash-other-characters\n create mode 100644 t/t4018/bash-posix-style-compact\n create mode 100644 t/t4018/bash-posix-style-function\n create mode 100644 t/t4018/bash-posix-style-whitespace\n create mode 100644 t/t4018/bash-subshell-function\n create mode 100644 t/t4018/bash-trailing-comment\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 2d0a03715b..5e8a973449 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -802,6 +802,9 @@ patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n+- `bash` suitable for source code in the Bourne-Again SHell language.\n+  Covers a superset of POSIX function definitions.\n+\n - `bibtex` suitable for files with BibTeX coded references.\n \n - `cpp` suitable for source code in the C and C++ languages.\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9d07797579..9675bc17db 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -27,6 +27,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \tada\n+\tbash\n \tbibtex\n \tcpp\n \tcsharp\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\nnew file mode 100644\nindex 0000000000..c0b276cb50\n--- /dev/null\n+++ b/t/t4018/bash-arithmetic-function\n@@ -0,0 +1,4 @@\n+RIGHT() ((\n+\n+    ChangeMe = \"$x\" + \"$y\"\n+))\ndiff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\nnew file mode 100644\nindex 0000000000..1ca3126f61\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-compact\n@@ -0,0 +1,6 @@\n+function RIGHT {\n+    function InvalidSyntax{\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\nnew file mode 100644\nindex 0000000000..f1de4fa831\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT {\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace\nnew file mode 100644\nindex 0000000000..ade85dd3a5\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-whitespace\n@@ -0,0 +1,4 @@\n+\t function \tRIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\nnew file mode 100644\nindex 0000000000..c5949e829b\n--- /dev/null\n+++ b/t/t4018/bash-conditional-function\n@@ -0,0 +1,4 @@\n+RIGHT() [[ \\\n+\n+    \"$a\" > \"$ChangeMe\"\n+]]\ndiff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\nnew file mode 100644\nindex 0000000000..8c8a05dd7a\n--- /dev/null\n+++ b/t/t4018/bash-missing-parentheses\n@@ -0,0 +1,6 @@\n+function RIGHT {\n+    functionInvalidSyntax {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\ndiff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact\nnew file mode 100644\nindex 0000000000..d9364cba67\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-compact\n@@ -0,0 +1,4 @@\n+function RIGHT(){\n+    :\n+    echo 'ChangeMe'\n+}\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\nnew file mode 100644\nindex 0000000000..555f9b2466\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-function\n@@ -0,0 +1,4 @@\n+function RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions\nnew file mode 100644\nindex 0000000000..2c9237ead4\n--- /dev/null\n+++ b/t/t4018/bash-nested-functions\n@@ -0,0 +1,6 @@\n+outer() {\n+    RIGHT() {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\nnew file mode 100644\nindex 0000000000..a3f390d525\n--- /dev/null\n+++ b/t/t4018/bash-other-characters\n@@ -0,0 +1,4 @@\n+_RIGHT_0n() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\nnew file mode 100644\nindex 0000000000..045bd2029b\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-compact\n@@ -0,0 +1,4 @@\n+RIGHT(){\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\nnew file mode 100644\nindex 0000000000..a4d144856e\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-function\n@@ -0,0 +1,4 @@\n+RIGHT() {\n+\n+    ChangeMe\n+}\ndiff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\nnew file mode 100644\nindex 0000000000..4d984f0aa4\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-whitespace\n@@ -0,0 +1,4 @@\n+\t RIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\nnew file mode 100644\nindex 0000000000..80baa09484\n--- /dev/null\n+++ b/t/t4018/bash-subshell-function\n@@ -0,0 +1,4 @@\n+RIGHT() (\n+\n+    ChangeMe=2\n+)\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\nnew file mode 100644\nindex 0000000000..f1edbeda31\n--- /dev/null\n+++ b/t/t4018/bash-trailing-comment\n@@ -0,0 +1,4 @@\n+RIGHT() { # Comment\n+\n+    ChangeMe\n+}\ndiff --git a/userdiff.c b/userdiff.c\nindex fde02f225b..eb698eaca7 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -23,6 +23,27 @@ IPATTERN(\"ada\",\n \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n+PATTERNS(\"bash\",\n+\t /* Optional leading indentation */\n+\t \"^[ \\t]*\"\n+\t /* Start of captured text */\n+\t \"(\"\n+\t \"(\"\n+\t     /* POSIX identifier with mandatory parentheses */\n+\t     \"[a-zA-Z_][a-zA-Z0-9_]*[ \\t]*\\\\([ \\t]*\\\\))\"\n+\t \"|\"\n+\t     /* Bashism identifier with optional parentheses */\n+\t     \"(function[ \\t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \\t]*\\\\([ \\t]*\\\\))|([ \\t]+))\"\n+\t \")\"\n+\t /* Optional whitespace */\n+\t \"[ \\t]*\"\n+\t /* Compound command starting with `{`, `(`, `((` or `[[` */\n+\t \"(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n+\t /* End of captured text */\n+\t \")\",\n+\t /* -- */\n+\t /* Characters not in the default $IFS value */\n+\t \"[^ \\t]+\"),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n\n"},{"id":"408146","messageId":"0d9f9ad1-2ba7-567e-97a1-a96f0b7cd4be@kdbg.org","threadId":"54468","inReplyTo":"6c6b5ed2166ec2c308c53bf87c78b422fdc5084f.camel@engmark.name","subject":"Re: [PATCH v3] userdiff: support Bash","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2020-10-22T06:08:29Z","receivedAt":"2020-10-22T06:08:36Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 22.10.20 um 01:45 schrieb Victor Engmark:\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 2d0a03715b..5e8a973449 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -802,6 +802,9 @@ patterns are available:\n>  \n>  - `ada` suitable for source code in the Ada language.\n>  \n> +- `bash` suitable for source code in the Bourne-Again SHell language.\n> +  Covers a superset of POSIX function definitions.\n\nOK. POSIX *shell* function definitions would have been even better, but\nI think I can live with this version.\n\n> diff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\n> new file mode 100644\n> index 0000000000..1ca3126f61\n> --- /dev/null\n> +++ b/t/t4018/bash-bashism-style-compact\n> @@ -0,0 +1,6 @@\n> +function RIGHT {\n> +    function InvalidSyntax{\n\nNicely done!\n\n> +        :\n> +        echo 'ChangeMe'\n> +    }\n> +}\n\n> diff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions\n> new file mode 100644\n> index 0000000000..2c9237ead4\n> --- /dev/null\n> +++ b/t/t4018/bash-nested-functions\n> @@ -0,0 +1,6 @@\n> +outer() {\n> +    RIGHT() {\n> +        :\n> +        echo 'ChangeMe'\n> +    }\n> +}\n\nThat's another very good addition!\n\n> diff --git a/userdiff.c b/userdiff.c\n> index fde02f225b..eb698eaca7 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -23,6 +23,27 @@ IPATTERN(\"ada\",\n>  \t \"[a-zA-Z][a-zA-Z0-9_]*\"\n>  \t \"|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?\"\n>  \t \"|=>|\\\\.\\\\.|\\\\*\\\\*|:=|/=|>=|<=|<<|>>|<>\"),\n> +PATTERNS(\"bash\",\n> +\t /* Optional leading indentation */\n> +\t \"^[ \\t]*\"\n> +\t /* Start of captured text */\n> +\t \"(\"\n> +\t \"(\"\n> +\t     /* POSIX identifier with mandatory parentheses */\n> +\t     \"[a-zA-Z_][a-zA-Z0-9_]*[ \\t]*\\\\([ \\t]*\\\\))\"\n> +\t \"|\"\n> +\t     /* Bashism identifier with optional parentheses */\n> +\t     \"(function[ \\t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \\t]*\\\\([ \\t]*\\\\))|([ \\t]+))\"\n> +\t \")\"\n> +\t /* Optional whitespace */\n> +\t \"[ \\t]*\"\n> +\t /* Compound command starting with `{`, `(`, `((` or `[[` */\n> +\t \"(\\\\{|\\\\(\\\\(?|\\\\[\\\\[)\"\n> +\t /* End of captured text */\n> +\t \")\",\n> +\t /* -- */\n> +\t /* Characters not in the default $IFS value */\n> +\t \"[^ \\t]+\"),\n>  PATTERNS(\"dts\",\n>  \t \"!;\\n\"\n>  \t \"!=\\n\"\n> \n\nThis is very well done. Thank you!\n\nAcked-by: Johannes Sixt <j6t@kdbg.org>\n\n-- Hannes\n"},{"id":"408176","messageId":"xmqqr1pqmamq.fsf@gitster.c.googlers.com","threadId":"54468","inReplyTo":"0d9f9ad1-2ba7-567e-97a1-a96f0b7cd4be@kdbg.org","subject":"Re: [PATCH v3] userdiff: support Bash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-10-22T17:30:21Z","receivedAt":"2020-10-22T17:30:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 22.10.20 um 01:45 schrieb Victor Engmark:\n>> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n>> index 2d0a03715b..5e8a973449 100644\n>> --- a/Documentation/gitattributes.txt\n>> +++ b/Documentation/gitattributes.txt\n>> @@ -802,6 +802,9 @@ patterns are available:\n>>  \n>>  - `ada` suitable for source code in the Ada language.\n>>  \n>> +- `bash` suitable for source code in the Bourne-Again SHell language.\n>> +  Covers a superset of POSIX function definitions.\n>\n> OK. POSIX *shell* function definitions would have been even better, but\n> I think I can live with this version.\n\nI can't, so I'll locally amend ...\n\n> This is very well done. Thank you!\n>\n> Acked-by: Johannes Sixt <j6t@kdbg.org>\n\n... and with this in the trailer block.\n\nThanks, both.  Queued.\n"}]}