{"thread":{"id":"52077","subject":"[PATCH v5 1/2] format-patch: create leading components of output directory","startedAt":"2019-10-21T10:25:29Z","lastAt":"2019-10-23T02:08:20Z","messageCount":4,"participants":["Bert Wesarg","Junio C Hamano"],"isPatch":true,"patchVersion":5,"patchTotal":2},"messages":[{"id":"384497","messageId":"ac3cc3df4452355dc41c58fe94961c37ca09ebec.1571653441.git.bert.wesarg@googlemail.com","threadId":"52077","inReplyTo":null,"subject":"[PATCH v5 1/2] format-patch: create leading components of output directory","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2019-10-21T10:25:22Z","receivedAt":"2019-10-21T10:25:29Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"'git format-patch -o <outdir>' did an equivalent of 'mkdir <outdir>'\nnot 'mkdir -p <outdir>', which is being corrected.\n\nAvoid the usage of 'adjust_shared_perm' on the leading directories which\nmay have security implications. Achieved by temporarily disabling of\n'config.sharedRepository' like 'git init' does.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\nChanges in v2:\n * squashed and base new tests on 'dl/format-patch-doc-test-cleanup'\n\nChanges in v3:\n * avoid applying adjust_shared_perm\n\nChanges in v4:\n * based on dl/format-patch-doc-test-cleanup and adopt it\n\nChanges in v5:\n * make tests self-contained\n\nCc: Denton Liu <liu.denton@gmail.com>\nCc: Junio C Hamano <gitster@pobox.com>\nCc: SZEDER Gábor <szeder.dev@gmail.com>\n---\n Documentation/config/format.txt    |  2 +-\n Documentation/git-format-patch.txt |  3 ++-\n builtin/log.c                      | 16 ++++++++++++++++\n t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++\n 4 files changed, 45 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex cb629fa769..40cad9278f 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -81,7 +81,7 @@ format.coverLetter::\n \n format.outputDirectory::\n \tSet a custom directory to store the resulting files instead of the\n-\tcurrent working directory.\n+\tcurrent working directory. All directory components will be created.\n \n format.useAutoBase::\n \tA boolean value which lets you enable the `--base=auto` option of\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 0ac56f4b70..2035d4d5d5 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -66,7 +66,8 @@ they are created in the current working directory. The default path\n can be set with the `format.outputDirectory` configuration option.\n The `-o` option takes precedence over `format.outputDirectory`.\n To store patches in the current working directory even when\n-`format.outputDirectory` points elsewhere, use `-o .`.\n+`format.outputDirectory` points elsewhere, use `-o .`. All directory\n+components will be created.\n \n By default, the subject of a single patch is \"[PATCH] \" followed by\n the concatenation of lines from the commit message up to the first blank\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 44b10b3415..8d08632858 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1765,10 +1765,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (output_directory) {\n+\t\tint saved;\n \t\tif (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n \t\t\trev.diffopt.use_color = GIT_COLOR_NEVER;\n \t\tif (use_stdout)\n \t\t\tdie(_(\"standard output, or directory, which one?\"));\n+\t\t/*\n+\t\t * We consider <outdir> as 'outside of gitdir', therefore avoid\n+\t\t * applying adjust_shared_perm in s-c-l-d.\n+\t\t */\n+\t\tsaved = get_shared_repository();\n+\t\tset_shared_repository(0);\n+\t\tswitch (safe_create_leading_directories_const(output_directory)) {\n+\t\tcase SCLD_OK:\n+\t\tcase SCLD_EXISTS:\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdie(_(\"could not create leading directories \"\n+\t\t\t      \"of '%s'\"), output_directory);\n+\t\t}\n+\t\tset_shared_repository(saved);\n \t\tif (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n \t\t\tdie_errno(_(\"could not create directory '%s'\"),\n \t\t\t\t  output_directory);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 72b09896cf..3aab25da76 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1606,6 +1606,32 @@ test_expect_success 'From line has expected format' '\n \ttest_cmp from filtered\n '\n \n+test_expect_success 'format-patch -o with no leading directories' '\n+\trm -fr patches &&\n+\tgit format-patch -o patches master..side &&\n+\tcount=$(git rev-list --count master..side) &&\n+\tls patches >list &&\n+\ttest_line_count = $count list\n+'\n+\n+test_expect_success 'format-patch -o with leading existing directories' '\n+\trm -rf existing-dir &&\n+\tmkdir existing-dir &&\n+\tgit format-patch -o existing-dir/patches master..side &&\n+\tcount=$(git rev-list --count master..side) &&\n+\tls existing-dir/patches >list &&\n+\ttest_line_count = $count list\n+'\n+\n+test_expect_success 'format-patch -o with leading non-existing directories' '\n+\trm -rf non-existing-dir &&\n+\tgit format-patch -o non-existing-dir/patches master..side &&\n+\tcount=$(git rev-list --count master..side) &&\n+\ttest_path_is_dir non-existing-dir\n+\tls non-existing-dir/patches >list &&\n+\ttest_line_count = $count list\n+'\n+\n test_expect_success 'format-patch format.outputDirectory option' '\n \ttest_config format.outputDirectory patches &&\n \trm -fr patches &&\n-- \n2.23.0.13.g28bc381d7c\n\n"},{"id":"384498","messageId":"d461ae79c23ded9cc570ba1856cbae4ca227a711.1571653441.git.bert.wesarg@googlemail.com","threadId":"52077","inReplyTo":"ac3cc3df4452355dc41c58fe94961c37ca09ebec.1571653441.git.bert.wesarg@googlemail.com","subject":"[PATCH v5 2/2] format-patch: configure a command to generate the output directory name","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2019-10-21T10:25:23Z","receivedAt":"2019-10-21T10:25:29Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"The 'format.outputDirectory' configuration is only able to store constant\ndirectory names. Though some may use\n\n   $ git format-patch -o $(createdir) …\n\nto name the directory dynamically. Provide a new configuration to be able\nto store such a command too.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\nChanges in v2:\n * rephrase motivation\n\nChanges in v3:\n * remove RFC\n\nChanges in v4:\n * based on dl/format-patch-doc-test-cleanup and adopt it\n\nChanges in v5:\n * none\n\nCc: Alexander Kuleshov <kuleshovmail@gmail.com>\nCc: Eric Sunshine <sunshine@sunshineco.com>\nCc: Denton Liu <liu.denton@gmail.com>\nCc: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config/format.txt    |  5 +++++\n Documentation/git-format-patch.txt |  6 +++++-\n builtin/log.c                      | 24 +++++++++++++++++++++++-\n t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++\n 4 files changed, 59 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\nindex 40cad9278f..420188a1c6 100644\n--- a/Documentation/config/format.txt\n+++ b/Documentation/config/format.txt\n@@ -83,6 +83,11 @@ format.outputDirectory::\n \tSet a custom directory to store the resulting files instead of the\n \tcurrent working directory. All directory components will be created.\n \n+format.outputDirectoryCmd::\n+\tThe command which is used to name a custom directory to store the\n+\tresulting files instead of the current working directory. All directory\n+\tcomponents will be created.\n+\n format.useAutoBase::\n \tA boolean value which lets you enable the `--base=auto` option of\n \tformat-patch by default.\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 2035d4d5d5..4936b9f91d 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -67,7 +67,11 @@ can be set with the `format.outputDirectory` configuration option.\n The `-o` option takes precedence over `format.outputDirectory`.\n To store patches in the current working directory even when\n `format.outputDirectory` points elsewhere, use `-o .`. All directory\n-components will be created.\n+components will be created. The 'format.outputDirectoryCmd' configuration can\n+be used to name a command to produce the directory name programmatically. The\n+command should produce the name to its standard output. The\n+`format.outputDirectory` configuration takes precedence over\n+`format.outputDirectoryCmd`.\n \n By default, the subject of a single patch is \"[PATCH] \" followed by\n the concatenation of lines from the commit message up to the first blank\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 8d08632858..3eb507c02f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -774,6 +774,7 @@ static const char *signature = git_version_string;\n static const char *signature_file;\n static int config_cover_letter;\n static const char *config_output_directory;\n+static const char *config_output_directory_cmd;\n \n enum {\n \tCOVER_UNSET,\n@@ -856,6 +857,8 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"format.outputdirectory\"))\n \t\treturn git_config_string(&config_output_directory, var, value);\n+\tif (!strcmp(var, \"format.outputdirectorycmd\"))\n+\t\treturn git_config_string(&config_output_directory_cmd, var, value);\n \tif (!strcmp(var, \"format.useautobase\")) {\n \t\tbase_auto = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1756,8 +1759,27 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tinit_display_notes(&rev.notes_opt);\n \n-\tif (!output_directory && !use_stdout)\n+\tif (!output_directory && !use_stdout) {\n+\t\t// outputDirectoryCmd can be preceeded by outputDirectory\n+\t\tif (!config_output_directory && config_output_directory_cmd) {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\t\tconst char *argv[1];\n+\t\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\t\tint rc;\n+\n+\t\t\targv[0] = config_output_directory_cmd;\n+\t\t\tcp.argv = argv;\n+\t\t\tcp.use_shell = 1;\n+\t\t\trc = capture_command(&cp, &buf, PATH_MAX);\n+\t\t\tif (rc)\n+\t\t\t\tdie(_(\"outputDirectoryCmd command failed: \"\n+\t\t\t\t      \"'%s'\"), config_output_directory_cmd);\n+\t\t\tstrbuf_setlen(&buf, strcspn(buf.buf, \"\\r\\n\"));\n+\t\t\tconfig_output_directory = strbuf_detach(&buf, NULL);\n+\t\t}\n+\n \t\toutput_directory = config_output_directory;\n+\t}\n \n \tif (!use_stdout)\n \t\toutput_directory = set_outdir(prefix, output_directory);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 3aab25da76..725706ded5 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1649,6 +1649,32 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \ttest_path_is_dir patchset\n '\n \n+test_expect_success 'format-patch format.outputDirectoryCmd option' '\n+\ttest_config format.outputDirectoryCmd \"echo patches\" &&\n+\trm -fr patches &&\n+\tgit format-patch master..side &&\n+\tcount=$(git rev-list --count master..side) &&\n+\tls patches >list &&\n+\ttest_line_count = $count list\n+'\n+\n+test_expect_success 'format-patch format.outputDirectory overrides format.outputDirectoryCmd' '\n+\ttest_config format.outputDirectoryCmd \"echo patches\" &&\n+\ttest_config format.outputDirectory patchset &&\n+\trm -fr patches patchset &&\n+\tgit format-patch master..side &&\n+\ttest_path_is_missing patches &&\n+\ttest_path_is_dir patchset\n+'\n+\n+test_expect_success 'format-patch -o overrides format.outputDirectoryCmd' '\n+\ttest_config format.outputDirectoryCmd \"echo patches\" &&\n+\trm -fr patches patchset &&\n+\tgit format-patch -o patchset master..side &&\n+\ttest_path_is_missing patches &&\n+\ttest_path_is_dir patchset\n+'\n+\n test_expect_success 'format-patch --base' '\n \tgit checkout patchid &&\n \n-- \n2.23.0.13.g28bc381d7c\n\n"},{"id":"384506","messageId":"CAKPyHN3QQuX4Z8-sySU3DbQYj1mfTqGjzgX8RFw_H6UFA07ohw@mail.gmail.com","threadId":"52077","inReplyTo":"ac3cc3df4452355dc41c58fe94961c37ca09ebec.1571653441.git.bert.wesarg@googlemail.com","subject":"Re: [PATCH v5 1/2] format-patch: create leading components of output directory","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2019-10-21T13:20:39Z","receivedAt":"2019-10-21T13:20:54Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Please ignore this. Will rebase on 2.24-rc0 and will only include the\ntest changes.\n\nBert\n\nOn Mon, Oct 21, 2019 at 12:25 PM Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n>\n> 'git format-patch -o <outdir>' did an equivalent of 'mkdir <outdir>'\n> not 'mkdir -p <outdir>', which is being corrected.\n>\n> Avoid the usage of 'adjust_shared_perm' on the leading directories which\n> may have security implications. Achieved by temporarily disabling of\n> 'config.sharedRepository' like 'git init' does.\n>\n> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n>\n> ---\n> Changes in v2:\n>  * squashed and base new tests on 'dl/format-patch-doc-test-cleanup'\n>\n> Changes in v3:\n>  * avoid applying adjust_shared_perm\n>\n> Changes in v4:\n>  * based on dl/format-patch-doc-test-cleanup and adopt it\n>\n> Changes in v5:\n>  * make tests self-contained\n>\n> Cc: Denton Liu <liu.denton@gmail.com>\n> Cc: Junio C Hamano <gitster@pobox.com>\n> Cc: SZEDER Gábor <szeder.dev@gmail.com>\n> ---\n>  Documentation/config/format.txt    |  2 +-\n>  Documentation/git-format-patch.txt |  3 ++-\n>  builtin/log.c                      | 16 ++++++++++++++++\n>  t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++\n>  4 files changed, 45 insertions(+), 2 deletions(-)\n>\n> diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt\n> index cb629fa769..40cad9278f 100644\n> --- a/Documentation/config/format.txt\n> +++ b/Documentation/config/format.txt\n> @@ -81,7 +81,7 @@ format.coverLetter::\n>\n>  format.outputDirectory::\n>         Set a custom directory to store the resulting files instead of the\n> -       current working directory.\n> +       current working directory. All directory components will be created.\n>\n>  format.useAutoBase::\n>         A boolean value which lets you enable the `--base=auto` option of\n> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\n> index 0ac56f4b70..2035d4d5d5 100644\n> --- a/Documentation/git-format-patch.txt\n> +++ b/Documentation/git-format-patch.txt\n> @@ -66,7 +66,8 @@ they are created in the current working directory. The default path\n>  can be set with the `format.outputDirectory` configuration option.\n>  The `-o` option takes precedence over `format.outputDirectory`.\n>  To store patches in the current working directory even when\n> -`format.outputDirectory` points elsewhere, use `-o .`.\n> +`format.outputDirectory` points elsewhere, use `-o .`. All directory\n> +components will be created.\n>\n>  By default, the subject of a single patch is \"[PATCH] \" followed by\n>  the concatenation of lines from the commit message up to the first blank\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 44b10b3415..8d08632858 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1765,10 +1765,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>                 setup_pager();\n>\n>         if (output_directory) {\n> +               int saved;\n>                 if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)\n>                         rev.diffopt.use_color = GIT_COLOR_NEVER;\n>                 if (use_stdout)\n>                         die(_(\"standard output, or directory, which one?\"));\n> +               /*\n> +                * We consider <outdir> as 'outside of gitdir', therefore avoid\n> +                * applying adjust_shared_perm in s-c-l-d.\n> +                */\n> +               saved = get_shared_repository();\n> +               set_shared_repository(0);\n> +               switch (safe_create_leading_directories_const(output_directory)) {\n> +               case SCLD_OK:\n> +               case SCLD_EXISTS:\n> +                       break;\n> +               default:\n> +                       die(_(\"could not create leading directories \"\n> +                             \"of '%s'\"), output_directory);\n> +               }\n> +               set_shared_repository(saved);\n>                 if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)\n>                         die_errno(_(\"could not create directory '%s'\"),\n>                                   output_directory);\n> diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\n> index 72b09896cf..3aab25da76 100755\n> --- a/t/t4014-format-patch.sh\n> +++ b/t/t4014-format-patch.sh\n> @@ -1606,6 +1606,32 @@ test_expect_success 'From line has expected format' '\n>         test_cmp from filtered\n>  '\n>\n> +test_expect_success 'format-patch -o with no leading directories' '\n> +       rm -fr patches &&\n> +       git format-patch -o patches master..side &&\n> +       count=$(git rev-list --count master..side) &&\n> +       ls patches >list &&\n> +       test_line_count = $count list\n> +'\n> +\n> +test_expect_success 'format-patch -o with leading existing directories' '\n> +       rm -rf existing-dir &&\n> +       mkdir existing-dir &&\n> +       git format-patch -o existing-dir/patches master..side &&\n> +       count=$(git rev-list --count master..side) &&\n> +       ls existing-dir/patches >list &&\n> +       test_line_count = $count list\n> +'\n> +\n> +test_expect_success 'format-patch -o with leading non-existing directories' '\n> +       rm -rf non-existing-dir &&\n> +       git format-patch -o non-existing-dir/patches master..side &&\n> +       count=$(git rev-list --count master..side) &&\n> +       test_path_is_dir non-existing-dir\n> +       ls non-existing-dir/patches >list &&\n> +       test_line_count = $count list\n> +'\n> +\n>  test_expect_success 'format-patch format.outputDirectory option' '\n>         test_config format.outputDirectory patches &&\n>         rm -fr patches &&\n> --\n> 2.23.0.13.g28bc381d7c\n>\n"},{"id":"384661","messageId":"xmqqh840jjsj.fsf@gitster-ct.c.googlers.com","threadId":"52077","inReplyTo":"CAKPyHN3QQuX4Z8-sySU3DbQYj1mfTqGjzgX8RFw_H6UFA07ohw@mail.gmail.com","subject":"Re: [PATCH v5 1/2] format-patch: create leading components of output directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-23T02:08:12Z","receivedAt":"2019-10-23T02:08:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> writes:\n\n> Please ignore this. Will rebase on 2.24-rc0 and will only include the\n> test changes.\n\nThanks.\n"}]}