{"thread":{"id":"50081","subject":"[PATCH v12 00/26] Convert \"git stash\" to C builtin","startedAt":"2018-12-20T19:44:52Z","lastAt":"2019-03-15T22:33:25Z","messageCount":106,"participants":["Paul-Sebastian Ungureanu","Junio C Hamano","Johannes Schindelin","Thomas Gummerer","SZEDER Gábor","Ævar Arnfjörð Bjarmason","Jeff King","Ben Peart"],"isPatch":true,"patchVersion":12,"patchTotal":26},"messages":[{"id":"365671","messageId":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"https://public-inbox.org/git/cover.1542925164.git.ungureanupaulsebastian@gmail.com/","subject":"[PATCH v12 00/26] Convert \"git stash\" to C builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:16Z","receivedAt":"2018-12-20T19:44:52Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Hello,\n\nThis is a new iteration of git-stash which also takes\nsd/stash-wo-user-name into account. I cherry-picked\nsome of dscho's commits (from [1]) to keep the scripted\nversion of `git stash` as `git-legacy-stash`.\n\nThank you for your suggestions!\n\nBest,\nPaul\n\n[1]\nhttps://github.com/dscho/git\n\nJoel Teichroeb (5):\n  stash: improve option parsing test coverage\n  stash: convert apply to builtin\n  stash: convert drop and clear to builtin\n  stash: convert branch to builtin\n  stash: convert pop to builtin\n\nJohannes Schindelin (4):\n  ident: add the ability to provide a \"fallback identity\"\n  stash: add back the original, scripted `git stash`\n  stash: optionally use the scripted version again\n  tests: add a special setup where stash.useBuiltin is off\n\nPaul-Sebastian Ungureanu (17):\n  sha1-name.c: add `get_oidf()` which acts like `get_oid()`\n  strbuf.c: add `strbuf_join_argv()`\n  strbuf.c: add `strbuf_insertf()` and `strbuf_vinsertf()`\n  t3903: modernize style\n  stash: rename test cases to be more descriptive\n  stash: add tests for `git stash show` config\n  stash: mention options in `show` synopsis\n  stash: convert list to builtin\n  stash: convert show to builtin\n  stash: convert store to builtin\n  stash: convert create to builtin\n  stash: convert push to builtin\n  stash: make push -q quiet\n  stash: convert save to builtin\n  stash: optimize `get_untracked_files()` and `check_changes()`\n  stash: replace all `write-tree` child processes with API calls\n  stash: convert `stash--helper.c` into `stash.c`\n\n .gitignore                          |    1 +\n Documentation/git-stash.txt         |    4 +-\n Makefile                            |    3 +-\n builtin.h                           |    1 +\n builtin/stash.c                     | 1636 +++++++++++++++++++++++++++\n cache.h                             |    2 +\n git-stash.sh => git-legacy-stash.sh |   34 +-\n git-sh-setup.sh                     |    1 +\n git.c                               |    6 +\n ident.c                             |   20 +\n sha1-name.c                         |   19 +\n strbuf.c                            |   51 +\n strbuf.h                            |   16 +\n t/README                            |    4 +\n t/t3903-stash.sh                    |  192 ++--\n t/t3907-stash-show-config.sh        |   83 ++\n 16 files changed, 2001 insertions(+), 72 deletions(-)\n create mode 100644 builtin/stash.c\n rename git-stash.sh => git-legacy-stash.sh (97%)\n create mode 100755 t/t3907-stash-show-config.sh\n\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365672","messageId":"6b3308038a727e27930f95b208dd3f2c94085375.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 01/26] sha1-name.c: add `get_oidf()` which acts like `get_oid()`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:17Z","receivedAt":"2018-12-20T19:44:53Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Compared to `get_oid()`, `get_oidf()` has as parameters\na pointer to `object_id`, a printf format string and\nadditional arguments. This will help simplify the code\nin subsequent commits.\n\nOriginal-idea-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n cache.h     |  1 +\n sha1-name.c | 19 +++++++++++++++++++\n 2 files changed, 20 insertions(+)\n\ndiff --git a/cache.h b/cache.h\nindex b7acd81d2e..28ccc97e10 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1335,6 +1335,7 @@ struct object_context {\n \tGET_OID_BLOB)\n \n extern int get_oid(const char *str, struct object_id *oid);\n+extern int get_oidf(struct object_id *oid, const char *fmt, ...);\n extern int get_oid_commit(const char *str, struct object_id *oid);\n extern int get_oid_committish(const char *str, struct object_id *oid);\n extern int get_oid_tree(const char *str, struct object_id *oid);\ndiff --git a/sha1-name.c b/sha1-name.c\nindex b24502811b..9524a09525 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -1516,6 +1516,25 @@ int get_oid(const char *name, struct object_id *oid)\n \treturn get_oid_with_context(name, 0, oid, &unused);\n }\n \n+/*\n+ * This returns a non-zero value if the string (built using printf\n+ * format and the given arguments) is not a valid object.\n+ */\n+int get_oidf(struct object_id *oid, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tint ret;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tva_end(ap);\n+\n+\tret = get_oid(sb.buf, oid);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n \n /*\n  * Many callers know that the user meant to name a commit-ish by\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365673","messageId":"554f15de006f6015390a8e1c6098c8bf2d06c849.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 02/26] strbuf.c: add `strbuf_join_argv()`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:18Z","receivedAt":"2018-12-20T19:44:53Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Implement `strbuf_join_argv()` to join arguments\ninto a strbuf.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n strbuf.c | 15 +++++++++++++++\n strbuf.h |  7 +++++++\n 2 files changed, 22 insertions(+)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex f6a6cf78b9..82e90f1dfe 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -268,6 +268,21 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)\n \tstrbuf_setlen(sb, sb->len + sb2->len);\n }\n \n+const char *strbuf_join_argv(struct strbuf *buf,\n+\t\t\t     int argc, const char **argv, char delim)\n+{\n+\tif (!argc)\n+\t\treturn buf->buf;\n+\n+\tstrbuf_addstr(buf, *argv);\n+\twhile (--argc) {\n+\t\tstrbuf_addch(buf, delim);\n+\t\tstrbuf_addstr(buf, *(++argv));\n+\t}\n+\n+\treturn buf->buf;\n+}\n+\n void strbuf_addchars(struct strbuf *sb, int c, size_t n)\n {\n \tstrbuf_grow(sb, n);\ndiff --git a/strbuf.h b/strbuf.h\nindex fc40873b65..be02150df3 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -288,6 +288,13 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)\n  */\n void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);\n \n+/**\n+ * Join the arguments into a buffer. `delim` is put between every\n+ * two arguments.\n+ */\n+const char *strbuf_join_argv(struct strbuf *buf, int argc,\n+\t\t\t     const char **argv, char delim);\n+\n /**\n  * This function can be used to expand a format string containing\n  * placeholders. To that end, it parses the string and calls the specified\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365674","messageId":"2260063a5c93f0cffe68e67f24d191a606ffaab4.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 03/26] strbuf.c: add `strbuf_insertf()` and `strbuf_vinsertf()`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:19Z","receivedAt":"2018-12-20T19:44:55Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Implement `strbuf_insertf()` and `strbuf_vinsertf()` to\ninsert data using a printf format string.\n\nOriginal-idea-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n strbuf.c | 36 ++++++++++++++++++++++++++++++++++++\n strbuf.h |  9 +++++++++\n 2 files changed, 45 insertions(+)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 82e90f1dfe..bfbbdadbf3 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -249,6 +249,42 @@ void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)\n \tstrbuf_splice(sb, pos, 0, data, len);\n }\n \n+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)\n+{\n+\tint len, len2;\n+\tchar save;\n+\tva_list cp;\n+\n+\tif (pos > sb->len)\n+\t\tdie(\"`pos' is too far after the end of the buffer\");\n+\tva_copy(cp, ap);\n+\tlen = vsnprintf(sb->buf + sb->len, 0, fmt, cp);\n+\tva_end(cp);\n+\tif (len < 0)\n+\t\tBUG(\"your vsnprintf is broken (returned %d)\", len);\n+\tif (!len)\n+\t\treturn; /* nothing to do */\n+\tif (unsigned_add_overflows(sb->len, len))\n+\t\tdie(\"you want to use way too much memory\");\n+\tstrbuf_grow(sb, len);\n+\tmemmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);\n+\t/* vsnprintf() will append a NUL, overwriting one of our characters */\n+\tsave = sb->buf[pos + len];\n+\tlen2 = vsnprintf(sb->buf + pos, sb->alloc - sb->len, fmt, ap);\n+\tsb->buf[pos + len] = save;\n+\tif (len2 != len)\n+\t\tBUG(\"your vsnprintf is broken (returns inconsistent lengths)\");\n+\tstrbuf_setlen(sb, sb->len + len);\n+}\n+\n+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tva_start(ap, fmt);\n+\tstrbuf_vinsertf(sb, pos, fmt, ap);\n+\tva_end(ap);\n+}\n+\n void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)\n {\n \tstrbuf_splice(sb, pos, len, \"\", 0);\ndiff --git a/strbuf.h b/strbuf.h\nindex be02150df3..8f8fe01e68 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -244,6 +244,15 @@ void strbuf_addchars(struct strbuf *sb, int c, size_t n);\n  */\n void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);\n \n+/**\n+ * Insert data to the given position of the buffer giving a printf format\n+ * string. The contents will be shifted, not overwritten.\n+ */\n+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,\n+\t\t     va_list ap);\n+\n+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);\n+\n /**\n  * Remove given amount of data from a given position of the buffer.\n  */\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365675","messageId":"7e3081091a405469e05bd64257fb07eb241b56a6.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 05/26] stash: improve option parsing test coverage","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:21Z","receivedAt":"2018-12-20T19:44:56Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nIn preparation for converting the stash command incrementally to\na builtin command, this patch improves test coverage of the option\nparsing. Both for having too many parameters, or too few.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n t/t3903-stash.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 5f8272b6f9..ac55629737 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -444,6 +444,36 @@ test_expect_failure 'stash file to directory' '\n \ttest foo = \"$(cat file/file)\"\n '\n \n+test_expect_success 'giving too many ref arguments does not modify files' '\n+\tgit stash clear &&\n+\ttest_when_finished \"git reset --hard HEAD\" &&\n+\techo foo >file2 &&\n+\tgit stash &&\n+\techo bar >file2 &&\n+\tgit stash &&\n+\ttest-tool chmtime =123456789 file2 &&\n+\tfor type in apply pop \"branch stash-branch\"\n+\tdo\n+\t\ttest_must_fail git stash $type stash@{0} stash@{1} 2>err &&\n+\t\ttest_i18ngrep \"Too many revisions\" err &&\n+\t\ttest 123456789 = $(test-tool chmtime -g file2) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'drop: too many arguments errors out (does nothing)' '\n+\tgit stash list >expect &&\n+\ttest_must_fail git stash drop stash@{0} stash@{1} 2>err &&\n+\ttest_i18ngrep \"Too many revisions\" err &&\n+\tgit stash list >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'show: too many arguments errors out (does nothing)' '\n+\ttest_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&\n+\ttest_i18ngrep \"Too many revisions\" err &&\n+\ttest_must_be_empty out\n+'\n+\n test_expect_success 'stash create - no changes' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n@@ -479,6 +509,11 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n+test_expect_success 'stash branch complains with no arguments' '\n+\ttest_must_fail git stash branch 2>err &&\n+\ttest_i18ngrep \"No branch name specified\" err\n+'\n+\n test_expect_success 'stash show format defaults to --stat' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365676","messageId":"57a654887e652251ae966ec31b4604dc8222f9c6.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 04/26] ident: add the ability to provide a \"fallback identity\"","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:20Z","receivedAt":"2018-12-20T19:44:57Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nIn 3bc2111fc2e9 (stash: tolerate missing user identity, 2018-11-18),\n`git stash` learned to provide a fallback identity for the case that no\nproper name/email was given (and `git stash` does not really care about\na correct identity anyway, but it does want to create a commit object).\n\nIn preparation for the same functionality in the upcoming built-in\nversion of `git stash`, let's offer the same functionality as an API\nfunction.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n cache.h |  1 +\n ident.c | 20 ++++++++++++++++++++\n 2 files changed, 21 insertions(+)\n\ndiff --git a/cache.h b/cache.h\nindex 28ccc97e10..dd2154bcd3 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1493,6 +1493,7 @@ extern const char *git_sequence_editor(void);\n extern const char *git_pager(int stdout_is_tty);\n extern int is_terminal_dumb(void);\n extern int git_ident_config(const char *, const char *, void *);\n+void prepare_fallback_ident(const char *name, const char *email);\n extern void reset_ident_date(void);\n \n struct ident_split {\ndiff --git a/ident.c b/ident.c\nindex 33bcf40644..bce20e8652 100644\n--- a/ident.c\n+++ b/ident.c\n@@ -505,6 +505,26 @@ int git_ident_config(const char *var, const char *value, void *data)\n \treturn 0;\n }\n \n+static void set_env_if(const char *key, const char *value, int *given, int bit)\n+{\n+\tif ((*given & bit) || getenv(key))\n+\t\treturn; /* nothing to do */\n+\tsetenv(key, value, 0);\n+\t*given |= bit;\n+}\n+\n+void prepare_fallback_ident(const char *name, const char *email)\n+{\n+\tset_env_if(\"GIT_AUTHOR_NAME\", name,\n+\t\t   &author_ident_explicitly_given, IDENT_NAME_GIVEN);\n+\tset_env_if(\"GIT_AUTHOR_EMAIL\", email,\n+\t\t   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);\n+\tset_env_if(\"GIT_COMMITTER_NAME\", name,\n+\t\t   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);\n+\tset_env_if(\"GIT_COMMITTER_EMAIL\", email,\n+\t\t   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);\n+}\n+\n static int buf_cmp(const char *a_begin, const char *a_end,\n \t\t   const char *b_begin, const char *b_end)\n {\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365677","messageId":"cfc95274424f76fa95be54a60045bd8a70e25073.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 06/26] t3903: modernize style","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:22Z","receivedAt":"2018-12-20T19:44:58Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Remove whitespaces after redirection operators and wrap\nlong lines.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n t/t3903-stash.sh | 120 ++++++++++++++++++++++++-----------------------\n 1 file changed, 61 insertions(+), 59 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex ac55629737..4e83facf23 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -8,22 +8,22 @@ test_description='Test git stash'\n . ./test-lib.sh\n \n test_expect_success 'stash some dirty working directory' '\n-\techo 1 > file &&\n+\techo 1 >file &&\n \tgit add file &&\n \techo unrelated >other-file &&\n \tgit add other-file &&\n \ttest_tick &&\n \tgit commit -m initial &&\n-\techo 2 > file &&\n+\techo 2 >file &&\n \tgit add file &&\n-\techo 3 > file &&\n+\techo 3 >file &&\n \ttest_tick &&\n \tgit stash &&\n \tgit diff-files --quiet &&\n \tgit diff-index --cached --quiet HEAD\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/file b/file\n index 0cfbf08..00750ed 100644\n --- a/file\n@@ -35,7 +35,7 @@ EOF\n \n test_expect_success 'parents of stash' '\n \ttest $(git rev-parse stash^) = $(git rev-parse HEAD) &&\n-\tgit diff stash^2..stash > output &&\n+\tgit diff stash^2..stash >output &&\n \ttest_cmp expect output\n '\n \n@@ -74,7 +74,7 @@ test_expect_success 'apply stashed changes' '\n \n test_expect_success 'apply stashed changes (including index)' '\n \tgit reset --hard HEAD^ &&\n-\techo 6 > other-file &&\n+\techo 6 >other-file &&\n \tgit add other-file &&\n \ttest_tick &&\n \tgit commit -m other-file &&\n@@ -99,12 +99,12 @@ test_expect_success 'stash drop complains of extra options' '\n \n test_expect_success 'drop top stash' '\n \tgit reset --hard &&\n-\tgit stash list > stashlist1 &&\n-\techo 7 > file &&\n+\tgit stash list >expected &&\n+\techo 7 >file &&\n \tgit stash &&\n \tgit stash drop &&\n-\tgit stash list > stashlist2 &&\n-\ttest_cmp stashlist1 stashlist2 &&\n+\tgit stash list >actual &&\n+\ttest_cmp expected actual &&\n \tgit stash apply &&\n \ttest 3 = $(cat file) &&\n \ttest 1 = $(git show :file) &&\n@@ -113,9 +113,9 @@ test_expect_success 'drop top stash' '\n \n test_expect_success 'drop middle stash' '\n \tgit reset --hard &&\n-\techo 8 > file &&\n+\techo 8 >file &&\n \tgit stash &&\n-\techo 9 > file &&\n+\techo 9 >file &&\n \tgit stash &&\n \tgit stash drop stash@{1} &&\n \ttest 2 = $(git stash list | wc -l) &&\n@@ -160,7 +160,7 @@ test_expect_success 'stash pop' '\n \ttest 0 = $(git stash list | wc -l)\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/file2 b/file2\n new file mode 100644\n index 0000000..1fe912c\n@@ -170,7 +170,7 @@ index 0000000..1fe912c\n +bar2\n EOF\n \n-cat > expect1 << EOF\n+cat >expect1 <<EOF\n diff --git a/file b/file\n index 257cc56..5716ca5 100644\n --- a/file\n@@ -180,7 +180,7 @@ index 257cc56..5716ca5 100644\n +bar\n EOF\n \n-cat > expect2 << EOF\n+cat >expect2 <<EOF\n diff --git a/file b/file\n index 7601807..5716ca5 100644\n --- a/file\n@@ -198,79 +198,79 @@ index 0000000..1fe912c\n EOF\n \n test_expect_success 'stash branch' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit commit file -m first &&\n-\techo bar > file &&\n-\techo bar2 > file2 &&\n+\techo bar >file &&\n+\techo bar2 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n-\techo baz > file &&\n+\techo baz >file &&\n \tgit commit file -m second &&\n \tgit stash branch stashbranch &&\n \ttest refs/heads/stashbranch = $(git symbolic-ref HEAD) &&\n \ttest $(git rev-parse HEAD) = $(git rev-parse master^) &&\n-\tgit diff --cached > output &&\n+\tgit diff --cached >output &&\n \ttest_cmp expect output &&\n-\tgit diff > output &&\n+\tgit diff >output &&\n \ttest_cmp expect1 output &&\n \tgit add file &&\n \tgit commit -m alternate\\ second &&\n-\tgit diff master..stashbranch > output &&\n+\tgit diff master..stashbranch >output &&\n \ttest_cmp output expect2 &&\n \ttest 0 = $(git stash list | wc -l)\n '\n \n test_expect_success 'apply -q is quiet' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\tgit stash apply -q > output.out 2>&1 &&\n+\tgit stash apply -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'save -q is quiet' '\n-\tgit stash save --quiet > output.out 2>&1 &&\n+\tgit stash save --quiet >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'pop -q is quiet' '\n-\tgit stash pop -q > output.out 2>&1 &&\n+\tgit stash pop -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'pop -q --index works and is quiet' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit add file &&\n \tgit stash save --quiet &&\n-\tgit stash pop -q --index > output.out 2>&1 &&\n+\tgit stash pop -q --index >output.out 2>&1 &&\n \ttest foo = \"$(git show :file)\" &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'drop -q is quiet' '\n \tgit stash &&\n-\tgit stash drop -q > output.out 2>&1 &&\n+\tgit stash drop -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'stash -k' '\n-\techo bar3 > file &&\n-\techo bar4 > file2 &&\n+\techo bar3 >file &&\n+\techo bar4 >file2 &&\n \tgit add file2 &&\n \tgit stash -k &&\n \ttest bar,bar4 = $(cat file),$(cat file2)\n '\n \n test_expect_success 'stash --no-keep-index' '\n-\techo bar33 > file &&\n-\techo bar44 > file2 &&\n+\techo bar33 >file &&\n+\techo bar44 >file2 &&\n \tgit add file2 &&\n \tgit stash --no-keep-index &&\n \ttest bar,bar2 = $(cat file),$(cat file2)\n '\n \n test_expect_success 'stash --invalid-option' '\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \ttest_must_fail git stash --invalid-option &&\n \ttest_must_fail git stash save --invalid-option &&\n@@ -486,11 +486,12 @@ test_expect_success 'stash branch - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tgit stash branch stash-branch ${STASH_ID} &&\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master && git branch -D stash-branch\" &&\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master &&\n+\tgit branch -D stash-branch\" &&\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n@@ -498,14 +499,15 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tgit stash branch stash-branch ${STASH_ID} &&\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master && git branch -D stash-branch\" &&\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master &&\n+\tgit branch -D stash-branch\" &&\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n@@ -518,10 +520,10 @@ test_expect_success 'stash show format defaults to --stat' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -536,10 +538,10 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \techo \"1\t0\tfile\" >expected &&\n@@ -551,10 +553,10 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -574,7 +576,7 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \techo \"1\t0\tfile\" >expected &&\n@@ -586,7 +588,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -606,9 +608,9 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\techo bar > file &&\n+\techo bar >file &&\n \tgit stash &&\n \ttest_must_fail git stash drop $(git rev-parse stash@{0}) &&\n \tgit stash pop &&\n@@ -620,9 +622,9 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\techo bar > file &&\n+\techo bar >file &&\n \tgit stash &&\n \ttest_must_fail git stash pop $(git rev-parse stash@{0}) &&\n \tgit stash pop &&\n@@ -632,8 +634,8 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re\n \n test_expect_success 'ref with non-existent reflog' '\n \tgit stash clear &&\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n \ttest_must_fail git rev-parse --quiet --verify does-not-exist &&\n@@ -653,8 +655,8 @@ test_expect_success 'ref with non-existent reflog' '\n test_expect_success 'invalid ref of the form stash@{n}, n >= N' '\n \tgit stash clear &&\n \ttest_must_fail git stash drop stash@{0} &&\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n \ttest_must_fail git stash drop stash@{1} &&\n@@ -724,7 +726,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu\n \ttest_i18ncmp expect actual\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/HEAD b/HEAD\n new file mode 100644\n index 0000000..fe0cbee\n@@ -737,14 +739,14 @@ EOF\n test_expect_success 'stash where working directory contains \"HEAD\" file' '\n \tgit stash clear &&\n \tgit reset --hard &&\n-\techo file-not-a-ref > HEAD &&\n+\techo file-not-a-ref >HEAD &&\n \tgit add HEAD &&\n \ttest_tick &&\n \tgit stash &&\n \tgit diff-files --quiet &&\n \tgit diff-index --cached --quiet HEAD &&\n \ttest \"$(git rev-parse stash^)\" = \"$(git rev-parse HEAD)\" &&\n-\tgit diff stash^..stash > output &&\n+\tgit diff stash^..stash >output &&\n \ttest_cmp expect output\n '\n \n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365678","messageId":"29c2db2e73bae6b8249a5b4d73ce784a4c861953.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 08/26] stash: add tests for `git stash show` config","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:24Z","receivedAt":"2018-12-20T19:45:00Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"This commit introduces tests for `git stash show`\nconfig. It tests all the cases where `stash.showStat`\nand `stash.showPatch` are unset or set to true / false.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n t/t3907-stash-show-config.sh | 83 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 83 insertions(+)\n create mode 100755 t/t3907-stash-show-config.sh\n\ndiff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh\nnew file mode 100755\nindex 0000000000..10914bba7b\n--- /dev/null\n+++ b/t/t3907-stash-show-config.sh\n@@ -0,0 +1,83 @@\n+#!/bin/sh\n+\n+test_description='Test git stash show configuration.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit file\n+'\n+\n+# takes three parameters:\n+# 1. the stash.showStat value (or \"<unset>\")\n+# 2. the stash.showPatch value (or \"<unset>\")\n+# 3. the diff options of the expected output (or nothing for no output)\n+test_stat_and_patch () {\n+\tif test \"<unset>\" = \"$1\"\n+\tthen\n+\t\ttest_unconfig stash.showStat\n+\telse\n+\t\ttest_config stash.showStat \"$1\"\n+\tfi &&\n+\n+\tif test \"<unset>\" = \"$2\"\n+\tthen\n+\t\ttest_unconfig stash.showPatch\n+\telse\n+\t\ttest_config stash.showPatch \"$2\"\n+\tfi &&\n+\n+\tshift 2 &&\n+\techo 2 >file.t &&\n+\tif test $# != 0\n+\tthen\n+\t\tgit diff \"$@\" >expect\n+\tfi &&\n+\tgit stash &&\n+\tgit stash show >actual &&\n+\n+\tif test $# = 0\n+\tthen\n+\t\ttest_must_be_empty actual\n+\telse\n+\t\ttest_cmp expect actual\n+\tfi\n+}\n+\n+test_expect_success 'showStat unset showPatch unset' '\n+\ttest_stat_and_patch \"<unset>\" \"<unset>\" --stat\n+'\n+\n+test_expect_success 'showStat unset showPatch false' '\n+\ttest_stat_and_patch \"<unset>\" false --stat\n+'\n+\n+test_expect_success 'showStat unset showPatch true' '\n+\ttest_stat_and_patch \"<unset>\" true --stat -p\n+'\n+\n+test_expect_success 'showStat false showPatch unset' '\n+\ttest_stat_and_patch false \"<unset>\"\n+'\n+\n+test_expect_success 'showStat false showPatch false' '\n+\ttest_stat_and_patch false false\n+'\n+\n+test_expect_success 'showStat false showPatch true' '\n+\ttest_stat_and_patch false true -p\n+'\n+\n+test_expect_success 'showStat true showPatch unset' '\n+\ttest_stat_and_patch true \"<unset>\" --stat\n+'\n+\n+test_expect_success 'showStat true showPatch false' '\n+\ttest_stat_and_patch true false --stat\n+'\n+\n+test_expect_success 'showStat true showPatch true' '\n+\ttest_stat_and_patch true true --stat -p\n+'\n+\n+test_done\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365679","messageId":"1e187c54fcb13555e463c5b6f910b4bc13974e3e.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 09/26] stash: mention options in `show` synopsis","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:25Z","receivedAt":"2018-12-20T19:45:00Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Mention in the documentation, that `show` accepts any\noption known to `git diff`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n Documentation/git-stash.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..e31ea7d303 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git stash' list [<options>]\n-'git stash' show [<stash>]\n+'git stash' show [<options>] [<stash>]\n 'git stash' drop [-q|--quiet] [<stash>]\n 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n 'git stash' branch <branchname> [<stash>]\n@@ -106,7 +106,7 @@ stash@{1}: On master: 9cc0589... Add git-stash\n The command takes options applicable to the 'git log'\n command to control what is shown and how. See linkgit:git-log[1].\n \n-show [<stash>]::\n+show [<options>] [<stash>]::\n \n \tShow the changes recorded in the stash entry as a diff between the\n \tstashed contents and the commit back when the stash entry was first\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365680","messageId":"f595ccc2c1b5dd439e8a6b86959d7a987cf9fb50.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 07/26] stash: rename test cases to be more descriptive","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:23Z","receivedAt":"2018-12-20T19:45:03Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Rename some test cases' labels to be more descriptive and under 80\ncharacters per line.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n t/t3903-stash.sh | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 4e83facf23..98c25a671c 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -604,7 +604,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '\n+test_expect_success 'drop: fail early if specified stash is not a stash ref' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n@@ -618,7 +618,7 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r\n \tgit reset --hard HEAD\n '\n \n-test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '\n+test_expect_success 'pop: fail early if specified stash is not a stash ref' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n@@ -682,7 +682,7 @@ test_expect_success 'invalid ref of the form \"n\", n >= N' '\n \tgit stash drop\n '\n \n-test_expect_success 'stash branch should not drop the stash if the branch exists' '\n+test_expect_success 'branch: do not drop the stash if the branch exists' '\n \tgit stash clear &&\n \techo foo >file &&\n \tgit add file &&\n@@ -693,7 +693,7 @@ test_expect_success 'stash branch should not drop the stash if the branch exists\n \tgit rev-parse stash@{0} --\n '\n \n-test_expect_success 'stash branch should not drop the stash if the apply fails' '\n+test_expect_success 'branch: should not drop the stash if the apply fails' '\n \tgit stash clear &&\n \tgit reset HEAD~1 --hard &&\n \techo foo >file &&\n@@ -707,7 +707,7 @@ test_expect_success 'stash branch should not drop the stash if the apply fails'\n \tgit rev-parse stash@{0} --\n '\n \n-test_expect_success 'stash apply shows status same as git status (relative to current directory)' '\n+test_expect_success 'apply: show same status as git status (relative to ./)' '\n \tgit stash clear &&\n \techo 1 >subdir/subfile1 &&\n \techo 2 >subdir/subfile2 &&\n@@ -1048,7 +1048,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '\n \ttest_i18ncmp expect actual\n '\n \n-test_expect_success 'stash push with pathspec shows no changes when there are none' '\n+test_expect_success 'push <pathspec>: show no changes when there are none' '\n \t>foo &&\n \tgit add foo &&\n \tgit commit -m \"tmp\" &&\n@@ -1058,7 +1058,7 @@ test_expect_success 'stash push with pathspec shows no changes when there are no\n \ttest_i18ncmp expect actual\n '\n \n-test_expect_success 'stash push with pathspec not in the repository errors out' '\n+test_expect_success 'push: <pathspec> not in the repository errors out' '\n \t>untracked &&\n \ttest_must_fail git stash push untracked &&\n \ttest_path_is_file untracked\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365681","messageId":"c8165faa0f594a5098fb7436042d78490e885c3f.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 10/26] stash: convert apply to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:26Z","receivedAt":"2018-12-20T19:45:04Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd a builtin helper for performing stash commands. Converting\nall at once proved hard to review, so starting with just apply\nlets conversion get started without the other commands being\nfinished.\n\nThe helper is being implemented as a drop in replacement for\nstash so that when it is complete it can simply be renamed and\nthe shell script deleted.\n\nDelete the contents of the apply_stash shell function and replace\nit with a call to stash--helper apply until pop is also\nconverted.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n .gitignore              |   1 +\n Makefile                |   1 +\n builtin.h               |   1 +\n builtin/stash--helper.c | 452 ++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            |  78 +------\n git.c                   |   1 +\n 6 files changed, 463 insertions(+), 71 deletions(-)\n create mode 100644 builtin/stash--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..6ecab90ab2 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,6 +162,7 @@\n /git-show-ref\n /git-stage\n /git-stash\n+/git-stash--helper\n /git-status\n /git-stripspace\n /git-submodule\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..c246fc7078 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1117,6 +1117,7 @@ BUILTIN_OBJS += builtin/shortlog.o\n BUILTIN_OBJS += builtin/show-branch.o\n BUILTIN_OBJS += builtin/show-index.o\n BUILTIN_OBJS += builtin/show-ref.o\n+BUILTIN_OBJS += builtin/stash--helper.o\n BUILTIN_OBJS += builtin/stripspace.o\n BUILTIN_OBJS += builtin/submodule--helper.o\n BUILTIN_OBJS += builtin/symbolic-ref.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..ff4460aff7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -225,6 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n+extern int cmd_stash__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nnew file mode 100644\nindex 0000000000..997b1c0ecf\n--- /dev/null\n+++ b/builtin/stash--helper.c\n@@ -0,0 +1,452 @@\n+#include \"builtin.h\"\n+#include \"config.h\"\n+#include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"lockfile.h\"\n+#include \"cache-tree.h\"\n+#include \"unpack-trees.h\"\n+#include \"merge-recursive.h\"\n+#include \"argv-array.h\"\n+#include \"run-command.h\"\n+#include \"dir.h\"\n+#include \"rerere.h\"\n+\n+static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n+static const char * const git_stash_helper_apply_usage[] = {\n+\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n+static const char *ref_stash = \"refs/stash\";\n+static struct strbuf stash_index_path = STRBUF_INIT;\n+\n+/*\n+ * w_commit is set to the commit containing the working tree\n+ * b_commit is set to the base commit\n+ * i_commit is set to the commit containing the index tree\n+ * u_commit is set to the commit containing the untracked files tree\n+ * w_tree is set to the working tree\n+ * b_tree is set to the base tree\n+ * i_tree is set to the index tree\n+ * u_tree is set to the untracked files tree\n+ */\n+\n+struct stash_info {\n+\tstruct object_id w_commit;\n+\tstruct object_id b_commit;\n+\tstruct object_id i_commit;\n+\tstruct object_id u_commit;\n+\tstruct object_id w_tree;\n+\tstruct object_id b_tree;\n+\tstruct object_id i_tree;\n+\tstruct object_id u_tree;\n+\tstruct strbuf revision;\n+\tint is_stash_ref;\n+\tint has_u;\n+};\n+\n+static void free_stash_info(struct stash_info *info)\n+{\n+\tstrbuf_release(&info->revision);\n+}\n+\n+static void assert_stash_like(struct stash_info *info, const char *revision)\n+{\n+\tif (get_oidf(&info->b_commit, \"%s^1\", revision) ||\n+\t    get_oidf(&info->w_tree, \"%s:\", revision) ||\n+\t    get_oidf(&info->b_tree, \"%s^1:\", revision) ||\n+\t    get_oidf(&info->i_tree, \"%s^2:\", revision))\n+\t\tdie(_(\"'%s' is not a stash-like commit\"), revision);\n+}\n+\n+static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n+{\n+\tint ret;\n+\tchar *end_of_rev;\n+\tchar *expanded_ref;\n+\tconst char *revision;\n+\tconst char *commit = NULL;\n+\tstruct object_id dummy;\n+\tstruct strbuf symbolic = STRBUF_INIT;\n+\n+\tif (argc > 1) {\n+\t\tint i;\n+\t\tstruct strbuf refs_msg = STRBUF_INIT;\n+\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tstrbuf_addf(&refs_msg, \" '%s'\", argv[i]);\n+\n+\t\tfprintf_ln(stderr, _(\"Too many revisions specified:%s\"),\n+\t\t\t   refs_msg.buf);\n+\t\tstrbuf_release(&refs_msg);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tif (argc == 1)\n+\t\tcommit = argv[0];\n+\n+\tstrbuf_init(&info->revision, 0);\n+\tif (!commit) {\n+\t\tif (!ref_exists(ref_stash)) {\n+\t\t\tfree_stash_info(info);\n+\t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tstrbuf_addf(&info->revision, \"%s@{0}\", ref_stash);\n+\t} else if (strspn(commit, \"0123456789\") == strlen(commit)) {\n+\t\tstrbuf_addf(&info->revision, \"%s@{%s}\", ref_stash, commit);\n+\t} else {\n+\t\tstrbuf_addstr(&info->revision, commit);\n+\t}\n+\n+\trevision = info->revision.buf;\n+\n+\tif (get_oid(revision, &info->w_commit)) {\n+\t\terror(_(\"%s is not a valid reference\"), revision);\n+\t\tfree_stash_info(info);\n+\t\treturn -1;\n+\t}\n+\n+\tassert_stash_like(info, revision);\n+\n+\tinfo->has_u = !get_oidf(&info->u_tree, \"%s^3:\", revision);\n+\n+\tend_of_rev = strchrnul(revision, '@');\n+\tstrbuf_add(&symbolic, revision, end_of_rev - revision);\n+\n+\tret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);\n+\tstrbuf_release(&symbolic);\n+\tswitch (ret) {\n+\tcase 0: /* Not found, but valid ref */\n+\t\tinfo->is_stash_ref = 0;\n+\t\tbreak;\n+\tcase 1:\n+\t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n+\t\tbreak;\n+\tdefault: /* Invalid or ambiguous */\n+\t\tfree_stash_info(info);\n+\t}\n+\n+\tfree(expanded_ref);\n+\treturn !(ret == 0 || ret == 1);\n+}\n+\n+static int reset_tree(struct object_id *i_tree, int update, int reset)\n+{\n+\tint nr_trees = 1;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct tree *tree;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\n+\tread_cache_preload(NULL);\n+\tif (refresh_cache(REFRESH_QUIET))\n+\t\treturn -1;\n+\n+\thold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\n+\ttree = parse_tree_indirect(i_tree);\n+\tif (parse_tree(tree))\n+\t\treturn -1;\n+\n+\tinit_tree_desc(t, tree->buffer, tree->size);\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.merge = 1;\n+\topts.reset = reset;\n+\topts.update = update;\n+\topts.fn = oneway_merge;\n+\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\n+\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n+\t\treturn error(_(\"unable to write new index file\"));\n+\n+\treturn 0;\n+}\n+\n+static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst char *w_commit_hex = oid_to_hex(w_commit);\n+\n+\t/*\n+\t * Diff-tree would not be very hard to replace with a native function,\n+\t * however it should be done together with apply_cached.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"diff-tree\", \"--binary\", NULL);\n+\targv_array_pushf(&cp.args, \"%s^2^..%s^2\", w_commit_hex, w_commit_hex);\n+\n+\treturn pipe_command(&cp, NULL, 0, out, 0, NULL, 0);\n+}\n+\n+static int apply_cached(struct strbuf *out)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Apply currently only reads either from stdin or a file, thus\n+\t * apply_all_patches would have to be updated to optionally take a\n+\t * buffer.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"apply\", \"--cached\", NULL);\n+\treturn pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);\n+}\n+\n+static int reset_head(void)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Reset is overall quite simple, however there is no current public\n+\t * API for resetting.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_push(&cp.args, \"reset\");\n+\n+\treturn run_command(&cp);\n+}\n+\n+static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst char *c_tree_hex = oid_to_hex(c_tree);\n+\n+\t/*\n+\t * diff-index is very similar to diff-tree above, and should be\n+\t * converted together with update_index.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"diff-index\", \"--cached\", \"--name-only\",\n+\t\t\t \"--diff-filter=A\", NULL);\n+\targv_array_push(&cp.args, c_tree_hex);\n+\treturn pipe_command(&cp, NULL, 0, out, 0, NULL, 0);\n+}\n+\n+static int update_index(struct strbuf *out)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Update-index is very complicated and may need to have a public\n+\t * function exposed in order to remove this forking.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"update-index\", \"--add\", \"--stdin\", NULL);\n+\treturn pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);\n+}\n+\n+static int restore_untracked(struct object_id *u_tree)\n+{\n+\tint res;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * We need to run restore files from a given index, but without\n+\t * affecting the current index, so we use GIT_INDEX_FILE with\n+\t * run_command to fork processes that will not interfere.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_push(&cp.args, \"read-tree\");\n+\targv_array_push(&cp.args, oid_to_hex(u_tree));\n+\targv_array_pushf(&cp.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp)) {\n+\t\tremove_path(stash_index_path.buf);\n+\t\treturn -1;\n+\t}\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"checkout-index\", \"--all\", NULL);\n+\targv_array_pushf(&cp.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tres = run_command(&cp);\n+\tremove_path(stash_index_path.buf);\n+\treturn res;\n+}\n+\n+static int do_apply_stash(const char *prefix, struct stash_info *info,\n+\t\t\t  int index, int quiet)\n+{\n+\tint ret;\n+\tint has_index = index;\n+\tstruct merge_options o;\n+\tstruct object_id c_tree;\n+\tstruct object_id index_tree;\n+\tstruct commit *result;\n+\tconst struct object_id *bases[1];\n+\n+\tread_cache_preload(NULL);\n+\tif (refresh_cache(REFRESH_QUIET))\n+\t\treturn -1;\n+\n+\tif (write_cache_as_tree(&c_tree, 0, NULL) || reset_tree(&c_tree, 0, 0))\n+\t\treturn error(_(\"cannot apply a stash in the middle of a merge\"));\n+\n+\tif (index) {\n+\t\tif (oideq(&info->b_tree, &info->i_tree) ||\n+\t\t    oideq(&c_tree, &info->i_tree)) {\n+\t\t\thas_index = 0;\n+\t\t} else {\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tif (diff_tree_binary(&out, &info->w_commit)) {\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn error(_(\"could not generate diff %s^!.\"),\n+\t\t\t\t\t     oid_to_hex(&info->w_commit));\n+\t\t\t}\n+\n+\t\t\tret = apply_cached(&out);\n+\t\t\tstrbuf_release(&out);\n+\t\t\tif (ret)\n+\t\t\t\treturn error(_(\"conflicts in index.\"\n+\t\t\t\t\t       \"Try without --index.\"));\n+\n+\t\t\tdiscard_cache();\n+\t\t\tread_cache();\n+\t\t\tif (write_cache_as_tree(&index_tree, 0, NULL))\n+\t\t\t\treturn error(_(\"could not save index tree\"));\n+\n+\t\t\treset_head();\n+\t\t}\n+\t}\n+\n+\tif (info->has_u && restore_untracked(&info->u_tree))\n+\t\treturn error(_(\"could not restore untracked files from stash\"));\n+\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"Updated upstream\";\n+\to.branch2 = \"Stashed changes\";\n+\n+\tif (oideq(&info->b_tree, &c_tree))\n+\t\to.branch1 = \"Version stash was based on\";\n+\n+\tif (quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (o.verbosity >= 3)\n+\t\tprintf_ln(_(\"Merging %s with %s\"), o.branch1, o.branch2);\n+\n+\tbases[0] = &info->b_tree;\n+\n+\tret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,\n+\t\t\t\t      &result);\n+\tif (ret) {\n+\t\trerere(0);\n+\n+\t\tif (index)\n+\t\t\tfprintf_ln(stderr, _(\"Index was not unstashed.\"));\n+\n+\t\treturn ret;\n+\t}\n+\n+\tif (has_index) {\n+\t\tif (reset_tree(&index_tree, 0, 0))\n+\t\t\treturn -1;\n+\t} else {\n+\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\tif (get_newly_staged(&out, &c_tree)) {\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (reset_tree(&c_tree, 0, 1)) {\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tret = update_index(&out);\n+\t\tstrbuf_release(&out);\n+\t\tif (ret)\n+\t\t\treturn -1;\n+\n+\t\tdiscard_cache();\n+\t}\n+\n+\tif (quiet) {\n+\t\tif (refresh_cache(REFRESH_QUIET))\n+\t\t\twarning(\"could not refresh index\");\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t/*\n+\t\t * Status is quite simple and could be replaced with calls to\n+\t\t * wt_status in the future, but it adds complexities which may\n+\t\t * require more tests.\n+\t\t */\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = prefix;\n+\t\targv_array_push(&cp.args, \"status\");\n+\t\trun_command(&cp);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int apply_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint quiet = 0;\n+\tint index = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_BOOL(0, \"index\", &index,\n+\t\t\t N_(\"attempt to recreate the index\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_apply_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tret = do_apply_stash(prefix, &info, index, quiet);\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n+int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n+{\n+\tpid_t pid = getpid();\n+\tconst char *index_file;\n+\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(git_default_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n+\n+\tindex_file = get_index_file();\n+\tstrbuf_addf(&stash_index_path, \"%s.stash.%\" PRIuMAX, index_file,\n+\t\t    (uintmax_t)pid);\n+\n+\tif (argc < 1)\n+\t\tusage_with_options(git_stash_helper_usage, options);\n+\tif (!strcmp(argv[0], \"apply\"))\n+\t\treturn !!apply_stash(argc, argv, prefix);\n+\n+\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n+\t\t      git_stash_helper_usage, options);\n+}\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 789ce2f41d..366a082853 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -583,76 +583,11 @@ assert_stash_ref() {\n }\n \n apply_stash () {\n-\n-\tassert_stash_like \"$@\"\n-\n-\tgit update-index -q --refresh || die \"$(gettext \"unable to refresh index\")\"\n-\n-\t# current index state\n-\tc_tree=$(git write-tree) ||\n-\t\tdie \"$(gettext \"Cannot apply a stash in the middle of a merge\")\"\n-\n-\tunstashed_index_tree=\n-\tif test -n \"$INDEX_OPTION\" && test \"$b_tree\" != \"$i_tree\" &&\n-\t\t\ttest \"$c_tree\" != \"$i_tree\"\n-\tthen\n-\t\tgit diff-tree --binary $s^2^..$s^2 | git apply --cached\n-\t\ttest $? -ne 0 &&\n-\t\t\tdie \"$(gettext \"Conflicts in index. Try without --index.\")\"\n-\t\tunstashed_index_tree=$(git write-tree) ||\n-\t\t\tdie \"$(gettext \"Could not save index tree\")\"\n-\t\tgit reset\n-\tfi\n-\n-\tif test -n \"$u_tree\"\n-\tthen\n-\t\tGIT_INDEX_FILE=\"$TMPindex\" git read-tree \"$u_tree\" &&\n-\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n-\t\trm -f \"$TMPindex\" ||\n-\t\tdie \"$(gettext \"Could not restore untracked files from stash entry\")\"\n-\tfi\n-\n-\teval \"\n-\t\tGITHEAD_$w_tree='Stashed changes' &&\n-\t\tGITHEAD_$c_tree='Updated upstream' &&\n-\t\tGITHEAD_$b_tree='Version stash was based on' &&\n-\t\texport GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree\n-\t\"\n-\n-\tif test -n \"$GIT_QUIET\"\n-\tthen\n-\t\tGIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY\n-\tfi\n-\tif git merge-recursive $b_tree -- $c_tree $w_tree\n-\tthen\n-\t\t# No conflict\n-\t\tif test -n \"$unstashed_index_tree\"\n-\t\tthen\n-\t\t\tgit read-tree \"$unstashed_index_tree\"\n-\t\telse\n-\t\t\ta=\"$TMP-added\" &&\n-\t\t\tgit diff-index --cached --name-only --diff-filter=A $c_tree >\"$a\" &&\n-\t\t\tgit read-tree --reset $c_tree &&\n-\t\t\tgit update-index --add --stdin <\"$a\" ||\n-\t\t\t\tdie \"$(gettext \"Cannot unstage modified files\")\"\n-\t\t\trm -f \"$a\"\n-\t\tfi\n-\t\tsquelch=\n-\t\tif test -n \"$GIT_QUIET\"\n-\t\tthen\n-\t\t\tsquelch='>/dev/null 2>&1'\n-\t\tfi\n-\t\t(cd \"$START_DIR\" && eval \"git status $squelch\") || :\n-\telse\n-\t\t# Merge conflict; keep the exit status from merge-recursive\n-\t\tstatus=$?\n-\t\tgit rerere\n-\t\tif test -n \"$INDEX_OPTION\"\n-\t\tthen\n-\t\t\tgettextln \"Index was not unstashed.\" >&2\n-\t\tfi\n-\t\texit $status\n-\tfi\n+\tcd \"$START_DIR\"\n+\tgit stash--helper apply \"$@\"\n+\tres=$?\n+\tcd_to_toplevel\n+\treturn $res\n }\n \n pop_stash() {\n@@ -730,7 +665,8 @@ push)\n \t;;\n apply)\n \tshift\n-\tapply_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper apply \"$@\"\n \t;;\n clear)\n \tshift\ndiff --git a/git.c b/git.c\nindex 4d53a3d50d..de9c774573 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,6 +554,7 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"stash--helper\", cmd_stash__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365682","messageId":"9f69a7f2530772992bd3319c450f516bb100675e.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 12/26] stash: convert branch to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:28Z","receivedAt":"2018-12-20T19:45:06Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd stash branch to the helper and delete the apply_to_branch\nfunction from the shell script.\n\nCheckout does not currently provide a function for checking out\na branch as cmd_checkout does a large amount of sanity checks\nfirst that we require here.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 46 +++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            | 17 ++-------------\n 2 files changed, 48 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 07b8ec5bcb..68b65165e4 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -14,6 +14,7 @@\n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n \tNULL\n };\n@@ -28,6 +29,11 @@ static const char * const git_stash_helper_apply_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_branch_usage[] = {\n+\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_clear_usage[] = {\n \tN_(\"git stash--helper clear\"),\n \tNULL\n@@ -537,6 +543,44 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int branch_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tconst char *branch = NULL;\n+\tstruct stash_info info;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_branch_usage, 0);\n+\n+\tif (!argc) {\n+\t\tfprintf_ln(stderr, _(\"No branch name specified\"));\n+\t\treturn -1;\n+\t}\n+\n+\tbranch = argv[0];\n+\n+\tif (get_stash_info(&info, argc - 1, argv + 1))\n+\t\treturn -1;\n+\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n+\targv_array_push(&cp.args, branch);\n+\targv_array_push(&cp.args, oid_to_hex(&info.b_commit));\n+\tret = run_command(&cp);\n+\tif (!ret)\n+\t\tret = do_apply_stash(prefix, &info, 1, 0);\n+\tif (!ret && info.is_stash_ref)\n+\t\tret = do_drop_stash(prefix, &info, 0);\n+\n+\tfree_stash_info(&info);\n+\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -563,6 +607,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!clear_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"drop\"))\n \t\treturn !!drop_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"branch\"))\n+\t\treturn !!branch_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex b8f70230f9..67db321a4c 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -615,20 +615,6 @@ drop_stash () {\n \tclear_stash\n }\n \n-apply_to_branch () {\n-\ttest -n \"$1\" || die \"$(gettext \"No branch name specified\")\"\n-\tbranch=$1\n-\tshift 1\n-\n-\tset -- --index \"$@\"\n-\tassert_stash_like \"$@\"\n-\n-\tgit checkout -b $branch $REV^ &&\n-\tapply_stash \"$@\" && {\n-\t\ttest -z \"$IS_STASH_REF\" || drop_stash \"$@\"\n-\t}\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -690,7 +676,8 @@ pop)\n \t;;\n branch)\n \tshift\n-\tapply_to_branch \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper branch \"$@\"\n \t;;\n *)\n \tcase $# in\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365683","messageId":"28c03869f67c04eef446b8849ab7e3ddc1bbccb0.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 15/26] stash: convert show to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:31Z","receivedAt":"2018-12-20T19:45:07Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash show to the helper and delete the show_stash, have_stash,\nassert_stash_like, is_stash_like and parse_flags_and_rev functions\nfrom the shell script now that they are no longer needed.\n\nIn shell version, although `git stash show` accepts `--index` and\n`--quiet` options, it ignores them. In C, both options are passed\nfurther to `git diff`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c |  87 ++++++++++++++++++++++++++\n git-stash.sh            | 132 +---------------------------------------\n 2 files changed, 88 insertions(+), 131 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex d66a4589a5..36651f745a 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -10,9 +10,12 @@\n #include \"run-command.h\"\n #include \"dir.h\"\n #include \"rerere.h\"\n+#include \"revision.h\"\n+#include \"log-tree.h\"\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper list [<options>]\"),\n+\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n@@ -25,6 +28,11 @@ static const char * const git_stash_helper_list_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_show_usage[] = {\n+\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_drop_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -645,6 +653,83 @@ static int list_stash(int argc, const char **argv, const char *prefix)\n \treturn run_command(&cp);\n }\n \n+static int show_stat = 1;\n+static int show_patch;\n+\n+static int git_stash_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"stash.showstat\")) {\n+\t\tshow_stat = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(var, \"stash.showpatch\")) {\n+\t\tshow_patch = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value, cb);\n+}\n+\n+static int show_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint i;\n+\tint opts = 0;\n+\tint ret = 0;\n+\tstruct stash_info info;\n+\tstruct rev_info rev;\n+\tstruct argv_array stash_args = ARGV_ARRAY_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\tinit_diff_ui_defaults();\n+\tgit_config(git_diff_ui_config, NULL);\n+\tinit_revisions(&rev, prefix);\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (argv[i][0] != '-')\n+\t\t\targv_array_push(&stash_args, argv[i]);\n+\t\telse\n+\t\t\topts++;\n+\t}\n+\n+\tret = get_stash_info(&info, stash_args.argc, stash_args.argv);\n+\targv_array_clear(&stash_args);\n+\tif (ret)\n+\t\treturn -1;\n+\n+\t/*\n+\t * The config settings are applied only if there are not passed\n+\t * any options.\n+\t */\n+\tif (!opts) {\n+\t\tgit_config(git_stash_config, NULL);\n+\t\tif (show_stat)\n+\t\t\trev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;\n+\n+\t\tif (show_patch)\n+\t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n+\n+\t\tif (!show_stat && !show_patch) {\n+\t\t\tfree_stash_info(&info);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\targc = setup_revisions(argc, argv, &rev, NULL);\n+\tif (argc > 1) {\n+\t\tfree_stash_info(&info);\n+\t\tusage_with_options(git_stash_helper_show_usage, options);\n+\t}\n+\n+\trev.diffopt.flags.recursive = 1;\n+\tsetup_diff_pager(&rev.diffopt);\n+\tdiff_tree_oid(&info.b_commit, &info.w_commit, \"\", &rev.diffopt);\n+\tlog_tree_diff_flush(&rev);\n+\n+\tfree_stash_info(&info);\n+\treturn diff_result_code(&rev.diffopt, 0);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -677,6 +762,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!branch_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"list\"))\n \t\treturn !!list_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"show\"))\n+\t\treturn !!show_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex ab3992b59d..d0318f859e 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -395,35 +395,6 @@ save_stash () {\n \tfi\n }\n \n-have_stash () {\n-\tgit rev-parse --verify --quiet $ref_stash >/dev/null\n-}\n-\n-show_stash () {\n-\tALLOW_UNKNOWN_FLAGS=t\n-\tassert_stash_like \"$@\"\n-\n-\tif test -z \"$FLAGS\"\n-\tthen\n-\t\tif test \"$(git config --bool stash.showStat || echo true)\" = \"true\"\n-\t\tthen\n-\t\t\tFLAGS=--stat\n-\t\tfi\n-\n-\t\tif test \"$(git config --bool stash.showPatch || echo false)\" = \"true\"\n-\t\tthen\n-\t\t\tFLAGS=${FLAGS}${FLAGS:+ }-p\n-\t\tfi\n-\n-\t\tif test -z \"$FLAGS\"\n-\t\tthen\n-\t\t\treturn 0\n-\t\tfi\n-\tfi\n-\n-\tgit diff ${FLAGS} $b_commit $w_commit\n-}\n-\n show_help () {\n \texec git help stash\n \texit 1\n@@ -465,107 +436,6 @@ show_help () {\n #   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n #\n \n-parse_flags_and_rev()\n-{\n-\ttest \"$PARSE_CACHE\" = \"$*\" && return 0 # optimisation\n-\tPARSE_CACHE=\"$*\"\n-\n-\tIS_STASH_LIKE=\n-\tIS_STASH_REF=\n-\tINDEX_OPTION=\n-\ts=\n-\tw_commit=\n-\tb_commit=\n-\ti_commit=\n-\tu_commit=\n-\tw_tree=\n-\tb_tree=\n-\ti_tree=\n-\tu_tree=\n-\n-\tFLAGS=\n-\tREV=\n-\tfor opt\n-\tdo\n-\t\tcase \"$opt\" in\n-\t\t\t-q|--quiet)\n-\t\t\t\tGIT_QUIET=-t\n-\t\t\t;;\n-\t\t\t--index)\n-\t\t\t\tINDEX_OPTION=--index\n-\t\t\t;;\n-\t\t\t--help)\n-\t\t\t\tshow_help\n-\t\t\t;;\n-\t\t\t-*)\n-\t\t\t\ttest \"$ALLOW_UNKNOWN_FLAGS\" = t ||\n-\t\t\t\t\tdie \"$(eval_gettext \"unknown option: \\$opt\")\"\n-\t\t\t\tFLAGS=\"${FLAGS}${FLAGS:+ }$opt\"\n-\t\t\t;;\n-\t\t\t*)\n-\t\t\t\tREV=\"${REV}${REV:+ }'$opt'\"\n-\t\t\t;;\n-\t\tesac\n-\tdone\n-\n-\teval set -- $REV\n-\n-\tcase $# in\n-\t\t0)\n-\t\t\thave_stash || die \"$(gettext \"No stash entries found.\")\"\n-\t\t\tset -- ${ref_stash}@{0}\n-\t\t;;\n-\t\t1)\n-\t\t\t:\n-\t\t;;\n-\t\t*)\n-\t\t\tdie \"$(eval_gettext \"Too many revisions specified: \\$REV\")\"\n-\t\t;;\n-\tesac\n-\n-\tcase \"$1\" in\n-\t\t*[!0-9]*)\n-\t\t\t:\n-\t\t;;\n-\t\t*)\n-\t\t\tset -- \"${ref_stash}@{$1}\"\n-\t\t;;\n-\tesac\n-\n-\tREV=$(git rev-parse --symbolic --verify --quiet \"$1\") || {\n-\t\treference=\"$1\"\n-\t\tdie \"$(eval_gettext \"\\$reference is not a valid reference\")\"\n-\t}\n-\n-\ti_commit=$(git rev-parse --verify --quiet \"$REV^2\") &&\n-\tset -- $(git rev-parse \"$REV\" \"$REV^1\" \"$REV:\" \"$REV^1:\" \"$REV^2:\" 2>/dev/null) &&\n-\ts=$1 &&\n-\tw_commit=$1 &&\n-\tb_commit=$2 &&\n-\tw_tree=$3 &&\n-\tb_tree=$4 &&\n-\ti_tree=$5 &&\n-\tIS_STASH_LIKE=t &&\n-\ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n-\tIS_STASH_REF=t\n-\n-\tu_commit=$(git rev-parse --verify --quiet \"$REV^3\") &&\n-\tu_tree=$(git rev-parse \"$REV^3:\" 2>/dev/null)\n-}\n-\n-is_stash_like()\n-{\n-\tparse_flags_and_rev \"$@\"\n-\ttest -n \"$IS_STASH_LIKE\"\n-}\n-\n-assert_stash_like() {\n-\tis_stash_like \"$@\" || {\n-\t\targs=\"$*\"\n-\t\tdie \"$(eval_gettext \"'\\$args' is not a stash-like commit\")\"\n-\t}\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -590,7 +460,7 @@ list)\n \t;;\n show)\n \tshift\n-\tshow_stash \"$@\"\n+\tgit stash--helper show \"$@\"\n \t;;\n save)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365684","messageId":"db883e70689a63918cf28710277b560d07091c67.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 16/26] stash: convert store to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:32Z","receivedAt":"2018-12-20T19:45:08Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash store to the helper and delete the store_stash function\nfrom the shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 62 +++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            | 43 ++--------------------------\n 2 files changed, 64 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 36651f745a..5dc6c068d7 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -58,6 +58,11 @@ static const char * const git_stash_helper_clear_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_store_usage[] = {\n+\tN_(\"git stash--helper store [-m|--message <message>] [-q|--quiet] <commit>\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -730,6 +735,61 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \treturn diff_result_code(&rev.diffopt, 0);\n }\n \n+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n+\t\t\t  int quiet)\n+{\n+\tif (!stash_msg)\n+\t\tstash_msg = \"Created via \\\"git stash store\\\".\";\n+\n+\tif (update_ref(stash_msg, ref_stash, w_commit, NULL,\n+\t\t       REF_FORCE_CREATE_REFLOG,\n+\t\t       quiet ? UPDATE_REFS_QUIET_ON_ERR :\n+\t\t       UPDATE_REFS_MSG_ON_ERR)) {\n+\t\tif (!quiet) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot update %s with %s\"),\n+\t\t\t\t   ref_stash, oid_to_hex(w_commit));\n+\t\t}\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int store_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint quiet = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct object_id obj;\n+\tstruct object_context dummy;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet\")),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, \"message\",\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_store_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n+\n+\tif (argc != 1) {\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"\\\"git stash store\\\" requires one \"\n+\t\t\t\t\t     \"<commit> argument\"));\n+\t\treturn -1;\n+\t}\n+\n+\tif (get_oid_with_context(argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,\n+\t\t\t\t &dummy)) {\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot update %s with %s\"),\n+\t\t\t\t\t     ref_stash, argv[0]);\n+\t\treturn -1;\n+\t}\n+\n+\treturn do_store_stash(&obj, stash_msg, quiet);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -764,6 +824,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!list_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"show\"))\n \t\treturn !!show_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"store\"))\n+\t\treturn !!store_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex d0318f859e..ff5556ccb0 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -208,45 +208,6 @@ create_stash () {\n \tdie \"$(gettext \"Cannot record working tree state\")\"\n }\n \n-store_stash () {\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\tstash_msg=\"$1\"\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t-q|--quiet)\n-\t\t\tquiet=t\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\ttest $# = 1 ||\n-\tdie \"$(eval_gettext \"\\\"$dashless store\\\" requires one <commit> argument\")\"\n-\n-\tw_commit=\"$1\"\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tstash_msg=\"Created via \\\"git stash store\\\".\"\n-\tfi\n-\n-\tgit update-ref --create-reflog -m \"$stash_msg\" $ref_stash $w_commit\n-\tret=$?\n-\ttest $ret != 0 && test -z \"$quiet\" &&\n-\tdie \"$(eval_gettext \"Cannot update \\$ref_stash with \\$w_commit\")\"\n-\treturn $ret\n-}\n-\n push_stash () {\n \tkeep_index=\n \tpatch_mode=\n@@ -325,7 +286,7 @@ push_stash () {\n \t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n \n \tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n-\tstore_stash -m \"$stash_msg\" -q $w_commit ||\n+\tgit stash--helper store -m \"$stash_msg\" -q $w_commit ||\n \tdie \"$(gettext \"Cannot save the current status\")\"\n \tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n \n@@ -485,7 +446,7 @@ create)\n \t;;\n store)\n \tshift\n-\tstore_stash \"$@\"\n+\tgit stash--helper store \"$@\"\n \t;;\n drop)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365685","messageId":"09b927684780e34eb65742d03ae9b20be90e929d.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 14/26] stash: convert list to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:30Z","receivedAt":"2018-12-20T19:45:10Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash list to the helper and delete the list_stash function\nfrom the shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 31 +++++++++++++++++++++++++++++++\n git-stash.sh            |  7 +------\n 2 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex d7ff78784b..d66a4589a5 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,6 +12,7 @@\n #include \"rerere.h\"\n \n static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper list [<options>]\"),\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n@@ -19,6 +20,11 @@ static const char * const git_stash_helper_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_list_usage[] = {\n+\tN_(\"git stash--helper list [<options>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_drop_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -616,6 +622,29 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int list_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_list_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n+\n+\tif (!ref_exists(ref_stash))\n+\t\treturn 0;\n+\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"log\", \"--format=%gd: %gs\", \"-g\",\n+\t\t\t \"--first-parent\", \"-m\", NULL);\n+\targv_array_pushv(&cp.args, argv);\n+\targv_array_push(&cp.args, ref_stash);\n+\targv_array_push(&cp.args, \"--\");\n+\treturn run_command(&cp);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -646,6 +675,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!pop_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"branch\"))\n \t\treturn !!branch_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"list\"))\n+\t\treturn !!list_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 8a9f907aa9..ab3992b59d 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -399,11 +399,6 @@ have_stash () {\n \tgit rev-parse --verify --quiet $ref_stash >/dev/null\n }\n \n-list_stash () {\n-\thave_stash || return 0\n-\tgit log --format=\"%gd: %gs\" -g --first-parent -m \"$@\" $ref_stash --\n-}\n-\n show_stash () {\n \tALLOW_UNKNOWN_FLAGS=t\n \tassert_stash_like \"$@\"\n@@ -591,7 +586,7 @@ test -n \"$seen_non_option\" || set \"push\" \"$@\"\n case \"$1\" in\n list)\n \tshift\n-\tlist_stash \"$@\"\n+\tgit stash--helper list \"$@\"\n \t;;\n show)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365686","messageId":"08bacf490dfa766fd5f6b35ac3fd96f37242c52b.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 13/26] stash: convert pop to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:29Z","receivedAt":"2018-12-20T19:45:12Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd stash pop to the helper and delete the pop_stash, drop_stash,\nassert_stash_ref functions from the shell script now that they\nare no longer needed.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 39 +++++++++++++++++++++++++++++++++-\n git-stash.sh            | 47 ++---------------------------------------\n 2 files changed, 40 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 68b65165e4..d7ff78784b 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -13,7 +13,7 @@\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n \tNULL\n@@ -24,6 +24,11 @@ static const char * const git_stash_helper_drop_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_pop_usage[] = {\n+\tN_(\"git stash--helper pop [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_apply_usage[] = {\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -543,6 +548,36 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int pop_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint index = 0;\n+\tint quiet = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_BOOL(0, \"index\", &index,\n+\t\t\t N_(\"attempt to recreate the index\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_pop_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tassert_stash_ref(&info);\n+\tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n+\t\tprintf_ln(_(\"The stash entry is kept in case \"\n+\t\t\t    \"you need it again.\"));\n+\telse\n+\t\tret = do_drop_stash(prefix, &info, quiet);\n+\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n \tint ret;\n@@ -607,6 +642,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!clear_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"drop\"))\n \t\treturn !!drop_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"pop\"))\n+\t\treturn !!pop_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"branch\"))\n \t\treturn !!branch_stash(argc, argv, prefix);\n \ndiff --git a/git-stash.sh b/git-stash.sh\nindex 67db321a4c..8a9f907aa9 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -571,50 +571,6 @@ assert_stash_like() {\n \t}\n }\n \n-is_stash_ref() {\n-\tis_stash_like \"$@\" && test -n \"$IS_STASH_REF\"\n-}\n-\n-assert_stash_ref() {\n-\tis_stash_ref \"$@\" || {\n-\t\targs=\"$*\"\n-\t\tdie \"$(eval_gettext \"'\\$args' is not a stash reference\")\"\n-\t}\n-}\n-\n-apply_stash () {\n-\tcd \"$START_DIR\"\n-\tgit stash--helper apply \"$@\"\n-\tres=$?\n-\tcd_to_toplevel\n-\treturn $res\n-}\n-\n-pop_stash() {\n-\tassert_stash_ref \"$@\"\n-\n-\tif apply_stash \"$@\"\n-\tthen\n-\t\tdrop_stash \"$@\"\n-\telse\n-\t\tstatus=$?\n-\t\tsay \"$(gettext \"The stash entry is kept in case you need it again.\")\"\n-\t\texit $status\n-\tfi\n-}\n-\n-drop_stash () {\n-\tassert_stash_ref \"$@\"\n-\n-\tgit reflog delete --updateref --rewrite \"${REV}\" &&\n-\t\tsay \"$(eval_gettext \"Dropped \\${REV} (\\$s)\")\" ||\n-\t\tdie \"$(eval_gettext \"\\${REV}: Could not drop stash entry\")\"\n-\n-\t# clear_stash if we just dropped the last stash entry\n-\tgit rev-parse --verify --quiet \"$ref_stash@{0}\" >/dev/null ||\n-\tclear_stash\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -672,7 +628,8 @@ drop)\n \t;;\n pop)\n \tshift\n-\tpop_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper pop \"$@\"\n \t;;\n branch)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365687","messageId":"42af83890f1b85f4ae19fc68c26e6a3afb9062cd.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 11/26] stash: convert drop and clear to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:27Z","receivedAt":"2018-12-20T19:45:13Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd the drop and clear commands to the builtin helper. These two\nare each simple, but are being added together as they are quite\nrelated.\n\nWe have to unfortunately keep the drop and clear functions in the\nshell script as functions are called with parameters internally\nthat are not valid when the commands are called externally. Once\npop is converted they can both be removed.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 117 ++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            |   4 +-\n 2 files changed, 119 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 997b1c0ecf..07b8ec5bcb 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,7 +12,14 @@\n #include \"rerere.h\"\n \n static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper clear\"),\n+\tNULL\n+};\n+\n+static const char * const git_stash_helper_drop_usage[] = {\n+\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n@@ -21,6 +28,11 @@ static const char * const git_stash_helper_apply_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_clear_usage[] = {\n+\tN_(\"git stash--helper clear\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -137,6 +149,32 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \treturn !(ret == 0 || ret == 1);\n }\n \n+static int do_clear_stash(void)\n+{\n+\tstruct object_id obj;\n+\tif (get_oid(ref_stash, &obj))\n+\t\treturn 0;\n+\n+\treturn delete_ref(NULL, ref_stash, &obj, 0);\n+}\n+\n+static int clear_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_clear_usage,\n+\t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n+\n+\tif (argc)\n+\t\treturn error(_(\"git stash clear with parameters is \"\n+\t\t\t       \"unimplemented\"));\n+\n+\treturn do_clear_stash();\n+}\n+\n static int reset_tree(struct object_id *i_tree, int update, int reset)\n {\n \tint nr_trees = 1;\n@@ -424,6 +462,81 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n+{\n+\tint ret;\n+\tstruct child_process cp_reflog = CHILD_PROCESS_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * reflog does not provide a simple function for deleting refs. One will\n+\t * need to be added to avoid implementing too much reflog code here\n+\t */\n+\n+\tcp_reflog.git_cmd = 1;\n+\targv_array_pushl(&cp_reflog.args, \"reflog\", \"delete\", \"--updateref\",\n+\t\t\t \"--rewrite\", NULL);\n+\targv_array_push(&cp_reflog.args, info->revision.buf);\n+\tret = run_command(&cp_reflog);\n+\tif (!ret) {\n+\t\tif (!quiet)\n+\t\t\tprintf_ln(_(\"Dropped %s (%s)\"), info->revision.buf,\n+\t\t\t\t  oid_to_hex(&info->w_commit));\n+\t} else {\n+\t\treturn error(_(\"%s: Could not drop stash entry\"),\n+\t\t\t     info->revision.buf);\n+\t}\n+\n+\t/*\n+\t * This could easily be replaced by get_oid, but currently it will throw\n+\t * a fatal error when a reflog is empty, which we can not recover from.\n+\t */\n+\tcp.git_cmd = 1;\n+\t/* Even though --quiet is specified, rev-parse still outputs the hash */\n+\tcp.no_stdout = 1;\n+\targv_array_pushl(&cp.args, \"rev-parse\", \"--verify\", \"--quiet\", NULL);\n+\targv_array_pushf(&cp.args, \"%s@{0}\", ref_stash);\n+\tret = run_command(&cp);\n+\n+\t/* do_clear_stash if we just dropped the last stash entry */\n+\tif (ret)\n+\t\tdo_clear_stash();\n+\n+\treturn 0;\n+}\n+\n+static void assert_stash_ref(struct stash_info *info)\n+{\n+\tif (!info->is_stash_ref) {\n+\t\tfree_stash_info(info);\n+\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n+\t\texit(128);\n+\t}\n+}\n+\n+static int drop_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint quiet = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_drop_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tassert_stash_ref(&info);\n+\n+\tret = do_drop_stash(prefix, &info, quiet);\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -446,6 +559,10 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_stash_helper_usage, options);\n \tif (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"clear\"))\n+\t\treturn !!clear_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"drop\"))\n+\t\treturn !!drop_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 366a082853..b8f70230f9 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -670,7 +670,7 @@ apply)\n \t;;\n clear)\n \tshift\n-\tclear_stash \"$@\"\n+\tgit stash--helper clear \"$@\"\n \t;;\n create)\n \tshift\n@@ -682,7 +682,7 @@ store)\n \t;;\n drop)\n \tshift\n-\tdrop_stash \"$@\"\n+\tgit stash--helper drop \"$@\"\n \t;;\n pop)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365688","messageId":"033e7911bbdca69bd785e04c5645390f5adae984.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 19/26] stash: make push -q quiet","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:35Z","receivedAt":"2018-12-20T19:45:14Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"There is a change in behaviour with this commit. When there was\nno initial commit, the shell version of stash would still display\na message. This commit makes `push` to not display any message if\n`--quiet` or `-q` is specified. Add tests for `--quiet`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 56 ++++++++++++++++++++++++++---------------\n t/t3903-stash.sh        | 23 +++++++++++++++++\n 2 files changed, 59 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex c77f62c895..5f9914ca27 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -971,7 +971,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n }\n \n static int stash_patch(struct stash_info *info, struct pathspec ps,\n-\t\t       struct strbuf *out_patch)\n+\t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n \tstruct strbuf out = STRBUF_INIT;\n@@ -1024,7 +1024,8 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n \tif (!out_patch->len) {\n-\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n \t\tret = 1;\n \t}\n \n@@ -1102,7 +1103,8 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \n static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n-\t\t\t   struct stash_info *info, struct strbuf *patch)\n+\t\t\t   struct stash_info *info, struct strbuf *patch,\n+\t\t\t   int quiet)\n {\n \tint ret = 0;\n \tint flags = 0;\n@@ -1122,7 +1124,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \trefresh_cache(REFRESH_QUIET);\n \n \tif (get_oid(\"HEAD\", &info->b_commit)) {\n-\t\tfprintf_ln(stderr, _(\"You do not have the initial commit yet\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"You do not have \"\n+\t\t\t\t\t     \"the initial commit yet\"));\n \t\tret = -1;\n \t\tgoto done;\n \t} else {\n@@ -1147,7 +1151,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tif (write_cache_as_tree(&info->i_tree, 0, NULL) ||\n \t    commit_tree(commit_tree_label.buf, commit_tree_label.len,\n \t\t\t&info->i_tree, parents, &info->i_commit, NULL, NULL)) {\n-\t\tfprintf_ln(stderr, _(\"Cannot save the current index state\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"index state\"));\n \t\tret = -1;\n \t\tgoto done;\n \t}\n@@ -1155,26 +1161,29 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tif (include_untracked && get_untracked_files(ps, include_untracked,\n \t\t\t\t\t\t     &untracked_files)) {\n \t\tif (save_untracked_files(info, &msg, untracked_files)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n-\t\t\t\t\t     \"the untracked files\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n+\t\t\t\t\t\t     \"the untracked files\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\n \t\tuntracked_commit_option = 1;\n \t}\n \tif (patch_mode) {\n-\t\tret = stash_patch(info, ps, patch);\n+\t\tret = stash_patch(info, ps, patch, quiet);\n \t\tif (ret < 0) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n-\t\t\t\t\t     \"worktree state\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t\t     \"worktree state\"));\n \t\t\tgoto done;\n \t\t} else if (ret > 0) {\n \t\t\tgoto done;\n \t\t}\n \t} else {\n \t\tif (stash_working_tree(info, ps)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n-\t\t\t\t\t     \"worktree state\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t\t     \"worktree state\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\n@@ -1200,7 +1209,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \n \tif (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,\n \t\t\tparents, &info->w_commit, NULL, NULL)) {\n-\t\tfprintf_ln(stderr, _(\"Cannot record working tree state\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot record \"\n+\t\t\t\t\t     \"working tree state\"));\n \t\tret = -1;\n \t\tgoto done;\n \t}\n@@ -1235,7 +1246,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tmemset(&ps, 0, sizeof(ps));\n \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n-\t\t\t      NULL);\n+\t\t\t      NULL, 0);\n \n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n@@ -1298,26 +1309,29 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \n \tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n \t\tret = -1;\n-\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n \t\tgoto done;\n \t}\n \n \tif (stash_msg)\n \t\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tif (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,\n-\t\t\t    &info, &patch)) {\n+\t\t\t    &info, &patch, quiet)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n \tif (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {\n \t\tret = -1;\n-\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n \t\tgoto done;\n \t}\n \n-\tprintf_ln(_(\"Saved working directory and index state %s\"),\n-\t\t  stash_msg_buf.buf);\n+\tif (!quiet)\n+\t\tprintf_ln(_(\"Saved working directory and index state %s\"),\n+\t\t\t  stash_msg_buf.buf);\n \n \tif (!patch_mode) {\n \t\tif (include_untracked && !ps.nr) {\n@@ -1418,7 +1432,9 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\targv_array_pushl(&cp.args, \"apply\", \"-R\", NULL);\n \n \t\tif (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot remove worktree changes\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot remove \"\n+\t\t\t\t\t\t     \"worktree changes\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 98c25a671c..b67d7a1120 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -1064,6 +1064,29 @@ test_expect_success 'push: <pathspec> not in the repository errors out' '\n \ttest_path_is_file untracked\n '\n \n+test_expect_success 'push: -q is quiet with changes' '\n+\t>foo &&\n+\tgit add foo &&\n+\tgit stash push -q >output 2>&1 &&\n+\ttest_must_be_empty output\n+'\n+\n+test_expect_success 'push: -q is quiet with no changes' '\n+\tgit stash push -q >output 2>&1 &&\n+\ttest_must_be_empty output\n+'\n+\n+test_expect_success 'push: -q is quiet even if there is no initial commit' '\n+\tgit init foo_dir &&\n+\ttest_when_finished rm -rf foo_dir &&\n+\t(\n+\t\tcd foo_dir &&\n+\t\t>bar &&\n+\t\ttest_must_fail git stash push -q >output 2>&1 &&\n+\t\ttest_must_be_empty output\n+\t)\n+'\n+\n test_expect_success 'untracked files are left in place when -u is not given' '\n \t>file &&\n \tgit add file &&\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365689","messageId":"73eac729193a64fe6ce5f2e38b73362263631b5b.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 22/26] stash: replace all `write-tree` child processes with API calls","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:38Z","receivedAt":"2018-12-20T19:45:18Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"This commit replaces spawning `git write-tree` with API calls.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 41 ++++++++++++-----------------------------\n 1 file changed, 12 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 4b63352927..39fcff32b7 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -950,9 +950,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n {\n \tint ret = 0;\n \tstruct strbuf untracked_msg = STRBUF_INIT;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tcp_upd_index.git_cmd = 1;\n \targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n@@ -967,15 +966,11 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \t\tgoto done;\n \t}\n \n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n-\tget_oid_hex(out.buf, &info->u_tree);\n \n \tif (commit_tree(untracked_msg.buf, untracked_msg.len,\n \t\t\t&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {\n@@ -984,8 +979,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \t}\n \n done:\n+\tdiscard_index(&istate);\n \tstrbuf_release(&untracked_msg);\n-\tstrbuf_release(&out);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\n }\n@@ -994,11 +989,10 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n \tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tremove_path(stash_index_path.buf);\n \n@@ -1024,17 +1018,12 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n \t/* State of the working tree. */\n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n-\tget_oid_hex(out.buf, &info->w_tree);\n-\n \tcp_diff_tree.git_cmd = 1;\n \targv_array_pushl(&cp_diff_tree.args, \"diff-tree\", \"-p\", \"HEAD\",\n \t\t\t oid_to_hex(&info->w_tree), \"--\", NULL);\n@@ -1050,7 +1039,7 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n done:\n-\tstrbuf_release(&out);\n+\tdiscard_index(&istate);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\n }\n@@ -1060,9 +1049,8 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \tint ret = 0;\n \tstruct rev_info rev;\n \tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct strbuf diff_output = STRBUF_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tset_alternate_index_output(stash_index_path.buf);\n \tif (reset_tree(&info->i_tree, 0, 0)) {\n@@ -1101,20 +1089,15 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \t\tgoto done;\n \t}\n \n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n-\tget_oid_hex(out.buf, &info->w_tree);\n-\n done:\n+\tdiscard_index(&istate);\n \tUNLEAK(rev);\n-\tstrbuf_release(&out);\n \tobject_array_clear(&rev.pending);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365690","messageId":"17f45e4bb48bdf9588d87c7b134afd703dbd69e6.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 21/26] stash: optimize `get_untracked_files()` and `check_changes()`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:37Z","receivedAt":"2018-12-20T19:45:19Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"This commits introduces a optimization by avoiding calling the\nsame functions again. For example, `git stash push -u`\nwould call at some points the following functions:\n\n * `check_changes()` (inside `do_push_stash()`)\n * `do_create_stash()`, which calls: `check_changes()` and\n`get_untracked_files()`\n\nNote that `check_changes()` also calls `get_untracked_files()`.\nSo, `check_changes()` is called 2 times and `get_untracked_files()`\n3 times.\n\nThe old function `check_changes()` now consists of two functions:\n`get_untracked_files()` and `check_changes_tracked_files()`.\n\nThese are the call chains for `push` and `create`:\n\n * `push_stash()` -> `do_push_stash()` -> `do_create_stash()`\n\n * `create_stash()` -> `do_create_stash()`\n\nTo prevent calling the same functions over and over again,\n`check_changes()` inside `do_create_stash()` is now placed\nin the caller functions (`create_stash()` and `do_push_stash()`).\nThis way `check_changes()` and `get_untracked files()` are called\nonly one time.\n\nhttps://public-inbox.org/git/20180818223329.GJ11326@hank.intra.tgummerer.com/\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 53 +++++++++++++++++++++++------------------\n 1 file changed, 30 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 19ead63c46..4b63352927 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -884,18 +884,18 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n }\n \n /*\n- * The return value of `check_changes()` can be:\n+ * The return value of `check_changes_tracked_files()` can be:\n  *\n  * < 0 if there was an error\n  * = 0 if there are no changes.\n  * > 0 if there are changes.\n  */\n-static int check_changes(struct pathspec ps, int include_untracked)\n+\n+static int check_changes_tracked_files(struct pathspec ps)\n {\n \tint result;\n \tstruct rev_info rev;\n \tstruct object_id dummy;\n-\tstruct strbuf out = STRBUF_INIT;\n \n \t/* No initial commit. */\n \tif (get_oid(\"HEAD\", &dummy))\n@@ -923,14 +923,26 @@ static int check_changes(struct pathspec ps, int include_untracked)\n \tif (diff_result_code(&rev.diffopt, result))\n \t\treturn 1;\n \n+\treturn 0;\n+}\n+\n+/*\n+ * The function will fill `untracked_files` with the names of untracked files\n+ * It will return 1 if there were any changes and 0 if there were not.\n+ */\n+\n+static int check_changes(struct pathspec ps, int include_untracked,\n+\t\t\t struct strbuf *untracked_files)\n+{\n+\tint ret = 0;\n+\tif (check_changes_tracked_files(ps))\n+\t\tret = 1;\n+\n \tif (include_untracked && get_untracked_files(ps, include_untracked,\n-\t\t\t\t\t\t     &out)) {\n-\t\tstrbuf_release(&out);\n-\t\treturn 1;\n-\t}\n+\t\t\t\t\t\t     untracked_files))\n+\t\tret = 1;\n \n-\tstrbuf_release(&out);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n@@ -1141,7 +1153,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\thead_commit = lookup_commit(the_repository, &info->b_commit);\n \t}\n \n-\tif (!check_changes(ps, include_untracked)) {\n+\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n \t\tret = 1;\n \t\tgoto done;\n \t}\n@@ -1166,8 +1178,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\tgoto done;\n \t}\n \n-\tif (include_untracked && get_untracked_files(ps, include_untracked,\n-\t\t\t\t\t\t     &untracked_files)) {\n+\tif (include_untracked) {\n \t\tif (save_untracked_files(info, &msg, untracked_files)) {\n \t\t\tif (!quiet)\n \t\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n@@ -1252,20 +1263,15 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     0);\n \n \tmemset(&ps, 0, sizeof(ps));\n-\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n-\t\t\t      NULL, 0);\n+\tif (!check_changes_tracked_files(ps))\n+\t\treturn 0;\n \n-\tif (!ret)\n+\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n+\tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n \tstrbuf_release(&stash_msg_buf);\n-\n-\t/*\n-\t * ret can be 1 if there were no changes. In this case, we should\n-\t * not error out.\n-\t */\n-\treturn ret < 0;\n+\treturn ret;\n }\n \n static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n@@ -1275,6 +1281,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \tstruct stash_info info;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct strbuf untracked_files = STRBUF_INIT;\n \n \tif (patch_mode && keep_index == -1)\n \t\tkeep_index = 1;\n@@ -1309,7 +1316,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\tgoto done;\n \t}\n \n-\tif (!check_changes(ps, include_untracked)) {\n+\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n \t\tif (!quiet)\n \t\t\tprintf_ln(_(\"No local changes to save\"));\n \t\tgoto done;\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365691","messageId":"764a526393ca8ae76394aecc8a87d66abb80c1b4.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 26/26] tests: add a special setup where stash.useBuiltin is off","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:42Z","receivedAt":"2018-12-20T19:45:20Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAdd a GIT_TEST_STASH_USE_BUILTIN=false test mode which is equivalent\nto running with stash.useBuiltin=false. This is needed to spot that\nwe're not introducing any regressions in the legacy stash version\nwhile we're carrying both it and the new built-in version.\n\nThis imitates the equivalent treatment for the built-in rebase in\n62c23938fae5 (tests: add a special setup where rebase.useBuiltin is off,\n2018-11-14).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/stash.c | 5 ++++-\n t/README        | 4 ++++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 346c9d2bb1..3ee8a41cda 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1518,7 +1518,10 @@ static int use_builtin_stash(void)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf out = STRBUF_INIT;\n-\tint ret;\n+\tint ret, env = git_env_bool(\"GIT_TEST_STASH_USE_BUILTIN\", -1);\n+\n+\tif (env != -1)\n+\t\treturn env;\n \n \targv_array_pushl(&cp.args,\n \t\t\t \"config\", \"--bool\", \"stash.usebuiltin\", NULL);\ndiff --git a/t/README b/t/README\nindex 28711cc508..9187eeea8e 100644\n--- a/t/README\n+++ b/t/README\n@@ -349,6 +349,10 @@ GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the\n builtin version of git-rebase. See 'rebase.useBuiltin' in\n git-config(1).\n \n+GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the\n+built-in version of git-stash. See 'stash.useBuiltin' in\n+git-config(1).\n+\n GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading\n of the index for the whole test suite by bypassing the default number of\n cache entries and thread minimums. Setting this to 1 will make the\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365692","messageId":"dc2811bd45d490261a275c29733ee6434df938d1.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 24/26] stash: add back the original, scripted `git stash`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:40Z","receivedAt":"2018-12-20T19:45:21Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis simply copies the version as of sd/stash-wo-user-name verbatim. As\nof now, it is not hooked up.\n\nThe next commit will change the builtin `stash` to hand off to the\nscripted `git stash` when `stash.useBuiltin=false`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n git-stash.sh | 769 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 769 insertions(+)\n create mode 100755 git-stash.sh\n\ndiff --git a/git-stash.sh b/git-stash.sh\nnew file mode 100755\nindex 0000000000..789ce2f41d\n--- /dev/null\n+++ b/git-stash.sh\n@@ -0,0 +1,769 @@\n+#!/bin/sh\n+# Copyright (c) 2007, Nanako Shiraishi\n+\n+dashless=$(basename \"$0\" | sed -e 's/-/ /')\n+USAGE=\"list [<options>]\n+   or: $dashless show [<stash>]\n+   or: $dashless drop [-q|--quiet] [<stash>]\n+   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n+   or: $dashless branch <branchname> [<stash>]\n+   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+\t\t      [-u|--include-untracked] [-a|--all] [<message>]\n+   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+\t\t       [-u|--include-untracked] [-a|--all] [-m <message>]\n+\t\t       [-- <pathspec>...]]\n+   or: $dashless clear\"\n+\n+SUBDIRECTORY_OK=Yes\n+OPTIONS_SPEC=\n+START_DIR=$(pwd)\n+. git-sh-setup\n+require_work_tree\n+prefix=$(git rev-parse --show-prefix) || exit 1\n+cd_to_toplevel\n+\n+TMP=\"$GIT_DIR/.git-stash.$$\"\n+TMPindex=${GIT_INDEX_FILE-\"$(git rev-parse --git-path index)\"}.stash.$$\n+trap 'rm -f \"$TMP-\"* \"$TMPindex\"' 0\n+\n+ref_stash=refs/stash\n+\n+if git config --get-colorbool color.interactive; then\n+       help_color=\"$(git config --get-color color.interactive.help 'red bold')\"\n+       reset_color=\"$(git config --get-color '' reset)\"\n+else\n+       help_color=\n+       reset_color=\n+fi\n+\n+no_changes () {\n+\tgit diff-index --quiet --cached HEAD --ignore-submodules -- \"$@\" &&\n+\tgit diff-files --quiet --ignore-submodules -- \"$@\" &&\n+\t(test -z \"$untracked\" || test -z \"$(untracked_files \"$@\")\")\n+}\n+\n+untracked_files () {\n+\tif test \"$1\" = \"-z\"\n+\tthen\n+\t\tshift\n+\t\tz=-z\n+\telse\n+\t\tz=\n+\tfi\n+\texcl_opt=--exclude-standard\n+\ttest \"$untracked\" = \"all\" && excl_opt=\n+\tgit ls-files -o $z $excl_opt -- \"$@\"\n+}\n+\n+prepare_fallback_ident () {\n+\tif ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1\n+\tthen\n+\t\tGIT_AUTHOR_NAME=\"git stash\"\n+\t\tGIT_AUTHOR_EMAIL=git@stash\n+\t\tGIT_COMMITTER_NAME=\"git stash\"\n+\t\tGIT_COMMITTER_EMAIL=git@stash\n+\t\texport GIT_AUTHOR_NAME\n+\t\texport GIT_AUTHOR_EMAIL\n+\t\texport GIT_COMMITTER_NAME\n+\t\texport GIT_COMMITTER_EMAIL\n+\tfi\n+}\n+\n+clear_stash () {\n+\tif test $# != 0\n+\tthen\n+\t\tdie \"$(gettext \"git stash clear with parameters is unimplemented\")\"\n+\tfi\n+\tif current=$(git rev-parse --verify --quiet $ref_stash)\n+\tthen\n+\t\tgit update-ref -d $ref_stash $current\n+\tfi\n+}\n+\n+create_stash () {\n+\n+\tprepare_fallback_ident\n+\n+\tstash_msg=\n+\tuntracked=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\tstash_msg=${1?\"BUG: create_stash () -m requires an argument\"}\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t-u|--include-untracked)\n+\t\t\tshift\n+\t\t\tuntracked=${1?\"BUG: create_stash () -u requires an argument\"}\n+\t\t\t;;\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\tgit update-index -q --refresh\n+\tif no_changes \"$@\"\n+\tthen\n+\t\texit 0\n+\tfi\n+\n+\t# state of the base commit\n+\tif b_commit=$(git rev-parse --verify HEAD)\n+\tthen\n+\t\thead=$(git rev-list --oneline -n 1 HEAD --)\n+\telse\n+\t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n+\tfi\n+\n+\tif branch=$(git symbolic-ref -q HEAD)\n+\tthen\n+\t\tbranch=${branch#refs/heads/}\n+\telse\n+\t\tbranch='(no branch)'\n+\tfi\n+\tmsg=$(printf '%s: %s' \"$branch\" \"$head\")\n+\n+\t# state of the index\n+\ti_tree=$(git write-tree) &&\n+\ti_commit=$(printf 'index on %s\\n' \"$msg\" |\n+\t\tgit commit-tree $i_tree -p $b_commit) ||\n+\t\tdie \"$(gettext \"Cannot save the current index state\")\"\n+\n+\tif test -n \"$untracked\"\n+\tthen\n+\t\t# Untracked files are stored by themselves in a parentless commit, for\n+\t\t# ease of unpacking later.\n+\t\tu_commit=$(\n+\t\t\tuntracked_files -z \"$@\" | (\n+\t\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n+\t\t\t\texport GIT_INDEX_FILE &&\n+\t\t\t\trm -f \"$TMPindex\" &&\n+\t\t\t\tgit update-index -z --add --remove --stdin &&\n+\t\t\t\tu_tree=$(git write-tree) &&\n+\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n+\t\t\t\trm -f \"$TMPindex\"\n+\t\t) ) || die \"$(gettext \"Cannot save the untracked files\")\"\n+\n+\t\tuntracked_commit_option=\"-p $u_commit\";\n+\telse\n+\t\tuntracked_commit_option=\n+\tfi\n+\n+\tif test -z \"$patch_mode\"\n+\tthen\n+\n+\t\t# state of the working tree\n+\t\tw_tree=$( (\n+\t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n+\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n+\t\t\texport GIT_INDEX_FILE &&\n+\t\t\tgit diff-index --name-only -z HEAD -- \"$@\" >\"$TMP-stagenames\" &&\n+\t\t\tgit update-index -z --add --remove --stdin <\"$TMP-stagenames\" &&\n+\t\t\tgit write-tree &&\n+\t\t\trm -f \"$TMPindex\"\n+\t\t) ) ||\n+\t\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n+\n+\telse\n+\n+\t\trm -f \"$TMP-index\" &&\n+\t\tGIT_INDEX_FILE=\"$TMP-index\" git read-tree HEAD &&\n+\n+\t\t# find out what the user wants\n+\t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n+\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n+\n+\t\t# state of the working tree\n+\t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n+\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n+\n+\t\tgit diff-tree -p HEAD $w_tree -- >\"$TMP-patch\" &&\n+\t\ttest -s \"$TMP-patch\" ||\n+\t\tdie \"$(gettext \"No changes selected\")\"\n+\n+\t\trm -f \"$TMP-index\" ||\n+\t\tdie \"$(gettext \"Cannot remove temporary index (can't happen)\")\"\n+\n+\tfi\n+\n+\t# create the stash\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tstash_msg=$(printf 'WIP on %s' \"$msg\")\n+\telse\n+\t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n+\tfi\n+\tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n+\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n+\tdie \"$(gettext \"Cannot record working tree state\")\"\n+}\n+\n+store_stash () {\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\tstash_msg=\"$1\"\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t-q|--quiet)\n+\t\t\tquiet=t\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\ttest $# = 1 ||\n+\tdie \"$(eval_gettext \"\\\"$dashless store\\\" requires one <commit> argument\")\"\n+\n+\tw_commit=\"$1\"\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tstash_msg=\"Created via \\\"git stash store\\\".\"\n+\tfi\n+\n+\tgit update-ref --create-reflog -m \"$stash_msg\" $ref_stash $w_commit\n+\tret=$?\n+\ttest $ret != 0 && test -z \"$quiet\" &&\n+\tdie \"$(eval_gettext \"Cannot update \\$ref_stash with \\$w_commit\")\"\n+\treturn $ret\n+}\n+\n+push_stash () {\n+\tkeep_index=\n+\tpatch_mode=\n+\tuntracked=\n+\tstash_msg=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-k|--keep-index)\n+\t\t\tkeep_index=t\n+\t\t\t;;\n+\t\t--no-keep-index)\n+\t\t\tkeep_index=n\n+\t\t\t;;\n+\t\t-p|--patch)\n+\t\t\tpatch_mode=t\n+\t\t\t# only default to keep if we don't already have an override\n+\t\t\ttest -z \"$keep_index\" && keep_index=t\n+\t\t\t;;\n+\t\t-q|--quiet)\n+\t\t\tGIT_QUIET=t\n+\t\t\t;;\n+\t\t-u|--include-untracked)\n+\t\t\tuntracked=untracked\n+\t\t\t;;\n+\t\t-a|--all)\n+\t\t\tuntracked=all\n+\t\t\t;;\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\ttest -z ${1+x} && usage\n+\t\t\tstash_msg=$1\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t--help)\n+\t\t\tshow_help\n+\t\t\t;;\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\t-*)\n+\t\t\toption=\"$1\"\n+\t\t\teval_gettextln \"error: unknown option for 'stash push': \\$option\"\n+\t\t\tusage\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\teval \"set $(git rev-parse --sq --prefix \"$prefix\" -- \"$@\")\"\n+\n+\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n+\tthen\n+\t\tdie \"$(gettext \"Can't use --patch and --include-untracked or --all at the same time\")\"\n+\tfi\n+\n+\ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n+\n+\tgit update-index -q --refresh\n+\tif no_changes \"$@\"\n+\tthen\n+\t\tsay \"$(gettext \"No local changes to save\")\"\n+\t\texit 0\n+\tfi\n+\n+\tgit reflog exists $ref_stash ||\n+\t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n+\n+\tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n+\tstore_stash -m \"$stash_msg\" -q $w_commit ||\n+\tdie \"$(gettext \"Cannot save the current status\")\"\n+\tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n+\n+\tif test -z \"$patch_mode\"\n+\tthen\n+\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n+\t\tif test -n \"$untracked\" && test $# = 0\n+\t\tthen\n+\t\t\tgit clean --force --quiet -d $CLEAN_X_OPTION\n+\t\tfi\n+\n+\t\tif test $# != 0\n+\t\tthen\n+\t\t\ttest -z \"$untracked\" && UPDATE_OPTION=\"-u\" || UPDATE_OPTION=\n+\t\t\ttest \"$untracked\" = \"all\" && FORCE_OPTION=\"--force\" || FORCE_OPTION=\n+\t\t\tgit add $UPDATE_OPTION $FORCE_OPTION -- \"$@\"\n+\t\t\tgit diff-index -p --cached --binary HEAD -- \"$@\" |\n+\t\t\tgit apply --index -R\n+\t\telse\n+\t\t\tgit reset --hard -q\n+\t\tfi\n+\n+\t\tif test \"$keep_index\" = \"t\" && test -n \"$i_tree\"\n+\t\tthen\n+\t\t\tgit read-tree --reset $i_tree\n+\t\t\tgit ls-files -z --modified -- \"$@\" |\n+\t\t\tgit checkout-index -z --force --stdin\n+\t\tfi\n+\telse\n+\t\tgit apply -R < \"$TMP-patch\" ||\n+\t\tdie \"$(gettext \"Cannot remove worktree changes\")\"\n+\n+\t\tif test \"$keep_index\" != \"t\"\n+\t\tthen\n+\t\t\tgit reset -q -- \"$@\"\n+\t\tfi\n+\tfi\n+}\n+\n+save_stash () {\n+\tpush_options=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\t-*)\n+\t\t\t# pass all options through to push_stash\n+\t\t\tpush_options=\"$push_options $1\"\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\tstash_msg=\"$*\"\n+\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tpush_stash $push_options\n+\telse\n+\t\tpush_stash $push_options -m \"$stash_msg\"\n+\tfi\n+}\n+\n+have_stash () {\n+\tgit rev-parse --verify --quiet $ref_stash >/dev/null\n+}\n+\n+list_stash () {\n+\thave_stash || return 0\n+\tgit log --format=\"%gd: %gs\" -g --first-parent -m \"$@\" $ref_stash --\n+}\n+\n+show_stash () {\n+\tALLOW_UNKNOWN_FLAGS=t\n+\tassert_stash_like \"$@\"\n+\n+\tif test -z \"$FLAGS\"\n+\tthen\n+\t\tif test \"$(git config --bool stash.showStat || echo true)\" = \"true\"\n+\t\tthen\n+\t\t\tFLAGS=--stat\n+\t\tfi\n+\n+\t\tif test \"$(git config --bool stash.showPatch || echo false)\" = \"true\"\n+\t\tthen\n+\t\t\tFLAGS=${FLAGS}${FLAGS:+ }-p\n+\t\tfi\n+\n+\t\tif test -z \"$FLAGS\"\n+\t\tthen\n+\t\t\treturn 0\n+\t\tfi\n+\tfi\n+\n+\tgit diff ${FLAGS} $b_commit $w_commit\n+}\n+\n+show_help () {\n+\texec git help stash\n+\texit 1\n+}\n+\n+#\n+# Parses the remaining options looking for flags and\n+# at most one revision defaulting to ${ref_stash}@{0}\n+# if none found.\n+#\n+# Derives related tree and commit objects from the\n+# revision, if one is found.\n+#\n+# stash records the work tree, and is a merge between the\n+# base commit (first parent) and the index tree (second parent).\n+#\n+#   REV is set to the symbolic version of the specified stash-like commit\n+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash\n+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref\n+#   s is set to the SHA1 of the stash commit\n+#   w_commit is set to the commit containing the working tree\n+#   b_commit is set to the base commit\n+#   i_commit is set to the commit containing the index tree\n+#   u_commit is set to the commit containing the untracked files tree\n+#   w_tree is set to the working tree\n+#   b_tree is set to the base tree\n+#   i_tree is set to the index tree\n+#   u_tree is set to the untracked files tree\n+#\n+#   GIT_QUIET is set to t if -q is specified\n+#   INDEX_OPTION is set to --index if --index is specified.\n+#   FLAGS is set to the remaining flags (if allowed)\n+#\n+# dies if:\n+#   * too many revisions specified\n+#   * no revision is specified and there is no stash stack\n+#   * a revision is specified which cannot be resolve to a SHA1\n+#   * a non-existent stash reference is specified\n+#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n+#\n+\n+parse_flags_and_rev()\n+{\n+\ttest \"$PARSE_CACHE\" = \"$*\" && return 0 # optimisation\n+\tPARSE_CACHE=\"$*\"\n+\n+\tIS_STASH_LIKE=\n+\tIS_STASH_REF=\n+\tINDEX_OPTION=\n+\ts=\n+\tw_commit=\n+\tb_commit=\n+\ti_commit=\n+\tu_commit=\n+\tw_tree=\n+\tb_tree=\n+\ti_tree=\n+\tu_tree=\n+\n+\tFLAGS=\n+\tREV=\n+\tfor opt\n+\tdo\n+\t\tcase \"$opt\" in\n+\t\t\t-q|--quiet)\n+\t\t\t\tGIT_QUIET=-t\n+\t\t\t;;\n+\t\t\t--index)\n+\t\t\t\tINDEX_OPTION=--index\n+\t\t\t;;\n+\t\t\t--help)\n+\t\t\t\tshow_help\n+\t\t\t;;\n+\t\t\t-*)\n+\t\t\t\ttest \"$ALLOW_UNKNOWN_FLAGS\" = t ||\n+\t\t\t\t\tdie \"$(eval_gettext \"unknown option: \\$opt\")\"\n+\t\t\t\tFLAGS=\"${FLAGS}${FLAGS:+ }$opt\"\n+\t\t\t;;\n+\t\t\t*)\n+\t\t\t\tREV=\"${REV}${REV:+ }'$opt'\"\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+\n+\teval set -- $REV\n+\n+\tcase $# in\n+\t\t0)\n+\t\t\thave_stash || die \"$(gettext \"No stash entries found.\")\"\n+\t\t\tset -- ${ref_stash}@{0}\n+\t\t;;\n+\t\t1)\n+\t\t\t:\n+\t\t;;\n+\t\t*)\n+\t\t\tdie \"$(eval_gettext \"Too many revisions specified: \\$REV\")\"\n+\t\t;;\n+\tesac\n+\n+\tcase \"$1\" in\n+\t\t*[!0-9]*)\n+\t\t\t:\n+\t\t;;\n+\t\t*)\n+\t\t\tset -- \"${ref_stash}@{$1}\"\n+\t\t;;\n+\tesac\n+\n+\tREV=$(git rev-parse --symbolic --verify --quiet \"$1\") || {\n+\t\treference=\"$1\"\n+\t\tdie \"$(eval_gettext \"\\$reference is not a valid reference\")\"\n+\t}\n+\n+\ti_commit=$(git rev-parse --verify --quiet \"$REV^2\") &&\n+\tset -- $(git rev-parse \"$REV\" \"$REV^1\" \"$REV:\" \"$REV^1:\" \"$REV^2:\" 2>/dev/null) &&\n+\ts=$1 &&\n+\tw_commit=$1 &&\n+\tb_commit=$2 &&\n+\tw_tree=$3 &&\n+\tb_tree=$4 &&\n+\ti_tree=$5 &&\n+\tIS_STASH_LIKE=t &&\n+\ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n+\tIS_STASH_REF=t\n+\n+\tu_commit=$(git rev-parse --verify --quiet \"$REV^3\") &&\n+\tu_tree=$(git rev-parse \"$REV^3:\" 2>/dev/null)\n+}\n+\n+is_stash_like()\n+{\n+\tparse_flags_and_rev \"$@\"\n+\ttest -n \"$IS_STASH_LIKE\"\n+}\n+\n+assert_stash_like() {\n+\tis_stash_like \"$@\" || {\n+\t\targs=\"$*\"\n+\t\tdie \"$(eval_gettext \"'\\$args' is not a stash-like commit\")\"\n+\t}\n+}\n+\n+is_stash_ref() {\n+\tis_stash_like \"$@\" && test -n \"$IS_STASH_REF\"\n+}\n+\n+assert_stash_ref() {\n+\tis_stash_ref \"$@\" || {\n+\t\targs=\"$*\"\n+\t\tdie \"$(eval_gettext \"'\\$args' is not a stash reference\")\"\n+\t}\n+}\n+\n+apply_stash () {\n+\n+\tassert_stash_like \"$@\"\n+\n+\tgit update-index -q --refresh || die \"$(gettext \"unable to refresh index\")\"\n+\n+\t# current index state\n+\tc_tree=$(git write-tree) ||\n+\t\tdie \"$(gettext \"Cannot apply a stash in the middle of a merge\")\"\n+\n+\tunstashed_index_tree=\n+\tif test -n \"$INDEX_OPTION\" && test \"$b_tree\" != \"$i_tree\" &&\n+\t\t\ttest \"$c_tree\" != \"$i_tree\"\n+\tthen\n+\t\tgit diff-tree --binary $s^2^..$s^2 | git apply --cached\n+\t\ttest $? -ne 0 &&\n+\t\t\tdie \"$(gettext \"Conflicts in index. Try without --index.\")\"\n+\t\tunstashed_index_tree=$(git write-tree) ||\n+\t\t\tdie \"$(gettext \"Could not save index tree\")\"\n+\t\tgit reset\n+\tfi\n+\n+\tif test -n \"$u_tree\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git read-tree \"$u_tree\" &&\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n+\t\trm -f \"$TMPindex\" ||\n+\t\tdie \"$(gettext \"Could not restore untracked files from stash entry\")\"\n+\tfi\n+\n+\teval \"\n+\t\tGITHEAD_$w_tree='Stashed changes' &&\n+\t\tGITHEAD_$c_tree='Updated upstream' &&\n+\t\tGITHEAD_$b_tree='Version stash was based on' &&\n+\t\texport GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree\n+\t\"\n+\n+\tif test -n \"$GIT_QUIET\"\n+\tthen\n+\t\tGIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY\n+\tfi\n+\tif git merge-recursive $b_tree -- $c_tree $w_tree\n+\tthen\n+\t\t# No conflict\n+\t\tif test -n \"$unstashed_index_tree\"\n+\t\tthen\n+\t\t\tgit read-tree \"$unstashed_index_tree\"\n+\t\telse\n+\t\t\ta=\"$TMP-added\" &&\n+\t\t\tgit diff-index --cached --name-only --diff-filter=A $c_tree >\"$a\" &&\n+\t\t\tgit read-tree --reset $c_tree &&\n+\t\t\tgit update-index --add --stdin <\"$a\" ||\n+\t\t\t\tdie \"$(gettext \"Cannot unstage modified files\")\"\n+\t\t\trm -f \"$a\"\n+\t\tfi\n+\t\tsquelch=\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\tsquelch='>/dev/null 2>&1'\n+\t\tfi\n+\t\t(cd \"$START_DIR\" && eval \"git status $squelch\") || :\n+\telse\n+\t\t# Merge conflict; keep the exit status from merge-recursive\n+\t\tstatus=$?\n+\t\tgit rerere\n+\t\tif test -n \"$INDEX_OPTION\"\n+\t\tthen\n+\t\t\tgettextln \"Index was not unstashed.\" >&2\n+\t\tfi\n+\t\texit $status\n+\tfi\n+}\n+\n+pop_stash() {\n+\tassert_stash_ref \"$@\"\n+\n+\tif apply_stash \"$@\"\n+\tthen\n+\t\tdrop_stash \"$@\"\n+\telse\n+\t\tstatus=$?\n+\t\tsay \"$(gettext \"The stash entry is kept in case you need it again.\")\"\n+\t\texit $status\n+\tfi\n+}\n+\n+drop_stash () {\n+\tassert_stash_ref \"$@\"\n+\n+\tgit reflog delete --updateref --rewrite \"${REV}\" &&\n+\t\tsay \"$(eval_gettext \"Dropped \\${REV} (\\$s)\")\" ||\n+\t\tdie \"$(eval_gettext \"\\${REV}: Could not drop stash entry\")\"\n+\n+\t# clear_stash if we just dropped the last stash entry\n+\tgit rev-parse --verify --quiet \"$ref_stash@{0}\" >/dev/null ||\n+\tclear_stash\n+}\n+\n+apply_to_branch () {\n+\ttest -n \"$1\" || die \"$(gettext \"No branch name specified\")\"\n+\tbranch=$1\n+\tshift 1\n+\n+\tset -- --index \"$@\"\n+\tassert_stash_like \"$@\"\n+\n+\tgit checkout -b $branch $REV^ &&\n+\tapply_stash \"$@\" && {\n+\t\ttest -z \"$IS_STASH_REF\" || drop_stash \"$@\"\n+\t}\n+}\n+\n+test \"$1\" = \"-p\" && set \"push\" \"$@\"\n+\n+PARSE_CACHE='--not-parsed'\n+# The default command is \"push\" if nothing but options are given\n+seen_non_option=\n+for opt\n+do\n+\tcase \"$opt\" in\n+\t--) break ;;\n+\t-*) ;;\n+\t*) seen_non_option=t; break ;;\n+\tesac\n+done\n+\n+test -n \"$seen_non_option\" || set \"push\" \"$@\"\n+\n+# Main command set\n+case \"$1\" in\n+list)\n+\tshift\n+\tlist_stash \"$@\"\n+\t;;\n+show)\n+\tshift\n+\tshow_stash \"$@\"\n+\t;;\n+save)\n+\tshift\n+\tsave_stash \"$@\"\n+\t;;\n+push)\n+\tshift\n+\tpush_stash \"$@\"\n+\t;;\n+apply)\n+\tshift\n+\tapply_stash \"$@\"\n+\t;;\n+clear)\n+\tshift\n+\tclear_stash \"$@\"\n+\t;;\n+create)\n+\tshift\n+\tcreate_stash -m \"$*\" && echo \"$w_commit\"\n+\t;;\n+store)\n+\tshift\n+\tstore_stash \"$@\"\n+\t;;\n+drop)\n+\tshift\n+\tdrop_stash \"$@\"\n+\t;;\n+pop)\n+\tshift\n+\tpop_stash \"$@\"\n+\t;;\n+branch)\n+\tshift\n+\tapply_to_branch \"$@\"\n+\t;;\n+*)\n+\tcase $# in\n+\t0)\n+\t\tpush_stash &&\n+\t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n+\t\t;;\n+\t*)\n+\t\tusage\n+\tesac\n+\t;;\n+esac\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365693","messageId":"1b16cdb63b0d40161bf3b70480ff7177f14fd3bf.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 20/26] stash: convert save to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:36Z","receivedAt":"2018-12-20T19:45:22Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash save to the helper and delete functions which are no\nlonger needed (`show_help()`, `save_stash()`, `push_stash()`,\n`create_stash()`, `clear_stash()`, `untracked_files()` and\n`no_changes()`).\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c |  50 ++++++\n git-stash.sh            | 328 +---------------------------------------\n 2 files changed, 52 insertions(+), 326 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 5f9914ca27..19ead63c46 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -26,6 +26,8 @@ static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n+\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n \n@@ -81,6 +83,12 @@ static const char * const git_stash_helper_push_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_save_usage[] = {\n+\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -1490,6 +1498,46 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     include_untracked);\n }\n \n+static int save_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint keep_index = -1;\n+\tint patch_mode = 0;\n+\tint include_untracked = 0;\n+\tint quiet = 0;\n+\tint ret = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct pathspec ps;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n+\t\t\t N_(\"keep index\")),\n+\t\tOPT_BOOL('p', \"patch\", &patch_mode,\n+\t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_SET_INT('a', \"all\", &include_untracked,\n+\t\t\t    N_(\"include ignore files\"), 2),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, \"message\",\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_save_usage,\n+\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\n+\tif (argc)\n+\t\tstash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');\n+\n+\tmemset(&ps, 0, sizeof(ps));\n+\tret = do_push_stash(ps, stash_msg, quiet, keep_index,\n+\t\t\t    patch_mode, include_untracked);\n+\n+\tstrbuf_release(&stash_msg_buf);\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -1530,6 +1578,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!create_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"push\"))\n \t\treturn !!push_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"save\"))\n+\t\treturn !!save_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 51d7a06601..695f1feba3 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -36,331 +36,6 @@ else\n        reset_color=\n fi\n \n-no_changes () {\n-\tgit diff-index --quiet --cached HEAD --ignore-submodules -- \"$@\" &&\n-\tgit diff-files --quiet --ignore-submodules -- \"$@\" &&\n-\t(test -z \"$untracked\" || test -z \"$(untracked_files \"$@\")\")\n-}\n-\n-untracked_files () {\n-\tif test \"$1\" = \"-z\"\n-\tthen\n-\t\tshift\n-\t\tz=-z\n-\telse\n-\t\tz=\n-\tfi\n-\texcl_opt=--exclude-standard\n-\ttest \"$untracked\" = \"all\" && excl_opt=\n-\tgit ls-files -o $z $excl_opt -- \"$@\"\n-}\n-\n-prepare_fallback_ident () {\n-\tif ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1\n-\tthen\n-\t\tGIT_AUTHOR_NAME=\"git stash\"\n-\t\tGIT_AUTHOR_EMAIL=git@stash\n-\t\tGIT_COMMITTER_NAME=\"git stash\"\n-\t\tGIT_COMMITTER_EMAIL=git@stash\n-\t\texport GIT_AUTHOR_NAME\n-\t\texport GIT_AUTHOR_EMAIL\n-\t\texport GIT_COMMITTER_NAME\n-\t\texport GIT_COMMITTER_EMAIL\n-\tfi\n-}\n-\n-clear_stash () {\n-\tif test $# != 0\n-\tthen\n-\t\tdie \"$(gettext \"git stash clear with parameters is unimplemented\")\"\n-\tfi\n-\tif current=$(git rev-parse --verify --quiet $ref_stash)\n-\tthen\n-\t\tgit update-ref -d $ref_stash $current\n-\tfi\n-}\n-\n-create_stash () {\n-\n-\tprepare_fallback_ident\n-\n-\tstash_msg=\n-\tuntracked=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\tstash_msg=${1?\"BUG: create_stash () -m requires an argument\"}\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t-u|--include-untracked)\n-\t\t\tshift\n-\t\t\tuntracked=${1?\"BUG: create_stash () -u requires an argument\"}\n-\t\t\t;;\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n-\tthen\n-\t\texit 0\n-\tfi\n-\n-\t# state of the base commit\n-\tif b_commit=$(git rev-parse --verify HEAD)\n-\tthen\n-\t\thead=$(git rev-list --oneline -n 1 HEAD --)\n-\telse\n-\t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n-\tfi\n-\n-\tif branch=$(git symbolic-ref -q HEAD)\n-\tthen\n-\t\tbranch=${branch#refs/heads/}\n-\telse\n-\t\tbranch='(no branch)'\n-\tfi\n-\tmsg=$(printf '%s: %s' \"$branch\" \"$head\")\n-\n-\t# state of the index\n-\ti_tree=$(git write-tree) &&\n-\ti_commit=$(printf 'index on %s\\n' \"$msg\" |\n-\t\tgit commit-tree $i_tree -p $b_commit) ||\n-\t\tdie \"$(gettext \"Cannot save the current index state\")\"\n-\n-\tif test -n \"$untracked\"\n-\tthen\n-\t\t# Untracked files are stored by themselves in a parentless commit, for\n-\t\t# ease of unpacking later.\n-\t\tu_commit=$(\n-\t\t\tuntracked_files -z \"$@\" | (\n-\t\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n-\t\t\t\texport GIT_INDEX_FILE &&\n-\t\t\t\trm -f \"$TMPindex\" &&\n-\t\t\t\tgit update-index -z --add --remove --stdin &&\n-\t\t\t\tu_tree=$(git write-tree) &&\n-\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n-\t\t\t\trm -f \"$TMPindex\"\n-\t\t) ) || die \"$(gettext \"Cannot save the untracked files\")\"\n-\n-\t\tuntracked_commit_option=\"-p $u_commit\";\n-\telse\n-\t\tuntracked_commit_option=\n-\tfi\n-\n-\tif test -z \"$patch_mode\"\n-\tthen\n-\n-\t\t# state of the working tree\n-\t\tw_tree=$( (\n-\t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n-\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n-\t\t\texport GIT_INDEX_FILE &&\n-\t\t\tgit diff-index --name-only -z HEAD -- \"$@\" >\"$TMP-stagenames\" &&\n-\t\t\tgit update-index -z --add --remove --stdin <\"$TMP-stagenames\" &&\n-\t\t\tgit write-tree &&\n-\t\t\trm -f \"$TMPindex\"\n-\t\t) ) ||\n-\t\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n-\n-\telse\n-\n-\t\trm -f \"$TMP-index\" &&\n-\t\tGIT_INDEX_FILE=\"$TMP-index\" git read-tree HEAD &&\n-\n-\t\t# find out what the user wants\n-\t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n-\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n-\n-\t\t# state of the working tree\n-\t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n-\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n-\n-\t\tgit diff-tree -p HEAD $w_tree -- >\"$TMP-patch\" &&\n-\t\ttest -s \"$TMP-patch\" ||\n-\t\tdie \"$(gettext \"No changes selected\")\"\n-\n-\t\trm -f \"$TMP-index\" ||\n-\t\tdie \"$(gettext \"Cannot remove temporary index (can't happen)\")\"\n-\n-\tfi\n-\n-\t# create the stash\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tstash_msg=$(printf 'WIP on %s' \"$msg\")\n-\telse\n-\t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n-\tfi\n-\tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n-\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n-\tdie \"$(gettext \"Cannot record working tree state\")\"\n-}\n-\n-push_stash () {\n-\tkeep_index=\n-\tpatch_mode=\n-\tuntracked=\n-\tstash_msg=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-k|--keep-index)\n-\t\t\tkeep_index=t\n-\t\t\t;;\n-\t\t--no-keep-index)\n-\t\t\tkeep_index=n\n-\t\t\t;;\n-\t\t-p|--patch)\n-\t\t\tpatch_mode=t\n-\t\t\t# only default to keep if we don't already have an override\n-\t\t\ttest -z \"$keep_index\" && keep_index=t\n-\t\t\t;;\n-\t\t-q|--quiet)\n-\t\t\tGIT_QUIET=t\n-\t\t\t;;\n-\t\t-u|--include-untracked)\n-\t\t\tuntracked=untracked\n-\t\t\t;;\n-\t\t-a|--all)\n-\t\t\tuntracked=all\n-\t\t\t;;\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\ttest -z ${1+x} && usage\n-\t\t\tstash_msg=$1\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t--help)\n-\t\t\tshow_help\n-\t\t\t;;\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\t-*)\n-\t\t\toption=\"$1\"\n-\t\t\teval_gettextln \"error: unknown option for 'stash push': \\$option\"\n-\t\t\tusage\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\teval \"set $(git rev-parse --sq --prefix \"$prefix\" -- \"$@\")\"\n-\n-\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n-\tthen\n-\t\tdie \"$(gettext \"Can't use --patch and --include-untracked or --all at the same time\")\"\n-\tfi\n-\n-\ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n-\n-\tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n-\tthen\n-\t\tsay \"$(gettext \"No local changes to save\")\"\n-\t\texit 0\n-\tfi\n-\n-\tgit reflog exists $ref_stash ||\n-\t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n-\n-\tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n-\tgit stash--helper store -m \"$stash_msg\" -q $w_commit ||\n-\tdie \"$(gettext \"Cannot save the current status\")\"\n-\tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n-\n-\tif test -z \"$patch_mode\"\n-\tthen\n-\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n-\t\tif test -n \"$untracked\" && test $# = 0\n-\t\tthen\n-\t\t\tgit clean --force --quiet -d $CLEAN_X_OPTION\n-\t\tfi\n-\n-\t\tif test $# != 0\n-\t\tthen\n-\t\t\ttest -z \"$untracked\" && UPDATE_OPTION=\"-u\" || UPDATE_OPTION=\n-\t\t\ttest \"$untracked\" = \"all\" && FORCE_OPTION=\"--force\" || FORCE_OPTION=\n-\t\t\tgit add $UPDATE_OPTION $FORCE_OPTION -- \"$@\"\n-\t\t\tgit diff-index -p --cached --binary HEAD -- \"$@\" |\n-\t\t\tgit apply --index -R\n-\t\telse\n-\t\t\tgit reset --hard -q\n-\t\tfi\n-\n-\t\tif test \"$keep_index\" = \"t\" && test -n \"$i_tree\"\n-\t\tthen\n-\t\t\tgit read-tree --reset $i_tree\n-\t\t\tgit ls-files -z --modified -- \"$@\" |\n-\t\t\tgit checkout-index -z --force --stdin\n-\t\tfi\n-\telse\n-\t\tgit apply -R < \"$TMP-patch\" ||\n-\t\tdie \"$(gettext \"Cannot remove worktree changes\")\"\n-\n-\t\tif test \"$keep_index\" != \"t\"\n-\t\tthen\n-\t\t\tgit reset -q -- \"$@\"\n-\t\tfi\n-\tfi\n-}\n-\n-save_stash () {\n-\tpush_options=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\t-*)\n-\t\t\t# pass all options through to push_stash\n-\t\t\tpush_options=\"$push_options $1\"\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\tstash_msg=\"$*\"\n-\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tpush_stash $push_options\n-\telse\n-\t\tpush_stash $push_options -m \"$stash_msg\"\n-\tfi\n-}\n-\n-show_help () {\n-\texec git help stash\n-\texit 1\n-}\n-\n #\n # Parses the remaining options looking for flags and\n # at most one revision defaulting to ${ref_stash}@{0}\n@@ -425,7 +100,8 @@ show)\n \t;;\n save)\n \tshift\n-\tsave_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper save \"$@\"\n \t;;\n push)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365694","messageId":"a6692eef2ca7c9d9e4701f087269d537248a4941.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 18/26] stash: convert push to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:34Z","receivedAt":"2018-12-20T19:45:24Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash push to the helper.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 245 +++++++++++++++++++++++++++++++++++++++-\n git-stash.sh            |   6 +-\n 2 files changed, 245 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 080c2f7aa6..c77f62c895 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -23,6 +23,9 @@ static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n+\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--] [<pathspec>...]]\"),\n \tNULL\n };\n \n@@ -71,6 +74,13 @@ static const char * const git_stash_helper_create_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_push_usage[] = {\n+\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--] [<pathspec>...]]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -1092,7 +1102,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \n static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n-\t\t\t   struct stash_info *info)\n+\t\t\t   struct stash_info *info, struct strbuf *patch)\n {\n \tint ret = 0;\n \tint flags = 0;\n@@ -1105,7 +1115,6 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tstruct strbuf msg = STRBUF_INIT;\n \tstruct strbuf commit_tree_label = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n-\tstruct strbuf patch = STRBUF_INIT;\n \n \tprepare_fallback_ident(\"git stash\", \"git@stash\");\n \n@@ -1154,7 +1163,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\tuntracked_commit_option = 1;\n \t}\n \tif (patch_mode) {\n-\t\tret = stash_patch(info, ps, &patch);\n+\t\tret = stash_patch(info, ps, patch);\n \t\tif (ret < 0) {\n \t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n \t\t\t\t\t     \"worktree state\"));\n@@ -1225,7 +1234,8 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \n \tmemset(&ps, 0, sizeof(ps));\n \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n+\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n+\t\t\t      NULL);\n \n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n@@ -1239,6 +1249,231 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \treturn ret < 0;\n }\n \n+static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n+\t\t\t int keep_index, int patch_mode, int include_untracked)\n+{\n+\tint ret = 0;\n+\tstruct stash_info info;\n+\tstruct strbuf patch = STRBUF_INIT;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\n+\tif (patch_mode && keep_index == -1)\n+\t\tkeep_index = 1;\n+\n+\tif (patch_mode && include_untracked) {\n+\t\tfprintf_ln(stderr, _(\"Can't use --patch and --include-untracked\"\n+\t\t\t\t     \" or --all at the same time\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tread_cache_preload(NULL);\n+\tif (!include_untracked && ps.nr) {\n+\t\tint i;\n+\t\tchar *ps_matched = xcalloc(ps.nr, 1);\n+\n+\t\tfor (i = 0; i < active_nr; i++)\n+\t\t\tce_path_match(&the_index, active_cache[i], &ps,\n+\t\t\t\t      ps_matched);\n+\n+\t\tif (report_path_error(ps_matched, &ps, NULL)) {\n+\t\t\tfprintf_ln(stderr, _(\"Did you forget to 'git add'?\"));\n+\t\t\tret = -1;\n+\t\t\tfree(ps_matched);\n+\t\t\tgoto done;\n+\t\t}\n+\t\tfree(ps_matched);\n+\t}\n+\n+\tif (refresh_cache(REFRESH_QUIET)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (!check_changes(ps, include_untracked)) {\n+\t\tif (!quiet)\n+\t\t\tprintf_ln(_(\"No local changes to save\"));\n+\t\tgoto done;\n+\t}\n+\n+\tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n+\t\tret = -1;\n+\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n+\t\tgoto done;\n+\t}\n+\n+\tif (stash_msg)\n+\t\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n+\tif (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,\n+\t\t\t    &info, &patch)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {\n+\t\tret = -1;\n+\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n+\t\tgoto done;\n+\t}\n+\n+\tprintf_ln(_(\"Saved working directory and index state %s\"),\n+\t\t  stash_msg_buf.buf);\n+\n+\tif (!patch_mode) {\n+\t\tif (include_untracked && !ps.nr) {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"clean\", \"--force\",\n+\t\t\t\t\t \"--quiet\", \"-d\", NULL);\n+\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n+\t\t\t\targv_array_push(&cp.args, \"-x\");\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tif (ps.nr) {\n+\t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_apply = CHILD_PROCESS_INIT;\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tcp_add.git_cmd = 1;\n+\t\t\targv_array_push(&cp_add.args, \"add\");\n+\t\t\tif (!include_untracked)\n+\t\t\t\targv_array_push(&cp_add.args, \"-u\");\n+\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n+\t\t\t\targv_array_push(&cp_add.args, \"--force\");\n+\t\t\targv_array_push(&cp_add.args, \"--\");\n+\t\t\tadd_pathspecs(&cp_add.args, ps);\n+\t\t\tif (run_command(&cp_add)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_diff.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_diff.args, \"diff-index\", \"-p\",\n+\t\t\t\t\t \"--cached\", \"--binary\", \"HEAD\", \"--\",\n+\t\t\t\t\t NULL);\n+\t\t\tadd_pathspecs(&cp_diff.args, ps);\n+\t\t\tif (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_apply.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_apply.args, \"apply\", \"--index\",\n+\t\t\t\t\t \"-R\", NULL);\n+\t\t\tif (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,\n+\t\t\t\t\t NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n+\t\t\t\t\t NULL);\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (keep_index == 1 && !is_null_oid(&info.i_tree)) {\n+\t\t\tstruct child_process cp_ls = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_checkout = CHILD_PROCESS_INIT;\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tif (reset_tree(&info.i_tree, 0, 1)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_ls.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_ls.args, \"ls-files\", \"-z\",\n+\t\t\t\t\t \"--modified\", \"--\", NULL);\n+\n+\t\t\tadd_pathspecs(&cp_ls.args, ps);\n+\t\t\tif (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_checkout.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_checkout.args, \"checkout-index\",\n+\t\t\t\t\t \"-z\", \"--force\", \"--stdin\", NULL);\n+\t\t\tif (pipe_command(&cp_checkout, out.buf, out.len, NULL,\n+\t\t\t\t\t 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tgoto done;\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\tcp.git_cmd = 1;\n+\t\targv_array_pushl(&cp.args, \"apply\", \"-R\", NULL);\n+\n+\t\tif (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot remove worktree changes\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (keep_index < 1) {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"reset\", \"-q\", \"--\", NULL);\n+\t\t\tadd_pathspecs(&cp.args, ps);\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&stash_msg_buf);\n+\treturn ret;\n+}\n+\n+static int push_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint keep_index = -1;\n+\tint patch_mode = 0;\n+\tint include_untracked = 0;\n+\tint quiet = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct pathspec ps;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n+\t\t\t N_(\"keep index\")),\n+\t\tOPT_BOOL('p', \"patch\", &patch_mode,\n+\t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_SET_INT('a', \"all\", &include_untracked,\n+\t\t\t    N_(\"include ignore files\"), 2),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_push_usage,\n+\t\t\t     0);\n+\n+\tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n+\treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n+\t\t\t     include_untracked);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -1277,6 +1512,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!store_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"create\"))\n \t\treturn !!create_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"push\"))\n+\t\treturn !!push_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex a9b3064ff0..51d7a06601 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -429,7 +429,8 @@ save)\n \t;;\n push)\n \tshift\n-\tpush_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper push \"$@\"\n \t;;\n apply)\n \tshift\n@@ -465,7 +466,8 @@ branch)\n *)\n \tcase $# in\n \t0)\n-\t\tpush_stash &&\n+\t\tcd \"$START_DIR\"\n+\t\tgit stash--helper push &&\n \t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n \t\t;;\n \t*)\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365695","messageId":"c05bb05e6eb40af1921e02711ff96350c8165cf2.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 25/26] stash: optionally use the scripted version again","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:41Z","receivedAt":"2018-12-20T19:45:25Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWe recently converted the `git stash` command from Unix shell scripts\nto builtins.\n\nLet's end users a way out when they discover a bug in the\nbuiltin command: `stash.useBuiltin`.\n\nAs the file name `git-stash` is already in use, let's rename the\nscripted backend to `git-legacy-stash`.\n\nTo make the test suite pass with `stash.useBuiltin=false`, this commit\nalso backports rudimentary support for `-q` (but only *just* enough\nto appease the test suite), and adds a super-ugly hack to force exit\ncode 129 for `git stash -h`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n .gitignore                          |  1 +\n Makefile                            |  1 +\n builtin/stash.c                     | 35 +++++++++++++++++++++++++++++\n git-stash.sh => git-legacy-stash.sh | 34 +++++++++++++++++++++++++---\n git-sh-setup.sh                     |  1 +\n git.c                               |  7 +++++-\n 6 files changed, 75 insertions(+), 4 deletions(-)\n rename git-stash.sh => git-legacy-stash.sh (97%)\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..7b0164675e 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -82,6 +82,7 @@\n /git-interpret-trailers\n /git-instaweb\n /git-legacy-rebase\n+/git-legacy-stash\n /git-log\n /git-ls-files\n /git-ls-remote\ndiff --git a/Makefile b/Makefile\nindex 8cee2731aa..810231a0b5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -617,6 +617,7 @@ SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-legacy-rebase.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-remote-testgit.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex fe32ff42fd..346c9d2bb1 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -13,6 +13,7 @@\n #include \"revision.h\"\n #include \"log-tree.h\"\n #include \"diffcore.h\"\n+#include \"exec-cmd.h\"\n \n #define INCLUDE_ALL_FILES 2\n \n@@ -1513,6 +1514,26 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int use_builtin_stash(void)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tint ret;\n+\n+\targv_array_pushl(&cp.args,\n+\t\t\t \"config\", \"--bool\", \"stash.usebuiltin\", NULL);\n+\tcp.git_cmd = 1;\n+\tif (capture_command(&cp, &out, 6)) {\n+\t\tstrbuf_release(&out);\n+\t\treturn 1;\n+\t}\n+\n+\tstrbuf_trim(&out);\n+\tret = !strcmp(\"true\", out.buf);\n+\tstrbuf_release(&out);\n+\treturn ret;\n+}\n+\n int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i = -1;\n@@ -1524,6 +1545,20 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tif (!use_builtin_stash()) {\n+\t\tconst char *path = mkpath(\"%s/git-legacy-stash\",\n+\t\t\t\t\t  git_exec_path());\n+\n+\t\tif (sane_execvp(path, (char **)argv) < 0)\n+\t\t\tdie_errno(_(\"could not exec %s\"), path);\n+\t\telse\n+\t\t\tBUG(\"sane_execvp() returned???\");\n+\t}\n+\n+\tprefix = setup_git_directory();\n+\ttrace_repo_setup(prefix);\n+\tsetup_work_tree();\n+\n \tgit_config(git_diff_basic_config, NULL);\n \n \targc = parse_options(argc, argv, prefix, options, git_stash_usage,\ndiff --git a/git-stash.sh b/git-legacy-stash.sh\nsimilarity index 97%\nrename from git-stash.sh\nrename to git-legacy-stash.sh\nindex 789ce2f41d..8a8c4a9270 100755\n--- a/git-stash.sh\n+++ b/git-legacy-stash.sh\n@@ -80,6 +80,28 @@ clear_stash () {\n \tfi\n }\n \n+maybe_quiet () {\n+\tcase \"$1\" in\n+\t--keep-stdout)\n+\t\tshift\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\teval \"$@\" 2>/dev/null\n+\t\telse\n+\t\t\teval \"$@\"\n+\t\tfi\n+\t\t;;\n+\t*)\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\teval \"$@\" >/dev/null 2>&1\n+\t\telse\n+\t\t\teval \"$@\"\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n create_stash () {\n \n \tprepare_fallback_ident\n@@ -112,15 +134,18 @@ create_stash () {\n \tdone\n \n \tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n+\tif maybe_quiet no_changes \"$@\"\n \tthen\n \t\texit 0\n \tfi\n \n \t# state of the base commit\n-\tif b_commit=$(git rev-parse --verify HEAD)\n+\tif b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)\n \tthen\n \t\thead=$(git rev-list --oneline -n 1 HEAD --)\n+\telif test -n \"$GIT_QUIET\"\n+\tthen\n+\t\texit 1\n \telse\n \t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n \tfi\n@@ -315,7 +340,7 @@ push_stash () {\n \ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n \n \tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n+\tif maybe_quiet no_changes \"$@\"\n \tthen\n \t\tsay \"$(gettext \"No local changes to save\")\"\n \t\texit 0\n@@ -370,6 +395,9 @@ save_stash () {\n \twhile test $# != 0\n \tdo\n \t\tcase \"$1\" in\n+\t\t-q|--quiet)\n+\t\t\tGIT_QUIET=t\n+\t\t\t;;\n \t\t--)\n \t\t\tshift\n \t\t\tbreak\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex 378928518b..10d9764185 100644\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -101,6 +101,7 @@ $LONG_USAGE\")\"\n \tcase \"$1\" in\n \t\t-h)\n \t\techo \"$LONG_USAGE\"\n+\t\tcase \"$0\" in *git-legacy-stash) exit 129;; esac\n \t\texit\n \tesac\n fi\ndiff --git a/git.c b/git.c\nindex 8a20909eae..591ebe9409 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,7 +554,12 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t{ \"stash\", cmd_stash, RUN_SETUP | NEED_WORK_TREE },\n+\t/*\n+\t * NEEDSWORK: Until the builtin stash is thoroughly robust and no\n+\t * longer needs redirection to the stash shell script this is kept as\n+\t * is, then should be changed to RUN_SETUP | NEED_WORK_TREE\n+\t */\n+\t{ \"stash\", cmd_stash },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365696","messageId":"a6d4d880ffb837a806bf361be28e0b5445b418c1.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 23/26] stash: convert `stash--helper.c` into `stash.c`","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:39Z","receivedAt":"2018-12-20T19:45:31Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"The old shell script `git-stash.sh`  was removed and replaced\nentirely by `builtin/stash.c`. In order to do that, `create` and\n`push` were adapted to work without `stash.sh`. For example, before\nthis commit, `git stash create` called `git stash--helper create\n--message \"$*\"`. If it called `git stash--helper create \"$@\"`, then\nsome of these changes wouldn't have been necessary.\n\nThis commit also removes the word `helper` since now stash is\ncalled directly and not by a shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n .gitignore                           |   1 -\n Makefile                             |   3 +-\n builtin.h                            |   2 +-\n builtin/{stash--helper.c => stash.c} | 154 +++++++++++++++------------\n git-stash.sh                         | 153 --------------------------\n git.c                                |   2 +-\n 6 files changed, 91 insertions(+), 224 deletions(-)\n rename builtin/{stash--helper.c => stash.c} (91%)\n delete mode 100755 git-stash.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 6ecab90ab2..0d77ea5894 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,7 +162,6 @@\n /git-show-ref\n /git-stage\n /git-stash\n-/git-stash--helper\n /git-status\n /git-stripspace\n /git-submodule\ndiff --git a/Makefile b/Makefile\nindex c246fc7078..8cee2731aa 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -619,7 +619,6 @@ SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-legacy-rebase.sh\n SCRIPT_SH += git-remote-testgit.sh\n SCRIPT_SH += git-request-pull.sh\n-SCRIPT_SH += git-stash.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n \n@@ -1117,7 +1116,7 @@ BUILTIN_OBJS += builtin/shortlog.o\n BUILTIN_OBJS += builtin/show-branch.o\n BUILTIN_OBJS += builtin/show-index.o\n BUILTIN_OBJS += builtin/show-ref.o\n-BUILTIN_OBJS += builtin/stash--helper.o\n+BUILTIN_OBJS += builtin/stash.o\n BUILTIN_OBJS += builtin/stripspace.o\n BUILTIN_OBJS += builtin/submodule--helper.o\n BUILTIN_OBJS += builtin/symbolic-ref.o\ndiff --git a/builtin.h b/builtin.h\nindex ff4460aff7..b78ab6e30b 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -225,7 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n-extern int cmd_stash__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_stash(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/stash--helper.c b/builtin/stash.c\nsimilarity index 91%\nrename from builtin/stash--helper.c\nrename to builtin/stash.c\nindex 39fcff32b7..fe32ff42fd 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash.c\n@@ -16,75 +16,70 @@\n \n #define INCLUDE_ALL_FILES 2\n \n-static const char * const git_stash_helper_usage[] = {\n-\tN_(\"git stash--helper list [<options>]\"),\n-\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n-\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n-\tN_(\"git stash--helper clear\"),\n-\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_usage[] = {\n+\tN_(\"git stash list [<options>]\"),\n+\tN_(\"git stash show [<options>] [<stash>]\"),\n+\tN_(\"git stash drop [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash branch <branchname> [<stash>]\"),\n+\tN_(\"git stash clear\"),\n+\tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n-\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_list_usage[] = {\n-\tN_(\"git stash--helper list [<options>]\"),\n+static const char * const git_stash_list_usage[] = {\n+\tN_(\"git stash list [<options>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_show_usage[] = {\n-\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n+static const char * const git_stash_show_usage[] = {\n+\tN_(\"git stash show [<options>] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_drop_usage[] = {\n-\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_drop_usage[] = {\n+\tN_(\"git stash drop [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_pop_usage[] = {\n-\tN_(\"git stash--helper pop [--index] [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_pop_usage[] = {\n+\tN_(\"git stash pop [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_apply_usage[] = {\n-\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_apply_usage[] = {\n+\tN_(\"git stash apply [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_branch_usage[] = {\n-\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n+static const char * const git_stash_branch_usage[] = {\n+\tN_(\"git stash branch <branchname> [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_clear_usage[] = {\n-\tN_(\"git stash--helper clear\"),\n+static const char * const git_stash_clear_usage[] = {\n+\tN_(\"git stash clear\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_store_usage[] = {\n-\tN_(\"git stash--helper store [-m|--message <message>] [-q|--quiet] <commit>\"),\n+static const char * const git_stash_store_usage[] = {\n+\tN_(\"git stash store [-m|--message <message>] [-q|--quiet] <commit>\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_create_usage[] = {\n-\tN_(\"git stash--helper create [<message>]\"),\n-\tNULL\n-};\n-\n-static const char * const git_stash_helper_push_usage[] = {\n-\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_push_usage[] = {\n+\tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_save_usage[] = {\n-\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_save_usage[] = {\n+\tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n@@ -221,7 +216,7 @@ static int clear_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_clear_usage,\n+\t\t\t     git_stash_clear_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (argc)\n@@ -526,7 +521,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_apply_usage, 0);\n+\t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -599,7 +594,7 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_drop_usage, 0);\n+\t\t\t     git_stash_drop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -625,7 +620,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_pop_usage, 0);\n+\t\t\t     git_stash_pop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -652,7 +647,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_branch_usage, 0);\n+\t\t\t     git_stash_branch_usage, 0);\n \n \tif (!argc) {\n \t\tfprintf_ln(stderr, _(\"No branch name specified\"));\n@@ -687,7 +682,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_list_usage,\n+\t\t\t     git_stash_list_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n \n \tif (!ref_exists(ref_stash))\n@@ -767,7 +762,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \targc = setup_revisions(argc, argv, &rev, NULL);\n \tif (argc > 1) {\n \t\tfree_stash_info(&info);\n-\t\tusage_with_options(git_stash_helper_show_usage, options);\n+\t\tusage_with_options(git_stash_show_usage, options);\n \t}\n \n \trev.diffopt.flags.recursive = 1;\n@@ -813,7 +808,7 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_store_usage,\n+\t\t\t     git_stash_store_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n \n \tif (argc != 1) {\n@@ -1227,29 +1222,18 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint include_untracked = 0;\n \tint ret = 0;\n-\tconst char *stash_msg = NULL;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct stash_info info;\n \tstruct pathspec ps;\n-\tstruct option options[] = {\n-\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n-\t\t\t N_(\"include untracked files in stash\")),\n-\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n-\t\t\t N_(\"stash message\")),\n-\t\tOPT_END()\n-\t};\n \n-\targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_create_usage,\n-\t\t\t     0);\n+\t/* Starting with argv[1], since argv[0] is \"create\" */\n+\tstrbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n \tif (!check_changes_tracked_files(ps))\n \t\treturn 0;\n \n-\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n@@ -1479,9 +1463,10 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_push_usage,\n-\t\t\t     0);\n+\tif (argc)\n+\t\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t\t     git_stash_push_usage,\n+\t\t\t\t     0);\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n \treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n@@ -1514,7 +1499,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_save_usage,\n+\t\t\t     git_stash_save_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tif (argc)\n@@ -1528,10 +1513,12 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n-int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n+int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n+\tint i = -1;\n \tpid_t pid = getpid();\n \tconst char *index_file;\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n \n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -1539,16 +1526,16 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_diff_basic_config, NULL);\n \n-\targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n+\targc = parse_options(argc, argv, prefix, options, git_stash_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n \n \tindex_file = get_index_file();\n \tstrbuf_addf(&stash_index_path, \"%s.stash.%\" PRIuMAX, index_file,\n \t\t    (uintmax_t)pid);\n \n-\tif (argc < 1)\n-\t\tusage_with_options(git_stash_helper_usage, options);\n-\tif (!strcmp(argv[0], \"apply\"))\n+\tif (!argc)\n+\t\treturn !!push_stash(0, NULL, prefix);\n+\telse if (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"clear\"))\n \t\treturn !!clear_stash(argc, argv, prefix);\n@@ -1570,7 +1557,42 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!push_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"save\"))\n \t\treturn !!save_stash(argc, argv, prefix);\n+\telse if (*argv[0] != '-')\n+\t\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n+\t\t\t      git_stash_usage, options);\n+\n+\tif (strcmp(argv[0], \"-p\")) {\n+\t\twhile (++i < argc && strcmp(argv[i], \"--\")) {\n+\t\t\t/*\n+\t\t\t * `akpqu` is a string which contains all short options,\n+\t\t\t * except `-m` which is verified separately.\n+\t\t\t */\n+\t\t\tif ((strlen(argv[i]) == 2) && *argv[i] == '-' &&\n+\t\t\t    strchr(\"akpqu\", argv[i][1]))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (!strcmp(argv[i], \"--all\") ||\n+\t\t\t    !strcmp(argv[i], \"--keep-index\") ||\n+\t\t\t    !strcmp(argv[i], \"--no-keep-index\") ||\n+\t\t\t    !strcmp(argv[i], \"--patch\") ||\n+\t\t\t    !strcmp(argv[i], \"--quiet\") ||\n+\t\t\t    !strcmp(argv[i], \"--include-untracked\"))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * `-m` and `--message=` are verified separately because\n+\t\t\t * they need to be immediately followed by a string\n+\t\t\t * (i.e.`-m\"foobar\"` or `--message=\"foobar\"`).\n+\t\t\t */\n+\t\t\tif (starts_with(argv[i], \"-m\") ||\n+\t\t\t    starts_with(argv[i], \"--message=\"))\n+\t\t\t\tcontinue;\n+\n+\t\t\tusage_with_options(git_stash_usage, options);\n+\t\t}\n+\t}\n \n-\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n-\t\t      git_stash_helper_usage, options);\n+\targv_array_push(&args, \"push\");\n+\targv_array_pushv(&args, argv);\n+\treturn !!push_stash(args.argc, args.argv, prefix);\n }\ndiff --git a/git-stash.sh b/git-stash.sh\ndeleted file mode 100755\nindex 695f1feba3..0000000000\n--- a/git-stash.sh\n+++ /dev/null\n@@ -1,153 +0,0 @@\n-#!/bin/sh\n-# Copyright (c) 2007, Nanako Shiraishi\n-\n-dashless=$(basename \"$0\" | sed -e 's/-/ /')\n-USAGE=\"list [<options>]\n-   or: $dashless show [<stash>]\n-   or: $dashless drop [-q|--quiet] [<stash>]\n-   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n-   or: $dashless branch <branchname> [<stash>]\n-   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n-\t\t      [-u|--include-untracked] [-a|--all] [<message>]\n-   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n-\t\t       [-u|--include-untracked] [-a|--all] [-m <message>]\n-\t\t       [-- <pathspec>...]]\n-   or: $dashless clear\"\n-\n-SUBDIRECTORY_OK=Yes\n-OPTIONS_SPEC=\n-START_DIR=$(pwd)\n-. git-sh-setup\n-require_work_tree\n-prefix=$(git rev-parse --show-prefix) || exit 1\n-cd_to_toplevel\n-\n-TMP=\"$GIT_DIR/.git-stash.$$\"\n-TMPindex=${GIT_INDEX_FILE-\"$(git rev-parse --git-path index)\"}.stash.$$\n-trap 'rm -f \"$TMP-\"* \"$TMPindex\"' 0\n-\n-ref_stash=refs/stash\n-\n-if git config --get-colorbool color.interactive; then\n-       help_color=\"$(git config --get-color color.interactive.help 'red bold')\"\n-       reset_color=\"$(git config --get-color '' reset)\"\n-else\n-       help_color=\n-       reset_color=\n-fi\n-\n-#\n-# Parses the remaining options looking for flags and\n-# at most one revision defaulting to ${ref_stash}@{0}\n-# if none found.\n-#\n-# Derives related tree and commit objects from the\n-# revision, if one is found.\n-#\n-# stash records the work tree, and is a merge between the\n-# base commit (first parent) and the index tree (second parent).\n-#\n-#   REV is set to the symbolic version of the specified stash-like commit\n-#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash\n-#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref\n-#   s is set to the SHA1 of the stash commit\n-#   w_commit is set to the commit containing the working tree\n-#   b_commit is set to the base commit\n-#   i_commit is set to the commit containing the index tree\n-#   u_commit is set to the commit containing the untracked files tree\n-#   w_tree is set to the working tree\n-#   b_tree is set to the base tree\n-#   i_tree is set to the index tree\n-#   u_tree is set to the untracked files tree\n-#\n-#   GIT_QUIET is set to t if -q is specified\n-#   INDEX_OPTION is set to --index if --index is specified.\n-#   FLAGS is set to the remaining flags (if allowed)\n-#\n-# dies if:\n-#   * too many revisions specified\n-#   * no revision is specified and there is no stash stack\n-#   * a revision is specified which cannot be resolve to a SHA1\n-#   * a non-existent stash reference is specified\n-#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n-#\n-\n-test \"$1\" = \"-p\" && set \"push\" \"$@\"\n-\n-PARSE_CACHE='--not-parsed'\n-# The default command is \"push\" if nothing but options are given\n-seen_non_option=\n-for opt\n-do\n-\tcase \"$opt\" in\n-\t--) break ;;\n-\t-*) ;;\n-\t*) seen_non_option=t; break ;;\n-\tesac\n-done\n-\n-test -n \"$seen_non_option\" || set \"push\" \"$@\"\n-\n-# Main command set\n-case \"$1\" in\n-list)\n-\tshift\n-\tgit stash--helper list \"$@\"\n-\t;;\n-show)\n-\tshift\n-\tgit stash--helper show \"$@\"\n-\t;;\n-save)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper save \"$@\"\n-\t;;\n-push)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper push \"$@\"\n-\t;;\n-apply)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper apply \"$@\"\n-\t;;\n-clear)\n-\tshift\n-\tgit stash--helper clear \"$@\"\n-\t;;\n-create)\n-\tshift\n-\tgit stash--helper create --message \"$*\"\n-\t;;\n-store)\n-\tshift\n-\tgit stash--helper store \"$@\"\n-\t;;\n-drop)\n-\tshift\n-\tgit stash--helper drop \"$@\"\n-\t;;\n-pop)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper pop \"$@\"\n-\t;;\n-branch)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper branch \"$@\"\n-\t;;\n-*)\n-\tcase $# in\n-\t0)\n-\t\tcd \"$START_DIR\"\n-\t\tgit stash--helper push &&\n-\t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n-\t\t;;\n-\t*)\n-\t\tusage\n-\tesac\n-\t;;\n-esac\ndiff --git a/git.c b/git.c\nindex de9c774573..8a20909eae 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,7 +554,7 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t{ \"stash--helper\", cmd_stash__helper, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"stash\", cmd_stash, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365697","messageId":"44eb8652aeea920705e76462b7f4b2333253a34f.1545331726.git.ungureanupaulsebastian@gmail.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"[PATCH v12 17/26] stash: convert create to builtin","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-12-20T19:44:33Z","receivedAt":"2018-12-20T19:45:34Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Add stash create to the helper.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/stash--helper.c | 453 +++++++++++++++++++++++++++++++++++++++-\n git-stash.sh            |   2 +-\n 2 files changed, 453 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 5dc6c068d7..080c2f7aa6 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,6 +12,9 @@\n #include \"rerere.h\"\n #include \"revision.h\"\n #include \"log-tree.h\"\n+#include \"diffcore.h\"\n+\n+#define INCLUDE_ALL_FILES 2\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper list [<options>]\"),\n@@ -63,6 +66,11 @@ static const char * const git_stash_helper_store_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_create_usage[] = {\n+\tN_(\"git stash--helper create [<message>]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -288,6 +296,24 @@ static int reset_head(void)\n \treturn run_command(&cp);\n }\n \n+static void add_diff_to_buf(struct diff_queue_struct *q,\n+\t\t\t    struct diff_options *options,\n+\t\t\t    void *data)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstrbuf_addstr(data, q->queue[i]->one->path);\n+\n+\t\t/*\n+\t\t * The reason we add \"0\" at the end of this strbuf\n+\t\t * is because we will pass the output further to\n+\t\t * \"git update-index -z ...\".\n+\t\t */\n+\t\tstrbuf_addch(data, '\\0');\n+\t}\n+}\n+\n static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n@@ -790,6 +816,429 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n \treturn do_store_stash(&obj, stash_msg, quiet);\n }\n \n+static void add_pathspecs(struct argv_array *args,\n+\t\t\t  struct pathspec ps) {\n+\tint i;\n+\n+\tfor (i = 0; i < ps.nr; i++)\n+\t\targv_array_push(args, ps.items[i].match);\n+}\n+\n+/*\n+ * `untracked_files` will be filled with the names of untracked files.\n+ * The return value is:\n+ *\n+ * = 0 if there are not any untracked files\n+ * > 0 if there are untracked files\n+ */\n+static int get_untracked_files(struct pathspec ps, int include_untracked,\n+\t\t\t       struct strbuf *untracked_files)\n+{\n+\tint i;\n+\tint max_len;\n+\tint found = 0;\n+\tchar *seen;\n+\tstruct dir_struct dir;\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tif (include_untracked != INCLUDE_ALL_FILES)\n+\t\tsetup_standard_excludes(&dir);\n+\n+\tseen = xcalloc(ps.nr, 1);\n+\n+\tmax_len = fill_directory(&dir, the_repository->index, &ps);\n+\tfor (i = 0; i < dir.nr; i++) {\n+\t\tstruct dir_entry *ent = dir.entries[i];\n+\t\tif (dir_path_match(&the_index, ent, &ps, max_len, seen)) {\n+\t\t\tfound++;\n+\t\t\tstrbuf_addstr(untracked_files, ent->name);\n+\t\t\t/* NUL-terminate: will be fed to update-index -z */\n+\t\t\tstrbuf_addch(untracked_files, 0);\n+\t\t}\n+\t\tfree(ent);\n+\t}\n+\n+\tfree(seen);\n+\tfree(dir.entries);\n+\tfree(dir.ignored);\n+\tclear_directory(&dir);\n+\treturn found;\n+}\n+\n+/*\n+ * The return value of `check_changes()` can be:\n+ *\n+ * < 0 if there was an error\n+ * = 0 if there are no changes.\n+ * > 0 if there are changes.\n+ */\n+static int check_changes(struct pathspec ps, int include_untracked)\n+{\n+\tint result;\n+\tstruct rev_info rev;\n+\tstruct object_id dummy;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\t/* No initial commit. */\n+\tif (get_oid(\"HEAD\", &dummy))\n+\t\treturn -1;\n+\n+\tif (read_cache() < 0)\n+\t\treturn -1;\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune_data = ps;\n+\n+\trev.diffopt.flags.quick = 1;\n+\trev.diffopt.flags.ignore_submodules = 1;\n+\trev.abbrev = 0;\n+\n+\tadd_head_to_pending(&rev);\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\tresult = run_diff_index(&rev, 1);\n+\tif (diff_result_code(&rev.diffopt, result))\n+\t\treturn 1;\n+\n+\tobject_array_clear(&rev.pending);\n+\tresult = run_diff_files(&rev, 0);\n+\tif (diff_result_code(&rev.diffopt, result))\n+\t\treturn 1;\n+\n+\tif (include_untracked && get_untracked_files(ps, include_untracked,\n+\t\t\t\t\t\t     &out)) {\n+\t\tstrbuf_release(&out);\n+\t\treturn 1;\n+\t}\n+\n+\tstrbuf_release(&out);\n+\treturn 0;\n+}\n+\n+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n+\t\t\t\tstruct strbuf files)\n+{\n+\tint ret = 0;\n+\tstruct strbuf untracked_msg = STRBUF_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\n+\tcp_upd_index.git_cmd = 1;\n+\targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n+\t\t\t \"--remove\", \"--stdin\", NULL);\n+\targv_array_pushf(&cp_upd_index.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tstrbuf_addf(&untracked_msg, \"untracked files on %s\\n\", msg->buf);\n+\tif (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,\n+\t\t\t NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\tget_oid_hex(out.buf, &info->u_tree);\n+\n+\tif (commit_tree(untracked_msg.buf, untracked_msg.len,\n+\t\t\t&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&untracked_msg);\n+\tstrbuf_release(&out);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int stash_patch(struct stash_info *info, struct pathspec ps,\n+\t\t       struct strbuf *out_patch)\n+{\n+\tint ret = 0;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n+\n+\tremove_path(stash_index_path.buf);\n+\n+\tcp_read_tree.git_cmd = 1;\n+\targv_array_pushl(&cp_read_tree.args, \"read-tree\", \"HEAD\", NULL);\n+\targv_array_pushf(&cp_read_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp_read_tree)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\t/* Find out what the user wants. */\n+\tcp_add_i.git_cmd = 1;\n+\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n+\t\t\t \"--\", NULL);\n+\tadd_pathspecs(&cp_add_i.args, ps);\n+\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp_add_i)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\t/* State of the working tree. */\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tget_oid_hex(out.buf, &info->w_tree);\n+\n+\tcp_diff_tree.git_cmd = 1;\n+\targv_array_pushl(&cp_diff_tree.args, \"diff-tree\", \"-p\", \"HEAD\",\n+\t\t\t oid_to_hex(&info->w_tree), \"--\", NULL);\n+\tif (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (!out_patch->len) {\n+\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n+\t\tret = 1;\n+\t}\n+\n+done:\n+\tstrbuf_release(&out);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n+{\n+\tint ret = 0;\n+\tstruct rev_info rev;\n+\tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct strbuf diff_output = STRBUF_INIT;\n+\n+\tset_alternate_index_output(stash_index_path.buf);\n+\tif (reset_tree(&info->i_tree, 0, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\tset_alternate_index_output(NULL);\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune_data = ps;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = add_diff_to_buf;\n+\trev.diffopt.format_callback_data = &diff_output;\n+\n+\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tadd_pending_object(&rev, parse_object(the_repository, &info->b_commit),\n+\t\t\t   \"\");\n+\tif (run_diff_index(&rev, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_upd_index.git_cmd = 1;\n+\targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n+\t\t\t \"--remove\", \"--stdin\", NULL);\n+\targv_array_pushf(&cp_upd_index.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tif (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,\n+\t\t\t NULL, 0, NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tget_oid_hex(out.buf, &info->w_tree);\n+\n+done:\n+\tUNLEAK(rev);\n+\tstrbuf_release(&out);\n+\tobject_array_clear(&rev.pending);\n+\tstrbuf_release(&diff_output);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n+\t\t\t   int include_untracked, int patch_mode,\n+\t\t\t   struct stash_info *info)\n+{\n+\tint ret = 0;\n+\tint flags = 0;\n+\tint untracked_commit_option = 0;\n+\tconst char *head_short_sha1 = NULL;\n+\tconst char *branch_ref = NULL;\n+\tconst char *branch_name = \"(no branch)\";\n+\tstruct commit *head_commit = NULL;\n+\tstruct commit_list *parents = NULL;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tstruct strbuf commit_tree_label = STRBUF_INIT;\n+\tstruct strbuf untracked_files = STRBUF_INIT;\n+\tstruct strbuf patch = STRBUF_INIT;\n+\n+\tprepare_fallback_ident(\"git stash\", \"git@stash\");\n+\n+\tread_cache_preload(NULL);\n+\trefresh_cache(REFRESH_QUIET);\n+\n+\tif (get_oid(\"HEAD\", &info->b_commit)) {\n+\t\tfprintf_ln(stderr, _(\"You do not have the initial commit yet\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t} else {\n+\t\thead_commit = lookup_commit(the_repository, &info->b_commit);\n+\t}\n+\n+\tif (!check_changes(ps, include_untracked)) {\n+\t\tret = 1;\n+\t\tgoto done;\n+\t}\n+\n+\tbranch_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n+\tif (flags & REF_ISSYMREF)\n+\t\tbranch_name = strrchr(branch_ref, '/') + 1;\n+\thead_short_sha1 = find_unique_abbrev(&head_commit->object.oid,\n+\t\t\t\t\t     DEFAULT_ABBREV);\n+\tstrbuf_addf(&msg, \"%s: %s \", branch_name, head_short_sha1);\n+\tpp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);\n+\n+\tstrbuf_addf(&commit_tree_label, \"index on %s\\n\", msg.buf);\n+\tcommit_list_insert(head_commit, &parents);\n+\tif (write_cache_as_tree(&info->i_tree, 0, NULL) ||\n+\t    commit_tree(commit_tree_label.buf, commit_tree_label.len,\n+\t\t\t&info->i_tree, parents, &info->i_commit, NULL, NULL)) {\n+\t\tfprintf_ln(stderr, _(\"Cannot save the current index state\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (include_untracked && get_untracked_files(ps, include_untracked,\n+\t\t\t\t\t\t     &untracked_files)) {\n+\t\tif (save_untracked_files(info, &msg, untracked_files)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n+\t\t\t\t\t     \"the untracked files\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t\tuntracked_commit_option = 1;\n+\t}\n+\tif (patch_mode) {\n+\t\tret = stash_patch(info, ps, &patch);\n+\t\tif (ret < 0) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"worktree state\"));\n+\t\t\tgoto done;\n+\t\t} else if (ret > 0) {\n+\t\t\tgoto done;\n+\t\t}\n+\t} else {\n+\t\tif (stash_working_tree(info, ps)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"worktree state\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tif (!stash_msg_buf->len)\n+\t\tstrbuf_addf(stash_msg_buf, \"WIP on %s\", msg.buf);\n+\telse\n+\t\tstrbuf_insertf(stash_msg_buf, 0, \"On %s: \", branch_name);\n+\n+\t/*\n+\t * `parents` will be empty after calling `commit_tree()`, so there is\n+\t * no need to call `free_commit_list()`\n+\t */\n+\tparents = NULL;\n+\tif (untracked_commit_option)\n+\t\tcommit_list_insert(lookup_commit(the_repository,\n+\t\t\t\t\t\t &info->u_commit),\n+\t\t\t\t   &parents);\n+\tcommit_list_insert(lookup_commit(the_repository, &info->i_commit),\n+\t\t\t   &parents);\n+\tcommit_list_insert(head_commit, &parents);\n+\n+\tif (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,\n+\t\t\tparents, &info->w_commit, NULL, NULL)) {\n+\t\tfprintf_ln(stderr, _(\"Cannot record working tree state\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&commit_tree_label);\n+\tstrbuf_release(&msg);\n+\tstrbuf_release(&untracked_files);\n+\treturn ret;\n+}\n+\n+static int create_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint include_untracked = 0;\n+\tint ret = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct stash_info info;\n+\tstruct pathspec ps;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n+\t\t\t N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_create_usage,\n+\t\t\t     0);\n+\n+\tmemset(&ps, 0, sizeof(ps));\n+\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n+\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n+\n+\tif (!ret)\n+\t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n+\n+\tstrbuf_release(&stash_msg_buf);\n+\n+\t/*\n+\t * ret can be 1 if there were no changes. In this case, we should\n+\t * not error out.\n+\t */\n+\treturn ret < 0;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -799,7 +1248,7 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_diff_basic_config, NULL);\n \n \targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n@@ -826,6 +1275,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!show_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"store\"))\n \t\treturn !!store_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"create\"))\n+\t\treturn !!create_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex ff5556ccb0..a9b3064ff0 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -442,7 +442,7 @@ clear)\n \t;;\n create)\n \tshift\n-\tcreate_stash -m \"$*\" && echo \"$w_commit\"\n+\tgit stash--helper create --message \"$*\"\n \t;;\n store)\n \tshift\n-- \n2.20.1.441.g764a526393\n\n"},{"id":"365816","messageId":"xmqqpntoq8zs.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"57a654887e652251ae966ec31b4604dc8222f9c6.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"Re: [PATCH v12 04/26] ident: add the ability to provide a \"fallback identity\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-26T21:21:59Z","receivedAt":"2018-12-26T21:22:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n\n> +static void set_env_if(const char *key, const char *value, int *given, int bit)\n> +{\n> +\tif ((*given & bit) || getenv(key))\n> +\t\treturn; /* nothing to do */\n> +\tsetenv(key, value, 0);\n> +\t*given |= bit;\n> +}\n\nWe call setenv(3) with overwrite=0 but we protect the call with a\ncheck for existing value with getenv(3), which feels a bit like an\nanti-pattern.  Wouldn't the following be simpler to follow, I wonder?\n\n\tif (!(*given & bit)) {\n\t\tsetenv(key, value, 1);\n\t\t*given |= bit;\n\t}\n\nThe only case these two may behave differently is when '*given' does\nnot have the 'bit' set but the environment 'key' already exists.\nThe proposed patch will leave 'bit' in '*given' unset, so when a\nlater code says \"let's see if author_ident is explicitly given, and\ncomplain otherwise\", such a check will trigger and cause complaint.\n\nOn the other hand, the simplified version does not allow the\n\"explicitly-given\" bits to be left unset, so it won't cause\ncomplaint.\n\nIsn't it a BUG() if *given lacks 'bit' when the corresponding\nenvironment variable 'key' is missing?  IOW, I would understand\nan implementation that is more elaborate than the simplified one I\njust gave above were something like\n\n\tif (!(*given & bit)) {\n\t\tif (getenv(key))\n\t\t\tBUG(\"why does %s exist and no %x bit set???\", key, bit);\n\t\tsetenv(key, value, 0);\n\t\t*given |= bit;\n\t}\n\nbut I do not quite understand the reasoning behind the \"check either\nthe bit, or the environment variable\" in the proposed patch.\n\n> +void prepare_fallback_ident(const char *name, const char *email)\n> +{\n> +\tset_env_if(\"GIT_AUTHOR_NAME\", name,\n> +\t\t   &author_ident_explicitly_given, IDENT_NAME_GIVEN);\n> +\tset_env_if(\"GIT_AUTHOR_EMAIL\", email,\n> +\t\t   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);\n> +\tset_env_if(\"GIT_COMMITTER_NAME\", name,\n> +\t\t   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);\n> +\tset_env_if(\"GIT_COMMITTER_EMAIL\", email,\n> +\t\t   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);\n> +}\n\nIntroducing this function alone without a caller and without\nfunction doc is a bit unfriendly to future callers, who must be\ncareful when to call it, I think.  For example, they must know that\nit will be a disaster if they call this before they call\ngit_ident_config(), right?\n\n> +\n>  static int buf_cmp(const char *a_begin, const char *a_end,\n>  \t\t   const char *b_begin, const char *b_end)\n>  {\n\n\n"},{"id":"365886","messageId":"nycvar.QRO.7.76.6.1812272221380.45@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"xmqqpntoq8zs.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v12 04/26] ident: add the ability to provide a \"fallback identity\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-12-27T21:24:51Z","receivedAt":"2018-12-27T21:25:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 26 Dec 2018, Junio C Hamano wrote:\n\n> Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n> \n> > +static void set_env_if(const char *key, const char *value, int *given, int bit)\n> > +{\n> > +\tif ((*given & bit) || getenv(key))\n> > +\t\treturn; /* nothing to do */\n> > +\tsetenv(key, value, 0);\n> > +\t*given |= bit;\n> > +}\n> \n> We call setenv(3) with overwrite=0 but we protect the call with a\n> check for existing value with getenv(3), which feels a bit like an\n> anti-pattern.  Wouldn't the following be simpler to follow, I wonder?\n> \n> \tif (!(*given & bit)) {\n> \t\tsetenv(key, value, 1);\n> \t\t*given |= bit;\n> \t}\n> \n> The only case these two may behave differently is when '*given' does\n> not have the 'bit' set but the environment 'key' already exists.\n\nIndeed, this is the case where your version would actually do the wrong\nthing. Imagine that GIT_AUTHOR_NAME is set already. Your code would\n*override* it. But that is not what we want to do here. We want to *fall\nback* if there is no already-configured value.\n\nAnd of course we won't set the `given` bit if we don't fall back here;\nthat should be done somewhere else, where that environment variable (that\nwe *refuse* to overwrite) is *actually* used.\n\nCiao,\nDscho\n\n> The proposed patch will leave 'bit' in '*given' unset, so when a\n> later code says \"let's see if author_ident is explicitly given, and\n> complain otherwise\", such a check will trigger and cause complaint.\n> \n> On the other hand, the simplified version does not allow the\n> \"explicitly-given\" bits to be left unset, so it won't cause\n> complaint.\n> \n> Isn't it a BUG() if *given lacks 'bit' when the corresponding\n> environment variable 'key' is missing?  IOW, I would understand\n> an implementation that is more elaborate than the simplified one I\n> just gave above were something like\n> \n> \tif (!(*given & bit)) {\n> \t\tif (getenv(key))\n> \t\t\tBUG(\"why does %s exist and no %x bit set???\", key, bit);\n> \t\tsetenv(key, value, 0);\n> \t\t*given |= bit;\n> \t}\n> \n> but I do not quite understand the reasoning behind the \"check either\n> the bit, or the environment variable\" in the proposed patch.\n> \n> > +void prepare_fallback_ident(const char *name, const char *email)\n> > +{\n> > +\tset_env_if(\"GIT_AUTHOR_NAME\", name,\n> > +\t\t   &author_ident_explicitly_given, IDENT_NAME_GIVEN);\n> > +\tset_env_if(\"GIT_AUTHOR_EMAIL\", email,\n> > +\t\t   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);\n> > +\tset_env_if(\"GIT_COMMITTER_NAME\", name,\n> > +\t\t   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);\n> > +\tset_env_if(\"GIT_COMMITTER_EMAIL\", email,\n> > +\t\t   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);\n> > +}\n> \n> Introducing this function alone without a caller and without\n> function doc is a bit unfriendly to future callers, who must be\n> careful when to call it, I think.  For example, they must know that\n> it will be a disaster if they call this before they call\n> git_ident_config(), right?\n> \n> > +\n> >  static int buf_cmp(const char *a_begin, const char *a_end,\n> >  \t\t   const char *b_begin, const char *b_end)\n> >  {\n> \n> \n> \n"},{"id":"365927","messageId":"xmqq4laxmmti.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1812272221380.45@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v12 04/26] ident: add the ability to provide a \"fallback identity\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-28T19:40:05Z","receivedAt":"2018-12-28T20:13:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Wed, 26 Dec 2018, Junio C Hamano wrote:\n>\n>> Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n>> \n>> > +static void set_env_if(const char *key, const char *value, int *given, int bit)\n>> > +{\n>> > +\tif ((*given & bit) || getenv(key))\n>> > +\t\treturn; /* nothing to do */\n>> > +\tsetenv(key, value, 0);\n>> > +\t*given |= bit;\n>> > +}\n>> \n>> We call setenv(3) with overwrite=0 but we protect the call with a\n>> check for existing value with getenv(3), which feels a bit like an\n>> anti-pattern.  Wouldn't the following be simpler to follow, I wonder?\n>> \n>> \tif (!(*given & bit)) {\n>> \t\tsetenv(key, value, 1);\n>> \t\t*given |= bit;\n>> \t}\n>> \n>> The only case these two may behave differently is when '*given' does\n>> not have the 'bit' set but the environment 'key' already exists.\n>\n> Indeed, this is the case where your version would actually do the wrong\n> thing. Imagine that GIT_AUTHOR_NAME is set already. Your code would\n> *override* it. But that is not what we want to do here. We want to *fall\n> back* if there is no already-configured value.\n>\n> And of course we won't set the `given` bit if we don't fall back here;\n> that should be done somewhere else, where that environment variable (that\n> we *refuse* to overwrite) is *actually* used.\n\nOK, so the designed calling sequence of the new \"prepare fallback\nident\" is that any process that wants to use fallback ident must\ncall the \"prepare\" function _before_ making a call to any other\nfunctions (IOW, it is a BUG() if things like git_committer_info() is\ncalled before prepare_fallback_ident() gets called).  Under that\ncondition, you are absolutely right that the two implementation\nbehaves differently.\n\nThat indeed indicates that this is unfriendly to future callers,\nwhich was the main issue I had with the patch.  A comment before the\nprepare_fallback_ident() function to explain that would have helped.\n\nAlso the first condition checking bit in *given does not help---it\nis quite misleading.  It would have helped if it were\n\n\tstatic void set_env_if( ... )\n\t{\n\t\tif (*given & bit)\n\t\t\tBUG(\"%s was checked before prepare_fallback got called\", key);\n\t\tif (getenv(key))\n\t\t\treturn; /* nothing to do */\n \t\tsetenv(key, value, 1);\n\t\t*given |= bit;\n\t}\n\nBecause (author|committer)_ident_explicitly_given would never say\n\"yes, already\" if the setting of fallback MUST be done before using\nother API functions in ident.c, it we were to have a check for that\ncondition, it would be testing for a BUG().  And I wouldn't have\nbeen confused by the code while reviewing.\n\n>> > +void prepare_fallback_ident(const char *name, const char *email)\n>> > +{\n>> > +\tset_env_if(\"GIT_AUTHOR_NAME\", name,\n>> > +\t\t   &author_ident_explicitly_given, IDENT_NAME_GIVEN);\n>> > +\tset_env_if(\"GIT_AUTHOR_EMAIL\", email,\n>> > +\t\t   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);\n>> > +\tset_env_if(\"GIT_COMMITTER_NAME\", name,\n>> > +\t\t   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);\n>> > +\tset_env_if(\"GIT_COMMITTER_EMAIL\", email,\n>> > +\t\t   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);\n>> > +}\n>> \n>> Introducing this function alone without a caller and without\n>> function doc is a bit unfriendly to future callers, who must be\n>> careful when to call it, I think.  For example, they must know that\n>> it will be a disaster if they call this before they call\n>> git_ident_config(), right?\n\nAs you can see from this \"For example,...\", the review comment (and\nthe \"simplified but does the wrong thing\" version) was written under\nan assumption different from the expected calling sequence the\nposted version makes.  What future writers of callers that use the\nfallback ident feature must know is (unlike the above) that they\nmust call the \"prepare\" before they call git_ident_config() or\nanything in ident.c.  A comment before this function that explain\nhow and when in the program flow this is designed to be used is\nneeded.\n\nThanks for a clarification.\n\n"},{"id":"366122","messageId":"xmqqa7khfh0c.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"cover.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"Re: [PATCH v12 00/26] Convert \"git stash\" to C builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-03T23:39:15Z","receivedAt":"2019-01-03T23:39:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n\n> This is a new iteration of git-stash which also takes\n> sd/stash-wo-user-name into account. I cherry-picked\n> some of dscho's commits (from [1]) to keep the scripted\n> version of `git stash` as `git-legacy-stash`.\n\nI took a brief look and left a comment on 04/26 last year.  I had\nsome time blocked for this topic today to take another look at the\nwhole series again.  Thanks for working on this.\n\nIt seems that the last three or so steps are new, relative to the\nprevious round.  I made sure that what is added back at step 24\nexactly matches the result of merging sd/stash-wo-user-name into the\ncurrent 'master', but such a manual validation is error prone.  Is\nit possible to avoid \"remove the scripted one prematurely at step\n23, and then add it back as 'oops, that was wrong' fix at step 24\"?\nThat would have been much more robust approach.\n\nThanks.\n"},{"id":"366225","messageId":"20190106224735.GI25639@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"17f45e4bb48bdf9588d87c7b134afd703dbd69e6.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"Re: [PATCH v12 21/26] stash: optimize `get_untracked_files()` and `check_changes()`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-01-06T22:47:35Z","receivedAt":"2019-01-06T22:47:41Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 12/20, Paul-Sebastian Ungureanu wrote:\n> This commits introduces a optimization by avoiding calling the\n> same functions again. For example, `git stash push -u`\n> would call at some points the following functions:\n> \n>  * `check_changes()` (inside `do_push_stash()`)\n>  * `do_create_stash()`, which calls: `check_changes()` and\n> `get_untracked_files()`\n> \n> Note that `check_changes()` also calls `get_untracked_files()`.\n> So, `check_changes()` is called 2 times and `get_untracked_files()`\n> 3 times.\n> \n> The old function `check_changes()` now consists of two functions:\n> `get_untracked_files()` and `check_changes_tracked_files()`.\n> \n> These are the call chains for `push` and `create`:\n> \n>  * `push_stash()` -> `do_push_stash()` -> `do_create_stash()`\n> \n>  * `create_stash()` -> `do_create_stash()`\n> \n> To prevent calling the same functions over and over again,\n> `check_changes()` inside `do_create_stash()` is now placed\n> in the caller functions (`create_stash()` and `do_push_stash()`).\n> This way `check_changes()` and `get_untracked files()` are called\n> only one time.\n> \n> https://public-inbox.org/git/20180818223329.GJ11326@hank.intra.tgummerer.com/\n\nI missed this the other time, but having this link in the commit\nmessage is unnecessary, and it may go stale (though in this case it\nincludes the Message-ID, so it is still useful as long as some archive\nexists.  The commit message explains well enough what this change is\ndoing, even without the link to the review.\n\nSorry I missed these things in the earlier rounds somehow.\n\n> Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> ---\n>  builtin/stash--helper.c | 53 +++++++++++++++++++++++------------------\n>  1 file changed, 30 insertions(+), 23 deletions(-)\n> \n> diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> index 19ead63c46..4b63352927 100644\n> --- a/builtin/stash--helper.c\n> +++ b/builtin/stash--helper.c\n> @@ -884,18 +884,18 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n>  }\n>  \n>  /*\n> - * The return value of `check_changes()` can be:\n> + * The return value of `check_changes_tracked_files()` can be:\n>   *\n>   * < 0 if there was an error\n>   * = 0 if there are no changes.\n>   * > 0 if there are changes.\n>   */\n> -static int check_changes(struct pathspec ps, int include_untracked)\n> +\n\nUnnecessary blank line after the comment here.\n\n> +static int check_changes_tracked_files(struct pathspec ps)\n>  {\n>  \tint result;\n>  \tstruct rev_info rev;\n>  \tstruct object_id dummy;\n> -\tstruct strbuf out = STRBUF_INIT;\n>  \n>  \t/* No initial commit. */\n>  \tif (get_oid(\"HEAD\", &dummy))\n> @@ -923,14 +923,26 @@ static int check_changes(struct pathspec ps, int include_untracked)\n>  \tif (diff_result_code(&rev.diffopt, result))\n>  \t\treturn 1;\n>  \n> +\treturn 0;\n> +}\n> +\n> +/*\n> + * The function will fill `untracked_files` with the names of untracked files\n> + * It will return 1 if there were any changes and 0 if there were not.\n> + */\n> +\n\nUnnecessary blank line.\n\n> +static int check_changes(struct pathspec ps, int include_untracked,\n> +\t\t\t struct strbuf *untracked_files)\n> +{\n> +\tint ret = 0;\n> +\tif (check_changes_tracked_files(ps))\n> +\t\tret = 1;\n> +\n>  \tif (include_untracked && get_untracked_files(ps, include_untracked,\n> -\t\t\t\t\t\t     &out)) {\n> -\t\tstrbuf_release(&out);\n> -\t\treturn 1;\n> -\t}\n> +\t\t\t\t\t\t     untracked_files))\n> +\t\tret = 1;\n>  \n> -\tstrbuf_release(&out);\n> -\treturn 0;\n> +\treturn ret;\n>  }\n>  \n>  static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n> @@ -1141,7 +1153,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n>  \t\thead_commit = lookup_commit(the_repository, &info->b_commit);\n>  \t}\n>  \n> -\tif (!check_changes(ps, include_untracked)) {\n> +\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n>  \t\tret = 1;\n>  \t\tgoto done;\n>  \t}\n> @@ -1166,8 +1178,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n>  \t\tgoto done;\n>  \t}\n>  \n> -\tif (include_untracked && get_untracked_files(ps, include_untracked,\n> -\t\t\t\t\t\t     &untracked_files)) {\n> +\tif (include_untracked) {\n>  \t\tif (save_untracked_files(info, &msg, untracked_files)) {\n>  \t\t\tif (!quiet)\n>  \t\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n> @@ -1252,20 +1263,15 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n>  \t\t\t     0);\n>  \n>  \tmemset(&ps, 0, sizeof(ps));\n> -\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n> -\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n> -\t\t\t      NULL, 0);\n> +\tif (!check_changes_tracked_files(ps))\n> +\t\treturn 0;\n>  \n> -\tif (!ret)\n> +\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n> +\tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n\nIt's a bit odd to have do_create_stash moved inside the if here, even\nthough it was introduced in this patch series.  It makes the patch a\nbit more noisy and harder to see what was changed here.\n\n>  \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n>  \n>  \tstrbuf_release(&stash_msg_buf);\n> -\n> -\t/*\n> -\t * ret can be 1 if there were no changes. In this case, we should\n> -\t * not error out.\n> -\t */\n> -\treturn ret < 0;\n> +\treturn ret;\n>  }\n>  \n>  static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n> @@ -1275,6 +1281,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n>  \tstruct stash_info info;\n>  \tstruct strbuf patch = STRBUF_INIT;\n>  \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n> +\tstruct strbuf untracked_files = STRBUF_INIT;\n\nDoes this strbuf also need to be released at the end of the function?\n\n>  \n>  \tif (patch_mode && keep_index == -1)\n>  \t\tkeep_index = 1;\n> @@ -1309,7 +1316,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n>  \t\tgoto done;\n>  \t}\n>  \n> -\tif (!check_changes(ps, include_untracked)) {\n> +\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n>  \t\tif (!quiet)\n>  \t\t\tprintf_ln(_(\"No local changes to save\"));\n>  \t\tgoto done;\n> -- \n> 2.20.1.441.g764a526393\n> \n"},{"id":"366229","messageId":"20190106225938.GJ25639@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"c05bb05e6eb40af1921e02711ff96350c8165cf2.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"Re: [PATCH v12 25/26] stash: optionally use the scripted version again","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-01-06T22:59:38Z","receivedAt":"2019-01-06T22:59:43Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 12/20, Paul-Sebastian Ungureanu wrote:\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> We recently converted the `git stash` command from Unix shell scripts\n> to builtins.\n> \n> Let's end users a way out when they discover a bug in the\n\ns/Let's/& give/ maybe?\n\nThe rest of the patch looks good to me.\n\n> builtin command: `stash.useBuiltin`.\n> \n> As the file name `git-stash` is already in use, let's rename the\n> scripted backend to `git-legacy-stash`.\n> \n> To make the test suite pass with `stash.useBuiltin=false`, this commit\n> also backports rudimentary support for `-q` (but only *just* enough\n> to appease the test suite), and adds a super-ugly hack to force exit\n> code 129 for `git stash -h`.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  .gitignore                          |  1 +\n>  Makefile                            |  1 +\n>  builtin/stash.c                     | 35 +++++++++++++++++++++++++++++\n>  git-stash.sh => git-legacy-stash.sh | 34 +++++++++++++++++++++++++---\n>  git-sh-setup.sh                     |  1 +\n>  git.c                               |  7 +++++-\n>  6 files changed, 75 insertions(+), 4 deletions(-)\n>  rename git-stash.sh => git-legacy-stash.sh (97%)\n> \n> diff --git a/.gitignore b/.gitignore\n> index 0d77ea5894..7b0164675e 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -82,6 +82,7 @@\n>  /git-interpret-trailers\n>  /git-instaweb\n>  /git-legacy-rebase\n> +/git-legacy-stash\n>  /git-log\n>  /git-ls-files\n>  /git-ls-remote\n> diff --git a/Makefile b/Makefile\n> index 8cee2731aa..810231a0b5 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -617,6 +617,7 @@ SCRIPT_SH += git-merge-resolve.sh\n>  SCRIPT_SH += git-mergetool.sh\n>  SCRIPT_SH += git-quiltimport.sh\n>  SCRIPT_SH += git-legacy-rebase.sh\n> +SCRIPT_SH += git-legacy-stash.sh\n>  SCRIPT_SH += git-remote-testgit.sh\n>  SCRIPT_SH += git-request-pull.sh\n>  SCRIPT_SH += git-submodule.sh\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index fe32ff42fd..346c9d2bb1 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -13,6 +13,7 @@\n>  #include \"revision.h\"\n>  #include \"log-tree.h\"\n>  #include \"diffcore.h\"\n> +#include \"exec-cmd.h\"\n>  \n>  #define INCLUDE_ALL_FILES 2\n>  \n> @@ -1513,6 +1514,26 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n>  \treturn ret;\n>  }\n>  \n> +static int use_builtin_stash(void)\n> +{\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tstruct strbuf out = STRBUF_INIT;\n> +\tint ret;\n> +\n> +\targv_array_pushl(&cp.args,\n> +\t\t\t \"config\", \"--bool\", \"stash.usebuiltin\", NULL);\n> +\tcp.git_cmd = 1;\n> +\tif (capture_command(&cp, &out, 6)) {\n> +\t\tstrbuf_release(&out);\n> +\t\treturn 1;\n> +\t}\n> +\n> +\tstrbuf_trim(&out);\n> +\tret = !strcmp(\"true\", out.buf);\n> +\tstrbuf_release(&out);\n> +\treturn ret;\n> +}\n> +\n>  int cmd_stash(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint i = -1;\n> @@ -1524,6 +1545,20 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n>  \t\tOPT_END()\n>  \t};\n>  \n> +\tif (!use_builtin_stash()) {\n> +\t\tconst char *path = mkpath(\"%s/git-legacy-stash\",\n> +\t\t\t\t\t  git_exec_path());\n> +\n> +\t\tif (sane_execvp(path, (char **)argv) < 0)\n> +\t\t\tdie_errno(_(\"could not exec %s\"), path);\n> +\t\telse\n> +\t\t\tBUG(\"sane_execvp() returned???\");\n> +\t}\n> +\n> +\tprefix = setup_git_directory();\n> +\ttrace_repo_setup(prefix);\n> +\tsetup_work_tree();\n> +\n>  \tgit_config(git_diff_basic_config, NULL);\n>  \n>  \targc = parse_options(argc, argv, prefix, options, git_stash_usage,\n> diff --git a/git-stash.sh b/git-legacy-stash.sh\n> similarity index 97%\n> rename from git-stash.sh\n> rename to git-legacy-stash.sh\n> index 789ce2f41d..8a8c4a9270 100755\n> --- a/git-stash.sh\n> +++ b/git-legacy-stash.sh\n> @@ -80,6 +80,28 @@ clear_stash () {\n>  \tfi\n>  }\n>  \n> +maybe_quiet () {\n> +\tcase \"$1\" in\n> +\t--keep-stdout)\n> +\t\tshift\n> +\t\tif test -n \"$GIT_QUIET\"\n> +\t\tthen\n> +\t\t\teval \"$@\" 2>/dev/null\n> +\t\telse\n> +\t\t\teval \"$@\"\n> +\t\tfi\n> +\t\t;;\n> +\t*)\n> +\t\tif test -n \"$GIT_QUIET\"\n> +\t\tthen\n> +\t\t\teval \"$@\" >/dev/null 2>&1\n> +\t\telse\n> +\t\t\teval \"$@\"\n> +\t\tfi\n> +\t\t;;\n> +\tesac\n> +}\n> +\n>  create_stash () {\n>  \n>  \tprepare_fallback_ident\n> @@ -112,15 +134,18 @@ create_stash () {\n>  \tdone\n>  \n>  \tgit update-index -q --refresh\n> -\tif no_changes \"$@\"\n> +\tif maybe_quiet no_changes \"$@\"\n>  \tthen\n>  \t\texit 0\n>  \tfi\n>  \n>  \t# state of the base commit\n> -\tif b_commit=$(git rev-parse --verify HEAD)\n> +\tif b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)\n>  \tthen\n>  \t\thead=$(git rev-list --oneline -n 1 HEAD --)\n> +\telif test -n \"$GIT_QUIET\"\n> +\tthen\n> +\t\texit 1\n>  \telse\n>  \t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n>  \tfi\n> @@ -315,7 +340,7 @@ push_stash () {\n>  \ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n>  \n>  \tgit update-index -q --refresh\n> -\tif no_changes \"$@\"\n> +\tif maybe_quiet no_changes \"$@\"\n>  \tthen\n>  \t\tsay \"$(gettext \"No local changes to save\")\"\n>  \t\texit 0\n> @@ -370,6 +395,9 @@ save_stash () {\n>  \twhile test $# != 0\n>  \tdo\n>  \t\tcase \"$1\" in\n> +\t\t-q|--quiet)\n> +\t\t\tGIT_QUIET=t\n> +\t\t\t;;\n>  \t\t--)\n>  \t\t\tshift\n>  \t\t\tbreak\n> diff --git a/git-sh-setup.sh b/git-sh-setup.sh\n> index 378928518b..10d9764185 100644\n> --- a/git-sh-setup.sh\n> +++ b/git-sh-setup.sh\n> @@ -101,6 +101,7 @@ $LONG_USAGE\")\"\n>  \tcase \"$1\" in\n>  \t\t-h)\n>  \t\techo \"$LONG_USAGE\"\n> +\t\tcase \"$0\" in *git-legacy-stash) exit 129;; esac\n>  \t\texit\n>  \tesac\n>  fi\n> diff --git a/git.c b/git.c\n> index 8a20909eae..591ebe9409 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -554,7 +554,12 @@ static struct cmd_struct commands[] = {\n>  \t{ \"show-index\", cmd_show_index },\n>  \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n>  \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n> -\t{ \"stash\", cmd_stash, RUN_SETUP | NEED_WORK_TREE },\n> +\t/*\n> +\t * NEEDSWORK: Until the builtin stash is thoroughly robust and no\n> +\t * longer needs redirection to the stash shell script this is kept as\n> +\t * is, then should be changed to RUN_SETUP | NEED_WORK_TREE\n> +\t */\n> +\t{ \"stash\", cmd_stash },\n>  \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"stripspace\", cmd_stripspace },\n>  \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n> -- \n> 2.20.1.441.g764a526393\n> \n"},{"id":"367085","messageId":"nycvar.QRO.7.76.6.1901181305460.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"xmqqa7khfh0c.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v12 00/26] Convert \"git stash\" to C builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-01-18T12:06:40Z","receivedAt":"2019-01-18T12:07:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 3 Jan 2019, Junio C Hamano wrote:\n\n> Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n> \n> > This is a new iteration of git-stash which also takes\n> > sd/stash-wo-user-name into account. I cherry-picked\n> > some of dscho's commits (from [1]) to keep the scripted\n> > version of `git stash` as `git-legacy-stash`.\n> \n> I took a brief look and left a comment on 04/26 last year.  I had\n> some time blocked for this topic today to take another look at the\n> whole series again.  Thanks for working on this.\n> \n> It seems that the last three or so steps are new, relative to the\n> previous round.  I made sure that what is added back at step 24\n> exactly matches the result of merging sd/stash-wo-user-name into the\n> current 'master', but such a manual validation is error prone.  Is\n> it possible to avoid \"remove the scripted one prematurely at step\n> 23, and then add it back as 'oops, that was wrong' fix at step 24\"?\n> That would have been much more robust approach.\n\nSorry, I should have thought of that. My mistake.\n\nAs it is, Thomas verified that they are identical, so should we go forward\nwith ps/stash-in-c as-is? I'd prefer that...\n\nCiao,\nDscho\n"},{"id":"367118","messageId":"xmqqr2d9lutq.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1901181305460.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v12 00/26] Convert \"git stash\" to C builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-18T17:49:37Z","receivedAt":"2019-01-18T17:49:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Thu, 3 Jan 2019, Junio C Hamano wrote:\n>\n>> Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n>> \n>> > This is a new iteration of git-stash which also takes\n>> > sd/stash-wo-user-name into account. I cherry-picked\n>> > some of dscho's commits (from [1]) to keep the scripted\n>> > version of `git stash` as `git-legacy-stash`.\n>> \n>> I took a brief look and left a comment on 04/26 last year.  I had\n>> some time blocked for this topic today to take another look at the\n>> whole series again.  Thanks for working on this.\n>> \n>> It seems that the last three or so steps are new, relative to the\n>> previous round.  I made sure that what is added back at step 24\n>> exactly matches the result of merging sd/stash-wo-user-name into the\n>> current 'master', but such a manual validation is error prone.  Is\n>> it possible to avoid \"remove the scripted one prematurely at step\n>> 23, and then add it back as 'oops, that was wrong' fix at step 24\"?\n>> That would have been much more robust approach.\n>\n> Sorry, I should have thought of that. My mistake.\n>\n> As it is, Thomas verified that they are identical, so should we go forward\n> with ps/stash-in-c as-is? I'd prefer that...\n\nYes, before sending the message you are responding to, I made sure\nthe scripted version added back is identical to the current one, and\nalso there is no in-flight updates/fixes to the scripted one.\n\nThe benefit that would come from a possible reroll to start the\nseries from the last three patches would be fairly limited.\n\nSuch a reorganized series would have allowed investigation of\nregressions and bugs during the development comparing the original\nand rewritten implementations slightly easier, but experience from\nseeing the evolution of these \"reimplement in C\" topics tells us\nthat we see major part of the regression fallouts after the series\nis declared \"feature complete\" anyway, so in the long run, the\nless-than-ideal organization of the topic does not matter much in\npractice.\n"},{"id":"368827","messageId":"20190208113059.GV10587@szeder.dev","threadId":"50081","inReplyTo":"a6692eef2ca7c9d9e4701f087269d537248a4941.1545331726.git.ungureanupaulsebastian@gmail.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-02-08T11:30:59Z","receivedAt":"2019-02-08T11:31:07Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Thu, Dec 20, 2018 at 09:44:34PM +0200, Paul-Sebastian Ungureanu wrote:\n> Add stash push to the helper.\n> \n> Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nThis patch causes rare failures in 't3903-stash.sh', I've seen it\nbreak for the first time today in a Travis CI build:\n\n  +echo bar3\n  +echo bar4\n  +git add file2\n  +git stash -k\n  Saved working directory and index state WIP on stashbranch: d3a23d9 alternate second\n  +cat file\n  +cat file2\n  +test bar,bar4 = bar,bar2\n  error: last command exited with $?=1\n  not ok 20 - stash -k\n\nSteps to reproduce:\n\n  $ git checkout -f fa38428f76\n  HEAD is now at fa38428f76 stash: convert push to builtin\n  \n  # fb7d1e3ac8 (test-lib: add the '--stress' option to run a test\n  # repeatedly under load, 2019-01-05)\n  $ git merge --no-commit fb7d1e3ac8\n  Automatic merge went well; stopped before committing as requested\n  $ make && cd t\n  <snip>\n  $ ./t3903-stash.sh --stress -r 1,13,14,20\n  # wait, it tends to fail in <30 repetitions\n\nI run stress testing on its parent for over 800 repetitions, no sign\nof failure yet.\n\n> ---\n>  builtin/stash--helper.c | 245 +++++++++++++++++++++++++++++++++++++++-\n>  git-stash.sh            |   6 +-\n>  2 files changed, 245 insertions(+), 6 deletions(-)\n> \n> diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> index 080c2f7aa6..c77f62c895 100644\n> --- a/builtin/stash--helper.c\n> +++ b/builtin/stash--helper.c\n> @@ -23,6 +23,9 @@ static const char * const git_stash_helper_usage[] = {\n>  \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n>  \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n>  \tN_(\"git stash--helper clear\"),\n> +\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n> +\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n> +\t   \"          [--] [<pathspec>...]]\"),\n>  \tNULL\n>  };\n>  \n> @@ -71,6 +74,13 @@ static const char * const git_stash_helper_create_usage[] = {\n>  \tNULL\n>  };\n>  \n> +static const char * const git_stash_helper_push_usage[] = {\n> +\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n> +\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n> +\t   \"          [--] [<pathspec>...]]\"),\n> +\tNULL\n> +};\n> +\n>  static const char *ref_stash = \"refs/stash\";\n>  static struct strbuf stash_index_path = STRBUF_INIT;\n>  \n> @@ -1092,7 +1102,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n>  \n>  static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n>  \t\t\t   int include_untracked, int patch_mode,\n> -\t\t\t   struct stash_info *info)\n> +\t\t\t   struct stash_info *info, struct strbuf *patch)\n>  {\n>  \tint ret = 0;\n>  \tint flags = 0;\n> @@ -1105,7 +1115,6 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n>  \tstruct strbuf msg = STRBUF_INIT;\n>  \tstruct strbuf commit_tree_label = STRBUF_INIT;\n>  \tstruct strbuf untracked_files = STRBUF_INIT;\n> -\tstruct strbuf patch = STRBUF_INIT;\n>  \n>  \tprepare_fallback_ident(\"git stash\", \"git@stash\");\n>  \n> @@ -1154,7 +1163,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n>  \t\tuntracked_commit_option = 1;\n>  \t}\n>  \tif (patch_mode) {\n> -\t\tret = stash_patch(info, ps, &patch);\n> +\t\tret = stash_patch(info, ps, patch);\n>  \t\tif (ret < 0) {\n>  \t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n>  \t\t\t\t\t     \"worktree state\"));\n> @@ -1225,7 +1234,8 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n>  \n>  \tmemset(&ps, 0, sizeof(ps));\n>  \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n> -\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n> +\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n> +\t\t\t      NULL);\n>  \n>  \tif (!ret)\n>  \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n> @@ -1239,6 +1249,231 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n>  \treturn ret < 0;\n>  }\n>  \n> +static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n> +\t\t\t int keep_index, int patch_mode, int include_untracked)\n> +{\n> +\tint ret = 0;\n> +\tstruct stash_info info;\n> +\tstruct strbuf patch = STRBUF_INIT;\n> +\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n> +\n> +\tif (patch_mode && keep_index == -1)\n> +\t\tkeep_index = 1;\n> +\n> +\tif (patch_mode && include_untracked) {\n> +\t\tfprintf_ln(stderr, _(\"Can't use --patch and --include-untracked\"\n> +\t\t\t\t     \" or --all at the same time\"));\n> +\t\tret = -1;\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tread_cache_preload(NULL);\n> +\tif (!include_untracked && ps.nr) {\n> +\t\tint i;\n> +\t\tchar *ps_matched = xcalloc(ps.nr, 1);\n> +\n> +\t\tfor (i = 0; i < active_nr; i++)\n> +\t\t\tce_path_match(&the_index, active_cache[i], &ps,\n> +\t\t\t\t      ps_matched);\n> +\n> +\t\tif (report_path_error(ps_matched, &ps, NULL)) {\n> +\t\t\tfprintf_ln(stderr, _(\"Did you forget to 'git add'?\"));\n> +\t\t\tret = -1;\n> +\t\t\tfree(ps_matched);\n> +\t\t\tgoto done;\n> +\t\t}\n> +\t\tfree(ps_matched);\n> +\t}\n> +\n> +\tif (refresh_cache(REFRESH_QUIET)) {\n> +\t\tret = -1;\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tif (!check_changes(ps, include_untracked)) {\n> +\t\tif (!quiet)\n> +\t\t\tprintf_ln(_(\"No local changes to save\"));\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n> +\t\tret = -1;\n> +\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tif (stash_msg)\n> +\t\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n> +\tif (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,\n> +\t\t\t    &info, &patch)) {\n> +\t\tret = -1;\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tif (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {\n> +\t\tret = -1;\n> +\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n> +\t\tgoto done;\n> +\t}\n> +\n> +\tprintf_ln(_(\"Saved working directory and index state %s\"),\n> +\t\t  stash_msg_buf.buf);\n> +\n> +\tif (!patch_mode) {\n> +\t\tif (include_untracked && !ps.nr) {\n> +\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\n> +\t\t\tcp.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp.args, \"clean\", \"--force\",\n> +\t\t\t\t\t \"--quiet\", \"-d\", NULL);\n> +\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n> +\t\t\t\targv_array_push(&cp.args, \"-x\");\n> +\t\t\tif (run_command(&cp)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t}\n> +\t\tif (ps.nr) {\n> +\t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n> +\t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n> +\t\t\tstruct child_process cp_apply = CHILD_PROCESS_INIT;\n> +\t\t\tstruct strbuf out = STRBUF_INIT;\n> +\n> +\t\t\tcp_add.git_cmd = 1;\n> +\t\t\targv_array_push(&cp_add.args, \"add\");\n> +\t\t\tif (!include_untracked)\n> +\t\t\t\targv_array_push(&cp_add.args, \"-u\");\n> +\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n> +\t\t\t\targv_array_push(&cp_add.args, \"--force\");\n> +\t\t\targv_array_push(&cp_add.args, \"--\");\n> +\t\t\tadd_pathspecs(&cp_add.args, ps);\n> +\t\t\tif (run_command(&cp_add)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\n> +\t\t\tcp_diff.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp_diff.args, \"diff-index\", \"-p\",\n> +\t\t\t\t\t \"--cached\", \"--binary\", \"HEAD\", \"--\",\n> +\t\t\t\t\t NULL);\n> +\t\t\tadd_pathspecs(&cp_diff.args, ps);\n> +\t\t\tif (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\n> +\t\t\tcp_apply.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp_apply.args, \"apply\", \"--index\",\n> +\t\t\t\t\t \"-R\", NULL);\n> +\t\t\tif (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,\n> +\t\t\t\t\t NULL, 0)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t} else {\n> +\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\t\t\tcp.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n> +\t\t\t\t\t NULL);\n> +\t\t\tif (run_command(&cp)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tif (keep_index == 1 && !is_null_oid(&info.i_tree)) {\n> +\t\t\tstruct child_process cp_ls = CHILD_PROCESS_INIT;\n> +\t\t\tstruct child_process cp_checkout = CHILD_PROCESS_INIT;\n> +\t\t\tstruct strbuf out = STRBUF_INIT;\n> +\n> +\t\t\tif (reset_tree(&info.i_tree, 0, 1)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\n> +\t\t\tcp_ls.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp_ls.args, \"ls-files\", \"-z\",\n> +\t\t\t\t\t \"--modified\", \"--\", NULL);\n> +\n> +\t\t\tadd_pathspecs(&cp_ls.args, ps);\n> +\t\t\tif (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\n> +\t\t\tcp_checkout.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp_checkout.args, \"checkout-index\",\n> +\t\t\t\t\t \"-z\", \"--force\", \"--stdin\", NULL);\n> +\t\t\tif (pipe_command(&cp_checkout, out.buf, out.len, NULL,\n> +\t\t\t\t\t 0, NULL, 0)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t}\n> +\t\tgoto done;\n> +\t} else {\n> +\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\n> +\t\tcp.git_cmd = 1;\n> +\t\targv_array_pushl(&cp.args, \"apply\", \"-R\", NULL);\n> +\n> +\t\tif (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {\n> +\t\t\tfprintf_ln(stderr, _(\"Cannot remove worktree changes\"));\n> +\t\t\tret = -1;\n> +\t\t\tgoto done;\n> +\t\t}\n> +\n> +\t\tif (keep_index < 1) {\n> +\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\n> +\t\t\tcp.git_cmd = 1;\n> +\t\t\targv_array_pushl(&cp.args, \"reset\", \"-q\", \"--\", NULL);\n> +\t\t\tadd_pathspecs(&cp.args, ps);\n> +\t\t\tif (run_command(&cp)) {\n> +\t\t\t\tret = -1;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t}\n> +\t\tgoto done;\n> +\t}\n> +\n> +done:\n> +\tstrbuf_release(&stash_msg_buf);\n> +\treturn ret;\n> +}\n> +\n> +static int push_stash(int argc, const char **argv, const char *prefix)\n> +{\n> +\tint keep_index = -1;\n> +\tint patch_mode = 0;\n> +\tint include_untracked = 0;\n> +\tint quiet = 0;\n> +\tconst char *stash_msg = NULL;\n> +\tstruct pathspec ps;\n> +\tstruct option options[] = {\n> +\t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n> +\t\t\t N_(\"keep index\")),\n> +\t\tOPT_BOOL('p', \"patch\", &patch_mode,\n> +\t\t\t N_(\"stash in patch mode\")),\n> +\t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n> +\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n> +\t\t\t N_(\"include untracked files in stash\")),\n> +\t\tOPT_SET_INT('a', \"all\", &include_untracked,\n> +\t\t\t    N_(\"include ignore files\"), 2),\n> +\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n> +\t\t\t   N_(\"stash message\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options,\n> +\t\t\t     git_stash_helper_push_usage,\n> +\t\t\t     0);\n> +\n> +\tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n> +\treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n> +\t\t\t     include_untracked);\n> +}\n> +\n>  int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n>  {\n>  \tpid_t pid = getpid();\n> @@ -1277,6 +1512,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n>  \t\treturn !!store_stash(argc, argv, prefix);\n>  \telse if (!strcmp(argv[0], \"create\"))\n>  \t\treturn !!create_stash(argc, argv, prefix);\n> +\telse if (!strcmp(argv[0], \"push\"))\n> +\t\treturn !!push_stash(argc, argv, prefix);\n>  \n>  \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n>  \t\t      git_stash_helper_usage, options);\n> diff --git a/git-stash.sh b/git-stash.sh\n> index a9b3064ff0..51d7a06601 100755\n> --- a/git-stash.sh\n> +++ b/git-stash.sh\n> @@ -429,7 +429,8 @@ save)\n>  \t;;\n>  push)\n>  \tshift\n> -\tpush_stash \"$@\"\n> +\tcd \"$START_DIR\"\n> +\tgit stash--helper push \"$@\"\n>  \t;;\n>  apply)\n>  \tshift\n> @@ -465,7 +466,8 @@ branch)\n>  *)\n>  \tcase $# in\n>  \t0)\n> -\t\tpush_stash &&\n> +\t\tcd \"$START_DIR\"\n> +\t\tgit stash--helper push &&\n>  \t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n>  \t\t;;\n>  \t*)\n> -- \n> 2.20.1.441.g764a526393\n> \n"},{"id":"368987","messageId":"20190210221712.GA9241@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"20190208113059.GV10587@szeder.dev","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-10T22:17:12Z","receivedAt":"2019-02-10T22:17:17Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 02/08, SZEDER Gábor wrote:\n> On Thu, Dec 20, 2018 at 09:44:34PM +0200, Paul-Sebastian Ungureanu wrote:\n> > Add stash push to the helper.\n> > \n> > Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> \n> This patch causes rare failures in 't3903-stash.sh', I've seen it\n> break for the first time today in a Travis CI build:\n\nThanks for reporting this.  I was going to take a look at it, but\nunfortunately I can't seem to reproduce the issue even with --stress\n(I let it run for ~1000 repetitions and then aborted it).\n\nWhich platform did you see/test this on, and which compile options did\nyou use?  I went through a failures on\nhttps://travis-ci.org/git/git/builds, but couldn't find this\nparticular one.  Could you point me at the failed run?\n\n>   +echo bar3\n>   +echo bar4\n>   +git add file2\n>   +git stash -k\n>   Saved working directory and index state WIP on stashbranch: d3a23d9 alternate second\n>   +cat file\n>   +cat file2\n>   +test bar,bar4 = bar,bar2\n>   error: last command exited with $?=1\n>   not ok 20 - stash -k\n> \n> Steps to reproduce:\n> \n>   $ git checkout -f fa38428f76\n>   HEAD is now at fa38428f76 stash: convert push to builtin\n>   \n>   # fb7d1e3ac8 (test-lib: add the '--stress' option to run a test\n>   # repeatedly under load, 2019-01-05)\n>   $ git merge --no-commit fb7d1e3ac8\n>   Automatic merge went well; stopped before committing as requested\n>   $ make && cd t\n>   <snip>\n>   $ ./t3903-stash.sh --stress -r 1,13,14,20\n>   # wait, it tends to fail in <30 repetitions\n> \n> I run stress testing on its parent for over 800 repetitions, no sign\n> of failure yet.\n"},{"id":"368991","messageId":"20190211011306.GA31807@szeder.dev","threadId":"50081","inReplyTo":"20190210221712.GA9241@hank.intra.tgummerer.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-02-11T01:13:06Z","receivedAt":"2019-02-11T01:13:17Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Feb 10, 2019 at 10:17:12PM +0000, Thomas Gummerer wrote:\n> On 02/08, SZEDER Gábor wrote:\n> > On Thu, Dec 20, 2018 at 09:44:34PM +0200, Paul-Sebastian Ungureanu wrote:\n> > > Add stash push to the helper.\n> > > \n> > > Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> > \n> > This patch causes rare failures in 't3903-stash.sh', I've seen it\n> > break for the first time today in a Travis CI build:\n> \n> Thanks for reporting this.  I was going to take a look at it, but\n> unfortunately I can't seem to reproduce the issue even with --stress\n> (I let it run for ~1000 repetitions and then aborted it).\n> \n> Which platform did you see/test this on, and which compile options did\n> you use?  I went through a failures on\n> https://travis-ci.org/git/git/builds, but couldn't find this\n> particular one.  Could you point me at the failed run?\n\nIt was in a Linux build job in one of my custom CI builds [1]:\n\n  https://travis-ci.org/szeder/git-cooking-topics-for-travis-ci/jobs/490420713#L3401\n\nand I tested it locally on Linux as well.  I don't think there are any\nunusual compiler options.  I run it with --stress on Travis CI's macOS\nas well, but couldn't trigger a failure there.\n\n\n[1] That build log doesn't include the trash dir of the failed test\n    in the log, most likely because of a faulty merge conflict\n    resolution on my part, but you can find a failed trash dir\n    embedded here:\n\n      https://travis-ci.org/szeder/git/jobs/491401882#L2309\n\n"},{"id":"369198","messageId":"20190212231837.GI6085@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"20190211011306.GA31807@szeder.dev","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-12T23:18:37Z","receivedAt":"2019-02-12T23:18:44Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 02/11, SZEDER Gábor wrote:\n> On Sun, Feb 10, 2019 at 10:17:12PM +0000, Thomas Gummerer wrote:\n> > On 02/08, SZEDER Gábor wrote:\n> > > On Thu, Dec 20, 2018 at 09:44:34PM +0200, Paul-Sebastian Ungureanu wrote:\n> > > > Add stash push to the helper.\n> > > > \n> > > > Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> > > \n> > > This patch causes rare failures in 't3903-stash.sh', I've seen it\n> > > break for the first time today in a Travis CI build:\n> > \n> > Thanks for reporting this.  I was going to take a look at it, but\n> > unfortunately I can't seem to reproduce the issue even with --stress\n> > (I let it run for ~1000 repetitions and then aborted it).\n> > \n> > Which platform did you see/test this on, and which compile options did\n> > you use?  I went through a failures on\n> > https://travis-ci.org/git/git/builds, but couldn't find this\n> > particular one.  Could you point me at the failed run?\n> \n> It was in a Linux build job in one of my custom CI builds [1]:\n> \n>   https://travis-ci.org/szeder/git-cooking-topics-for-travis-ci/jobs/490420713#L3401\n> \n> and I tested it locally on Linux as well.  I don't think there are any\n> unusual compiler options.  I run it with --stress on Travis CI's macOS\n> as well, but couldn't trigger a failure there.\n\nThanks.  I still didn't manage to reproduce it locally, but I was now\nable to test it on Travis CI.\n\nThe diff below fixes the issue, but I still need to spend some time to\nbetter understand why it does.  I'll hopefully be in a position to\nsend a patch with a proper log message why this is the right fix in\nthe next couple of days.\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex c77f62c895..3dab488bd6 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n \tstruct tree *tree;\n \tstruct lock_file lock_file = LOCK_INIT;\n \n+\tdiscard_cache();\n \tread_cache_preload(NULL);\n \tif (refresh_cache(REFRESH_QUIET))\n \t\treturn -1;\n\n\n> [1] That build log doesn't include the trash dir of the failed test\n>     in the log, most likely because of a faulty merge conflict\n>     resolution on my part, but you can find a failed trash dir\n>     embedded here:\n> \n>       https://travis-ci.org/szeder/git/jobs/491401882#L2309\n> \n"},{"id":"369609","messageId":"20190219002336.GN1622@szeder.dev","threadId":"50081","inReplyTo":"20190212231837.GI6085@hank.intra.tgummerer.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-02-19T00:23:36Z","receivedAt":"2019-02-19T00:23:42Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Feb 12, 2019 at 11:18:37PM +0000, Thomas Gummerer wrote:\n> Thanks.  I still didn't manage to reproduce it locally, but I was now\n> able to test it on Travis CI.\n> \n> The diff below fixes the issue, but I still need to spend some time to\n> better understand why it does.\n\nThere is nothing like a fix that works, but you have no idea why :)\n\nFWIW, I'm at a couple of thousands of '--stress' repetitions with your\npatch below, and not a single failure yet.\n\n\n> I'll hopefully be in a position to\n> send a patch with a proper log message why this is the right fix in\n> the next couple of days.\n> \n> diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> index c77f62c895..3dab488bd6 100644\n> --- a/builtin/stash--helper.c\n> +++ b/builtin/stash--helper.c\n> @@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n>  \tstruct tree *tree;\n>  \tstruct lock_file lock_file = LOCK_INIT;\n>  \n> +\tdiscard_cache();\n>  \tread_cache_preload(NULL);\n>  \tif (refresh_cache(REFRESH_QUIET))\n>  \t\treturn -1;\n> \n> \n"},{"id":"369625","messageId":"nycvar.QRO.7.76.6.1902191127420.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"20190219002336.GN1622@szeder.dev","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-02-19T10:47:51Z","receivedAt":"2019-02-19T10:48:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Gábor & Thomas,\n\nOn Tue, 19 Feb 2019, SZEDER Gábor wrote:\n\n> On Tue, Feb 12, 2019 at 11:18:37PM +0000, Thomas Gummerer wrote:\n> > Thanks.  I still didn't manage to reproduce it locally, but I was now\n> > able to test it on Travis CI.\n> > \n> > The diff below fixes the issue, but I still need to spend some time to\n> > better understand why it does.\n> \n> There is nothing like a fix that works, but you have no idea why :)\n\nI know why. Now. See below for the analysis.\n\n> FWIW, I'm at a couple of thousands of '--stress' repetitions with your\n> patch below, and not a single failure yet.\n\nGood, and yes, there is a problem.\n\n> > I'll hopefully be in a position to\n> > send a patch with a proper log message why this is the right fix in\n> > the next couple of days.\n> > \n> > diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> > index c77f62c895..3dab488bd6 100644\n> > --- a/builtin/stash--helper.c\n> > +++ b/builtin/stash--helper.c\n> > @@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n> >  \tstruct tree *tree;\n> >  \tstruct lock_file lock_file = LOCK_INIT;\n> >  \n> > +\tdiscard_cache();\n> >  \tread_cache_preload(NULL);\n> >  \tif (refresh_cache(REFRESH_QUIET))\n> >  \t\treturn -1;\n> > \n\nSo this is working, but it is not the correct spot for that\n`discard_cache()`, as it forces unnecessary cycles on code paths calling\n`reset_tree()` (which corresponds to `git read-tree`, admittedly a bit\nconfusing) with a fully up to date index.\n\nThe real fix, I believe, is this:\n\n-- snip --\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 2d6dfce883..516dee0fa4 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1372,6 +1372,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\t\t}\n \t\t} else {\n \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\t\tdiscard_cache();\n \t\t\tcp.git_cmd = 1;\n \t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n \t\t\t\t\t NULL);\n-- snap --\n\nAnd the reason this is needed: we spawn a `git reset --hard` here, which\nwill change the index, but outside of the current process. So the\nin-process copy is stale. And when the index' mtime does not help us\ndetect that, we run into that test breakage.\n\nNow, I seriously believe that we missed the best time to move\nps/stash-in-c into `next` for cooking. The best time would have been just\nafter Paul submitted the latest patch series: we know for a fact that he\nis too busy to really take care of this patch series, so keeping it in\n`pu` puts everybody into that awkward spot where nobody wants to step on\nPaul's toes messing with his patch series, but where Paul also lacks the\ntime to push it further, so everything is stuck in a limbo and is *so very\nmuch* not cooking at all. You might say that it has turned bad because we\nfailed to stoke the fire appropriately.\n\nSince it is now way too late in the v2.21.0 process, this problem is only\nexacerbated, because it won't even enter `next` \"better late than never\".\n\nTo address this unfortunate situation, my current plan is to take over\nfrom Paul (we had been chatting about this privately in the past, and he\nis okay with this because of University eating all his time).\n\nI will open the whole bag again, most likely squashing the late fixups\ninto the patches that introduced the problems, re-review with a much finer\ncomb than the patch series has enjoyed on the Git mailing list (even just\na quick look at `do_apply_stash()` revealed an unnecessary `reset_tree()`\ncall that *no* reviewer spotted, even I myself, but then, I am hardly\nsolely responsible for that review), and most likely I'll even take my\nsweet little time changing the code to avoid more spawned Git processes.\n\nIt will take a long time, and the `stash` project that has been discussed\nrecently to be given to GSoC students is no longer available, as I will\ntake care of it before GSoC even starts, and I won't spend much time\nreviewing other people's code in the meantime.\n\nI will start that only after v2.21.0 final is out, obviously.\n\nOnce I submit a new iteration, it will look quite a bit different from\nbefore, and reviewers will have to re-review *everything*, wasting\neverybody's time even more. It will have to be re-reviewed in its entirety\nanyway because it has been *such* a long time since the latest review, and\nthat's just the price we all have to pay for missing the right moment to\nadvance this to `next`. Thomas, I will ask you to review, and Gábor, I\nwill expect you to review that iteration, too, as you are now a bit\nfamiliar with the code, and I will really need your help here.\n\nAnyway, that's my plan for now.\n\nCiao,\nDscho"},{"id":"369663","messageId":"xmqqbm371rdn.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1902191127420.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-19T19:59:32Z","receivedAt":"2019-02-19T19:59:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> > diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n>> > index c77f62c895..3dab488bd6 100644\n>> > --- a/builtin/stash--helper.c\n>> > +++ b/builtin/stash--helper.c\n>> > @@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n>> >  \tstruct tree *tree;\n>> >  \tstruct lock_file lock_file = LOCK_INIT;\n>> >  \n>> > +\tdiscard_cache();\n>> >  \tread_cache_preload(NULL);\n>> >  \tif (refresh_cache(REFRESH_QUIET))\n>> >  \t\treturn -1;\n>> > \n>\n> So this is working, but it is not the correct spot for that\n> `discard_cache()`, as it forces unnecessary cycles on code paths calling\n> `reset_tree()` (which corresponds to `git read-tree`, admittedly a bit\n> confusing) with a fully up to date index.\n>\n> The real fix, I believe, is this:\n>\n> -- snip --\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index 2d6dfce883..516dee0fa4 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -1372,6 +1372,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n>  \t\t\t}\n>  \t\t} else {\n>  \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\t\t\tdiscard_cache();\n>  \t\t\tcp.git_cmd = 1;\n>  \t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n>  \t\t\t\t\t NULL);\n> -- snap --\n>\n> And the reason this is needed: we spawn a `git reset --hard` here, which\n> will change the index, but outside of the current process. So the\n> in-process copy is stale. And when the index' mtime does not help us\n> detect that, we run into that test breakage.\n\nIn non-patch mode with pathspec, there is an invocation of \"apply\n--index -R\" of a patch that takes the contents of the HEAD to what\nis in the index, updating the on-disk index and making our in-core\ncopy stale.  Wouldn't we need to do the same?  Otherwise, the same\n\"reset_tree()\" you are tryhing to protect with this discard_cache()\nwill call read_cache_preload(), no?\n\nAmong the calls to reset_tree() in this file, I think the one that\nfollows the \"reset --hard\" (your fix above) and \"apply --index -R\"\n(the other side of the same if/else) is the only one that wants to\nread from the result of an external command we just spawned from the\non-disk index, so perhaps moving discard_cache() to just before that\ncall may be a better fix.\n\n\n\n"},{"id":"369681","messageId":"20190219235913.GM6085@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1902191127420.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-19T23:59:13Z","receivedAt":"2019-02-19T23:59:19Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 02/19, Johannes Schindelin wrote:\n> Hi Gábor & Thomas,\n> \n> On Tue, 19 Feb 2019, SZEDER Gábor wrote:\n> \n> > > I'll hopefully be in a position to\n> > > send a patch with a proper log message why this is the right fix in\n> > > the next couple of days.\n> > > \n> > > diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> > > index c77f62c895..3dab488bd6 100644\n> > > --- a/builtin/stash--helper.c\n> > > +++ b/builtin/stash--helper.c\n> > > @@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n> > >  \tstruct tree *tree;\n> > >  \tstruct lock_file lock_file = LOCK_INIT;\n> > >  \n> > > +\tdiscard_cache();\n> > >  \tread_cache_preload(NULL);\n> > >  \tif (refresh_cache(REFRESH_QUIET))\n> > >  \t\treturn -1;\n> > > \n> \n> So this is working, but it is not the correct spot for that\n> `discard_cache()`, as it forces unnecessary cycles on code paths calling\n> `reset_tree()` (which corresponds to `git read-tree`, admittedly a bit\n> confusing) with a fully up to date index.\n> \n> The real fix, I believe, is this:\n> \n> -- snip --\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index 2d6dfce883..516dee0fa4 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -1372,6 +1372,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n>  \t\t\t}\n>  \t\t} else {\n>  \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\t\t\tdiscard_cache();\n>  \t\t\tcp.git_cmd = 1;\n>  \t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n>  \t\t\t\t\t NULL);\n> -- snap --\n> \n> And the reason this is needed: we spawn a `git reset --hard` here, which\n> will change the index, but outside of the current process. So the\n> in-process copy is stale. And when the index' mtime does not help us\n> detect that, we run into that test breakage.\n\nRight, I agree with this assessment, and also agree that this is a\nbetter place to discard the cache, rather than doing it in\n'reset_tree()'.\n\n> Now, I seriously believe that we missed the best time to move\n> ps/stash-in-c into `next` for cooking. The best time would have been just\n> after Paul submitted the latest patch series: we know for a fact that he\n> is too busy to really take care of this patch series, so keeping it in\n> `pu` puts everybody into that awkward spot where nobody wants to step on\n> Paul's toes messing with his patch series, but where Paul also lacks the\n> time to push it further, so everything is stuck in a limbo and is *so very\n> much* not cooking at all. You might say that it has turned bad because we\n> failed to stoke the fire appropriately.\n> \n> Since it is now way too late in the v2.21.0 process, this problem is only\n> exacerbated, because it won't even enter `next` \"better late than never\".\n>\n> To address this unfortunate situation, my current plan is to take over\n> from Paul (we had been chatting about this privately in the past, and he\n> is okay with this because of University eating all his time).\n> \n> I will open the whole bag again, most likely squashing the late fixups\n> into the patches that introduced the problems, re-review with a much finer\n> comb than the patch series has enjoyed on the Git mailing list (even just\n> a quick look at `do_apply_stash()` revealed an unnecessary `reset_tree()`\n> call that *no* reviewer spotted, even I myself, but then, I am hardly\n> solely responsible for that review), and most likely I'll even take my\n> sweet little time changing the code to avoid more spawned Git processes.\n> \n> It will take a long time, and the `stash` project that has been discussed\n> recently to be given to GSoC students is no longer available, as I will\n> take care of it before GSoC even starts, and I won't spend much time\n> reviewing other people's code in the meantime.\n>\n> I will start that only after v2.21.0 final is out, obviously.\n> \n> Once I submit a new iteration, it will look quite a bit different from\n> before, and reviewers will have to re-review *everything*, wasting\n> everybody's time even more. It will have to be re-reviewed in its entirety\n> anyway because it has been *such* a long time since the latest review, and\n> that's just the price we all have to pay for missing the right moment to\n> advance this to `next`. Thomas, I will ask you to review, and Gábor, I\n> will expect you to review that iteration, too, as you are now a bit\n> familiar with the code, and I will really need your help here.\n> \n> Anyway, that's my plan for now.\n\nI must say I am not very happy about this plan.  The series has been\nmarked as \"Will merge to 'next'\" in previous iterations, but then we\nfound some issues that prevented that.  However I thought we were fine\nfixing those on top at this point, rather than starting with a new\niteration again.\n\nI was always under the impression that once the problem that was\ndiscovered here was fixed we'd advance the series to 'next' with the\npatch that comes out of this discussion on top.  Whether it's in next\nshortly before 2.21 or not doesn't seem to make much of a difference\nto me, as this wasn't going to make the 2.21 release anyway.  My hope\nwas that we could get it into 'next' shortly after 2.21 is released to\nget the series some further exposure (which may well turn up some\nother issues that we are not aware of yet, but such is the life of\nsoftware).\n\nSure there are some things that no reviewer spotted, but I'd think so\nwill there be next time and the time after that.  I don't believe that\ncode review can eliminate all issues, but I think we all did our best\nto avoid a good chunk of them and unfortunately missed some in the\nprocess.\n\nReviewing this series again in a slightly new form is not something I\nam personally looking forward to.  I'd be more than happy to review\npatches on top of this series, but after reading it many times over,\nin slightly different versions, it gets harder and harder to review\nand to find the motivation to review this.  Now if we really think\nthis series has to be completely overhauled, so be it, but I don't\nthink we would end up with a better end result than if we were to\ncontinue to work on top of this.\n\n> Ciao,\n> Dscho\n\n"},{"id":"369693","messageId":"xmqqmumrvzwk.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190219235913.GM6085@hank.intra.tgummerer.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-02-20T04:37:15Z","receivedAt":"2019-02-20T04:37:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n>> Now, I seriously believe that we missed the best time to move\n>> ps/stash-in-c into `next` for cooking. The best time would have been just\n>> ...\n>> Anyway, that's my plan for now.\n>\n> I must say I am not very happy about this plan.  The series has been\n> marked as \"Will merge to 'next'\" in previous iterations, but then we\n> found some issues that prevented that.  However I thought we were fine\n> fixing those on top at this point, rather than starting with a new\n> iteration again.\n\nFirst before going into anything else, let me thank, and let me\ninvite readers of this thread to join me thanking, Paul (Sebi) for\nsticking with this topic for this long.  It is above and beyond what\nGSoC calls for.\n\nHaving said that.\n\nI too was somehow led to believe that the topic was in a good enough\nshape, with some room for clean-up by reordering the patches to make\nthem into a more logical progression and squashing an existing and\nrecently figured out \"oops, that was wrong\" fixes into the patches\nwhere the breakages originate.\n\nAnd that was where the \"Will merge to\" originally came from.  Thanks\nto tools like range-diff, a topic that goes through such reordering\nand squashing of patches should not have to \"waste\" a lot of review\ncycles out of those who have seen the previous round.\n\nIt however is a totally different matter if the topic was so\nunsalvageable that it needs a total rewrite---that would need\nanother round of careful review, of course, and it would be\nirresponsive to merge a topic in such a messy state to 'next'.  But\nmy impression was that the topic was not _that_ bad, so Dscho's\nmessage and the plan were something that was totally unexpected to\nme, too..\n\n> I was always under the impression that once the problem that was\n> discovered here was fixed we'd advance the series to 'next' with the\n> patch that comes out of this discussion on top.  Whether it's in next\n> shortly before 2.21 or not doesn't seem to make much of a difference\n> to me, as this wasn't going to make the 2.21 release anyway.  My hope\n> was that we could get it into 'next' shortly after 2.21 is released to\n> get the series some further exposure (which may well turn up some\n> other issues that we are not aware of yet, but such is the life of\n> software).\n\nI was hoping similar, but also was hoping that people would use the\ntime wisely while waiting for the next cycle to polish the topic with\nreordering and squashing, so that it can hit 'next' early once the\ntree opens.\n\nAnyway.\n\nI actually have a different issue with this topic, though.  It is\nwonderful to see a GSoC student's continued involvement in the\nproject, but it is not healthy that we need so much work on top of\nwhat was marked \"done\" at the end of the GSoC period.  Especially\nthe impression I am getting for the post GSoC work of this topic is\nnot \"we are already done converting to built-in during GSoC, and now\nwe are extending the command\", but \"we ran out of time during GSoC;\nhere is what we would have seen at the end of GSoC in an ideal\nworld.\"\n\nI wonder if this is an unfortunate indication that our expectation\nis unrealistically high when we accept students' applications.\nBeing overly ambitious is *not* students' fault, but those of us on\nthe list, especially those who mentor, have far deeper experience\nwith how our code and project is structured than any students do.\nWe should be able to, and should not hesitate to, say things like\n\"that's overly ambitious---for such and such, you'd need to even\ninvent an internal API---can we reduce the scope and still produce a\nuseful end result?\"\n\nOne suggestion I have is to have success criteria (e.g. \"gets merged\nto 'master' before GSoC ends\" [*1*]) clearly spelled out in the\napplication.  Something like that would help managing the\nexpectation and biting way too much for a summer, I'd hope.\n\n    Side note *1*.  Of course, depending on the alignment of the\n    stars ^W our ~10-12 week development cycle and the end of GSoC,\n    getting merged to 'master' might become impossible if it\n    coincides with the pre-release freeze period.  But we on the\n    list and the mentors know how the project works, and can help\n    stating a more realistic success criterion if the development\n    cycle and other quirks specific to this project gets in the way.\n\nThanks.\n"},{"id":"369738","messageId":"nycvar.QRO.7.76.6.1902202201061.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"xmqqbm371rdn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-02-20T21:01:27Z","receivedAt":"2019-02-20T21:01:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 19 Feb 2019, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> > diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n> >> > index c77f62c895..3dab488bd6 100644\n> >> > --- a/builtin/stash--helper.c\n> >> > +++ b/builtin/stash--helper.c\n> >> > @@ -231,6 +231,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)\n> >> >  \tstruct tree *tree;\n> >> >  \tstruct lock_file lock_file = LOCK_INIT;\n> >> >  \n> >> > +\tdiscard_cache();\n> >> >  \tread_cache_preload(NULL);\n> >> >  \tif (refresh_cache(REFRESH_QUIET))\n> >> >  \t\treturn -1;\n> >> > \n> >\n> > So this is working, but it is not the correct spot for that\n> > `discard_cache()`, as it forces unnecessary cycles on code paths calling\n> > `reset_tree()` (which corresponds to `git read-tree`, admittedly a bit\n> > confusing) with a fully up to date index.\n> >\n> > The real fix, I believe, is this:\n> >\n> > -- snip --\n> > diff --git a/builtin/stash.c b/builtin/stash.c\n> > index 2d6dfce883..516dee0fa4 100644\n> > --- a/builtin/stash.c\n> > +++ b/builtin/stash.c\n> > @@ -1372,6 +1372,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n> >  \t\t\t}\n> >  \t\t} else {\n> >  \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n> > +\t\t\tdiscard_cache();\n> >  \t\t\tcp.git_cmd = 1;\n> >  \t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n> >  \t\t\t\t\t NULL);\n> > -- snap --\n> >\n> > And the reason this is needed: we spawn a `git reset --hard` here, which\n> > will change the index, but outside of the current process. So the\n> > in-process copy is stale. And when the index' mtime does not help us\n> > detect that, we run into that test breakage.\n> \n> In non-patch mode with pathspec, there is an invocation of \"apply\n> --index -R\" of a patch that takes the contents of the HEAD to what\n> is in the index, updating the on-disk index and making our in-core\n> copy stale.  Wouldn't we need to do the same?  Otherwise, the same\n> \"reset_tree()\" you are tryhing to protect with this discard_cache()\n> will call read_cache_preload(), no?\n> \n> Among the calls to reset_tree() in this file, I think the one that\n> follows the \"reset --hard\" (your fix above) and \"apply --index -R\"\n> (the other side of the same if/else) is the only one that wants to\n> read from the result of an external command we just spawned from the\n> on-disk index, so perhaps moving discard_cache() to just before that\n> call may be a better fix.\n\nGood catch!\nDscho\n"},{"id":"369740","messageId":"nycvar.QRO.7.76.6.1902202205470.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"xmqqmumrvzwk.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-02-20T21:10:01Z","receivedAt":"2019-02-20T21:10:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 19 Feb 2019, Junio C Hamano wrote:\n\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> >> Now, I seriously believe that we missed the best time to move\n> >> ps/stash-in-c into `next` for cooking. The best time would have been just\n> >> ...\n> >> Anyway, that's my plan for now.\n> >\n> > I must say I am not very happy about this plan.  The series has been\n> > marked as \"Will merge to 'next'\" in previous iterations, but then we\n> > found some issues that prevented that.  However I thought we were fine\n> > fixing those on top at this point, rather than starting with a new\n> > iteration again.\n> \n> First before going into anything else, let me thank, and let me\n> invite readers of this thread to join me thanking, Paul (Sebi) for\n> sticking with this topic for this long.  It is above and beyond what\n> GSoC calls for.\n\nIndeed. He put in quite a few dozen hours of work *after* GSoC.\n\n> Having said that.\n> \n> I too was somehow led to believe that the topic was in a good enough\n> shape, with some room for clean-up by reordering the patches to make\n> them into a more logical progression and squashing an existing and\n> recently figured out \"oops, that was wrong\" fixes into the patches\n> where the breakages originate.\n> \n> And that was where the \"Will merge to\" originally came from.  Thanks\n> to tools like range-diff, a topic that goes through such reordering\n> and squashing of patches should not have to \"waste\" a lot of review\n> cycles out of those who have seen the previous round.\n> \n> It however is a totally different matter if the topic was so\n> unsalvageable that it needs a total rewrite---that would need\n> another round of careful review, of course, and it would be\n> irresponsive to merge a topic in such a messy state to 'next'.  But\n> my impression was that the topic was not _that_ bad, so Dscho's\n> message and the plan were something that was totally unexpected to\n> me, too..\n\nMy throwing hands into the air and giving up on advancing it in the\ncurrent form to `next` was based on its rotting away in `pu`...\n\nIf it had been in `next` while Hannes, others and I found and fixed bugs,\nthen it would truly be cooking, but in `pu`? It kind of stopped pretty\nmuch all the development around it.\n\nAnd that's the only reason why I came up with that plan (that will require\nat least 50 hours from my side, I know that): because I thought that your\nnot advancing the patches to `next` meant that this extra work was exactly\nwhat you were expecting.\n\n> > I was always under the impression that once the problem that was\n> > discovered here was fixed we'd advance the series to 'next' with the\n> > patch that comes out of this discussion on top.  Whether it's in next\n> > shortly before 2.21 or not doesn't seem to make much of a difference\n> > to me, as this wasn't going to make the 2.21 release anyway.  My hope\n> > was that we could get it into 'next' shortly after 2.21 is released to\n> > get the series some further exposure (which may well turn up some\n> > other issues that we are not aware of yet, but such is the life of\n> > software).\n> \n> I was hoping similar, but also was hoping that people would use the\n> time wisely while waiting for the next cycle to polish the topic with\n> reordering and squashing, so that it can hit 'next' early once the\n> tree opens.\n> \n> Anyway.\n> \n> I actually have a different issue with this topic, though.  It is\n> wonderful to see a GSoC student's continued involvement in the\n> project, but it is not healthy that we need so much work on top of\n> what was marked \"done\" at the end of the GSoC period.  Especially\n> the impression I am getting for the post GSoC work of this topic is\n> not \"we are already done converting to built-in during GSoC, and now\n> we are extending the command\", but \"we ran out of time during GSoC;\n> here is what we would have seen at the end of GSoC in an ideal\n> world.\"\n> \n> I wonder if this is an unfortunate indication that our expectation\n> is unrealistically high when we accept students' applications.\n\nYes, you are right. This is on me. Guilty as charged.\n\nAnd I don't mean this flippantly. I thought long and hard about this, and\nI've come to the conclusion that I will not offer to mentor this round of\nGSoC as a consequence.\n\nCiao,\nDscho\n\n> Being overly ambitious is *not* students' fault, but those of us on\n> the list, especially those who mentor, have far deeper experience\n> with how our code and project is structured than any students do.\n> We should be able to, and should not hesitate to, say things like\n> \"that's overly ambitious---for such and such, you'd need to even\n> invent an internal API---can we reduce the scope and still produce a\n> useful end result?\"\n> \n> One suggestion I have is to have success criteria (e.g. \"gets merged\n> to 'master' before GSoC ends\" [*1*]) clearly spelled out in the\n> application.  Something like that would help managing the\n> expectation and biting way too much for a summer, I'd hope.\n> \n>     Side note *1*.  Of course, depending on the alignment of the\n>     stars ^W our ~10-12 week development cycle and the end of GSoC,\n>     getting merged to 'master' might become impossible if it\n>     coincides with the pre-release freeze period.  But we on the\n>     list and the mentors know how the project works, and can help\n>     stating a more realistic success criterion if the development\n>     cycle and other quirks specific to this project gets in the way.\n> \n> Thanks.\n> \n"},{"id":"369742","messageId":"20190220223028.GN6085@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"xmqqmumrvzwk.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v12 18/26] stash: convert push to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-20T22:30:29Z","receivedAt":"2019-02-20T22:30:35Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 02/19, Junio C Hamano wrote:\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> >> Now, I seriously believe that we missed the best time to move\n> >> ps/stash-in-c into `next` for cooking. The best time would have been just\n> >> ...\n> >> Anyway, that's my plan for now.\n> >\n> > I must say I am not very happy about this plan.  The series has been\n> > marked as \"Will merge to 'next'\" in previous iterations, but then we\n> > found some issues that prevented that.  However I thought we were fine\n> > fixing those on top at this point, rather than starting with a new\n> > iteration again.\n> \n> First before going into anything else, let me thank, and let me\n> invite readers of this thread to join me thanking, Paul (Sebi) for\n> sticking with this topic for this long.  It is above and beyond what\n> GSoC calls for.\n\nIndeed, thanks for all your work on this Paul-Sebastian!\n\n> Having said that.\n> \n> I too was somehow led to believe that the topic was in a good enough\n> shape, with some room for clean-up by reordering the patches to make\n> them into a more logical progression and squashing an existing and\n> recently figured out \"oops, that was wrong\" fixes into the patches\n> where the breakages originate.\n> \n> And that was where the \"Will merge to\" originally came from.  Thanks\n> to tools like range-diff, a topic that goes through such reordering\n> and squashing of patches should not have to \"waste\" a lot of review\n> cycles out of those who have seen the previous round.\n\nRight, I had the impression that we were okay with doing the cleanups\non top of what is already in 'pu'.  Especially since the topic with\nJohannes Sixt's patch on top was marked as \"Will merge to 'next'\" at\none point if I remember correctly.  I didn't think that it being in\n'pu' vs. it being in 'next' would make too much of a difference there,\nand that it's just a by-product of where in the development cycle we\nare rather than an indication of which way we are taking the branch.\n\n> It however is a totally different matter if the topic was so\n> unsalvageable that it needs a total rewrite---that would need\n> another round of careful review, of course, and it would be\n> irresponsive to merge a topic in such a messy state to 'next'.  But\n> my impression was that the topic was not _that_ bad, so Dscho's\n> message and the plan were something that was totally unexpected to\n> me, too..\n\nIndeed, the topic did not get any worse over the time it was in 'pu',\nindeed it got a couple of fixes on top.  And my impression was that\nDscho still would have wanted to get the topic merged to next much\nearlier, so I don't quite understand what changed since then, other\nthan getting a few fixes on top.\n\n> > I was always under the impression that once the problem that was\n> > discovered here was fixed we'd advance the series to 'next' with the\n> > patch that comes out of this discussion on top.  Whether it's in next\n> > shortly before 2.21 or not doesn't seem to make much of a difference\n> > to me, as this wasn't going to make the 2.21 release anyway.  My hope\n> > was that we could get it into 'next' shortly after 2.21 is released to\n> > get the series some further exposure (which may well turn up some\n> > other issues that we are not aware of yet, but such is the life of\n> > software).\n> \n> I was hoping similar, but also was hoping that people would use the\n> time wisely while waiting for the next cycle to polish the topic with\n> reordering and squashing, so that it can hit 'next' early once the\n> tree opens.\n\nI'd be happy to do this myself, but as mentioned above I thought we\nwere okay with just having the patches on top, and leave the work\nPaul-Sebastian sent until now as it was.  If that impression was wrong\nI'm happy to put in some work to help with the cleanup.\n\n> Anyway.\n> \n> I actually have a different issue with this topic, though.  It is\n> wonderful to see a GSoC student's continued involvement in the\n> project, but it is not healthy that we need so much work on top of\n> what was marked \"done\" at the end of the GSoC period.  Especially\n> the impression I am getting for the post GSoC work of this topic is\n> not \"we are already done converting to built-in during GSoC, and now\n> we are extending the command\", but \"we ran out of time during GSoC;\n> here is what we would have seen at the end of GSoC in an ideal\n> world.\"\n> \n> I wonder if this is an unfortunate indication that our expectation\n> is unrealistically high when we accept students' applications.\n> Being overly ambitious is *not* students' fault, but those of us on\n> the list, especially those who mentor, have far deeper experience\n> with how our code and project is structured than any students do.\n> We should be able to, and should not hesitate to, say things like\n> \"that's overly ambitious---for such and such, you'd need to even\n> invent an internal API---can we reduce the scope and still produce a\n> useful end result?\"\n> \n> One suggestion I have is to have success criteria (e.g. \"gets merged\n> to 'master' before GSoC ends\" [*1*]) clearly spelled out in the\n> application.  Something like that would help managing the\n> expectation and biting way too much for a summer, I'd hope.\n\n[Adding Christian and Olga to cc here as this discussion should be\ninteresting to them as well as GSoC mentors]\n\nI think one thing we underestimated at least here is how long it takes\nfrom \"everything that we intended to do is done\" to \"this is reviewed\nand ready to merge into 'next' and 'master'\".  This is partly my fault\nas well because it took me quite a while to review the series on the\nlist, which certainly didn't help in moving things along.\n\nWhile having set criteria is a good idea, I think we should still give\nsome leeway to the mentors in terms of the actual success/fail rating\nin the program.  Not getting things merged is definitely not a good\nexperience, but it would be much worse to not get it merged, and also\nfail GSoC and not getting paid for the efforts over the summer.  We\nshouldn't punish students for the failure of the mentors to estimate\nprojects correctly.\n\nOne thing we should do I think is to say the project should be\n\"complete\" at least a month before the end of GSoC, and that the last\nmonth should only be dedicated to polishing the patches.  I don't know\nhow to make sure the students still have enough work to do during that\nlast period while they are just waiting for reviewers.\n\nAn other alternative (just thinking out loud here) is to make sure the\nproject has a few deliverables that can and will be merged\nindividually, even if they later build on top of eachother.  This\nwould mean getting students to send multiple series, when the patches\nmay normally go better in a single series, but I think it would help\nmoving the review cycle along faster.\n\nDunno, maybe there are other alternatives that I'm missing as well.\n\n>     Side note *1*.  Of course, depending on the alignment of the\n>     stars ^W our ~10-12 week development cycle and the end of GSoC,\n>     getting merged to 'master' might become impossible if it\n>     coincides with the pre-release freeze period.  But we on the\n>     list and the mentors know how the project works, and can help\n>     stating a more realistic success criterion if the development\n>     cycle and other quirks specific to this project gets in the way.\n> \n> Thanks.\n"},{"id":"370157","messageId":"20190225231631.30507-1-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1902191127420.41@tvgsbejvaqbjf.bet","subject":"[PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:04Z","receivedAt":"2019-02-25T23:16:50Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"As I was advocating for this series to go into 'next' without a large\nrefactor of this series, I'll put my money were my mouth is and try to\nmake the cleanups and fixes required, though without trying to avoid\nfurther external process calls, or changing the series around too\nmuch.\n\nOne thing to consider here is that we have a GSoC project planned\nbased on 'git stash'.  If we can't get this to 'next' soon, I'd vote\nfor taking that project out of this years GSoC, and maybe try again\nnext year, if nobody implemented the feature in the meantime.\n\nOne thing that came up in the latest reviews, was to keep the stash\nscript intact throughout the series, and to not re-introduce it after\ndeleting it.  I did however not do that, as that would make the\nrange-diff quite a bit harder to read.  In addition removing the\nscript bit by bit also allowed us to find the precise commit in which\nthe missing 'discard_cache()' bug was introduced, which made it a bit\neasier to pinpoint where the bug comes from imo.\n\nAdditionally now that we established that we re-introduce the same\nshell script in a previous review, it's now easy to see that we're\nstill doing the right thing in this patch series.\n\nSo what did change in this round?\n- Squashed Johannes Sixt patch into the relevant patch\n- Pulled out the test from Matthew Kraai into a separate patch, and\n  squashed the rest of the patch into the relevant commit\n(The two changes above were credited by adding Helped-by trailers)\n- Fixed the missing discard_cache before reset --hard and apply\n  --index -R.  t3903-stash.sh --stress now passed more than 250\n  iterations without error.\n- Addressed my and Junios review comments on the previous round\n- Some small fixups\n\nrange-diff below:\n\n 1:  0c1599c33c !  1:  baa5d369e4 strbuf.c: add `strbuf_join_argv()`\n    @@ -6,7 +6,7 @@\n         into a strbuf.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/strbuf.c b/strbuf.c\n      --- a/strbuf.c\n 2:  bfc3fe33f6 !  2:  08bb77502c strbuf.c: add `strbuf_insertf()` and `strbuf_vinsertf()`\n    @@ -7,7 +7,8 @@\n     \n         Original-idea-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Helped-by: Johannes Sixt <j6t@kdbg.org>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/strbuf.c b/strbuf.c\n      --- a/strbuf.c\n    @@ -37,7 +38,7 @@\n     +\tmemmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);\n     +\t/* vsnprintf() will append a NUL, overwriting one of our characters */\n     +\tsave = sb->buf[pos + len];\n    -+\tlen2 = vsnprintf(sb->buf + pos, sb->alloc - sb->len, fmt, ap);\n    ++\tlen2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);\n     +\tsb->buf[pos + len] = save;\n     +\tif (len2 != len)\n     +\t\tBUG(\"your vsnprintf is broken (returns inconsistent lengths)\");\n 3:  97f56073ce !  3:  2956b2bd27 ident: add the ability to provide a \"fallback identity\"\n    @@ -12,7 +12,9 @@\n         function.\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    [tg: add docs; make it a bug to call the function before other\n    +    functions in the ident API]\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/cache.h b/cache.h\n      --- a/cache.h\n    @@ -21,6 +23,10 @@\n      extern const char *git_pager(int stdout_is_tty);\n      extern int is_terminal_dumb(void);\n      extern int git_ident_config(const char *, const char *, void *);\n    ++/*\n    ++ * Prepare an ident to fall back on if the user didn't configure it.\n    ++ * Must be called before any other function from the ident API.\n    ++ */\n     +void prepare_fallback_ident(const char *name, const char *email);\n      extern void reset_ident_date(void);\n      \n    @@ -35,7 +41,9 @@\n      \n     +static void set_env_if(const char *key, const char *value, int *given, int bit)\n     +{\n    -+\tif ((*given & bit) || getenv(key))\n    ++\tif (*given & bit)\n    ++\t\tBUG(\"%s was checked before prepare_fallback got called\", key);\n    ++\tif (getenv(key))\n     +\t\treturn; /* nothing to do */\n     +\tsetenv(key, value, 0);\n     +\t*given |= bit;\n 4:  0654fe70a8 !  4:  55db527e68 stash: improve option parsing test coverage\n    @@ -8,7 +8,7 @@\n     \n         Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\n      --- a/t/t3903-stash.sh\n 5:  caf7bc3cc6 !  5:  7a5eb3a9e8 t3903: modernize style\n    @@ -6,7 +6,7 @@\n         long lines.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\n      --- a/t/t3903-stash.sh\n26:  ed5d77f7d3 !  6:  453543916d stash: fix segmentation fault when files were added with intent\n    @@ -1,45 +1,17 @@\n     Author: Matthew Kraai <mkraai@its.jnj.com>\n     \n    -    stash: fix segmentation fault when files were added with intent\n    +    t3903: add test for --intent-to-add file\n     \n    -    After `git add -N <file>`, the index is in a special state. A state for\n    -    which the built-in stash was not prepared, as it failed to initialize\n    -    the `rev` structure in that case before using `&rev.pending`.  If\n    -    `reset_tree()` returns a non-zero value, `stash_working_tree()`\n    -    calls `object_array_clear()` with `&rev.pending`.  If `rev` is not\n    -    initialized, this causes a segmentation fault.\n    +    Add a test showing the 'git stash' behaviour with a file that has been\n    +    added with 'git add --intent-to-add'.  Stash fails to stash the file,\n    +    so the purpose of this test is mainly to make sure git doesn't crash,\n    +    but exits normally in this situation.\n     \n    -    Prevent this by initializing `rev` before calling `reset_tree()`.\n    -\n    -    This fixes https://github.com/git-for-windows/git/issues/2006.\n    -\n    -    [jes: modified the commit message in preparation for sending this patch\n    -    to the Git mailing list.]\n    +    [tg: pulled the test out into a separate commit]\n     \n         Signed-off-by: Matthew Kraai <mkraai@its.jnj.com>\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    -\n    - diff --git a/builtin/stash.c b/builtin/stash.c\n    - --- a/builtin/stash.c\n    - +++ b/builtin/stash.c\n    -@@\n    - \tstruct strbuf diff_output = STRBUF_INIT;\n    - \tstruct index_state istate = { NULL };\n    - \n    -+\tinit_revisions(&rev, NULL);\n    -+\n    - \tset_alternate_index_output(stash_index_path.buf);\n    - \tif (reset_tree(&info->i_tree, 0, 0)) {\n    - \t\tret = -1;\n    -@@\n    - \t}\n    - \tset_alternate_index_output(NULL);\n    - \n    --\tinit_revisions(&rev, NULL);\n    - \trev.prune_data = ps;\n    - \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n    - \trev.diffopt.format_callback = add_diff_to_buf;\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\n      --- a/t/t3903-stash.sh\n 6:  ee77c6a603 !  7:  587d3e8e49 stash: rename test cases to be more descriptive\n    @@ -6,7 +6,7 @@\n         characters per line.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\n      --- a/t/t3903-stash.sh\n 7:  cb2152ebce !  8:  b03c17e744 stash: add tests for `git stash show` config\n    @@ -7,7 +7,7 @@\n         and `stash.showPatch` are unset or set to true / false.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh\n      new file mode 100755\n 8:  983084d9ec !  9:  010c5f4ce2 stash: mention options in `show` synopsis\n    @@ -6,7 +6,7 @@\n         option known to `git diff`.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\n      --- a/Documentation/git-stash.txt\n 9:  f6bbd78127 ! 10:  45670448e8 stash: convert apply to builtin\n    @@ -17,7 +17,7 @@\n     \n         Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/.gitignore b/.gitignore\n      --- a/.gitignore\n    @@ -96,7 +96,6 @@\n     + * i_tree is set to the index tree\n     + * u_tree is set to the untracked files tree\n     + */\n    -+\n     +struct stash_info {\n     +\tstruct object_id w_commit;\n     +\tstruct object_id b_commit;\n    @@ -357,7 +356,7 @@\n     +\tif (refresh_cache(REFRESH_QUIET))\n     +\t\treturn -1;\n     +\n    -+\tif (write_cache_as_tree(&c_tree, 0, NULL) || reset_tree(&c_tree, 0, 0))\n    ++\tif (write_cache_as_tree(&c_tree, 0, NULL))\n     +\t\treturn error(_(\"cannot apply a stash in the middle of a merge\"));\n     +\n     +\tif (index) {\n10:  cdca49bc4c ! 11:  cea038dd3c stash: convert drop and clear to builtin\n    @@ -13,7 +13,7 @@\n     \n         Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -128,9 +128,9 @@\n     +static void assert_stash_ref(struct stash_info *info)\n     +{\n     +\tif (!info->is_stash_ref) {\n    -+\t\tfree_stash_info(info);\n     +\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n    -+\t\texit(128);\n    ++\t\tfree_stash_info(info);\n    ++\t\texit(1);\n     +\t}\n     +}\n     +\n11:  f596f3366c ! 12:  deb4f6cee9 stash: convert branch to builtin\n    @@ -11,7 +11,7 @@\n     \n         Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n12:  e1d01876a4 ! 13:  75f9431abf stash: convert pop to builtin\n    @@ -8,7 +8,7 @@\n     \n         Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n13:  9b77b07ba4 ! 14:  f6814704ee stash: convert list to builtin\n    @@ -6,7 +6,7 @@\n         from the shell script.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n14:  b4493f269e ! 15:  d1098f2c8e stash: convert show to builtin\n    @@ -11,7 +11,7 @@\n         further to `git diff`.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n15:  847eb0b0a8 ! 16:  9706cab487 stash: convert store to builtin\n    @@ -6,7 +6,7 @@\n         from the shell script.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n16:  1f5a011d90 ! 17:  a5d6a3bd14 stash: convert create to builtin\n    @@ -5,7 +5,8 @@\n         Add stash create to the helper.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Helped-by: Matthew Kraai <mkraai@its.jnj.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -45,11 +46,7 @@\n     +\tfor (i = 0; i < q->nr; i++) {\n     +\t\tstrbuf_addstr(data, q->queue[i]->one->path);\n     +\n    -+\t\t/*\n    -+\t\t * The reason we add \"0\" at the end of this strbuf\n    -+\t\t * is because we will pass the output further to\n    -+\t\t * \"git update-index -z ...\".\n    -+\t\t */\n    ++\t\t/* NUL-terminate: will be fed to update-index -z */\n     +\t\tstrbuf_addch(data, '\\0');\n     +\t}\n     +}\n    @@ -98,7 +95,7 @@\n     +\t\t\tfound++;\n     +\t\t\tstrbuf_addstr(untracked_files, ent->name);\n     +\t\t\t/* NUL-terminate: will be fed to update-index -z */\n    -+\t\t\tstrbuf_addch(untracked_files, 0);\n    ++\t\t\tstrbuf_addch(untracked_files, '\\0');\n     +\t\t}\n     +\t\tfree(ent);\n     +\t}\n    @@ -278,6 +275,8 @@\n     +\tstruct strbuf out = STRBUF_INIT;\n     +\tstruct strbuf diff_output = STRBUF_INIT;\n     +\n    ++\tinit_revisions(&rev, NULL);\n    ++\n     +\tset_alternate_index_output(stash_index_path.buf);\n     +\tif (reset_tree(&info->i_tree, 0, 0)) {\n     +\t\tret = -1;\n    @@ -285,7 +284,6 @@\n     +\t}\n     +\tset_alternate_index_output(NULL);\n     +\n    -+\tinit_revisions(&rev, NULL);\n     +\trev.prune_data = ps;\n     +\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n     +\trev.diffopt.format_callback = add_diff_to_buf;\n    @@ -471,7 +469,6 @@\n     +\tmemset(&ps, 0, sizeof(ps));\n     +\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n     +\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n    -+\n     +\tif (!ret)\n     +\t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n     +\n17:  fa38428f76 ! 18:  3065f08c65 stash: convert push to builtin\n    @@ -5,7 +5,7 @@\n         Add stash push to the helper.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -67,9 +67,9 @@\n     -\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n     +\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n     +\t\t\t      NULL);\n    - \n      \tif (!ret)\n      \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n    + \n     @@\n      \treturn ret < 0;\n      }\n    @@ -158,6 +158,7 @@\n     +\t\t\t\tgoto done;\n     +\t\t\t}\n     +\t\t}\n    ++\t\tdiscard_cache();\n     +\t\tif (ps.nr) {\n     +\t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n     +\t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n18:  9a95010a11 ! 19:  31dd0edc0c stash: make push -q quiet\n    @@ -8,7 +8,7 @@\n         `--quiet` or `-q` is specified. Add tests for `--quiet`.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -118,9 +118,9 @@\n      \tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n     -\t\t\t      NULL);\n     +\t\t\t      NULL, 0);\n    - \n      \tif (!ret)\n      \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n    + \n     @@\n      \n      \tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n19:  cf5b27d699 ! 20:  f5644a4fdc stash: convert save to builtin\n    @@ -8,7 +8,7 @@\n         `no_changes()`).\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n20:  168e6cff5e ! 21:  367511ab8f stash: optimize `get_untracked_files()` and `check_changes()`\n    @@ -29,10 +29,8 @@\n         This way `check_changes()` and `get_untracked files()` are called\n         only one time.\n     \n    -    https://public-inbox.org/git/20180818223329.GJ11326@hank.intra.tgummerer.com/\n    -\n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -49,7 +47,6 @@\n       * > 0 if there are changes.\n       */\n     -static int check_changes(struct pathspec ps, int include_untracked)\n    -+\n     +static int check_changes_tracked_files(struct pathspec ps)\n      {\n      \tint result;\n    @@ -70,7 +67,6 @@\n     + * The function will fill `untracked_files` with the names of untracked files\n     + * It will return 1 if there were any changes and 0 if there were not.\n     + */\n    -+\n     +static int check_changes(struct pathspec ps, int include_untracked,\n     +\t\t\t struct strbuf *untracked_files)\n     +{\n    @@ -115,15 +111,13 @@\n      \t\t\t     0);\n      \n      \tmemset(&ps, 0, sizeof(ps));\n    --\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n    --\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n    --\t\t\t      NULL, 0);\n     +\tif (!check_changes_tracked_files(ps))\n     +\t\treturn 0;\n    - \n    --\tif (!ret)\n    -+\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n    -+\tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n    ++\n    + \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n    + \tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n    + \t\t\t      NULL, 0);\n    +@@\n      \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n      \n      \tstrbuf_release(&stash_msg_buf);\n21:  559edead8f ! 22:  376bb4adc9 stash: replace all `write-tree` child processes with API calls\n    @@ -2,10 +2,11 @@\n     \n         stash: replace all `write-tree` child processes with API calls\n     \n    -    This commit replaces spawning `git write-tree` with API calls.\n    +    Avoid spawning write-tree child processes by replacing the calls with\n    +    in-core API calls.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\n      --- a/builtin/stash--helper.c\n    @@ -100,8 +101,8 @@\n      \tstruct strbuf diff_output = STRBUF_INIT;\n     +\tstruct index_state istate = { NULL };\n      \n    - \tset_alternate_index_output(stash_index_path.buf);\n    - \tif (reset_tree(&info->i_tree, 0, 0)) {\n    + \tinit_revisions(&rev, NULL);\n    + \n     @@\n      \t\tgoto done;\n      \t}\n22:  51809c70ca ! 23:  56a5ce2aeb stash: convert `stash--helper.c` into `stash.c`\n    @@ -13,7 +13,7 @@\n         called directly and not by a shell script.\n     \n         Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/.gitignore b/.gitignore\n      --- a/.gitignore\n    @@ -273,9 +273,11 @@\n      \t\treturn 0;\n      \n     -\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n    - \tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n    +-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n    ++\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n    + \t\t\t      NULL, 0);\n    + \tif (!ret)\n      \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n    - \n     @@\n      \t\tOPT_END()\n      \t};\n23:  c907fe1cd6 ! 24:  830c1d6dbe stash: add back the original, scripted `git stash`\n    @@ -9,7 +9,7 @@\n         scripted `git stash` when `stash.useBuiltin=false`.\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/git-stash.sh b/git-stash.sh\n      new file mode 100755\n24:  26799a208f ! 25:  00fb753d5e stash: optionally use the scripted version again\n    @@ -17,7 +17,7 @@\n         code 129 for `git stash -h`.\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/.gitignore b/.gitignore\n      --- a/.gitignore\n25:  bec65d5b78 ! 26:  49b7f82db9 tests: add a special setup where stash.useBuiltin is off\n    @@ -12,7 +12,7 @@\n         2018-11-14).\n     \n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n     \n      diff --git a/builtin/stash.c b/builtin/stash.c\n      --- a/builtin/stash.c\n27:  8187099d9c <  -:  ---------- strbuf_vinsertf: provide the correct buffer size to vsnprintf\n\n\nJoel Teichroeb (5):\n  stash: improve option parsing test coverage\n  stash: convert apply to builtin\n  stash: convert drop and clear to builtin\n  stash: convert branch to builtin\n  stash: convert pop to builtin\n\nJohannes Schindelin (4):\n  ident: add the ability to provide a \"fallback identity\"\n  stash: add back the original, scripted `git stash`\n  stash: optionally use the scripted version again\n  tests: add a special setup where stash.useBuiltin is off\n\nMatthew Kraai (1):\n  t3903: add test for --intent-to-add file\n\nPaul-Sebastian Ungureanu (17):\n  sha1-name.c: add `get_oidf()` which acts like `get_oid()`\n  strbuf.c: add `strbuf_join_argv()`\n  strbuf.c: add `strbuf_insertf()` and `strbuf_vinsertf()`\n  t3903: modernize style\n  stash: rename test cases to be more descriptive\n  stash: add tests for `git stash show` config\n  stash: mention options in `show` synopsis\n  stash: convert list to builtin\n  stash: convert show to builtin\n  stash: convert store to builtin\n  stash: convert create to builtin\n  stash: convert push to builtin\n  stash: make push -q quiet\n  stash: convert save to builtin\n  stash: optimize `get_untracked_files()` and `check_changes()`\n  stash: replace all `write-tree` child processes with API calls\n  stash: convert `stash--helper.c` into `stash.c`\n\n .gitignore                          |    1 +\n Documentation/git-stash.txt         |    4 +-\n Makefile                            |    3 +-\n builtin.h                           |    1 +\n builtin/stash.c                     | 1633 +++++++++++++++++++++++++++\n cache.h                             |    6 +\n git-stash.sh => git-legacy-stash.sh |   34 +-\n git-sh-setup.sh                     |    1 +\n git.c                               |    6 +\n ident.c                             |   22 +\n sha1-name.c                         |   19 +\n strbuf.c                            |   51 +\n strbuf.h                            |   16 +\n t/README                            |    4 +\n t/t3903-stash.sh                    |  200 ++--\n t/t3907-stash-show-config.sh        |   83 ++\n 16 files changed, 2012 insertions(+), 72 deletions(-)\n create mode 100644 builtin/stash.c\n rename git-stash.sh => git-legacy-stash.sh (97%)\n create mode 100755 t/t3907-stash-show-config.sh\n\n-- \n2.21.0.rc2.291.g17236886c5\n"},{"id":"370158","messageId":"20190225231631.30507-2-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 01/27] sha1-name.c: add `get_oidf()` which acts like `get_oid()`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:05Z","receivedAt":"2019-02-25T23:16:50Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nCompared to `get_oid()`, `get_oidf()` has as parameters\na pointer to `object_id`, a printf format string and\nadditional arguments. This will help simplify the code\nin subsequent commits.\n\nOriginal-idea-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n cache.h     |  1 +\n sha1-name.c | 19 +++++++++++++++++++\n 2 files changed, 20 insertions(+)\n\ndiff --git a/cache.h b/cache.h\nindex ca36b44ee0..7b6b89fc4c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1333,6 +1333,7 @@ struct object_context {\n \tGET_OID_BLOB)\n \n extern int get_oid(const char *str, struct object_id *oid);\n+extern int get_oidf(struct object_id *oid, const char *fmt, ...);\n extern int get_oid_commit(const char *str, struct object_id *oid);\n extern int get_oid_committish(const char *str, struct object_id *oid);\n extern int get_oid_tree(const char *str, struct object_id *oid);\ndiff --git a/sha1-name.c b/sha1-name.c\nindex faa60f69e3..cf0e8a3f85 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -1542,6 +1542,25 @@ int get_oid(const char *name, struct object_id *oid)\n \treturn get_oid_with_context(name, 0, oid, &unused);\n }\n \n+/*\n+ * This returns a non-zero value if the string (built using printf\n+ * format and the given arguments) is not a valid object.\n+ */\n+int get_oidf(struct object_id *oid, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tint ret;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tva_end(ap);\n+\n+\tret = get_oid(sb.buf, oid);\n+\tstrbuf_release(&sb);\n+\n+\treturn ret;\n+}\n \n /*\n  * Many callers know that the user meant to name a commit-ish by\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370159","messageId":"20190225231631.30507-3-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 02/27] strbuf.c: add `strbuf_join_argv()`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:06Z","receivedAt":"2019-02-25T23:16:54Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nImplement `strbuf_join_argv()` to join arguments\ninto a strbuf.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n strbuf.c | 15 +++++++++++++++\n strbuf.h |  7 +++++++\n 2 files changed, 22 insertions(+)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex f6a6cf78b9..82e90f1dfe 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -268,6 +268,21 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)\n \tstrbuf_setlen(sb, sb->len + sb2->len);\n }\n \n+const char *strbuf_join_argv(struct strbuf *buf,\n+\t\t\t     int argc, const char **argv, char delim)\n+{\n+\tif (!argc)\n+\t\treturn buf->buf;\n+\n+\tstrbuf_addstr(buf, *argv);\n+\twhile (--argc) {\n+\t\tstrbuf_addch(buf, delim);\n+\t\tstrbuf_addstr(buf, *(++argv));\n+\t}\n+\n+\treturn buf->buf;\n+}\n+\n void strbuf_addchars(struct strbuf *sb, int c, size_t n)\n {\n \tstrbuf_grow(sb, n);\ndiff --git a/strbuf.h b/strbuf.h\nindex fc40873b65..be02150df3 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -288,6 +288,13 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)\n  */\n void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);\n \n+/**\n+ * Join the arguments into a buffer. `delim` is put between every\n+ * two arguments.\n+ */\n+const char *strbuf_join_argv(struct strbuf *buf, int argc,\n+\t\t\t     const char **argv, char delim);\n+\n /**\n  * This function can be used to expand a format string containing\n  * placeholders. To that end, it parses the string and calls the specified\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370160","messageId":"20190225231631.30507-4-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 03/27] strbuf.c: add `strbuf_insertf()` and `strbuf_vinsertf()`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:07Z","receivedAt":"2019-02-25T23:16:55Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nImplement `strbuf_insertf()` and `strbuf_vinsertf()` to\ninsert data using a printf format string.\n\nOriginal-idea-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nHelped-by: Johannes Sixt <j6t@kdbg.org>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n strbuf.c | 36 ++++++++++++++++++++++++++++++++++++\n strbuf.h |  9 +++++++++\n 2 files changed, 45 insertions(+)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 82e90f1dfe..87ecf7f975 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -249,6 +249,42 @@ void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)\n \tstrbuf_splice(sb, pos, 0, data, len);\n }\n \n+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)\n+{\n+\tint len, len2;\n+\tchar save;\n+\tva_list cp;\n+\n+\tif (pos > sb->len)\n+\t\tdie(\"`pos' is too far after the end of the buffer\");\n+\tva_copy(cp, ap);\n+\tlen = vsnprintf(sb->buf + sb->len, 0, fmt, cp);\n+\tva_end(cp);\n+\tif (len < 0)\n+\t\tBUG(\"your vsnprintf is broken (returned %d)\", len);\n+\tif (!len)\n+\t\treturn; /* nothing to do */\n+\tif (unsigned_add_overflows(sb->len, len))\n+\t\tdie(\"you want to use way too much memory\");\n+\tstrbuf_grow(sb, len);\n+\tmemmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);\n+\t/* vsnprintf() will append a NUL, overwriting one of our characters */\n+\tsave = sb->buf[pos + len];\n+\tlen2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);\n+\tsb->buf[pos + len] = save;\n+\tif (len2 != len)\n+\t\tBUG(\"your vsnprintf is broken (returns inconsistent lengths)\");\n+\tstrbuf_setlen(sb, sb->len + len);\n+}\n+\n+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tva_start(ap, fmt);\n+\tstrbuf_vinsertf(sb, pos, fmt, ap);\n+\tva_end(ap);\n+}\n+\n void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)\n {\n \tstrbuf_splice(sb, pos, len, \"\", 0);\ndiff --git a/strbuf.h b/strbuf.h\nindex be02150df3..8f8fe01e68 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -244,6 +244,15 @@ void strbuf_addchars(struct strbuf *sb, int c, size_t n);\n  */\n void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);\n \n+/**\n+ * Insert data to the given position of the buffer giving a printf format\n+ * string. The contents will be shifted, not overwritten.\n+ */\n+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,\n+\t\t     va_list ap);\n+\n+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);\n+\n /**\n  * Remove given amount of data from a given position of the buffer.\n  */\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370162","messageId":"20190225231631.30507-6-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 05/27] stash: improve option parsing test coverage","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:09Z","receivedAt":"2019-02-25T23:16:58Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nIn preparation for converting the stash command incrementally to\na builtin command, this patch improves test coverage of the option\nparsing. Both for having too many parameters, or too few.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n t/t3903-stash.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 5f8272b6f9..ac55629737 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -444,6 +444,36 @@ test_expect_failure 'stash file to directory' '\n \ttest foo = \"$(cat file/file)\"\n '\n \n+test_expect_success 'giving too many ref arguments does not modify files' '\n+\tgit stash clear &&\n+\ttest_when_finished \"git reset --hard HEAD\" &&\n+\techo foo >file2 &&\n+\tgit stash &&\n+\techo bar >file2 &&\n+\tgit stash &&\n+\ttest-tool chmtime =123456789 file2 &&\n+\tfor type in apply pop \"branch stash-branch\"\n+\tdo\n+\t\ttest_must_fail git stash $type stash@{0} stash@{1} 2>err &&\n+\t\ttest_i18ngrep \"Too many revisions\" err &&\n+\t\ttest 123456789 = $(test-tool chmtime -g file2) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'drop: too many arguments errors out (does nothing)' '\n+\tgit stash list >expect &&\n+\ttest_must_fail git stash drop stash@{0} stash@{1} 2>err &&\n+\ttest_i18ngrep \"Too many revisions\" err &&\n+\tgit stash list >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'show: too many arguments errors out (does nothing)' '\n+\ttest_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&\n+\ttest_i18ngrep \"Too many revisions\" err &&\n+\ttest_must_be_empty out\n+'\n+\n test_expect_success 'stash create - no changes' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n@@ -479,6 +509,11 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n+test_expect_success 'stash branch complains with no arguments' '\n+\ttest_must_fail git stash branch 2>err &&\n+\ttest_i18ngrep \"No branch name specified\" err\n+'\n+\n test_expect_success 'stash show format defaults to --stat' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370161","messageId":"20190225231631.30507-5-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 04/27] ident: add the ability to provide a \"fallback identity\"","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:08Z","receivedAt":"2019-02-25T23:16:59Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nIn 3bc2111fc2e9 (stash: tolerate missing user identity, 2018-11-18),\n`git stash` learned to provide a fallback identity for the case that no\nproper name/email was given (and `git stash` does not really care about\na correct identity anyway, but it does want to create a commit object).\n\nIn preparation for the same functionality in the upcoming built-in\nversion of `git stash`, let's offer the same functionality as an API\nfunction.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n[tg: add docs; make it a bug to call the function before other\nfunctions in the ident API]\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n cache.h |  5 +++++\n ident.c | 22 ++++++++++++++++++++++\n 2 files changed, 27 insertions(+)\n\ndiff --git a/cache.h b/cache.h\nindex 7b6b89fc4c..611e554dea 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1491,6 +1491,11 @@ extern const char *git_sequence_editor(void);\n extern const char *git_pager(int stdout_is_tty);\n extern int is_terminal_dumb(void);\n extern int git_ident_config(const char *, const char *, void *);\n+/*\n+ * Prepare an ident to fall back on if the user didn't configure it.\n+ * Must be called before any other function from the ident API.\n+ */\n+void prepare_fallback_ident(const char *name, const char *email);\n extern void reset_ident_date(void);\n \n struct ident_split {\ndiff --git a/ident.c b/ident.c\nindex 33bcf40644..f30bd623f0 100644\n--- a/ident.c\n+++ b/ident.c\n@@ -505,6 +505,28 @@ int git_ident_config(const char *var, const char *value, void *data)\n \treturn 0;\n }\n \n+static void set_env_if(const char *key, const char *value, int *given, int bit)\n+{\n+\tif (*given & bit)\n+\t\tBUG(\"%s was checked before prepare_fallback got called\", key);\n+\tif (getenv(key))\n+\t\treturn; /* nothing to do */\n+\tsetenv(key, value, 0);\n+\t*given |= bit;\n+}\n+\n+void prepare_fallback_ident(const char *name, const char *email)\n+{\n+\tset_env_if(\"GIT_AUTHOR_NAME\", name,\n+\t\t   &author_ident_explicitly_given, IDENT_NAME_GIVEN);\n+\tset_env_if(\"GIT_AUTHOR_EMAIL\", email,\n+\t\t   &author_ident_explicitly_given, IDENT_MAIL_GIVEN);\n+\tset_env_if(\"GIT_COMMITTER_NAME\", name,\n+\t\t   &committer_ident_explicitly_given, IDENT_NAME_GIVEN);\n+\tset_env_if(\"GIT_COMMITTER_EMAIL\", email,\n+\t\t   &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);\n+}\n+\n static int buf_cmp(const char *a_begin, const char *a_end,\n \t\t   const char *b_begin, const char *b_end)\n {\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370163","messageId":"20190225231631.30507-7-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 06/27] t3903: modernize style","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:10Z","receivedAt":"2019-02-25T23:17:01Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nRemove whitespaces after redirection operators and wrap\nlong lines.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n t/t3903-stash.sh | 120 ++++++++++++++++++++++++-----------------------\n 1 file changed, 61 insertions(+), 59 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex ac55629737..4e83facf23 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -8,22 +8,22 @@ test_description='Test git stash'\n . ./test-lib.sh\n \n test_expect_success 'stash some dirty working directory' '\n-\techo 1 > file &&\n+\techo 1 >file &&\n \tgit add file &&\n \techo unrelated >other-file &&\n \tgit add other-file &&\n \ttest_tick &&\n \tgit commit -m initial &&\n-\techo 2 > file &&\n+\techo 2 >file &&\n \tgit add file &&\n-\techo 3 > file &&\n+\techo 3 >file &&\n \ttest_tick &&\n \tgit stash &&\n \tgit diff-files --quiet &&\n \tgit diff-index --cached --quiet HEAD\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/file b/file\n index 0cfbf08..00750ed 100644\n --- a/file\n@@ -35,7 +35,7 @@ EOF\n \n test_expect_success 'parents of stash' '\n \ttest $(git rev-parse stash^) = $(git rev-parse HEAD) &&\n-\tgit diff stash^2..stash > output &&\n+\tgit diff stash^2..stash >output &&\n \ttest_cmp expect output\n '\n \n@@ -74,7 +74,7 @@ test_expect_success 'apply stashed changes' '\n \n test_expect_success 'apply stashed changes (including index)' '\n \tgit reset --hard HEAD^ &&\n-\techo 6 > other-file &&\n+\techo 6 >other-file &&\n \tgit add other-file &&\n \ttest_tick &&\n \tgit commit -m other-file &&\n@@ -99,12 +99,12 @@ test_expect_success 'stash drop complains of extra options' '\n \n test_expect_success 'drop top stash' '\n \tgit reset --hard &&\n-\tgit stash list > stashlist1 &&\n-\techo 7 > file &&\n+\tgit stash list >expected &&\n+\techo 7 >file &&\n \tgit stash &&\n \tgit stash drop &&\n-\tgit stash list > stashlist2 &&\n-\ttest_cmp stashlist1 stashlist2 &&\n+\tgit stash list >actual &&\n+\ttest_cmp expected actual &&\n \tgit stash apply &&\n \ttest 3 = $(cat file) &&\n \ttest 1 = $(git show :file) &&\n@@ -113,9 +113,9 @@ test_expect_success 'drop top stash' '\n \n test_expect_success 'drop middle stash' '\n \tgit reset --hard &&\n-\techo 8 > file &&\n+\techo 8 >file &&\n \tgit stash &&\n-\techo 9 > file &&\n+\techo 9 >file &&\n \tgit stash &&\n \tgit stash drop stash@{1} &&\n \ttest 2 = $(git stash list | wc -l) &&\n@@ -160,7 +160,7 @@ test_expect_success 'stash pop' '\n \ttest 0 = $(git stash list | wc -l)\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/file2 b/file2\n new file mode 100644\n index 0000000..1fe912c\n@@ -170,7 +170,7 @@ index 0000000..1fe912c\n +bar2\n EOF\n \n-cat > expect1 << EOF\n+cat >expect1 <<EOF\n diff --git a/file b/file\n index 257cc56..5716ca5 100644\n --- a/file\n@@ -180,7 +180,7 @@ index 257cc56..5716ca5 100644\n +bar\n EOF\n \n-cat > expect2 << EOF\n+cat >expect2 <<EOF\n diff --git a/file b/file\n index 7601807..5716ca5 100644\n --- a/file\n@@ -198,79 +198,79 @@ index 0000000..1fe912c\n EOF\n \n test_expect_success 'stash branch' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit commit file -m first &&\n-\techo bar > file &&\n-\techo bar2 > file2 &&\n+\techo bar >file &&\n+\techo bar2 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n-\techo baz > file &&\n+\techo baz >file &&\n \tgit commit file -m second &&\n \tgit stash branch stashbranch &&\n \ttest refs/heads/stashbranch = $(git symbolic-ref HEAD) &&\n \ttest $(git rev-parse HEAD) = $(git rev-parse master^) &&\n-\tgit diff --cached > output &&\n+\tgit diff --cached >output &&\n \ttest_cmp expect output &&\n-\tgit diff > output &&\n+\tgit diff >output &&\n \ttest_cmp expect1 output &&\n \tgit add file &&\n \tgit commit -m alternate\\ second &&\n-\tgit diff master..stashbranch > output &&\n+\tgit diff master..stashbranch >output &&\n \ttest_cmp output expect2 &&\n \ttest 0 = $(git stash list | wc -l)\n '\n \n test_expect_success 'apply -q is quiet' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\tgit stash apply -q > output.out 2>&1 &&\n+\tgit stash apply -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'save -q is quiet' '\n-\tgit stash save --quiet > output.out 2>&1 &&\n+\tgit stash save --quiet >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'pop -q is quiet' '\n-\tgit stash pop -q > output.out 2>&1 &&\n+\tgit stash pop -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'pop -q --index works and is quiet' '\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit add file &&\n \tgit stash save --quiet &&\n-\tgit stash pop -q --index > output.out 2>&1 &&\n+\tgit stash pop -q --index >output.out 2>&1 &&\n \ttest foo = \"$(git show :file)\" &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'drop -q is quiet' '\n \tgit stash &&\n-\tgit stash drop -q > output.out 2>&1 &&\n+\tgit stash drop -q >output.out 2>&1 &&\n \ttest_must_be_empty output.out\n '\n \n test_expect_success 'stash -k' '\n-\techo bar3 > file &&\n-\techo bar4 > file2 &&\n+\techo bar3 >file &&\n+\techo bar4 >file2 &&\n \tgit add file2 &&\n \tgit stash -k &&\n \ttest bar,bar4 = $(cat file),$(cat file2)\n '\n \n test_expect_success 'stash --no-keep-index' '\n-\techo bar33 > file &&\n-\techo bar44 > file2 &&\n+\techo bar33 >file &&\n+\techo bar44 >file2 &&\n \tgit add file2 &&\n \tgit stash --no-keep-index &&\n \ttest bar,bar2 = $(cat file),$(cat file2)\n '\n \n test_expect_success 'stash --invalid-option' '\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \ttest_must_fail git stash --invalid-option &&\n \ttest_must_fail git stash save --invalid-option &&\n@@ -486,11 +486,12 @@ test_expect_success 'stash branch - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tgit stash branch stash-branch ${STASH_ID} &&\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master && git branch -D stash-branch\" &&\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master &&\n+\tgit branch -D stash-branch\" &&\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n@@ -498,14 +499,15 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tgit stash branch stash-branch ${STASH_ID} &&\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master && git branch -D stash-branch\" &&\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master &&\n+\tgit branch -D stash-branch\" &&\n \ttest $(git ls-files --modified | wc -l) -eq 1\n '\n \n@@ -518,10 +520,10 @@ test_expect_success 'stash show format defaults to --stat' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -536,10 +538,10 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \techo \"1\t0\tfile\" >expected &&\n@@ -551,10 +553,10 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tgit stash &&\n \ttest_when_finished \"git stash drop\" &&\n-\techo bar >> file &&\n+\techo bar >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -574,7 +576,7 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \techo \"1\t0\tfile\" >expected &&\n@@ -586,7 +588,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD\" &&\n \tgit reset --hard &&\n-\techo foo >> file &&\n+\techo foo >>file &&\n \tSTASH_ID=$(git stash create) &&\n \tgit reset --hard &&\n \tcat >expected <<-EOF &&\n@@ -606,9 +608,9 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\techo bar > file &&\n+\techo bar >file &&\n \tgit stash &&\n \ttest_must_fail git stash drop $(git rev-parse stash@{0}) &&\n \tgit stash pop &&\n@@ -620,9 +622,9 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n-\techo foo > file &&\n+\techo foo >file &&\n \tgit stash &&\n-\techo bar > file &&\n+\techo bar >file &&\n \tgit stash &&\n \ttest_must_fail git stash pop $(git rev-parse stash@{0}) &&\n \tgit stash pop &&\n@@ -632,8 +634,8 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re\n \n test_expect_success 'ref with non-existent reflog' '\n \tgit stash clear &&\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n \ttest_must_fail git rev-parse --quiet --verify does-not-exist &&\n@@ -653,8 +655,8 @@ test_expect_success 'ref with non-existent reflog' '\n test_expect_success 'invalid ref of the form stash@{n}, n >= N' '\n \tgit stash clear &&\n \ttest_must_fail git stash drop stash@{0} &&\n-\techo bar5 > file &&\n-\techo bar6 > file2 &&\n+\techo bar5 >file &&\n+\techo bar6 >file2 &&\n \tgit add file2 &&\n \tgit stash &&\n \ttest_must_fail git stash drop stash@{1} &&\n@@ -724,7 +726,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu\n \ttest_i18ncmp expect actual\n '\n \n-cat > expect << EOF\n+cat >expect <<EOF\n diff --git a/HEAD b/HEAD\n new file mode 100644\n index 0000000..fe0cbee\n@@ -737,14 +739,14 @@ EOF\n test_expect_success 'stash where working directory contains \"HEAD\" file' '\n \tgit stash clear &&\n \tgit reset --hard &&\n-\techo file-not-a-ref > HEAD &&\n+\techo file-not-a-ref >HEAD &&\n \tgit add HEAD &&\n \ttest_tick &&\n \tgit stash &&\n \tgit diff-files --quiet &&\n \tgit diff-index --cached --quiet HEAD &&\n \ttest \"$(git rev-parse stash^)\" = \"$(git rev-parse HEAD)\" &&\n-\tgit diff stash^..stash > output &&\n+\tgit diff stash^..stash >output &&\n \ttest_cmp expect output\n '\n \n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370164","messageId":"20190225231631.30507-8-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 07/27] t3903: add test for --intent-to-add file","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:11Z","receivedAt":"2019-02-25T23:17:03Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Matthew Kraai <mkraai@its.jnj.com>\n\nAdd a test showing the 'git stash' behaviour with a file that has been\nadded with 'git add --intent-to-add'.  Stash fails to stash the file,\nso the purpose of this test is mainly to make sure git doesn't crash,\nbut exits normally in this situation.\n\nThis is in preparation for converting stash into a builtin.\n\n[tg: pulled the test out into a separate commit]\n\nSigned-off-by: Matthew Kraai <mkraai@its.jnj.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n t/t3903-stash.sh | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 4e83facf23..fc292f339f 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -287,6 +287,14 @@ test_expect_success 'stash an added file' '\n \ttest new = \"$(cat file3)\"\n '\n \n+test_expect_success 'stash --intent-to-add file' '\n+\tgit reset --hard &&\n+\techo new >file4 &&\n+\tgit add --intent-to-add file4 &&\n+\ttest_when_finished \"git rm -f file4\" &&\n+\ttest_must_fail git stash\n+'\n+\n test_expect_success 'stash rm then recreate' '\n \tgit reset --hard &&\n \tgit rm file &&\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370165","messageId":"20190225231631.30507-9-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 08/27] stash: rename test cases to be more descriptive","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:12Z","receivedAt":"2019-02-25T23:17:04Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nRename some test cases' labels to be more descriptive and under 80\ncharacters per line.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n t/t3903-stash.sh | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex fc292f339f..023421a36c 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -612,7 +612,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '\n+test_expect_success 'drop: fail early if specified stash is not a stash ref' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n@@ -626,7 +626,7 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r\n \tgit reset --hard HEAD\n '\n \n-test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '\n+test_expect_success 'pop: fail early if specified stash is not a stash ref' '\n \tgit stash clear &&\n \ttest_when_finished \"git reset --hard HEAD && git stash clear\" &&\n \tgit reset --hard &&\n@@ -690,7 +690,7 @@ test_expect_success 'invalid ref of the form \"n\", n >= N' '\n \tgit stash drop\n '\n \n-test_expect_success 'stash branch should not drop the stash if the branch exists' '\n+test_expect_success 'branch: do not drop the stash if the branch exists' '\n \tgit stash clear &&\n \techo foo >file &&\n \tgit add file &&\n@@ -701,7 +701,7 @@ test_expect_success 'stash branch should not drop the stash if the branch exists\n \tgit rev-parse stash@{0} --\n '\n \n-test_expect_success 'stash branch should not drop the stash if the apply fails' '\n+test_expect_success 'branch: should not drop the stash if the apply fails' '\n \tgit stash clear &&\n \tgit reset HEAD~1 --hard &&\n \techo foo >file &&\n@@ -715,7 +715,7 @@ test_expect_success 'stash branch should not drop the stash if the apply fails'\n \tgit rev-parse stash@{0} --\n '\n \n-test_expect_success 'stash apply shows status same as git status (relative to current directory)' '\n+test_expect_success 'apply: show same status as git status (relative to ./)' '\n \tgit stash clear &&\n \techo 1 >subdir/subfile1 &&\n \techo 2 >subdir/subfile2 &&\n@@ -1056,7 +1056,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '\n \ttest_i18ncmp expect actual\n '\n \n-test_expect_success 'stash push with pathspec shows no changes when there are none' '\n+test_expect_success 'push <pathspec>: show no changes when there are none' '\n \t>foo &&\n \tgit add foo &&\n \tgit commit -m \"tmp\" &&\n@@ -1066,7 +1066,7 @@ test_expect_success 'stash push with pathspec shows no changes when there are no\n \ttest_i18ncmp expect actual\n '\n \n-test_expect_success 'stash push with pathspec not in the repository errors out' '\n+test_expect_success 'push: <pathspec> not in the repository errors out' '\n \t>untracked &&\n \ttest_must_fail git stash push untracked &&\n \ttest_path_is_file untracked\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370166","messageId":"20190225231631.30507-10-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 09/27] stash: add tests for `git stash show` config","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:13Z","receivedAt":"2019-02-25T23:17:06Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nThis commit introduces tests for `git stash show`\nconfig. It tests all the cases where `stash.showStat`\nand `stash.showPatch` are unset or set to true / false.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n t/t3907-stash-show-config.sh | 83 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 83 insertions(+)\n create mode 100755 t/t3907-stash-show-config.sh\n\ndiff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh\nnew file mode 100755\nindex 0000000000..10914bba7b\n--- /dev/null\n+++ b/t/t3907-stash-show-config.sh\n@@ -0,0 +1,83 @@\n+#!/bin/sh\n+\n+test_description='Test git stash show configuration.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit file\n+'\n+\n+# takes three parameters:\n+# 1. the stash.showStat value (or \"<unset>\")\n+# 2. the stash.showPatch value (or \"<unset>\")\n+# 3. the diff options of the expected output (or nothing for no output)\n+test_stat_and_patch () {\n+\tif test \"<unset>\" = \"$1\"\n+\tthen\n+\t\ttest_unconfig stash.showStat\n+\telse\n+\t\ttest_config stash.showStat \"$1\"\n+\tfi &&\n+\n+\tif test \"<unset>\" = \"$2\"\n+\tthen\n+\t\ttest_unconfig stash.showPatch\n+\telse\n+\t\ttest_config stash.showPatch \"$2\"\n+\tfi &&\n+\n+\tshift 2 &&\n+\techo 2 >file.t &&\n+\tif test $# != 0\n+\tthen\n+\t\tgit diff \"$@\" >expect\n+\tfi &&\n+\tgit stash &&\n+\tgit stash show >actual &&\n+\n+\tif test $# = 0\n+\tthen\n+\t\ttest_must_be_empty actual\n+\telse\n+\t\ttest_cmp expect actual\n+\tfi\n+}\n+\n+test_expect_success 'showStat unset showPatch unset' '\n+\ttest_stat_and_patch \"<unset>\" \"<unset>\" --stat\n+'\n+\n+test_expect_success 'showStat unset showPatch false' '\n+\ttest_stat_and_patch \"<unset>\" false --stat\n+'\n+\n+test_expect_success 'showStat unset showPatch true' '\n+\ttest_stat_and_patch \"<unset>\" true --stat -p\n+'\n+\n+test_expect_success 'showStat false showPatch unset' '\n+\ttest_stat_and_patch false \"<unset>\"\n+'\n+\n+test_expect_success 'showStat false showPatch false' '\n+\ttest_stat_and_patch false false\n+'\n+\n+test_expect_success 'showStat false showPatch true' '\n+\ttest_stat_and_patch false true -p\n+'\n+\n+test_expect_success 'showStat true showPatch unset' '\n+\ttest_stat_and_patch true \"<unset>\" --stat\n+'\n+\n+test_expect_success 'showStat true showPatch false' '\n+\ttest_stat_and_patch true false --stat\n+'\n+\n+test_expect_success 'showStat true showPatch true' '\n+\ttest_stat_and_patch true true --stat -p\n+'\n+\n+test_done\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370167","messageId":"20190225231631.30507-11-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 10/27] stash: mention options in `show` synopsis","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:14Z","receivedAt":"2019-02-25T23:17:07Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nMention in the documentation, that `show` accepts any\noption known to `git diff`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n Documentation/git-stash.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..e31ea7d303 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git stash' list [<options>]\n-'git stash' show [<stash>]\n+'git stash' show [<options>] [<stash>]\n 'git stash' drop [-q|--quiet] [<stash>]\n 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n 'git stash' branch <branchname> [<stash>]\n@@ -106,7 +106,7 @@ stash@{1}: On master: 9cc0589... Add git-stash\n The command takes options applicable to the 'git log'\n command to control what is shown and how. See linkgit:git-log[1].\n \n-show [<stash>]::\n+show [<options>] [<stash>]::\n \n \tShow the changes recorded in the stash entry as a diff between the\n \tstashed contents and the commit back when the stash entry was first\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370168","messageId":"20190225231631.30507-12-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 11/27] stash: convert apply to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:15Z","receivedAt":"2019-02-25T23:17:09Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd a builtin helper for performing stash commands. Converting\nall at once proved hard to review, so starting with just apply\nlets conversion get started without the other commands being\nfinished.\n\nThe helper is being implemented as a drop in replacement for\nstash so that when it is complete it can simply be renamed and\nthe shell script deleted.\n\nDelete the contents of the apply_stash shell function and replace\nit with a call to stash--helper apply until pop is also\nconverted.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n .gitignore              |   1 +\n Makefile                |   1 +\n builtin.h               |   1 +\n builtin/stash--helper.c | 451 ++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            |  78 +------\n git.c                   |   1 +\n 6 files changed, 462 insertions(+), 71 deletions(-)\n create mode 100644 builtin/stash--helper.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..6ecab90ab2 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,6 +162,7 @@\n /git-show-ref\n /git-stage\n /git-stash\n+/git-stash--helper\n /git-status\n /git-stripspace\n /git-submodule\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..c246fc7078 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1117,6 +1117,7 @@ BUILTIN_OBJS += builtin/shortlog.o\n BUILTIN_OBJS += builtin/show-branch.o\n BUILTIN_OBJS += builtin/show-index.o\n BUILTIN_OBJS += builtin/show-ref.o\n+BUILTIN_OBJS += builtin/stash--helper.o\n BUILTIN_OBJS += builtin/stripspace.o\n BUILTIN_OBJS += builtin/submodule--helper.o\n BUILTIN_OBJS += builtin/symbolic-ref.o\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..ff4460aff7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -225,6 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n+extern int cmd_stash__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nnew file mode 100644\nindex 0000000000..03511f466b\n--- /dev/null\n+++ b/builtin/stash--helper.c\n@@ -0,0 +1,451 @@\n+#include \"builtin.h\"\n+#include \"config.h\"\n+#include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"lockfile.h\"\n+#include \"cache-tree.h\"\n+#include \"unpack-trees.h\"\n+#include \"merge-recursive.h\"\n+#include \"argv-array.h\"\n+#include \"run-command.h\"\n+#include \"dir.h\"\n+#include \"rerere.h\"\n+\n+static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n+static const char * const git_stash_helper_apply_usage[] = {\n+\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n+static const char *ref_stash = \"refs/stash\";\n+static struct strbuf stash_index_path = STRBUF_INIT;\n+\n+/*\n+ * w_commit is set to the commit containing the working tree\n+ * b_commit is set to the base commit\n+ * i_commit is set to the commit containing the index tree\n+ * u_commit is set to the commit containing the untracked files tree\n+ * w_tree is set to the working tree\n+ * b_tree is set to the base tree\n+ * i_tree is set to the index tree\n+ * u_tree is set to the untracked files tree\n+ */\n+struct stash_info {\n+\tstruct object_id w_commit;\n+\tstruct object_id b_commit;\n+\tstruct object_id i_commit;\n+\tstruct object_id u_commit;\n+\tstruct object_id w_tree;\n+\tstruct object_id b_tree;\n+\tstruct object_id i_tree;\n+\tstruct object_id u_tree;\n+\tstruct strbuf revision;\n+\tint is_stash_ref;\n+\tint has_u;\n+};\n+\n+static void free_stash_info(struct stash_info *info)\n+{\n+\tstrbuf_release(&info->revision);\n+}\n+\n+static void assert_stash_like(struct stash_info *info, const char *revision)\n+{\n+\tif (get_oidf(&info->b_commit, \"%s^1\", revision) ||\n+\t    get_oidf(&info->w_tree, \"%s:\", revision) ||\n+\t    get_oidf(&info->b_tree, \"%s^1:\", revision) ||\n+\t    get_oidf(&info->i_tree, \"%s^2:\", revision))\n+\t\tdie(_(\"'%s' is not a stash-like commit\"), revision);\n+}\n+\n+static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n+{\n+\tint ret;\n+\tchar *end_of_rev;\n+\tchar *expanded_ref;\n+\tconst char *revision;\n+\tconst char *commit = NULL;\n+\tstruct object_id dummy;\n+\tstruct strbuf symbolic = STRBUF_INIT;\n+\n+\tif (argc > 1) {\n+\t\tint i;\n+\t\tstruct strbuf refs_msg = STRBUF_INIT;\n+\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tstrbuf_addf(&refs_msg, \" '%s'\", argv[i]);\n+\n+\t\tfprintf_ln(stderr, _(\"Too many revisions specified:%s\"),\n+\t\t\t   refs_msg.buf);\n+\t\tstrbuf_release(&refs_msg);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tif (argc == 1)\n+\t\tcommit = argv[0];\n+\n+\tstrbuf_init(&info->revision, 0);\n+\tif (!commit) {\n+\t\tif (!ref_exists(ref_stash)) {\n+\t\t\tfree_stash_info(info);\n+\t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tstrbuf_addf(&info->revision, \"%s@{0}\", ref_stash);\n+\t} else if (strspn(commit, \"0123456789\") == strlen(commit)) {\n+\t\tstrbuf_addf(&info->revision, \"%s@{%s}\", ref_stash, commit);\n+\t} else {\n+\t\tstrbuf_addstr(&info->revision, commit);\n+\t}\n+\n+\trevision = info->revision.buf;\n+\n+\tif (get_oid(revision, &info->w_commit)) {\n+\t\terror(_(\"%s is not a valid reference\"), revision);\n+\t\tfree_stash_info(info);\n+\t\treturn -1;\n+\t}\n+\n+\tassert_stash_like(info, revision);\n+\n+\tinfo->has_u = !get_oidf(&info->u_tree, \"%s^3:\", revision);\n+\n+\tend_of_rev = strchrnul(revision, '@');\n+\tstrbuf_add(&symbolic, revision, end_of_rev - revision);\n+\n+\tret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);\n+\tstrbuf_release(&symbolic);\n+\tswitch (ret) {\n+\tcase 0: /* Not found, but valid ref */\n+\t\tinfo->is_stash_ref = 0;\n+\t\tbreak;\n+\tcase 1:\n+\t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n+\t\tbreak;\n+\tdefault: /* Invalid or ambiguous */\n+\t\tfree_stash_info(info);\n+\t}\n+\n+\tfree(expanded_ref);\n+\treturn !(ret == 0 || ret == 1);\n+}\n+\n+static int reset_tree(struct object_id *i_tree, int update, int reset)\n+{\n+\tint nr_trees = 1;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct tree *tree;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\n+\tread_cache_preload(NULL);\n+\tif (refresh_cache(REFRESH_QUIET))\n+\t\treturn -1;\n+\n+\thold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\n+\ttree = parse_tree_indirect(i_tree);\n+\tif (parse_tree(tree))\n+\t\treturn -1;\n+\n+\tinit_tree_desc(t, tree->buffer, tree->size);\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.merge = 1;\n+\topts.reset = reset;\n+\topts.update = update;\n+\topts.fn = oneway_merge;\n+\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\n+\tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n+\t\treturn error(_(\"unable to write new index file\"));\n+\n+\treturn 0;\n+}\n+\n+static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst char *w_commit_hex = oid_to_hex(w_commit);\n+\n+\t/*\n+\t * Diff-tree would not be very hard to replace with a native function,\n+\t * however it should be done together with apply_cached.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"diff-tree\", \"--binary\", NULL);\n+\targv_array_pushf(&cp.args, \"%s^2^..%s^2\", w_commit_hex, w_commit_hex);\n+\n+\treturn pipe_command(&cp, NULL, 0, out, 0, NULL, 0);\n+}\n+\n+static int apply_cached(struct strbuf *out)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Apply currently only reads either from stdin or a file, thus\n+\t * apply_all_patches would have to be updated to optionally take a\n+\t * buffer.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"apply\", \"--cached\", NULL);\n+\treturn pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);\n+}\n+\n+static int reset_head(void)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Reset is overall quite simple, however there is no current public\n+\t * API for resetting.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_push(&cp.args, \"reset\");\n+\n+\treturn run_command(&cp);\n+}\n+\n+static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst char *c_tree_hex = oid_to_hex(c_tree);\n+\n+\t/*\n+\t * diff-index is very similar to diff-tree above, and should be\n+\t * converted together with update_index.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"diff-index\", \"--cached\", \"--name-only\",\n+\t\t\t \"--diff-filter=A\", NULL);\n+\targv_array_push(&cp.args, c_tree_hex);\n+\treturn pipe_command(&cp, NULL, 0, out, 0, NULL, 0);\n+}\n+\n+static int update_index(struct strbuf *out)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * Update-index is very complicated and may need to have a public\n+\t * function exposed in order to remove this forking.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"update-index\", \"--add\", \"--stdin\", NULL);\n+\treturn pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);\n+}\n+\n+static int restore_untracked(struct object_id *u_tree)\n+{\n+\tint res;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * We need to run restore files from a given index, but without\n+\t * affecting the current index, so we use GIT_INDEX_FILE with\n+\t * run_command to fork processes that will not interfere.\n+\t */\n+\tcp.git_cmd = 1;\n+\targv_array_push(&cp.args, \"read-tree\");\n+\targv_array_push(&cp.args, oid_to_hex(u_tree));\n+\targv_array_pushf(&cp.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp)) {\n+\t\tremove_path(stash_index_path.buf);\n+\t\treturn -1;\n+\t}\n+\n+\tchild_process_init(&cp);\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"checkout-index\", \"--all\", NULL);\n+\targv_array_pushf(&cp.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tres = run_command(&cp);\n+\tremove_path(stash_index_path.buf);\n+\treturn res;\n+}\n+\n+static int do_apply_stash(const char *prefix, struct stash_info *info,\n+\t\t\t  int index, int quiet)\n+{\n+\tint ret;\n+\tint has_index = index;\n+\tstruct merge_options o;\n+\tstruct object_id c_tree;\n+\tstruct object_id index_tree;\n+\tstruct commit *result;\n+\tconst struct object_id *bases[1];\n+\n+\tread_cache_preload(NULL);\n+\tif (refresh_cache(REFRESH_QUIET))\n+\t\treturn -1;\n+\n+\tif (write_cache_as_tree(&c_tree, 0, NULL))\n+\t\treturn error(_(\"cannot apply a stash in the middle of a merge\"));\n+\n+\tif (index) {\n+\t\tif (oideq(&info->b_tree, &info->i_tree) ||\n+\t\t    oideq(&c_tree, &info->i_tree)) {\n+\t\t\thas_index = 0;\n+\t\t} else {\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tif (diff_tree_binary(&out, &info->w_commit)) {\n+\t\t\t\tstrbuf_release(&out);\n+\t\t\t\treturn error(_(\"could not generate diff %s^!.\"),\n+\t\t\t\t\t     oid_to_hex(&info->w_commit));\n+\t\t\t}\n+\n+\t\t\tret = apply_cached(&out);\n+\t\t\tstrbuf_release(&out);\n+\t\t\tif (ret)\n+\t\t\t\treturn error(_(\"conflicts in index.\"\n+\t\t\t\t\t       \"Try without --index.\"));\n+\n+\t\t\tdiscard_cache();\n+\t\t\tread_cache();\n+\t\t\tif (write_cache_as_tree(&index_tree, 0, NULL))\n+\t\t\t\treturn error(_(\"could not save index tree\"));\n+\n+\t\t\treset_head();\n+\t\t}\n+\t}\n+\n+\tif (info->has_u && restore_untracked(&info->u_tree))\n+\t\treturn error(_(\"could not restore untracked files from stash\"));\n+\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"Updated upstream\";\n+\to.branch2 = \"Stashed changes\";\n+\n+\tif (oideq(&info->b_tree, &c_tree))\n+\t\to.branch1 = \"Version stash was based on\";\n+\n+\tif (quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (o.verbosity >= 3)\n+\t\tprintf_ln(_(\"Merging %s with %s\"), o.branch1, o.branch2);\n+\n+\tbases[0] = &info->b_tree;\n+\n+\tret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,\n+\t\t\t\t      &result);\n+\tif (ret) {\n+\t\trerere(0);\n+\n+\t\tif (index)\n+\t\t\tfprintf_ln(stderr, _(\"Index was not unstashed.\"));\n+\n+\t\treturn ret;\n+\t}\n+\n+\tif (has_index) {\n+\t\tif (reset_tree(&index_tree, 0, 0))\n+\t\t\treturn -1;\n+\t} else {\n+\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\tif (get_newly_staged(&out, &c_tree)) {\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (reset_tree(&c_tree, 0, 1)) {\n+\t\t\tstrbuf_release(&out);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tret = update_index(&out);\n+\t\tstrbuf_release(&out);\n+\t\tif (ret)\n+\t\t\treturn -1;\n+\n+\t\tdiscard_cache();\n+\t}\n+\n+\tif (quiet) {\n+\t\tif (refresh_cache(REFRESH_QUIET))\n+\t\t\twarning(\"could not refresh index\");\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t/*\n+\t\t * Status is quite simple and could be replaced with calls to\n+\t\t * wt_status in the future, but it adds complexities which may\n+\t\t * require more tests.\n+\t\t */\n+\t\tcp.git_cmd = 1;\n+\t\tcp.dir = prefix;\n+\t\targv_array_push(&cp.args, \"status\");\n+\t\trun_command(&cp);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int apply_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint quiet = 0;\n+\tint index = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_BOOL(0, \"index\", &index,\n+\t\t\t N_(\"attempt to recreate the index\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_apply_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tret = do_apply_stash(prefix, &info, index, quiet);\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n+int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n+{\n+\tpid_t pid = getpid();\n+\tconst char *index_file;\n+\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\tgit_config(git_default_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n+\n+\tindex_file = get_index_file();\n+\tstrbuf_addf(&stash_index_path, \"%s.stash.%\" PRIuMAX, index_file,\n+\t\t    (uintmax_t)pid);\n+\n+\tif (argc < 1)\n+\t\tusage_with_options(git_stash_helper_usage, options);\n+\tif (!strcmp(argv[0], \"apply\"))\n+\t\treturn !!apply_stash(argc, argv, prefix);\n+\n+\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n+\t\t      git_stash_helper_usage, options);\n+}\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 789ce2f41d..366a082853 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -583,76 +583,11 @@ assert_stash_ref() {\n }\n \n apply_stash () {\n-\n-\tassert_stash_like \"$@\"\n-\n-\tgit update-index -q --refresh || die \"$(gettext \"unable to refresh index\")\"\n-\n-\t# current index state\n-\tc_tree=$(git write-tree) ||\n-\t\tdie \"$(gettext \"Cannot apply a stash in the middle of a merge\")\"\n-\n-\tunstashed_index_tree=\n-\tif test -n \"$INDEX_OPTION\" && test \"$b_tree\" != \"$i_tree\" &&\n-\t\t\ttest \"$c_tree\" != \"$i_tree\"\n-\tthen\n-\t\tgit diff-tree --binary $s^2^..$s^2 | git apply --cached\n-\t\ttest $? -ne 0 &&\n-\t\t\tdie \"$(gettext \"Conflicts in index. Try without --index.\")\"\n-\t\tunstashed_index_tree=$(git write-tree) ||\n-\t\t\tdie \"$(gettext \"Could not save index tree\")\"\n-\t\tgit reset\n-\tfi\n-\n-\tif test -n \"$u_tree\"\n-\tthen\n-\t\tGIT_INDEX_FILE=\"$TMPindex\" git read-tree \"$u_tree\" &&\n-\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n-\t\trm -f \"$TMPindex\" ||\n-\t\tdie \"$(gettext \"Could not restore untracked files from stash entry\")\"\n-\tfi\n-\n-\teval \"\n-\t\tGITHEAD_$w_tree='Stashed changes' &&\n-\t\tGITHEAD_$c_tree='Updated upstream' &&\n-\t\tGITHEAD_$b_tree='Version stash was based on' &&\n-\t\texport GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree\n-\t\"\n-\n-\tif test -n \"$GIT_QUIET\"\n-\tthen\n-\t\tGIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY\n-\tfi\n-\tif git merge-recursive $b_tree -- $c_tree $w_tree\n-\tthen\n-\t\t# No conflict\n-\t\tif test -n \"$unstashed_index_tree\"\n-\t\tthen\n-\t\t\tgit read-tree \"$unstashed_index_tree\"\n-\t\telse\n-\t\t\ta=\"$TMP-added\" &&\n-\t\t\tgit diff-index --cached --name-only --diff-filter=A $c_tree >\"$a\" &&\n-\t\t\tgit read-tree --reset $c_tree &&\n-\t\t\tgit update-index --add --stdin <\"$a\" ||\n-\t\t\t\tdie \"$(gettext \"Cannot unstage modified files\")\"\n-\t\t\trm -f \"$a\"\n-\t\tfi\n-\t\tsquelch=\n-\t\tif test -n \"$GIT_QUIET\"\n-\t\tthen\n-\t\t\tsquelch='>/dev/null 2>&1'\n-\t\tfi\n-\t\t(cd \"$START_DIR\" && eval \"git status $squelch\") || :\n-\telse\n-\t\t# Merge conflict; keep the exit status from merge-recursive\n-\t\tstatus=$?\n-\t\tgit rerere\n-\t\tif test -n \"$INDEX_OPTION\"\n-\t\tthen\n-\t\t\tgettextln \"Index was not unstashed.\" >&2\n-\t\tfi\n-\t\texit $status\n-\tfi\n+\tcd \"$START_DIR\"\n+\tgit stash--helper apply \"$@\"\n+\tres=$?\n+\tcd_to_toplevel\n+\treturn $res\n }\n \n pop_stash() {\n@@ -730,7 +665,8 @@ push)\n \t;;\n apply)\n \tshift\n-\tapply_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper apply \"$@\"\n \t;;\n clear)\n \tshift\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..76ee02802e 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,6 +554,7 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"stash--helper\", cmd_stash__helper, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370169","messageId":"20190225231631.30507-13-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:16Z","receivedAt":"2019-02-25T23:17:10Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd the drop and clear commands to the builtin helper. These two\nare each simple, but are being added together as they are quite\nrelated.\n\nWe have to unfortunately keep the drop and clear functions in the\nshell script as functions are called with parameters internally\nthat are not valid when the commands are called externally. Once\npop is converted they can both be removed.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 117 ++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            |   4 +-\n 2 files changed, 119 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 03511f466b..c515f0b358 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,7 +12,14 @@\n #include \"rerere.h\"\n \n static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper clear\"),\n+\tNULL\n+};\n+\n+static const char * const git_stash_helper_drop_usage[] = {\n+\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n@@ -21,6 +28,11 @@ static const char * const git_stash_helper_apply_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_clear_usage[] = {\n+\tN_(\"git stash--helper clear\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -136,6 +148,32 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \treturn !(ret == 0 || ret == 1);\n }\n \n+static int do_clear_stash(void)\n+{\n+\tstruct object_id obj;\n+\tif (get_oid(ref_stash, &obj))\n+\t\treturn 0;\n+\n+\treturn delete_ref(NULL, ref_stash, &obj, 0);\n+}\n+\n+static int clear_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_clear_usage,\n+\t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n+\n+\tif (argc)\n+\t\treturn error(_(\"git stash clear with parameters is \"\n+\t\t\t       \"unimplemented\"));\n+\n+\treturn do_clear_stash();\n+}\n+\n static int reset_tree(struct object_id *i_tree, int update, int reset)\n {\n \tint nr_trees = 1;\n@@ -423,6 +461,81 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n+{\n+\tint ret;\n+\tstruct child_process cp_reflog = CHILD_PROCESS_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t/*\n+\t * reflog does not provide a simple function for deleting refs. One will\n+\t * need to be added to avoid implementing too much reflog code here\n+\t */\n+\n+\tcp_reflog.git_cmd = 1;\n+\targv_array_pushl(&cp_reflog.args, \"reflog\", \"delete\", \"--updateref\",\n+\t\t\t \"--rewrite\", NULL);\n+\targv_array_push(&cp_reflog.args, info->revision.buf);\n+\tret = run_command(&cp_reflog);\n+\tif (!ret) {\n+\t\tif (!quiet)\n+\t\t\tprintf_ln(_(\"Dropped %s (%s)\"), info->revision.buf,\n+\t\t\t\t  oid_to_hex(&info->w_commit));\n+\t} else {\n+\t\treturn error(_(\"%s: Could not drop stash entry\"),\n+\t\t\t     info->revision.buf);\n+\t}\n+\n+\t/*\n+\t * This could easily be replaced by get_oid, but currently it will throw\n+\t * a fatal error when a reflog is empty, which we can not recover from.\n+\t */\n+\tcp.git_cmd = 1;\n+\t/* Even though --quiet is specified, rev-parse still outputs the hash */\n+\tcp.no_stdout = 1;\n+\targv_array_pushl(&cp.args, \"rev-parse\", \"--verify\", \"--quiet\", NULL);\n+\targv_array_pushf(&cp.args, \"%s@{0}\", ref_stash);\n+\tret = run_command(&cp);\n+\n+\t/* do_clear_stash if we just dropped the last stash entry */\n+\tif (ret)\n+\t\tdo_clear_stash();\n+\n+\treturn 0;\n+}\n+\n+static void assert_stash_ref(struct stash_info *info)\n+{\n+\tif (!info->is_stash_ref) {\n+\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n+\t\tfree_stash_info(info);\n+\t\texit(1);\n+\t}\n+}\n+\n+static int drop_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint quiet = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_drop_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tassert_stash_ref(&info);\n+\n+\tret = do_drop_stash(prefix, &info, quiet);\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -445,6 +558,10 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_stash_helper_usage, options);\n \tif (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"clear\"))\n+\t\treturn !!clear_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"drop\"))\n+\t\treturn !!drop_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 366a082853..b8f70230f9 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -670,7 +670,7 @@ apply)\n \t;;\n clear)\n \tshift\n-\tclear_stash \"$@\"\n+\tgit stash--helper clear \"$@\"\n \t;;\n create)\n \tshift\n@@ -682,7 +682,7 @@ store)\n \t;;\n drop)\n \tshift\n-\tdrop_stash \"$@\"\n+\tgit stash--helper drop \"$@\"\n \t;;\n pop)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370170","messageId":"20190225231631.30507-14-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 13/27] stash: convert branch to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:17Z","receivedAt":"2019-02-25T23:17:12Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd stash branch to the helper and delete the apply_to_branch\nfunction from the shell script.\n\nCheckout does not currently provide a function for checking out\na branch as cmd_checkout does a large amount of sanity checks\nfirst that we require here.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 46 +++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            | 17 ++-------------\n 2 files changed, 48 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex c515f0b358..c9874e2f5d 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -14,6 +14,7 @@\n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n \tNULL\n };\n@@ -28,6 +29,11 @@ static const char * const git_stash_helper_apply_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_branch_usage[] = {\n+\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_clear_usage[] = {\n \tN_(\"git stash--helper clear\"),\n \tNULL\n@@ -536,6 +542,44 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int branch_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tconst char *branch = NULL;\n+\tstruct stash_info info;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_branch_usage, 0);\n+\n+\tif (!argc) {\n+\t\tfprintf_ln(stderr, _(\"No branch name specified\"));\n+\t\treturn -1;\n+\t}\n+\n+\tbranch = argv[0];\n+\n+\tif (get_stash_info(&info, argc - 1, argv + 1))\n+\t\treturn -1;\n+\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n+\targv_array_push(&cp.args, branch);\n+\targv_array_push(&cp.args, oid_to_hex(&info.b_commit));\n+\tret = run_command(&cp);\n+\tif (!ret)\n+\t\tret = do_apply_stash(prefix, &info, 1, 0);\n+\tif (!ret && info.is_stash_ref)\n+\t\tret = do_drop_stash(prefix, &info, 0);\n+\n+\tfree_stash_info(&info);\n+\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -562,6 +606,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!clear_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"drop\"))\n \t\treturn !!drop_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"branch\"))\n+\t\treturn !!branch_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex b8f70230f9..67db321a4c 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -615,20 +615,6 @@ drop_stash () {\n \tclear_stash\n }\n \n-apply_to_branch () {\n-\ttest -n \"$1\" || die \"$(gettext \"No branch name specified\")\"\n-\tbranch=$1\n-\tshift 1\n-\n-\tset -- --index \"$@\"\n-\tassert_stash_like \"$@\"\n-\n-\tgit checkout -b $branch $REV^ &&\n-\tapply_stash \"$@\" && {\n-\t\ttest -z \"$IS_STASH_REF\" || drop_stash \"$@\"\n-\t}\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -690,7 +676,8 @@ pop)\n \t;;\n branch)\n \tshift\n-\tapply_to_branch \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper branch \"$@\"\n \t;;\n *)\n \tcase $# in\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370171","messageId":"20190225231631.30507-15-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 14/27] stash: convert pop to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:18Z","receivedAt":"2019-02-25T23:17:14Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Joel Teichroeb <joel@teichroeb.net>\n\nAdd stash pop to the helper and delete the pop_stash, drop_stash,\nassert_stash_ref functions from the shell script now that they\nare no longer needed.\n\nSigned-off-by: Joel Teichroeb <joel@teichroeb.net>\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 39 +++++++++++++++++++++++++++++++++-\n git-stash.sh            | 47 ++---------------------------------------\n 2 files changed, 40 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex c9874e2f5d..63e1e84e78 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -13,7 +13,7 @@\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n \tNULL\n@@ -24,6 +24,11 @@ static const char * const git_stash_helper_drop_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_pop_usage[] = {\n+\tN_(\"git stash--helper pop [--index] [-q|--quiet] [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_apply_usage[] = {\n \tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -542,6 +547,36 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int pop_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret;\n+\tint index = 0;\n+\tint quiet = 0;\n+\tstruct stash_info info;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n+\t\tOPT_BOOL(0, \"index\", &index,\n+\t\t\t N_(\"attempt to recreate the index\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_pop_usage, 0);\n+\n+\tif (get_stash_info(&info, argc, argv))\n+\t\treturn -1;\n+\n+\tassert_stash_ref(&info);\n+\tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n+\t\tprintf_ln(_(\"The stash entry is kept in case \"\n+\t\t\t    \"you need it again.\"));\n+\telse\n+\t\tret = do_drop_stash(prefix, &info, quiet);\n+\n+\tfree_stash_info(&info);\n+\treturn ret;\n+}\n+\n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n \tint ret;\n@@ -606,6 +641,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!clear_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"drop\"))\n \t\treturn !!drop_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"pop\"))\n+\t\treturn !!pop_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"branch\"))\n \t\treturn !!branch_stash(argc, argv, prefix);\n \ndiff --git a/git-stash.sh b/git-stash.sh\nindex 67db321a4c..8a9f907aa9 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -571,50 +571,6 @@ assert_stash_like() {\n \t}\n }\n \n-is_stash_ref() {\n-\tis_stash_like \"$@\" && test -n \"$IS_STASH_REF\"\n-}\n-\n-assert_stash_ref() {\n-\tis_stash_ref \"$@\" || {\n-\t\targs=\"$*\"\n-\t\tdie \"$(eval_gettext \"'\\$args' is not a stash reference\")\"\n-\t}\n-}\n-\n-apply_stash () {\n-\tcd \"$START_DIR\"\n-\tgit stash--helper apply \"$@\"\n-\tres=$?\n-\tcd_to_toplevel\n-\treturn $res\n-}\n-\n-pop_stash() {\n-\tassert_stash_ref \"$@\"\n-\n-\tif apply_stash \"$@\"\n-\tthen\n-\t\tdrop_stash \"$@\"\n-\telse\n-\t\tstatus=$?\n-\t\tsay \"$(gettext \"The stash entry is kept in case you need it again.\")\"\n-\t\texit $status\n-\tfi\n-}\n-\n-drop_stash () {\n-\tassert_stash_ref \"$@\"\n-\n-\tgit reflog delete --updateref --rewrite \"${REV}\" &&\n-\t\tsay \"$(eval_gettext \"Dropped \\${REV} (\\$s)\")\" ||\n-\t\tdie \"$(eval_gettext \"\\${REV}: Could not drop stash entry\")\"\n-\n-\t# clear_stash if we just dropped the last stash entry\n-\tgit rev-parse --verify --quiet \"$ref_stash@{0}\" >/dev/null ||\n-\tclear_stash\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -672,7 +628,8 @@ drop)\n \t;;\n pop)\n \tshift\n-\tpop_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper pop \"$@\"\n \t;;\n branch)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370172","messageId":"20190225231631.30507-16-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 15/27] stash: convert list to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:19Z","receivedAt":"2019-02-25T23:17:16Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash list to the helper and delete the list_stash function\nfrom the shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 31 +++++++++++++++++++++++++++++++\n git-stash.sh            |  7 +------\n 2 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 63e1e84e78..f296224577 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,6 +12,7 @@\n #include \"rerere.h\"\n \n static const char * const git_stash_helper_usage[] = {\n+\tN_(\"git stash--helper list [<options>]\"),\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n@@ -19,6 +20,11 @@ static const char * const git_stash_helper_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_list_usage[] = {\n+\tN_(\"git stash--helper list [<options>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_drop_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -615,6 +621,29 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int list_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_list_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n+\n+\tif (!ref_exists(ref_stash))\n+\t\treturn 0;\n+\n+\tcp.git_cmd = 1;\n+\targv_array_pushl(&cp.args, \"log\", \"--format=%gd: %gs\", \"-g\",\n+\t\t\t \"--first-parent\", \"-m\", NULL);\n+\targv_array_pushv(&cp.args, argv);\n+\targv_array_push(&cp.args, ref_stash);\n+\targv_array_push(&cp.args, \"--\");\n+\treturn run_command(&cp);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -645,6 +674,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!pop_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"branch\"))\n \t\treturn !!branch_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"list\"))\n+\t\treturn !!list_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 8a9f907aa9..ab3992b59d 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -399,11 +399,6 @@ have_stash () {\n \tgit rev-parse --verify --quiet $ref_stash >/dev/null\n }\n \n-list_stash () {\n-\thave_stash || return 0\n-\tgit log --format=\"%gd: %gs\" -g --first-parent -m \"$@\" $ref_stash --\n-}\n-\n show_stash () {\n \tALLOW_UNKNOWN_FLAGS=t\n \tassert_stash_like \"$@\"\n@@ -591,7 +586,7 @@ test -n \"$seen_non_option\" || set \"push\" \"$@\"\n case \"$1\" in\n list)\n \tshift\n-\tlist_stash \"$@\"\n+\tgit stash--helper list \"$@\"\n \t;;\n show)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370173","messageId":"20190225231631.30507-17-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 16/27] stash: convert show to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:20Z","receivedAt":"2019-02-25T23:17:17Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash show to the helper and delete the show_stash, have_stash,\nassert_stash_like, is_stash_like and parse_flags_and_rev functions\nfrom the shell script now that they are no longer needed.\n\nIn shell version, although `git stash show` accepts `--index` and\n`--quiet` options, it ignores them. In C, both options are passed\nfurther to `git diff`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c |  87 ++++++++++++++++++++++++++\n git-stash.sh            | 132 +---------------------------------------\n 2 files changed, 88 insertions(+), 131 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex f296224577..4028b64314 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -10,9 +10,12 @@\n #include \"run-command.h\"\n #include \"dir.h\"\n #include \"rerere.h\"\n+#include \"revision.h\"\n+#include \"log-tree.h\"\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper list [<options>]\"),\n+\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n@@ -25,6 +28,11 @@ static const char * const git_stash_helper_list_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_show_usage[] = {\n+\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n+\tNULL\n+};\n+\n static const char * const git_stash_helper_drop_usage[] = {\n \tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n \tNULL\n@@ -644,6 +652,83 @@ static int list_stash(int argc, const char **argv, const char *prefix)\n \treturn run_command(&cp);\n }\n \n+static int show_stat = 1;\n+static int show_patch;\n+\n+static int git_stash_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"stash.showstat\")) {\n+\t\tshow_stat = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(var, \"stash.showpatch\")) {\n+\t\tshow_patch = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value, cb);\n+}\n+\n+static int show_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint i;\n+\tint opts = 0;\n+\tint ret = 0;\n+\tstruct stash_info info;\n+\tstruct rev_info rev;\n+\tstruct argv_array stash_args = ARGV_ARRAY_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\tinit_diff_ui_defaults();\n+\tgit_config(git_diff_ui_config, NULL);\n+\tinit_revisions(&rev, prefix);\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (argv[i][0] != '-')\n+\t\t\targv_array_push(&stash_args, argv[i]);\n+\t\telse\n+\t\t\topts++;\n+\t}\n+\n+\tret = get_stash_info(&info, stash_args.argc, stash_args.argv);\n+\targv_array_clear(&stash_args);\n+\tif (ret)\n+\t\treturn -1;\n+\n+\t/*\n+\t * The config settings are applied only if there are not passed\n+\t * any options.\n+\t */\n+\tif (!opts) {\n+\t\tgit_config(git_stash_config, NULL);\n+\t\tif (show_stat)\n+\t\t\trev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;\n+\n+\t\tif (show_patch)\n+\t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n+\n+\t\tif (!show_stat && !show_patch) {\n+\t\t\tfree_stash_info(&info);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\targc = setup_revisions(argc, argv, &rev, NULL);\n+\tif (argc > 1) {\n+\t\tfree_stash_info(&info);\n+\t\tusage_with_options(git_stash_helper_show_usage, options);\n+\t}\n+\n+\trev.diffopt.flags.recursive = 1;\n+\tsetup_diff_pager(&rev.diffopt);\n+\tdiff_tree_oid(&info.b_commit, &info.w_commit, \"\", &rev.diffopt);\n+\tlog_tree_diff_flush(&rev);\n+\n+\tfree_stash_info(&info);\n+\treturn diff_result_code(&rev.diffopt, 0);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -676,6 +761,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!branch_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"list\"))\n \t\treturn !!list_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"show\"))\n+\t\treturn !!show_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex ab3992b59d..d0318f859e 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -395,35 +395,6 @@ save_stash () {\n \tfi\n }\n \n-have_stash () {\n-\tgit rev-parse --verify --quiet $ref_stash >/dev/null\n-}\n-\n-show_stash () {\n-\tALLOW_UNKNOWN_FLAGS=t\n-\tassert_stash_like \"$@\"\n-\n-\tif test -z \"$FLAGS\"\n-\tthen\n-\t\tif test \"$(git config --bool stash.showStat || echo true)\" = \"true\"\n-\t\tthen\n-\t\t\tFLAGS=--stat\n-\t\tfi\n-\n-\t\tif test \"$(git config --bool stash.showPatch || echo false)\" = \"true\"\n-\t\tthen\n-\t\t\tFLAGS=${FLAGS}${FLAGS:+ }-p\n-\t\tfi\n-\n-\t\tif test -z \"$FLAGS\"\n-\t\tthen\n-\t\t\treturn 0\n-\t\tfi\n-\tfi\n-\n-\tgit diff ${FLAGS} $b_commit $w_commit\n-}\n-\n show_help () {\n \texec git help stash\n \texit 1\n@@ -465,107 +436,6 @@ show_help () {\n #   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n #\n \n-parse_flags_and_rev()\n-{\n-\ttest \"$PARSE_CACHE\" = \"$*\" && return 0 # optimisation\n-\tPARSE_CACHE=\"$*\"\n-\n-\tIS_STASH_LIKE=\n-\tIS_STASH_REF=\n-\tINDEX_OPTION=\n-\ts=\n-\tw_commit=\n-\tb_commit=\n-\ti_commit=\n-\tu_commit=\n-\tw_tree=\n-\tb_tree=\n-\ti_tree=\n-\tu_tree=\n-\n-\tFLAGS=\n-\tREV=\n-\tfor opt\n-\tdo\n-\t\tcase \"$opt\" in\n-\t\t\t-q|--quiet)\n-\t\t\t\tGIT_QUIET=-t\n-\t\t\t;;\n-\t\t\t--index)\n-\t\t\t\tINDEX_OPTION=--index\n-\t\t\t;;\n-\t\t\t--help)\n-\t\t\t\tshow_help\n-\t\t\t;;\n-\t\t\t-*)\n-\t\t\t\ttest \"$ALLOW_UNKNOWN_FLAGS\" = t ||\n-\t\t\t\t\tdie \"$(eval_gettext \"unknown option: \\$opt\")\"\n-\t\t\t\tFLAGS=\"${FLAGS}${FLAGS:+ }$opt\"\n-\t\t\t;;\n-\t\t\t*)\n-\t\t\t\tREV=\"${REV}${REV:+ }'$opt'\"\n-\t\t\t;;\n-\t\tesac\n-\tdone\n-\n-\teval set -- $REV\n-\n-\tcase $# in\n-\t\t0)\n-\t\t\thave_stash || die \"$(gettext \"No stash entries found.\")\"\n-\t\t\tset -- ${ref_stash}@{0}\n-\t\t;;\n-\t\t1)\n-\t\t\t:\n-\t\t;;\n-\t\t*)\n-\t\t\tdie \"$(eval_gettext \"Too many revisions specified: \\$REV\")\"\n-\t\t;;\n-\tesac\n-\n-\tcase \"$1\" in\n-\t\t*[!0-9]*)\n-\t\t\t:\n-\t\t;;\n-\t\t*)\n-\t\t\tset -- \"${ref_stash}@{$1}\"\n-\t\t;;\n-\tesac\n-\n-\tREV=$(git rev-parse --symbolic --verify --quiet \"$1\") || {\n-\t\treference=\"$1\"\n-\t\tdie \"$(eval_gettext \"\\$reference is not a valid reference\")\"\n-\t}\n-\n-\ti_commit=$(git rev-parse --verify --quiet \"$REV^2\") &&\n-\tset -- $(git rev-parse \"$REV\" \"$REV^1\" \"$REV:\" \"$REV^1:\" \"$REV^2:\" 2>/dev/null) &&\n-\ts=$1 &&\n-\tw_commit=$1 &&\n-\tb_commit=$2 &&\n-\tw_tree=$3 &&\n-\tb_tree=$4 &&\n-\ti_tree=$5 &&\n-\tIS_STASH_LIKE=t &&\n-\ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n-\tIS_STASH_REF=t\n-\n-\tu_commit=$(git rev-parse --verify --quiet \"$REV^3\") &&\n-\tu_tree=$(git rev-parse \"$REV^3:\" 2>/dev/null)\n-}\n-\n-is_stash_like()\n-{\n-\tparse_flags_and_rev \"$@\"\n-\ttest -n \"$IS_STASH_LIKE\"\n-}\n-\n-assert_stash_like() {\n-\tis_stash_like \"$@\" || {\n-\t\targs=\"$*\"\n-\t\tdie \"$(eval_gettext \"'\\$args' is not a stash-like commit\")\"\n-\t}\n-}\n-\n test \"$1\" = \"-p\" && set \"push\" \"$@\"\n \n PARSE_CACHE='--not-parsed'\n@@ -590,7 +460,7 @@ list)\n \t;;\n show)\n \tshift\n-\tshow_stash \"$@\"\n+\tgit stash--helper show \"$@\"\n \t;;\n save)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370174","messageId":"20190225231631.30507-18-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 17/27] stash: convert store to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:21Z","receivedAt":"2019-02-25T23:17:20Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash store to the helper and delete the store_stash function\nfrom the shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 62 +++++++++++++++++++++++++++++++++++++++++\n git-stash.sh            | 43 ++--------------------------\n 2 files changed, 64 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 4028b64314..763a5ffd25 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -58,6 +58,11 @@ static const char * const git_stash_helper_clear_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_store_usage[] = {\n+\tN_(\"git stash--helper store [-m|--message <message>] [-q|--quiet] <commit>\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -729,6 +734,61 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \treturn diff_result_code(&rev.diffopt, 0);\n }\n \n+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n+\t\t\t  int quiet)\n+{\n+\tif (!stash_msg)\n+\t\tstash_msg = \"Created via \\\"git stash store\\\".\";\n+\n+\tif (update_ref(stash_msg, ref_stash, w_commit, NULL,\n+\t\t       REF_FORCE_CREATE_REFLOG,\n+\t\t       quiet ? UPDATE_REFS_QUIET_ON_ERR :\n+\t\t       UPDATE_REFS_MSG_ON_ERR)) {\n+\t\tif (!quiet) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot update %s with %s\"),\n+\t\t\t\t   ref_stash, oid_to_hex(w_commit));\n+\t\t}\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int store_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint quiet = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct object_id obj;\n+\tstruct object_context dummy;\n+\tstruct option options[] = {\n+\t\tOPT__QUIET(&quiet, N_(\"be quiet\")),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, \"message\",\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_store_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n+\n+\tif (argc != 1) {\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"\\\"git stash store\\\" requires one \"\n+\t\t\t\t\t     \"<commit> argument\"));\n+\t\treturn -1;\n+\t}\n+\n+\tif (get_oid_with_context(argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,\n+\t\t\t\t &dummy)) {\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot update %s with %s\"),\n+\t\t\t\t\t     ref_stash, argv[0]);\n+\t\treturn -1;\n+\t}\n+\n+\treturn do_store_stash(&obj, stash_msg, quiet);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -763,6 +823,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!list_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"show\"))\n \t\treturn !!show_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"store\"))\n+\t\treturn !!store_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex d0318f859e..ff5556ccb0 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -208,45 +208,6 @@ create_stash () {\n \tdie \"$(gettext \"Cannot record working tree state\")\"\n }\n \n-store_stash () {\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\tstash_msg=\"$1\"\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t-q|--quiet)\n-\t\t\tquiet=t\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\ttest $# = 1 ||\n-\tdie \"$(eval_gettext \"\\\"$dashless store\\\" requires one <commit> argument\")\"\n-\n-\tw_commit=\"$1\"\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tstash_msg=\"Created via \\\"git stash store\\\".\"\n-\tfi\n-\n-\tgit update-ref --create-reflog -m \"$stash_msg\" $ref_stash $w_commit\n-\tret=$?\n-\ttest $ret != 0 && test -z \"$quiet\" &&\n-\tdie \"$(eval_gettext \"Cannot update \\$ref_stash with \\$w_commit\")\"\n-\treturn $ret\n-}\n-\n push_stash () {\n \tkeep_index=\n \tpatch_mode=\n@@ -325,7 +286,7 @@ push_stash () {\n \t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n \n \tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n-\tstore_stash -m \"$stash_msg\" -q $w_commit ||\n+\tgit stash--helper store -m \"$stash_msg\" -q $w_commit ||\n \tdie \"$(gettext \"Cannot save the current status\")\"\n \tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n \n@@ -485,7 +446,7 @@ create)\n \t;;\n store)\n \tshift\n-\tstore_stash \"$@\"\n+\tgit stash--helper store \"$@\"\n \t;;\n drop)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370175","messageId":"20190225231631.30507-19-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 18/27] stash: convert create to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:22Z","receivedAt":"2019-02-25T23:17:22Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash create to the helper.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nHelped-by: Matthew Kraai <mkraai@its.jnj.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 449 +++++++++++++++++++++++++++++++++++++++-\n git-stash.sh            |   2 +-\n 2 files changed, 449 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 763a5ffd25..56864c126b 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -12,6 +12,9 @@\n #include \"rerere.h\"\n #include \"revision.h\"\n #include \"log-tree.h\"\n+#include \"diffcore.h\"\n+\n+#define INCLUDE_ALL_FILES 2\n \n static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper list [<options>]\"),\n@@ -63,6 +66,11 @@ static const char * const git_stash_helper_store_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_create_usage[] = {\n+\tN_(\"git stash--helper create [<message>]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -287,6 +295,20 @@ static int reset_head(void)\n \treturn run_command(&cp);\n }\n \n+static void add_diff_to_buf(struct diff_queue_struct *q,\n+\t\t\t    struct diff_options *options,\n+\t\t\t    void *data)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstrbuf_addstr(data, q->queue[i]->one->path);\n+\n+\t\t/* NUL-terminate: will be fed to update-index -z */\n+\t\tstrbuf_addch(data, '\\0');\n+\t}\n+}\n+\n static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n@@ -789,6 +811,429 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n \treturn do_store_stash(&obj, stash_msg, quiet);\n }\n \n+static void add_pathspecs(struct argv_array *args,\n+\t\t\t  struct pathspec ps) {\n+\tint i;\n+\n+\tfor (i = 0; i < ps.nr; i++)\n+\t\targv_array_push(args, ps.items[i].match);\n+}\n+\n+/*\n+ * `untracked_files` will be filled with the names of untracked files.\n+ * The return value is:\n+ *\n+ * = 0 if there are not any untracked files\n+ * > 0 if there are untracked files\n+ */\n+static int get_untracked_files(struct pathspec ps, int include_untracked,\n+\t\t\t       struct strbuf *untracked_files)\n+{\n+\tint i;\n+\tint max_len;\n+\tint found = 0;\n+\tchar *seen;\n+\tstruct dir_struct dir;\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tif (include_untracked != INCLUDE_ALL_FILES)\n+\t\tsetup_standard_excludes(&dir);\n+\n+\tseen = xcalloc(ps.nr, 1);\n+\n+\tmax_len = fill_directory(&dir, the_repository->index, &ps);\n+\tfor (i = 0; i < dir.nr; i++) {\n+\t\tstruct dir_entry *ent = dir.entries[i];\n+\t\tif (dir_path_match(&the_index, ent, &ps, max_len, seen)) {\n+\t\t\tfound++;\n+\t\t\tstrbuf_addstr(untracked_files, ent->name);\n+\t\t\t/* NUL-terminate: will be fed to update-index -z */\n+\t\t\tstrbuf_addch(untracked_files, '\\0');\n+\t\t}\n+\t\tfree(ent);\n+\t}\n+\n+\tfree(seen);\n+\tfree(dir.entries);\n+\tfree(dir.ignored);\n+\tclear_directory(&dir);\n+\treturn found;\n+}\n+\n+/*\n+ * The return value of `check_changes()` can be:\n+ *\n+ * < 0 if there was an error\n+ * = 0 if there are no changes.\n+ * > 0 if there are changes.\n+ */\n+static int check_changes(struct pathspec ps, int include_untracked)\n+{\n+\tint result;\n+\tstruct rev_info rev;\n+\tstruct object_id dummy;\n+\tstruct strbuf out = STRBUF_INIT;\n+\n+\t/* No initial commit. */\n+\tif (get_oid(\"HEAD\", &dummy))\n+\t\treturn -1;\n+\n+\tif (read_cache() < 0)\n+\t\treturn -1;\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune_data = ps;\n+\n+\trev.diffopt.flags.quick = 1;\n+\trev.diffopt.flags.ignore_submodules = 1;\n+\trev.abbrev = 0;\n+\n+\tadd_head_to_pending(&rev);\n+\tdiff_setup_done(&rev.diffopt);\n+\n+\tresult = run_diff_index(&rev, 1);\n+\tif (diff_result_code(&rev.diffopt, result))\n+\t\treturn 1;\n+\n+\tobject_array_clear(&rev.pending);\n+\tresult = run_diff_files(&rev, 0);\n+\tif (diff_result_code(&rev.diffopt, result))\n+\t\treturn 1;\n+\n+\tif (include_untracked && get_untracked_files(ps, include_untracked,\n+\t\t\t\t\t\t     &out)) {\n+\t\tstrbuf_release(&out);\n+\t\treturn 1;\n+\t}\n+\n+\tstrbuf_release(&out);\n+\treturn 0;\n+}\n+\n+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n+\t\t\t\tstruct strbuf files)\n+{\n+\tint ret = 0;\n+\tstruct strbuf untracked_msg = STRBUF_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\n+\tcp_upd_index.git_cmd = 1;\n+\targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n+\t\t\t \"--remove\", \"--stdin\", NULL);\n+\targv_array_pushf(&cp_upd_index.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tstrbuf_addf(&untracked_msg, \"untracked files on %s\\n\", msg->buf);\n+\tif (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,\n+\t\t\t NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\tget_oid_hex(out.buf, &info->u_tree);\n+\n+\tif (commit_tree(untracked_msg.buf, untracked_msg.len,\n+\t\t\t&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&untracked_msg);\n+\tstrbuf_release(&out);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int stash_patch(struct stash_info *info, struct pathspec ps,\n+\t\t       struct strbuf *out_patch)\n+{\n+\tint ret = 0;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n+\n+\tremove_path(stash_index_path.buf);\n+\n+\tcp_read_tree.git_cmd = 1;\n+\targv_array_pushl(&cp_read_tree.args, \"read-tree\", \"HEAD\", NULL);\n+\targv_array_pushf(&cp_read_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp_read_tree)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\t/* Find out what the user wants. */\n+\tcp_add_i.git_cmd = 1;\n+\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n+\t\t\t \"--\", NULL);\n+\tadd_pathspecs(&cp_add_i.args, ps);\n+\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (run_command(&cp_add_i)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\t/* State of the working tree. */\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tget_oid_hex(out.buf, &info->w_tree);\n+\n+\tcp_diff_tree.git_cmd = 1;\n+\targv_array_pushl(&cp_diff_tree.args, \"diff-tree\", \"-p\", \"HEAD\",\n+\t\t\t oid_to_hex(&info->w_tree), \"--\", NULL);\n+\tif (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (!out_patch->len) {\n+\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n+\t\tret = 1;\n+\t}\n+\n+done:\n+\tstrbuf_release(&out);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n+{\n+\tint ret = 0;\n+\tstruct rev_info rev;\n+\tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n+\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tstruct strbuf diff_output = STRBUF_INIT;\n+\n+\tinit_revisions(&rev, NULL);\n+\n+\tset_alternate_index_output(stash_index_path.buf);\n+\tif (reset_tree(&info->i_tree, 0, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\tset_alternate_index_output(NULL);\n+\n+\trev.prune_data = ps;\n+\trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = add_diff_to_buf;\n+\trev.diffopt.format_callback_data = &diff_output;\n+\n+\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tadd_pending_object(&rev, parse_object(the_repository, &info->b_commit),\n+\t\t\t   \"\");\n+\tif (run_diff_index(&rev, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_upd_index.git_cmd = 1;\n+\targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n+\t\t\t \"--remove\", \"--stdin\", NULL);\n+\targv_array_pushf(&cp_upd_index.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\n+\tif (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,\n+\t\t\t NULL, 0, NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tcp_write_tree.git_cmd = 1;\n+\targv_array_push(&cp_write_tree.args, \"write-tree\");\n+\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n+\t\t\t stash_index_path.buf);\n+\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tget_oid_hex(out.buf, &info->w_tree);\n+\n+done:\n+\tUNLEAK(rev);\n+\tstrbuf_release(&out);\n+\tobject_array_clear(&rev.pending);\n+\tstrbuf_release(&diff_output);\n+\tremove_path(stash_index_path.buf);\n+\treturn ret;\n+}\n+\n+static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n+\t\t\t   int include_untracked, int patch_mode,\n+\t\t\t   struct stash_info *info)\n+{\n+\tint ret = 0;\n+\tint flags = 0;\n+\tint untracked_commit_option = 0;\n+\tconst char *head_short_sha1 = NULL;\n+\tconst char *branch_ref = NULL;\n+\tconst char *branch_name = \"(no branch)\";\n+\tstruct commit *head_commit = NULL;\n+\tstruct commit_list *parents = NULL;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tstruct strbuf commit_tree_label = STRBUF_INIT;\n+\tstruct strbuf untracked_files = STRBUF_INIT;\n+\tstruct strbuf patch = STRBUF_INIT;\n+\n+\tprepare_fallback_ident(\"git stash\", \"git@stash\");\n+\n+\tread_cache_preload(NULL);\n+\trefresh_cache(REFRESH_QUIET);\n+\n+\tif (get_oid(\"HEAD\", &info->b_commit)) {\n+\t\tfprintf_ln(stderr, _(\"You do not have the initial commit yet\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t} else {\n+\t\thead_commit = lookup_commit(the_repository, &info->b_commit);\n+\t}\n+\n+\tif (!check_changes(ps, include_untracked)) {\n+\t\tret = 1;\n+\t\tgoto done;\n+\t}\n+\n+\tbranch_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flags);\n+\tif (flags & REF_ISSYMREF)\n+\t\tbranch_name = strrchr(branch_ref, '/') + 1;\n+\thead_short_sha1 = find_unique_abbrev(&head_commit->object.oid,\n+\t\t\t\t\t     DEFAULT_ABBREV);\n+\tstrbuf_addf(&msg, \"%s: %s \", branch_name, head_short_sha1);\n+\tpp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);\n+\n+\tstrbuf_addf(&commit_tree_label, \"index on %s\\n\", msg.buf);\n+\tcommit_list_insert(head_commit, &parents);\n+\tif (write_cache_as_tree(&info->i_tree, 0, NULL) ||\n+\t    commit_tree(commit_tree_label.buf, commit_tree_label.len,\n+\t\t\t&info->i_tree, parents, &info->i_commit, NULL, NULL)) {\n+\t\tfprintf_ln(stderr, _(\"Cannot save the current index state\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (include_untracked && get_untracked_files(ps, include_untracked,\n+\t\t\t\t\t\t     &untracked_files)) {\n+\t\tif (save_untracked_files(info, &msg, untracked_files)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n+\t\t\t\t\t     \"the untracked files\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t\tuntracked_commit_option = 1;\n+\t}\n+\tif (patch_mode) {\n+\t\tret = stash_patch(info, ps, &patch);\n+\t\tif (ret < 0) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"worktree state\"));\n+\t\t\tgoto done;\n+\t\t} else if (ret > 0) {\n+\t\t\tgoto done;\n+\t\t}\n+\t} else {\n+\t\tif (stash_working_tree(info, ps)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"worktree state\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tif (!stash_msg_buf->len)\n+\t\tstrbuf_addf(stash_msg_buf, \"WIP on %s\", msg.buf);\n+\telse\n+\t\tstrbuf_insertf(stash_msg_buf, 0, \"On %s: \", branch_name);\n+\n+\t/*\n+\t * `parents` will be empty after calling `commit_tree()`, so there is\n+\t * no need to call `free_commit_list()`\n+\t */\n+\tparents = NULL;\n+\tif (untracked_commit_option)\n+\t\tcommit_list_insert(lookup_commit(the_repository,\n+\t\t\t\t\t\t &info->u_commit),\n+\t\t\t\t   &parents);\n+\tcommit_list_insert(lookup_commit(the_repository, &info->i_commit),\n+\t\t\t   &parents);\n+\tcommit_list_insert(head_commit, &parents);\n+\n+\tif (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,\n+\t\t\tparents, &info->w_commit, NULL, NULL)) {\n+\t\tfprintf_ln(stderr, _(\"Cannot record working tree state\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&commit_tree_label);\n+\tstrbuf_release(&msg);\n+\tstrbuf_release(&untracked_files);\n+\treturn ret;\n+}\n+\n+static int create_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint include_untracked = 0;\n+\tint ret = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct stash_info info;\n+\tstruct pathspec ps;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n+\t\t\t N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_create_usage,\n+\t\t\t     0);\n+\n+\tmemset(&ps, 0, sizeof(ps));\n+\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n+\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n+\tif (!ret)\n+\t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n+\n+\tstrbuf_release(&stash_msg_buf);\n+\n+\t/*\n+\t * ret can be 1 if there were no changes. In this case, we should\n+\t * not error out.\n+\t */\n+\treturn ret < 0;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -798,7 +1243,7 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_diff_basic_config, NULL);\n \n \targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n@@ -825,6 +1270,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!show_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"store\"))\n \t\treturn !!store_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"create\"))\n+\t\treturn !!create_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex ff5556ccb0..a9b3064ff0 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -442,7 +442,7 @@ clear)\n \t;;\n create)\n \tshift\n-\tcreate_stash -m \"$*\" && echo \"$w_commit\"\n+\tgit stash--helper create --message \"$*\"\n \t;;\n store)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370176","messageId":"20190225231631.30507-20-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 19/27] stash: convert push to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:23Z","receivedAt":"2019-02-25T23:17:24Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash push to the helper.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 246 +++++++++++++++++++++++++++++++++++++++-\n git-stash.sh            |   6 +-\n 2 files changed, 246 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 56864c126b..30c7aad76a 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -23,6 +23,9 @@ static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n \tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n \tN_(\"git stash--helper clear\"),\n+\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--] [<pathspec>...]]\"),\n \tNULL\n };\n \n@@ -71,6 +74,13 @@ static const char * const git_stash_helper_create_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_push_usage[] = {\n+\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--] [<pathspec>...]]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -1088,7 +1098,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \n static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n-\t\t\t   struct stash_info *info)\n+\t\t\t   struct stash_info *info, struct strbuf *patch)\n {\n \tint ret = 0;\n \tint flags = 0;\n@@ -1101,7 +1111,6 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tstruct strbuf msg = STRBUF_INIT;\n \tstruct strbuf commit_tree_label = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n-\tstruct strbuf patch = STRBUF_INIT;\n \n \tprepare_fallback_ident(\"git stash\", \"git@stash\");\n \n@@ -1150,7 +1159,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\tuntracked_commit_option = 1;\n \t}\n \tif (patch_mode) {\n-\t\tret = stash_patch(info, ps, &patch);\n+\t\tret = stash_patch(info, ps, patch);\n \t\tif (ret < 0) {\n \t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n \t\t\t\t\t     \"worktree state\"));\n@@ -1221,7 +1230,8 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \n \tmemset(&ps, 0, sizeof(ps));\n \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info);\n+\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n+\t\t\t      NULL);\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n@@ -1234,6 +1244,232 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \treturn ret < 0;\n }\n \n+static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n+\t\t\t int keep_index, int patch_mode, int include_untracked)\n+{\n+\tint ret = 0;\n+\tstruct stash_info info;\n+\tstruct strbuf patch = STRBUF_INIT;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\n+\tif (patch_mode && keep_index == -1)\n+\t\tkeep_index = 1;\n+\n+\tif (patch_mode && include_untracked) {\n+\t\tfprintf_ln(stderr, _(\"Can't use --patch and --include-untracked\"\n+\t\t\t\t     \" or --all at the same time\"));\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tread_cache_preload(NULL);\n+\tif (!include_untracked && ps.nr) {\n+\t\tint i;\n+\t\tchar *ps_matched = xcalloc(ps.nr, 1);\n+\n+\t\tfor (i = 0; i < active_nr; i++)\n+\t\t\tce_path_match(&the_index, active_cache[i], &ps,\n+\t\t\t\t      ps_matched);\n+\n+\t\tif (report_path_error(ps_matched, &ps, NULL)) {\n+\t\t\tfprintf_ln(stderr, _(\"Did you forget to 'git add'?\"));\n+\t\t\tret = -1;\n+\t\t\tfree(ps_matched);\n+\t\t\tgoto done;\n+\t\t}\n+\t\tfree(ps_matched);\n+\t}\n+\n+\tif (refresh_cache(REFRESH_QUIET)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (!check_changes(ps, include_untracked)) {\n+\t\tif (!quiet)\n+\t\t\tprintf_ln(_(\"No local changes to save\"));\n+\t\tgoto done;\n+\t}\n+\n+\tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n+\t\tret = -1;\n+\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n+\t\tgoto done;\n+\t}\n+\n+\tif (stash_msg)\n+\t\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n+\tif (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,\n+\t\t\t    &info, &patch)) {\n+\t\tret = -1;\n+\t\tgoto done;\n+\t}\n+\n+\tif (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {\n+\t\tret = -1;\n+\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n+\t\tgoto done;\n+\t}\n+\n+\tprintf_ln(_(\"Saved working directory and index state %s\"),\n+\t\t  stash_msg_buf.buf);\n+\n+\tif (!patch_mode) {\n+\t\tif (include_untracked && !ps.nr) {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"clean\", \"--force\",\n+\t\t\t\t\t \"--quiet\", \"-d\", NULL);\n+\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n+\t\t\t\targv_array_push(&cp.args, \"-x\");\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tdiscard_cache();\n+\t\tif (ps.nr) {\n+\t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_apply = CHILD_PROCESS_INIT;\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tcp_add.git_cmd = 1;\n+\t\t\targv_array_push(&cp_add.args, \"add\");\n+\t\t\tif (!include_untracked)\n+\t\t\t\targv_array_push(&cp_add.args, \"-u\");\n+\t\t\tif (include_untracked == INCLUDE_ALL_FILES)\n+\t\t\t\targv_array_push(&cp_add.args, \"--force\");\n+\t\t\targv_array_push(&cp_add.args, \"--\");\n+\t\t\tadd_pathspecs(&cp_add.args, ps);\n+\t\t\tif (run_command(&cp_add)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_diff.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_diff.args, \"diff-index\", \"-p\",\n+\t\t\t\t\t \"--cached\", \"--binary\", \"HEAD\", \"--\",\n+\t\t\t\t\t NULL);\n+\t\t\tadd_pathspecs(&cp_diff.args, ps);\n+\t\t\tif (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_apply.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_apply.args, \"apply\", \"--index\",\n+\t\t\t\t\t \"-R\", NULL);\n+\t\t\tif (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,\n+\t\t\t\t\t NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"reset\", \"--hard\", \"-q\",\n+\t\t\t\t\t NULL);\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (keep_index == 1 && !is_null_oid(&info.i_tree)) {\n+\t\t\tstruct child_process cp_ls = CHILD_PROCESS_INIT;\n+\t\t\tstruct child_process cp_checkout = CHILD_PROCESS_INIT;\n+\t\t\tstruct strbuf out = STRBUF_INIT;\n+\n+\t\t\tif (reset_tree(&info.i_tree, 0, 1)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_ls.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_ls.args, \"ls-files\", \"-z\",\n+\t\t\t\t\t \"--modified\", \"--\", NULL);\n+\n+\t\t\tadd_pathspecs(&cp_ls.args, ps);\n+\t\t\tif (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tcp_checkout.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp_checkout.args, \"checkout-index\",\n+\t\t\t\t\t \"-z\", \"--force\", \"--stdin\", NULL);\n+\t\t\tif (pipe_command(&cp_checkout, out.buf, out.len, NULL,\n+\t\t\t\t\t 0, NULL, 0)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tgoto done;\n+\t} else {\n+\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\tcp.git_cmd = 1;\n+\t\targv_array_pushl(&cp.args, \"apply\", \"-R\", NULL);\n+\n+\t\tif (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {\n+\t\t\tfprintf_ln(stderr, _(\"Cannot remove worktree changes\"));\n+\t\t\tret = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (keep_index < 1) {\n+\t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\n+\t\t\tcp.git_cmd = 1;\n+\t\t\targv_array_pushl(&cp.args, \"reset\", \"-q\", \"--\", NULL);\n+\t\t\tadd_pathspecs(&cp.args, ps);\n+\t\t\tif (run_command(&cp)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t\tgoto done;\n+\t}\n+\n+done:\n+\tstrbuf_release(&stash_msg_buf);\n+\treturn ret;\n+}\n+\n+static int push_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint keep_index = -1;\n+\tint patch_mode = 0;\n+\tint include_untracked = 0;\n+\tint quiet = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct pathspec ps;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n+\t\t\t N_(\"keep index\")),\n+\t\tOPT_BOOL('p', \"patch\", &patch_mode,\n+\t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_SET_INT('a', \"all\", &include_untracked,\n+\t\t\t    N_(\"include ignore files\"), 2),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_push_usage,\n+\t\t\t     0);\n+\n+\tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n+\treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n+\t\t\t     include_untracked);\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -1272,6 +1508,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!store_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"create\"))\n \t\treturn !!create_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"push\"))\n+\t\treturn !!push_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex a9b3064ff0..51d7a06601 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -429,7 +429,8 @@ save)\n \t;;\n push)\n \tshift\n-\tpush_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper push \"$@\"\n \t;;\n apply)\n \tshift\n@@ -465,7 +466,8 @@ branch)\n *)\n \tcase $# in\n \t0)\n-\t\tpush_stash &&\n+\t\tcd \"$START_DIR\"\n+\t\tgit stash--helper push &&\n \t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n \t\t;;\n \t*)\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370177","messageId":"20190225231631.30507-21-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 20/27] stash: make push -q quiet","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:24Z","receivedAt":"2019-02-25T23:17:25Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nThere is a change in behaviour with this commit. When there was\nno initial commit, the shell version of stash would still display\na message. This commit makes `push` to not display any message if\n`--quiet` or `-q` is specified. Add tests for `--quiet`.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 56 ++++++++++++++++++++++++++---------------\n t/t3903-stash.sh        | 23 +++++++++++++++++\n 2 files changed, 59 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 30c7aad76a..1c61352093 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -966,7 +966,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n }\n \n static int stash_patch(struct stash_info *info, struct pathspec ps,\n-\t\t       struct strbuf *out_patch)\n+\t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n \tstruct strbuf out = STRBUF_INIT;\n@@ -1019,7 +1019,8 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n \tif (!out_patch->len) {\n-\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"No changes selected\"));\n \t\tret = 1;\n \t}\n \n@@ -1098,7 +1099,8 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \n static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n-\t\t\t   struct stash_info *info, struct strbuf *patch)\n+\t\t\t   struct stash_info *info, struct strbuf *patch,\n+\t\t\t   int quiet)\n {\n \tint ret = 0;\n \tint flags = 0;\n@@ -1118,7 +1120,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \trefresh_cache(REFRESH_QUIET);\n \n \tif (get_oid(\"HEAD\", &info->b_commit)) {\n-\t\tfprintf_ln(stderr, _(\"You do not have the initial commit yet\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"You do not have \"\n+\t\t\t\t\t     \"the initial commit yet\"));\n \t\tret = -1;\n \t\tgoto done;\n \t} else {\n@@ -1143,7 +1147,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tif (write_cache_as_tree(&info->i_tree, 0, NULL) ||\n \t    commit_tree(commit_tree_label.buf, commit_tree_label.len,\n \t\t\t&info->i_tree, parents, &info->i_commit, NULL, NULL)) {\n-\t\tfprintf_ln(stderr, _(\"Cannot save the current index state\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t     \"index state\"));\n \t\tret = -1;\n \t\tgoto done;\n \t}\n@@ -1151,26 +1157,29 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \tif (include_untracked && get_untracked_files(ps, include_untracked,\n \t\t\t\t\t\t     &untracked_files)) {\n \t\tif (save_untracked_files(info, &msg, untracked_files)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n-\t\t\t\t\t     \"the untracked files\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n+\t\t\t\t\t\t     \"the untracked files\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\n \t\tuntracked_commit_option = 1;\n \t}\n \tif (patch_mode) {\n-\t\tret = stash_patch(info, ps, patch);\n+\t\tret = stash_patch(info, ps, patch, quiet);\n \t\tif (ret < 0) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n-\t\t\t\t\t     \"worktree state\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t\t     \"worktree state\"));\n \t\t\tgoto done;\n \t\t} else if (ret > 0) {\n \t\t\tgoto done;\n \t\t}\n \t} else {\n \t\tif (stash_working_tree(info, ps)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n-\t\t\t\t\t     \"worktree state\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot save the current \"\n+\t\t\t\t\t\t     \"worktree state\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\n@@ -1196,7 +1205,9 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \n \tif (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,\n \t\t\tparents, &info->w_commit, NULL, NULL)) {\n-\t\tfprintf_ln(stderr, _(\"Cannot record working tree state\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot record \"\n+\t\t\t\t\t     \"working tree state\"));\n \t\tret = -1;\n \t\tgoto done;\n \t}\n@@ -1231,7 +1242,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tmemset(&ps, 0, sizeof(ps));\n \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n-\t\t\t      NULL);\n+\t\t\t      NULL, 0);\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n@@ -1293,26 +1304,29 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \n \tif (!reflog_exists(ref_stash) && do_clear_stash()) {\n \t\tret = -1;\n-\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot initialize stash\"));\n \t\tgoto done;\n \t}\n \n \tif (stash_msg)\n \t\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tif (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,\n-\t\t\t    &info, &patch)) {\n+\t\t\t    &info, &patch, quiet)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n \tif (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {\n \t\tret = -1;\n-\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n+\t\tif (!quiet)\n+\t\t\tfprintf_ln(stderr, _(\"Cannot save the current status\"));\n \t\tgoto done;\n \t}\n \n-\tprintf_ln(_(\"Saved working directory and index state %s\"),\n-\t\t  stash_msg_buf.buf);\n+\tif (!quiet)\n+\t\tprintf_ln(_(\"Saved working directory and index state %s\"),\n+\t\t\t  stash_msg_buf.buf);\n \n \tif (!patch_mode) {\n \t\tif (include_untracked && !ps.nr) {\n@@ -1414,7 +1428,9 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\targv_array_pushl(&cp.args, \"apply\", \"-R\", NULL);\n \n \t\tif (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {\n-\t\t\tfprintf_ln(stderr, _(\"Cannot remove worktree changes\"));\n+\t\t\tif (!quiet)\n+\t\t\t\tfprintf_ln(stderr, _(\"Cannot remove \"\n+\t\t\t\t\t\t     \"worktree changes\"));\n \t\t\tret = -1;\n \t\t\tgoto done;\n \t\t}\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 023421a36c..7dfa3a8038 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -1072,6 +1072,29 @@ test_expect_success 'push: <pathspec> not in the repository errors out' '\n \ttest_path_is_file untracked\n '\n \n+test_expect_success 'push: -q is quiet with changes' '\n+\t>foo &&\n+\tgit add foo &&\n+\tgit stash push -q >output 2>&1 &&\n+\ttest_must_be_empty output\n+'\n+\n+test_expect_success 'push: -q is quiet with no changes' '\n+\tgit stash push -q >output 2>&1 &&\n+\ttest_must_be_empty output\n+'\n+\n+test_expect_success 'push: -q is quiet even if there is no initial commit' '\n+\tgit init foo_dir &&\n+\ttest_when_finished rm -rf foo_dir &&\n+\t(\n+\t\tcd foo_dir &&\n+\t\t>bar &&\n+\t\ttest_must_fail git stash push -q >output 2>&1 &&\n+\t\ttest_must_be_empty output\n+\t)\n+'\n+\n test_expect_success 'untracked files are left in place when -u is not given' '\n \t>file &&\n \tgit add file &&\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370178","messageId":"20190225231631.30507-22-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 21/27] stash: convert save to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:25Z","receivedAt":"2019-02-25T23:17:27Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAdd stash save to the helper and delete functions which are no\nlonger needed (`show_help()`, `save_stash()`, `push_stash()`,\n`create_stash()`, `clear_stash()`, `untracked_files()` and\n`no_changes()`).\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c |  50 ++++++\n git-stash.sh            | 328 +---------------------------------------\n 2 files changed, 52 insertions(+), 326 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 1c61352093..8e4e96e353 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -26,6 +26,8 @@ static const char * const git_stash_helper_usage[] = {\n \tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n+\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n \n@@ -81,6 +83,12 @@ static const char * const git_stash_helper_push_usage[] = {\n \tNULL\n };\n \n+static const char * const git_stash_helper_save_usage[] = {\n+\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n+\tNULL\n+};\n+\n static const char *ref_stash = \"refs/stash\";\n static struct strbuf stash_index_path = STRBUF_INIT;\n \n@@ -1486,6 +1494,46 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     include_untracked);\n }\n \n+static int save_stash(int argc, const char **argv, const char *prefix)\n+{\n+\tint keep_index = -1;\n+\tint patch_mode = 0;\n+\tint include_untracked = 0;\n+\tint quiet = 0;\n+\tint ret = 0;\n+\tconst char *stash_msg = NULL;\n+\tstruct pathspec ps;\n+\tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct option options[] = {\n+\t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n+\t\t\t N_(\"keep index\")),\n+\t\tOPT_BOOL('p', \"patch\", &patch_mode,\n+\t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n+\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n+\t\t\t N_(\"include untracked files in stash\")),\n+\t\tOPT_SET_INT('a', \"all\", &include_untracked,\n+\t\t\t    N_(\"include ignore files\"), 2),\n+\t\tOPT_STRING('m', \"message\", &stash_msg, \"message\",\n+\t\t\t   N_(\"stash message\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_stash_helper_save_usage,\n+\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\n+\tif (argc)\n+\t\tstash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');\n+\n+\tmemset(&ps, 0, sizeof(ps));\n+\tret = do_push_stash(ps, stash_msg, quiet, keep_index,\n+\t\t\t    patch_mode, include_untracked);\n+\n+\tstrbuf_release(&stash_msg_buf);\n+\treturn ret;\n+}\n+\n int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n {\n \tpid_t pid = getpid();\n@@ -1526,6 +1574,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!create_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"push\"))\n \t\treturn !!push_stash(argc, argv, prefix);\n+\telse if (!strcmp(argv[0], \"save\"))\n+\t\treturn !!save_stash(argc, argv, prefix);\n \n \tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t      git_stash_helper_usage, options);\ndiff --git a/git-stash.sh b/git-stash.sh\nindex 51d7a06601..695f1feba3 100755\n--- a/git-stash.sh\n+++ b/git-stash.sh\n@@ -36,331 +36,6 @@ else\n        reset_color=\n fi\n \n-no_changes () {\n-\tgit diff-index --quiet --cached HEAD --ignore-submodules -- \"$@\" &&\n-\tgit diff-files --quiet --ignore-submodules -- \"$@\" &&\n-\t(test -z \"$untracked\" || test -z \"$(untracked_files \"$@\")\")\n-}\n-\n-untracked_files () {\n-\tif test \"$1\" = \"-z\"\n-\tthen\n-\t\tshift\n-\t\tz=-z\n-\telse\n-\t\tz=\n-\tfi\n-\texcl_opt=--exclude-standard\n-\ttest \"$untracked\" = \"all\" && excl_opt=\n-\tgit ls-files -o $z $excl_opt -- \"$@\"\n-}\n-\n-prepare_fallback_ident () {\n-\tif ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1\n-\tthen\n-\t\tGIT_AUTHOR_NAME=\"git stash\"\n-\t\tGIT_AUTHOR_EMAIL=git@stash\n-\t\tGIT_COMMITTER_NAME=\"git stash\"\n-\t\tGIT_COMMITTER_EMAIL=git@stash\n-\t\texport GIT_AUTHOR_NAME\n-\t\texport GIT_AUTHOR_EMAIL\n-\t\texport GIT_COMMITTER_NAME\n-\t\texport GIT_COMMITTER_EMAIL\n-\tfi\n-}\n-\n-clear_stash () {\n-\tif test $# != 0\n-\tthen\n-\t\tdie \"$(gettext \"git stash clear with parameters is unimplemented\")\"\n-\tfi\n-\tif current=$(git rev-parse --verify --quiet $ref_stash)\n-\tthen\n-\t\tgit update-ref -d $ref_stash $current\n-\tfi\n-}\n-\n-create_stash () {\n-\n-\tprepare_fallback_ident\n-\n-\tstash_msg=\n-\tuntracked=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\tstash_msg=${1?\"BUG: create_stash () -m requires an argument\"}\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t-u|--include-untracked)\n-\t\t\tshift\n-\t\t\tuntracked=${1?\"BUG: create_stash () -u requires an argument\"}\n-\t\t\t;;\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n-\tthen\n-\t\texit 0\n-\tfi\n-\n-\t# state of the base commit\n-\tif b_commit=$(git rev-parse --verify HEAD)\n-\tthen\n-\t\thead=$(git rev-list --oneline -n 1 HEAD --)\n-\telse\n-\t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n-\tfi\n-\n-\tif branch=$(git symbolic-ref -q HEAD)\n-\tthen\n-\t\tbranch=${branch#refs/heads/}\n-\telse\n-\t\tbranch='(no branch)'\n-\tfi\n-\tmsg=$(printf '%s: %s' \"$branch\" \"$head\")\n-\n-\t# state of the index\n-\ti_tree=$(git write-tree) &&\n-\ti_commit=$(printf 'index on %s\\n' \"$msg\" |\n-\t\tgit commit-tree $i_tree -p $b_commit) ||\n-\t\tdie \"$(gettext \"Cannot save the current index state\")\"\n-\n-\tif test -n \"$untracked\"\n-\tthen\n-\t\t# Untracked files are stored by themselves in a parentless commit, for\n-\t\t# ease of unpacking later.\n-\t\tu_commit=$(\n-\t\t\tuntracked_files -z \"$@\" | (\n-\t\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n-\t\t\t\texport GIT_INDEX_FILE &&\n-\t\t\t\trm -f \"$TMPindex\" &&\n-\t\t\t\tgit update-index -z --add --remove --stdin &&\n-\t\t\t\tu_tree=$(git write-tree) &&\n-\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n-\t\t\t\trm -f \"$TMPindex\"\n-\t\t) ) || die \"$(gettext \"Cannot save the untracked files\")\"\n-\n-\t\tuntracked_commit_option=\"-p $u_commit\";\n-\telse\n-\t\tuntracked_commit_option=\n-\tfi\n-\n-\tif test -z \"$patch_mode\"\n-\tthen\n-\n-\t\t# state of the working tree\n-\t\tw_tree=$( (\n-\t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n-\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n-\t\t\texport GIT_INDEX_FILE &&\n-\t\t\tgit diff-index --name-only -z HEAD -- \"$@\" >\"$TMP-stagenames\" &&\n-\t\t\tgit update-index -z --add --remove --stdin <\"$TMP-stagenames\" &&\n-\t\t\tgit write-tree &&\n-\t\t\trm -f \"$TMPindex\"\n-\t\t) ) ||\n-\t\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n-\n-\telse\n-\n-\t\trm -f \"$TMP-index\" &&\n-\t\tGIT_INDEX_FILE=\"$TMP-index\" git read-tree HEAD &&\n-\n-\t\t# find out what the user wants\n-\t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n-\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n-\n-\t\t# state of the working tree\n-\t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n-\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n-\n-\t\tgit diff-tree -p HEAD $w_tree -- >\"$TMP-patch\" &&\n-\t\ttest -s \"$TMP-patch\" ||\n-\t\tdie \"$(gettext \"No changes selected\")\"\n-\n-\t\trm -f \"$TMP-index\" ||\n-\t\tdie \"$(gettext \"Cannot remove temporary index (can't happen)\")\"\n-\n-\tfi\n-\n-\t# create the stash\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tstash_msg=$(printf 'WIP on %s' \"$msg\")\n-\telse\n-\t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n-\tfi\n-\tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n-\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n-\tdie \"$(gettext \"Cannot record working tree state\")\"\n-}\n-\n-push_stash () {\n-\tkeep_index=\n-\tpatch_mode=\n-\tuntracked=\n-\tstash_msg=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t-k|--keep-index)\n-\t\t\tkeep_index=t\n-\t\t\t;;\n-\t\t--no-keep-index)\n-\t\t\tkeep_index=n\n-\t\t\t;;\n-\t\t-p|--patch)\n-\t\t\tpatch_mode=t\n-\t\t\t# only default to keep if we don't already have an override\n-\t\t\ttest -z \"$keep_index\" && keep_index=t\n-\t\t\t;;\n-\t\t-q|--quiet)\n-\t\t\tGIT_QUIET=t\n-\t\t\t;;\n-\t\t-u|--include-untracked)\n-\t\t\tuntracked=untracked\n-\t\t\t;;\n-\t\t-a|--all)\n-\t\t\tuntracked=all\n-\t\t\t;;\n-\t\t-m|--message)\n-\t\t\tshift\n-\t\t\ttest -z ${1+x} && usage\n-\t\t\tstash_msg=$1\n-\t\t\t;;\n-\t\t-m*)\n-\t\t\tstash_msg=${1#-m}\n-\t\t\t;;\n-\t\t--message=*)\n-\t\t\tstash_msg=${1#--message=}\n-\t\t\t;;\n-\t\t--help)\n-\t\t\tshow_help\n-\t\t\t;;\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\t-*)\n-\t\t\toption=\"$1\"\n-\t\t\teval_gettextln \"error: unknown option for 'stash push': \\$option\"\n-\t\t\tusage\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\teval \"set $(git rev-parse --sq --prefix \"$prefix\" -- \"$@\")\"\n-\n-\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n-\tthen\n-\t\tdie \"$(gettext \"Can't use --patch and --include-untracked or --all at the same time\")\"\n-\tfi\n-\n-\ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n-\n-\tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n-\tthen\n-\t\tsay \"$(gettext \"No local changes to save\")\"\n-\t\texit 0\n-\tfi\n-\n-\tgit reflog exists $ref_stash ||\n-\t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n-\n-\tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n-\tgit stash--helper store -m \"$stash_msg\" -q $w_commit ||\n-\tdie \"$(gettext \"Cannot save the current status\")\"\n-\tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n-\n-\tif test -z \"$patch_mode\"\n-\tthen\n-\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n-\t\tif test -n \"$untracked\" && test $# = 0\n-\t\tthen\n-\t\t\tgit clean --force --quiet -d $CLEAN_X_OPTION\n-\t\tfi\n-\n-\t\tif test $# != 0\n-\t\tthen\n-\t\t\ttest -z \"$untracked\" && UPDATE_OPTION=\"-u\" || UPDATE_OPTION=\n-\t\t\ttest \"$untracked\" = \"all\" && FORCE_OPTION=\"--force\" || FORCE_OPTION=\n-\t\t\tgit add $UPDATE_OPTION $FORCE_OPTION -- \"$@\"\n-\t\t\tgit diff-index -p --cached --binary HEAD -- \"$@\" |\n-\t\t\tgit apply --index -R\n-\t\telse\n-\t\t\tgit reset --hard -q\n-\t\tfi\n-\n-\t\tif test \"$keep_index\" = \"t\" && test -n \"$i_tree\"\n-\t\tthen\n-\t\t\tgit read-tree --reset $i_tree\n-\t\t\tgit ls-files -z --modified -- \"$@\" |\n-\t\t\tgit checkout-index -z --force --stdin\n-\t\tfi\n-\telse\n-\t\tgit apply -R < \"$TMP-patch\" ||\n-\t\tdie \"$(gettext \"Cannot remove worktree changes\")\"\n-\n-\t\tif test \"$keep_index\" != \"t\"\n-\t\tthen\n-\t\t\tgit reset -q -- \"$@\"\n-\t\tfi\n-\tfi\n-}\n-\n-save_stash () {\n-\tpush_options=\n-\twhile test $# != 0\n-\tdo\n-\t\tcase \"$1\" in\n-\t\t--)\n-\t\t\tshift\n-\t\t\tbreak\n-\t\t\t;;\n-\t\t-*)\n-\t\t\t# pass all options through to push_stash\n-\t\t\tpush_options=\"$push_options $1\"\n-\t\t\t;;\n-\t\t*)\n-\t\t\tbreak\n-\t\t\t;;\n-\t\tesac\n-\t\tshift\n-\tdone\n-\n-\tstash_msg=\"$*\"\n-\n-\tif test -z \"$stash_msg\"\n-\tthen\n-\t\tpush_stash $push_options\n-\telse\n-\t\tpush_stash $push_options -m \"$stash_msg\"\n-\tfi\n-}\n-\n-show_help () {\n-\texec git help stash\n-\texit 1\n-}\n-\n #\n # Parses the remaining options looking for flags and\n # at most one revision defaulting to ${ref_stash}@{0}\n@@ -425,7 +100,8 @@ show)\n \t;;\n save)\n \tshift\n-\tsave_stash \"$@\"\n+\tcd \"$START_DIR\"\n+\tgit stash--helper save \"$@\"\n \t;;\n push)\n \tshift\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370180","messageId":"20190225231631.30507-23-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 22/27] stash: optimize `get_untracked_files()` and `check_changes()`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:26Z","receivedAt":"2019-02-25T23:17:28Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nThis commits introduces a optimization by avoiding calling the\nsame functions again. For example, `git stash push -u`\nwould call at some points the following functions:\n\n * `check_changes()` (inside `do_push_stash()`)\n * `do_create_stash()`, which calls: `check_changes()` and\n`get_untracked_files()`\n\nNote that `check_changes()` also calls `get_untracked_files()`.\nSo, `check_changes()` is called 2 times and `get_untracked_files()`\n3 times.\n\nThe old function `check_changes()` now consists of two functions:\n`get_untracked_files()` and `check_changes_tracked_files()`.\n\nThese are the call chains for `push` and `create`:\n\n * `push_stash()` -> `do_push_stash()` -> `do_create_stash()`\n\n * `create_stash()` -> `do_create_stash()`\n\nTo prevent calling the same functions over and over again,\n`check_changes()` inside `do_create_stash()` is now placed\nin the caller functions (`create_stash()` and `do_push_stash()`).\nThis way `check_changes()` and `get_untracked files()` are called\nonly one time.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 46 ++++++++++++++++++++++++-----------------\n 1 file changed, 27 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 8e4e96e353..4c51b58206 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -879,18 +879,17 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n }\n \n /*\n- * The return value of `check_changes()` can be:\n+ * The return value of `check_changes_tracked_files()` can be:\n  *\n  * < 0 if there was an error\n  * = 0 if there are no changes.\n  * > 0 if there are changes.\n  */\n-static int check_changes(struct pathspec ps, int include_untracked)\n+static int check_changes_tracked_files(struct pathspec ps)\n {\n \tint result;\n \tstruct rev_info rev;\n \tstruct object_id dummy;\n-\tstruct strbuf out = STRBUF_INIT;\n \n \t/* No initial commit. */\n \tif (get_oid(\"HEAD\", &dummy))\n@@ -918,14 +917,25 @@ static int check_changes(struct pathspec ps, int include_untracked)\n \tif (diff_result_code(&rev.diffopt, result))\n \t\treturn 1;\n \n+\treturn 0;\n+}\n+\n+/*\n+ * The function will fill `untracked_files` with the names of untracked files\n+ * It will return 1 if there were any changes and 0 if there were not.\n+ */\n+static int check_changes(struct pathspec ps, int include_untracked,\n+\t\t\t struct strbuf *untracked_files)\n+{\n+\tint ret = 0;\n+\tif (check_changes_tracked_files(ps))\n+\t\tret = 1;\n+\n \tif (include_untracked && get_untracked_files(ps, include_untracked,\n-\t\t\t\t\t\t     &out)) {\n-\t\tstrbuf_release(&out);\n-\t\treturn 1;\n-\t}\n+\t\t\t\t\t\t     untracked_files))\n+\t\tret = 1;\n \n-\tstrbuf_release(&out);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n@@ -1137,7 +1147,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\thead_commit = lookup_commit(the_repository, &info->b_commit);\n \t}\n \n-\tif (!check_changes(ps, include_untracked)) {\n+\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n \t\tret = 1;\n \t\tgoto done;\n \t}\n@@ -1162,8 +1172,7 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \t\tgoto done;\n \t}\n \n-\tif (include_untracked && get_untracked_files(ps, include_untracked,\n-\t\t\t\t\t\t     &untracked_files)) {\n+\tif (include_untracked) {\n \t\tif (save_untracked_files(info, &msg, untracked_files)) {\n \t\t\tif (!quiet)\n \t\t\t\tfprintf_ln(stderr, _(\"Cannot save \"\n@@ -1248,6 +1257,9 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     0);\n \n \tmemset(&ps, 0, sizeof(ps));\n+\tif (!check_changes_tracked_files(ps))\n+\t\treturn 0;\n+\n \tstrbuf_addstr(&stash_msg_buf, stash_msg);\n \tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n \t\t\t      NULL, 0);\n@@ -1255,12 +1267,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n \tstrbuf_release(&stash_msg_buf);\n-\n-\t/*\n-\t * ret can be 1 if there were no changes. In this case, we should\n-\t * not error out.\n-\t */\n-\treturn ret < 0;\n+\treturn ret;\n }\n \n static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n@@ -1270,6 +1277,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \tstruct stash_info info;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n+\tstruct strbuf untracked_files = STRBUF_INIT;\n \n \tif (patch_mode && keep_index == -1)\n \t\tkeep_index = 1;\n@@ -1304,7 +1312,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\tgoto done;\n \t}\n \n-\tif (!check_changes(ps, include_untracked)) {\n+\tif (!check_changes(ps, include_untracked, &untracked_files)) {\n \t\tif (!quiet)\n \t\t\tprintf_ln(_(\"No local changes to save\"));\n \t\tgoto done;\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370179","messageId":"20190225231631.30507-24-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 23/27] stash: replace all `write-tree` child processes with API calls","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:27Z","receivedAt":"2019-02-25T23:17:29Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nAvoid spawning write-tree child processes by replacing the calls with\nin-core API calls.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash--helper.c | 41 ++++++++++++-----------------------------\n 1 file changed, 12 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/stash--helper.c b/builtin/stash--helper.c\nindex 4c51b58206..5f8c99c12f 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash--helper.c\n@@ -943,9 +943,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n {\n \tint ret = 0;\n \tstruct strbuf untracked_msg = STRBUF_INIT;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tcp_upd_index.git_cmd = 1;\n \targv_array_pushl(&cp_upd_index.args, \"update-index\", \"-z\", \"--add\",\n@@ -960,15 +959,11 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \t\tgoto done;\n \t}\n \n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n-\tget_oid_hex(out.buf, &info->u_tree);\n \n \tif (commit_tree(untracked_msg.buf, untracked_msg.len,\n \t\t\t&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {\n@@ -977,8 +972,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \t}\n \n done:\n+\tdiscard_index(&istate);\n \tstrbuf_release(&untracked_msg);\n-\tstrbuf_release(&out);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\n }\n@@ -987,11 +982,10 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n \tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tremove_path(stash_index_path.buf);\n \n@@ -1017,17 +1011,12 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n \t/* State of the working tree. */\n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n-\tget_oid_hex(out.buf, &info->w_tree);\n-\n \tcp_diff_tree.git_cmd = 1;\n \targv_array_pushl(&cp_diff_tree.args, \"diff-tree\", \"-p\", \"HEAD\",\n \t\t\t oid_to_hex(&info->w_tree), \"--\", NULL);\n@@ -1043,7 +1032,7 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \t}\n \n done:\n-\tstrbuf_release(&out);\n+\tdiscard_index(&istate);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\n }\n@@ -1053,9 +1042,8 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \tint ret = 0;\n \tstruct rev_info rev;\n \tstruct child_process cp_upd_index = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_write_tree = CHILD_PROCESS_INIT;\n-\tstruct strbuf out = STRBUF_INIT;\n \tstruct strbuf diff_output = STRBUF_INIT;\n+\tstruct index_state istate = { NULL };\n \n \tinit_revisions(&rev, NULL);\n \n@@ -1095,20 +1083,15 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \t\tgoto done;\n \t}\n \n-\tcp_write_tree.git_cmd = 1;\n-\targv_array_push(&cp_write_tree.args, \"write-tree\");\n-\targv_array_pushf(&cp_write_tree.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (pipe_command(&cp_write_tree, NULL, 0, &out, 0,NULL, 0)) {\n+\tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n+\t\t\t\tNULL)) {\n \t\tret = -1;\n \t\tgoto done;\n \t}\n \n-\tget_oid_hex(out.buf, &info->w_tree);\n-\n done:\n+\tdiscard_index(&istate);\n \tUNLEAK(rev);\n-\tstrbuf_release(&out);\n \tobject_array_clear(&rev.pending);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370181","messageId":"20190225231631.30507-25-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 24/27] stash: convert `stash--helper.c` into `stash.c`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:28Z","receivedAt":"2019-02-25T23:17:33Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n\nThe old shell script `git-stash.sh`  was removed and replaced\nentirely by `builtin/stash.c`. In order to do that, `create` and\n`push` were adapted to work without `stash.sh`. For example, before\nthis commit, `git stash create` called `git stash--helper create\n--message \"$*\"`. If it called `git stash--helper create \"$@\"`, then\nsome of these changes wouldn't have been necessary.\n\nThis commit also removes the word `helper` since now stash is\ncalled directly and not by a shell script.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n .gitignore                           |   1 -\n Makefile                             |   3 +-\n builtin.h                            |   2 +-\n builtin/{stash--helper.c => stash.c} | 156 +++++++++++++++------------\n git-stash.sh                         | 153 --------------------------\n git.c                                |   2 +-\n 6 files changed, 92 insertions(+), 225 deletions(-)\n rename builtin/{stash--helper.c => stash.c} (91%)\n delete mode 100755 git-stash.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 6ecab90ab2..0d77ea5894 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,7 +162,6 @@\n /git-show-ref\n /git-stage\n /git-stash\n-/git-stash--helper\n /git-status\n /git-stripspace\n /git-submodule\ndiff --git a/Makefile b/Makefile\nindex c246fc7078..8cee2731aa 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -619,7 +619,6 @@ SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-legacy-rebase.sh\n SCRIPT_SH += git-remote-testgit.sh\n SCRIPT_SH += git-request-pull.sh\n-SCRIPT_SH += git-stash.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n \n@@ -1117,7 +1116,7 @@ BUILTIN_OBJS += builtin/shortlog.o\n BUILTIN_OBJS += builtin/show-branch.o\n BUILTIN_OBJS += builtin/show-index.o\n BUILTIN_OBJS += builtin/show-ref.o\n-BUILTIN_OBJS += builtin/stash--helper.o\n+BUILTIN_OBJS += builtin/stash.o\n BUILTIN_OBJS += builtin/stripspace.o\n BUILTIN_OBJS += builtin/submodule--helper.o\n BUILTIN_OBJS += builtin/symbolic-ref.o\ndiff --git a/builtin.h b/builtin.h\nindex ff4460aff7..b78ab6e30b 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -225,7 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n-extern int cmd_stash__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_stash(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/stash--helper.c b/builtin/stash.c\nsimilarity index 91%\nrename from builtin/stash--helper.c\nrename to builtin/stash.c\nindex 5f8c99c12f..d9f3956ef5 100644\n--- a/builtin/stash--helper.c\n+++ b/builtin/stash.c\n@@ -16,75 +16,70 @@\n \n #define INCLUDE_ALL_FILES 2\n \n-static const char * const git_stash_helper_usage[] = {\n-\tN_(\"git stash--helper list [<options>]\"),\n-\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n-\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n-\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n-\tN_(\"git stash--helper clear\"),\n-\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_usage[] = {\n+\tN_(\"git stash list [<options>]\"),\n+\tN_(\"git stash show [<options>] [<stash>]\"),\n+\tN_(\"git stash drop [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]\"),\n+\tN_(\"git stash branch <branchname> [<stash>]\"),\n+\tN_(\"git stash clear\"),\n+\tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n-\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+\tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_list_usage[] = {\n-\tN_(\"git stash--helper list [<options>]\"),\n+static const char * const git_stash_list_usage[] = {\n+\tN_(\"git stash list [<options>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_show_usage[] = {\n-\tN_(\"git stash--helper show [<options>] [<stash>]\"),\n+static const char * const git_stash_show_usage[] = {\n+\tN_(\"git stash show [<options>] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_drop_usage[] = {\n-\tN_(\"git stash--helper drop [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_drop_usage[] = {\n+\tN_(\"git stash drop [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_pop_usage[] = {\n-\tN_(\"git stash--helper pop [--index] [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_pop_usage[] = {\n+\tN_(\"git stash pop [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_apply_usage[] = {\n-\tN_(\"git stash--helper apply [--index] [-q|--quiet] [<stash>]\"),\n+static const char * const git_stash_apply_usage[] = {\n+\tN_(\"git stash apply [--index] [-q|--quiet] [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_branch_usage[] = {\n-\tN_(\"git stash--helper branch <branchname> [<stash>]\"),\n+static const char * const git_stash_branch_usage[] = {\n+\tN_(\"git stash branch <branchname> [<stash>]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_clear_usage[] = {\n-\tN_(\"git stash--helper clear\"),\n+static const char * const git_stash_clear_usage[] = {\n+\tN_(\"git stash clear\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_store_usage[] = {\n-\tN_(\"git stash--helper store [-m|--message <message>] [-q|--quiet] <commit>\"),\n+static const char * const git_stash_store_usage[] = {\n+\tN_(\"git stash store [-m|--message <message>] [-q|--quiet] <commit>\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_create_usage[] = {\n-\tN_(\"git stash--helper create [<message>]\"),\n-\tNULL\n-};\n-\n-static const char * const git_stash_helper_push_usage[] = {\n-\tN_(\"git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_push_usage[] = {\n+\tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n \tNULL\n };\n \n-static const char * const git_stash_helper_save_usage[] = {\n-\tN_(\"git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n+static const char * const git_stash_save_usage[] = {\n+\tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n \tNULL\n };\n@@ -220,7 +215,7 @@ static int clear_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_clear_usage,\n+\t\t\t     git_stash_clear_usage,\n \t\t\t     PARSE_OPT_STOP_AT_NON_OPTION);\n \n \tif (argc)\n@@ -521,7 +516,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_apply_usage, 0);\n+\t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -594,7 +589,7 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_drop_usage, 0);\n+\t\t\t     git_stash_drop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -620,7 +615,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_pop_usage, 0);\n+\t\t\t     git_stash_pop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n \t\treturn -1;\n@@ -647,7 +642,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_branch_usage, 0);\n+\t\t\t     git_stash_branch_usage, 0);\n \n \tif (!argc) {\n \t\tfprintf_ln(stderr, _(\"No branch name specified\"));\n@@ -682,7 +677,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_list_usage,\n+\t\t\t     git_stash_list_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n \n \tif (!ref_exists(ref_stash))\n@@ -762,7 +757,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \targc = setup_revisions(argc, argv, &rev, NULL);\n \tif (argc > 1) {\n \t\tfree_stash_info(&info);\n-\t\tusage_with_options(git_stash_helper_show_usage, options);\n+\t\tusage_with_options(git_stash_show_usage, options);\n \t}\n \n \trev.diffopt.flags.recursive = 1;\n@@ -808,7 +803,7 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_store_usage,\n+\t\t\t     git_stash_store_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN);\n \n \tif (argc != 1) {\n@@ -1221,30 +1216,19 @@ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint include_untracked = 0;\n \tint ret = 0;\n-\tconst char *stash_msg = NULL;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct stash_info info;\n \tstruct pathspec ps;\n-\tstruct option options[] = {\n-\t\tOPT_BOOL('u', \"include-untracked\", &include_untracked,\n-\t\t\t N_(\"include untracked files in stash\")),\n-\t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n-\t\t\t N_(\"stash message\")),\n-\t\tOPT_END()\n-\t};\n \n-\targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_create_usage,\n-\t\t\t     0);\n+\t/* Starting with argv[1], since argv[0] is \"create\" */\n+\tstrbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n \tif (!check_changes_tracked_files(ps))\n \t\treturn 0;\n \n-\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n+\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n \t\t\t      NULL, 0);\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n@@ -1476,9 +1460,10 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_push_usage,\n-\t\t\t     0);\n+\tif (argc)\n+\t\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t\t     git_stash_push_usage,\n+\t\t\t\t     0);\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n \treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n@@ -1511,7 +1496,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \t};\n \n \targc = parse_options(argc, argv, prefix, options,\n-\t\t\t     git_stash_helper_save_usage,\n+\t\t\t     git_stash_save_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tif (argc)\n@@ -1525,10 +1510,12 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n-int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n+int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n+\tint i = -1;\n \tpid_t pid = getpid();\n \tconst char *index_file;\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n \n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -1536,16 +1523,16 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_diff_basic_config, NULL);\n \n-\targc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,\n+\targc = parse_options(argc, argv, prefix, options, git_stash_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);\n \n \tindex_file = get_index_file();\n \tstrbuf_addf(&stash_index_path, \"%s.stash.%\" PRIuMAX, index_file,\n \t\t    (uintmax_t)pid);\n \n-\tif (argc < 1)\n-\t\tusage_with_options(git_stash_helper_usage, options);\n-\tif (!strcmp(argv[0], \"apply\"))\n+\tif (!argc)\n+\t\treturn !!push_stash(0, NULL, prefix);\n+\telse if (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"clear\"))\n \t\treturn !!clear_stash(argc, argv, prefix);\n@@ -1567,7 +1554,42 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix)\n \t\treturn !!push_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"save\"))\n \t\treturn !!save_stash(argc, argv, prefix);\n+\telse if (*argv[0] != '-')\n+\t\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n+\t\t\t      git_stash_usage, options);\n+\n+\tif (strcmp(argv[0], \"-p\")) {\n+\t\twhile (++i < argc && strcmp(argv[i], \"--\")) {\n+\t\t\t/*\n+\t\t\t * `akpqu` is a string which contains all short options,\n+\t\t\t * except `-m` which is verified separately.\n+\t\t\t */\n+\t\t\tif ((strlen(argv[i]) == 2) && *argv[i] == '-' &&\n+\t\t\t    strchr(\"akpqu\", argv[i][1]))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (!strcmp(argv[i], \"--all\") ||\n+\t\t\t    !strcmp(argv[i], \"--keep-index\") ||\n+\t\t\t    !strcmp(argv[i], \"--no-keep-index\") ||\n+\t\t\t    !strcmp(argv[i], \"--patch\") ||\n+\t\t\t    !strcmp(argv[i], \"--quiet\") ||\n+\t\t\t    !strcmp(argv[i], \"--include-untracked\"))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * `-m` and `--message=` are verified separately because\n+\t\t\t * they need to be immediately followed by a string\n+\t\t\t * (i.e.`-m\"foobar\"` or `--message=\"foobar\"`).\n+\t\t\t */\n+\t\t\tif (starts_with(argv[i], \"-m\") ||\n+\t\t\t    starts_with(argv[i], \"--message=\"))\n+\t\t\t\tcontinue;\n+\n+\t\t\tusage_with_options(git_stash_usage, options);\n+\t\t}\n+\t}\n \n-\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n-\t\t      git_stash_helper_usage, options);\n+\targv_array_push(&args, \"push\");\n+\targv_array_pushv(&args, argv);\n+\treturn !!push_stash(args.argc, args.argv, prefix);\n }\ndiff --git a/git-stash.sh b/git-stash.sh\ndeleted file mode 100755\nindex 695f1feba3..0000000000\n--- a/git-stash.sh\n+++ /dev/null\n@@ -1,153 +0,0 @@\n-#!/bin/sh\n-# Copyright (c) 2007, Nanako Shiraishi\n-\n-dashless=$(basename \"$0\" | sed -e 's/-/ /')\n-USAGE=\"list [<options>]\n-   or: $dashless show [<stash>]\n-   or: $dashless drop [-q|--quiet] [<stash>]\n-   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n-   or: $dashless branch <branchname> [<stash>]\n-   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n-\t\t      [-u|--include-untracked] [-a|--all] [<message>]\n-   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n-\t\t       [-u|--include-untracked] [-a|--all] [-m <message>]\n-\t\t       [-- <pathspec>...]]\n-   or: $dashless clear\"\n-\n-SUBDIRECTORY_OK=Yes\n-OPTIONS_SPEC=\n-START_DIR=$(pwd)\n-. git-sh-setup\n-require_work_tree\n-prefix=$(git rev-parse --show-prefix) || exit 1\n-cd_to_toplevel\n-\n-TMP=\"$GIT_DIR/.git-stash.$$\"\n-TMPindex=${GIT_INDEX_FILE-\"$(git rev-parse --git-path index)\"}.stash.$$\n-trap 'rm -f \"$TMP-\"* \"$TMPindex\"' 0\n-\n-ref_stash=refs/stash\n-\n-if git config --get-colorbool color.interactive; then\n-       help_color=\"$(git config --get-color color.interactive.help 'red bold')\"\n-       reset_color=\"$(git config --get-color '' reset)\"\n-else\n-       help_color=\n-       reset_color=\n-fi\n-\n-#\n-# Parses the remaining options looking for flags and\n-# at most one revision defaulting to ${ref_stash}@{0}\n-# if none found.\n-#\n-# Derives related tree and commit objects from the\n-# revision, if one is found.\n-#\n-# stash records the work tree, and is a merge between the\n-# base commit (first parent) and the index tree (second parent).\n-#\n-#   REV is set to the symbolic version of the specified stash-like commit\n-#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash\n-#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref\n-#   s is set to the SHA1 of the stash commit\n-#   w_commit is set to the commit containing the working tree\n-#   b_commit is set to the base commit\n-#   i_commit is set to the commit containing the index tree\n-#   u_commit is set to the commit containing the untracked files tree\n-#   w_tree is set to the working tree\n-#   b_tree is set to the base tree\n-#   i_tree is set to the index tree\n-#   u_tree is set to the untracked files tree\n-#\n-#   GIT_QUIET is set to t if -q is specified\n-#   INDEX_OPTION is set to --index if --index is specified.\n-#   FLAGS is set to the remaining flags (if allowed)\n-#\n-# dies if:\n-#   * too many revisions specified\n-#   * no revision is specified and there is no stash stack\n-#   * a revision is specified which cannot be resolve to a SHA1\n-#   * a non-existent stash reference is specified\n-#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n-#\n-\n-test \"$1\" = \"-p\" && set \"push\" \"$@\"\n-\n-PARSE_CACHE='--not-parsed'\n-# The default command is \"push\" if nothing but options are given\n-seen_non_option=\n-for opt\n-do\n-\tcase \"$opt\" in\n-\t--) break ;;\n-\t-*) ;;\n-\t*) seen_non_option=t; break ;;\n-\tesac\n-done\n-\n-test -n \"$seen_non_option\" || set \"push\" \"$@\"\n-\n-# Main command set\n-case \"$1\" in\n-list)\n-\tshift\n-\tgit stash--helper list \"$@\"\n-\t;;\n-show)\n-\tshift\n-\tgit stash--helper show \"$@\"\n-\t;;\n-save)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper save \"$@\"\n-\t;;\n-push)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper push \"$@\"\n-\t;;\n-apply)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper apply \"$@\"\n-\t;;\n-clear)\n-\tshift\n-\tgit stash--helper clear \"$@\"\n-\t;;\n-create)\n-\tshift\n-\tgit stash--helper create --message \"$*\"\n-\t;;\n-store)\n-\tshift\n-\tgit stash--helper store \"$@\"\n-\t;;\n-drop)\n-\tshift\n-\tgit stash--helper drop \"$@\"\n-\t;;\n-pop)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper pop \"$@\"\n-\t;;\n-branch)\n-\tshift\n-\tcd \"$START_DIR\"\n-\tgit stash--helper branch \"$@\"\n-\t;;\n-*)\n-\tcase $# in\n-\t0)\n-\t\tcd \"$START_DIR\"\n-\t\tgit stash--helper push &&\n-\t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n-\t\t;;\n-\t*)\n-\t\tusage\n-\tesac\n-\t;;\n-esac\ndiff --git a/git.c b/git.c\nindex 76ee02802e..49ab91b4ec 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,7 +554,7 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t{ \"stash--helper\", cmd_stash__helper, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"stash\", cmd_stash, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370182","messageId":"20190225231631.30507-27-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 26/27] stash: optionally use the scripted version again","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:30Z","receivedAt":"2019-02-25T23:17:35Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nWe recently converted the `git stash` command from Unix shell scripts\nto builtins.\n\nLet's end users a way out when they discover a bug in the\nbuiltin command: `stash.useBuiltin`.\n\nAs the file name `git-stash` is already in use, let's rename the\nscripted backend to `git-legacy-stash`.\n\nTo make the test suite pass with `stash.useBuiltin=false`, this commit\nalso backports rudimentary support for `-q` (but only *just* enough\nto appease the test suite), and adds a super-ugly hack to force exit\ncode 129 for `git stash -h`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n .gitignore                          |  1 +\n Makefile                            |  1 +\n builtin/stash.c                     | 35 +++++++++++++++++++++++++++++\n git-stash.sh => git-legacy-stash.sh | 34 +++++++++++++++++++++++++---\n git-sh-setup.sh                     |  1 +\n git.c                               |  7 +++++-\n 6 files changed, 75 insertions(+), 4 deletions(-)\n rename git-stash.sh => git-legacy-stash.sh (97%)\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..7b0164675e 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -82,6 +82,7 @@\n /git-interpret-trailers\n /git-instaweb\n /git-legacy-rebase\n+/git-legacy-stash\n /git-log\n /git-ls-files\n /git-ls-remote\ndiff --git a/Makefile b/Makefile\nindex 8cee2731aa..810231a0b5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -617,6 +617,7 @@ SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-legacy-rebase.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-remote-testgit.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex d9f3956ef5..49c6d7948a 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -13,6 +13,7 @@\n #include \"revision.h\"\n #include \"log-tree.h\"\n #include \"diffcore.h\"\n+#include \"exec-cmd.h\"\n \n #define INCLUDE_ALL_FILES 2\n \n@@ -1510,6 +1511,26 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n+static int use_builtin_stash(void)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf out = STRBUF_INIT;\n+\tint ret;\n+\n+\targv_array_pushl(&cp.args,\n+\t\t\t \"config\", \"--bool\", \"stash.usebuiltin\", NULL);\n+\tcp.git_cmd = 1;\n+\tif (capture_command(&cp, &out, 6)) {\n+\t\tstrbuf_release(&out);\n+\t\treturn 1;\n+\t}\n+\n+\tstrbuf_trim(&out);\n+\tret = !strcmp(\"true\", out.buf);\n+\tstrbuf_release(&out);\n+\treturn ret;\n+}\n+\n int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i = -1;\n@@ -1521,6 +1542,20 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tif (!use_builtin_stash()) {\n+\t\tconst char *path = mkpath(\"%s/git-legacy-stash\",\n+\t\t\t\t\t  git_exec_path());\n+\n+\t\tif (sane_execvp(path, (char **)argv) < 0)\n+\t\t\tdie_errno(_(\"could not exec %s\"), path);\n+\t\telse\n+\t\t\tBUG(\"sane_execvp() returned???\");\n+\t}\n+\n+\tprefix = setup_git_directory();\n+\ttrace_repo_setup(prefix);\n+\tsetup_work_tree();\n+\n \tgit_config(git_diff_basic_config, NULL);\n \n \targc = parse_options(argc, argv, prefix, options, git_stash_usage,\ndiff --git a/git-stash.sh b/git-legacy-stash.sh\nsimilarity index 97%\nrename from git-stash.sh\nrename to git-legacy-stash.sh\nindex 789ce2f41d..8a8c4a9270 100755\n--- a/git-stash.sh\n+++ b/git-legacy-stash.sh\n@@ -80,6 +80,28 @@ clear_stash () {\n \tfi\n }\n \n+maybe_quiet () {\n+\tcase \"$1\" in\n+\t--keep-stdout)\n+\t\tshift\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\teval \"$@\" 2>/dev/null\n+\t\telse\n+\t\t\teval \"$@\"\n+\t\tfi\n+\t\t;;\n+\t*)\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\teval \"$@\" >/dev/null 2>&1\n+\t\telse\n+\t\t\teval \"$@\"\n+\t\tfi\n+\t\t;;\n+\tesac\n+}\n+\n create_stash () {\n \n \tprepare_fallback_ident\n@@ -112,15 +134,18 @@ create_stash () {\n \tdone\n \n \tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n+\tif maybe_quiet no_changes \"$@\"\n \tthen\n \t\texit 0\n \tfi\n \n \t# state of the base commit\n-\tif b_commit=$(git rev-parse --verify HEAD)\n+\tif b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)\n \tthen\n \t\thead=$(git rev-list --oneline -n 1 HEAD --)\n+\telif test -n \"$GIT_QUIET\"\n+\tthen\n+\t\texit 1\n \telse\n \t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n \tfi\n@@ -315,7 +340,7 @@ push_stash () {\n \ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n \n \tgit update-index -q --refresh\n-\tif no_changes \"$@\"\n+\tif maybe_quiet no_changes \"$@\"\n \tthen\n \t\tsay \"$(gettext \"No local changes to save\")\"\n \t\texit 0\n@@ -370,6 +395,9 @@ save_stash () {\n \twhile test $# != 0\n \tdo\n \t\tcase \"$1\" in\n+\t\t-q|--quiet)\n+\t\t\tGIT_QUIET=t\n+\t\t\t;;\n \t\t--)\n \t\t\tshift\n \t\t\tbreak\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex 378928518b..10d9764185 100644\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -101,6 +101,7 @@ $LONG_USAGE\")\"\n \tcase \"$1\" in\n \t\t-h)\n \t\techo \"$LONG_USAGE\"\n+\t\tcase \"$0\" in *git-legacy-stash) exit 129;; esac\n \t\texit\n \tesac\n fi\ndiff --git a/git.c b/git.c\nindex 49ab91b4ec..8de729c9e6 100644\n--- a/git.c\n+++ b/git.c\n@@ -554,7 +554,12 @@ static struct cmd_struct commands[] = {\n \t{ \"show-index\", cmd_show_index },\n \t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t{ \"stage\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t{ \"stash\", cmd_stash, RUN_SETUP | NEED_WORK_TREE },\n+\t/*\n+\t * NEEDSWORK: Until the builtin stash is thoroughly robust and no\n+\t * longer needs redirection to the stash shell script this is kept as\n+\t * is, then should be changed to RUN_SETUP | NEED_WORK_TREE\n+\t */\n+\t{ \"stash\", cmd_stash },\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370183","messageId":"20190225231631.30507-26-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 25/27] stash: add back the original, scripted `git stash`","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:29Z","receivedAt":"2019-02-25T23:17:36Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis simply copies the version as of sd/stash-wo-user-name verbatim. As\nof now, it is not hooked up.\n\nThe next commit will change the builtin `stash` to hand off to the\nscripted `git stash` when `stash.useBuiltin=false`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n git-stash.sh | 769 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 769 insertions(+)\n create mode 100755 git-stash.sh\n\ndiff --git a/git-stash.sh b/git-stash.sh\nnew file mode 100755\nindex 0000000000..789ce2f41d\n--- /dev/null\n+++ b/git-stash.sh\n@@ -0,0 +1,769 @@\n+#!/bin/sh\n+# Copyright (c) 2007, Nanako Shiraishi\n+\n+dashless=$(basename \"$0\" | sed -e 's/-/ /')\n+USAGE=\"list [<options>]\n+   or: $dashless show [<stash>]\n+   or: $dashless drop [-q|--quiet] [<stash>]\n+   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]\n+   or: $dashless branch <branchname> [<stash>]\n+   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+\t\t      [-u|--include-untracked] [-a|--all] [<message>]\n+   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]\n+\t\t       [-u|--include-untracked] [-a|--all] [-m <message>]\n+\t\t       [-- <pathspec>...]]\n+   or: $dashless clear\"\n+\n+SUBDIRECTORY_OK=Yes\n+OPTIONS_SPEC=\n+START_DIR=$(pwd)\n+. git-sh-setup\n+require_work_tree\n+prefix=$(git rev-parse --show-prefix) || exit 1\n+cd_to_toplevel\n+\n+TMP=\"$GIT_DIR/.git-stash.$$\"\n+TMPindex=${GIT_INDEX_FILE-\"$(git rev-parse --git-path index)\"}.stash.$$\n+trap 'rm -f \"$TMP-\"* \"$TMPindex\"' 0\n+\n+ref_stash=refs/stash\n+\n+if git config --get-colorbool color.interactive; then\n+       help_color=\"$(git config --get-color color.interactive.help 'red bold')\"\n+       reset_color=\"$(git config --get-color '' reset)\"\n+else\n+       help_color=\n+       reset_color=\n+fi\n+\n+no_changes () {\n+\tgit diff-index --quiet --cached HEAD --ignore-submodules -- \"$@\" &&\n+\tgit diff-files --quiet --ignore-submodules -- \"$@\" &&\n+\t(test -z \"$untracked\" || test -z \"$(untracked_files \"$@\")\")\n+}\n+\n+untracked_files () {\n+\tif test \"$1\" = \"-z\"\n+\tthen\n+\t\tshift\n+\t\tz=-z\n+\telse\n+\t\tz=\n+\tfi\n+\texcl_opt=--exclude-standard\n+\ttest \"$untracked\" = \"all\" && excl_opt=\n+\tgit ls-files -o $z $excl_opt -- \"$@\"\n+}\n+\n+prepare_fallback_ident () {\n+\tif ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1\n+\tthen\n+\t\tGIT_AUTHOR_NAME=\"git stash\"\n+\t\tGIT_AUTHOR_EMAIL=git@stash\n+\t\tGIT_COMMITTER_NAME=\"git stash\"\n+\t\tGIT_COMMITTER_EMAIL=git@stash\n+\t\texport GIT_AUTHOR_NAME\n+\t\texport GIT_AUTHOR_EMAIL\n+\t\texport GIT_COMMITTER_NAME\n+\t\texport GIT_COMMITTER_EMAIL\n+\tfi\n+}\n+\n+clear_stash () {\n+\tif test $# != 0\n+\tthen\n+\t\tdie \"$(gettext \"git stash clear with parameters is unimplemented\")\"\n+\tfi\n+\tif current=$(git rev-parse --verify --quiet $ref_stash)\n+\tthen\n+\t\tgit update-ref -d $ref_stash $current\n+\tfi\n+}\n+\n+create_stash () {\n+\n+\tprepare_fallback_ident\n+\n+\tstash_msg=\n+\tuntracked=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\tstash_msg=${1?\"BUG: create_stash () -m requires an argument\"}\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t-u|--include-untracked)\n+\t\t\tshift\n+\t\t\tuntracked=${1?\"BUG: create_stash () -u requires an argument\"}\n+\t\t\t;;\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\tgit update-index -q --refresh\n+\tif no_changes \"$@\"\n+\tthen\n+\t\texit 0\n+\tfi\n+\n+\t# state of the base commit\n+\tif b_commit=$(git rev-parse --verify HEAD)\n+\tthen\n+\t\thead=$(git rev-list --oneline -n 1 HEAD --)\n+\telse\n+\t\tdie \"$(gettext \"You do not have the initial commit yet\")\"\n+\tfi\n+\n+\tif branch=$(git symbolic-ref -q HEAD)\n+\tthen\n+\t\tbranch=${branch#refs/heads/}\n+\telse\n+\t\tbranch='(no branch)'\n+\tfi\n+\tmsg=$(printf '%s: %s' \"$branch\" \"$head\")\n+\n+\t# state of the index\n+\ti_tree=$(git write-tree) &&\n+\ti_commit=$(printf 'index on %s\\n' \"$msg\" |\n+\t\tgit commit-tree $i_tree -p $b_commit) ||\n+\t\tdie \"$(gettext \"Cannot save the current index state\")\"\n+\n+\tif test -n \"$untracked\"\n+\tthen\n+\t\t# Untracked files are stored by themselves in a parentless commit, for\n+\t\t# ease of unpacking later.\n+\t\tu_commit=$(\n+\t\t\tuntracked_files -z \"$@\" | (\n+\t\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n+\t\t\t\texport GIT_INDEX_FILE &&\n+\t\t\t\trm -f \"$TMPindex\" &&\n+\t\t\t\tgit update-index -z --add --remove --stdin &&\n+\t\t\t\tu_tree=$(git write-tree) &&\n+\t\t\t\tprintf 'untracked files on %s\\n' \"$msg\" | git commit-tree $u_tree  &&\n+\t\t\t\trm -f \"$TMPindex\"\n+\t\t) ) || die \"$(gettext \"Cannot save the untracked files\")\"\n+\n+\t\tuntracked_commit_option=\"-p $u_commit\";\n+\telse\n+\t\tuntracked_commit_option=\n+\tfi\n+\n+\tif test -z \"$patch_mode\"\n+\tthen\n+\n+\t\t# state of the working tree\n+\t\tw_tree=$( (\n+\t\t\tgit read-tree --index-output=\"$TMPindex\" -m $i_tree &&\n+\t\t\tGIT_INDEX_FILE=\"$TMPindex\" &&\n+\t\t\texport GIT_INDEX_FILE &&\n+\t\t\tgit diff-index --name-only -z HEAD -- \"$@\" >\"$TMP-stagenames\" &&\n+\t\t\tgit update-index -z --add --remove --stdin <\"$TMP-stagenames\" &&\n+\t\t\tgit write-tree &&\n+\t\t\trm -f \"$TMPindex\"\n+\t\t) ) ||\n+\t\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n+\n+\telse\n+\n+\t\trm -f \"$TMP-index\" &&\n+\t\tGIT_INDEX_FILE=\"$TMP-index\" git read-tree HEAD &&\n+\n+\t\t# find out what the user wants\n+\t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n+\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n+\n+\t\t# state of the working tree\n+\t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n+\t\tdie \"$(gettext \"Cannot save the current worktree state\")\"\n+\n+\t\tgit diff-tree -p HEAD $w_tree -- >\"$TMP-patch\" &&\n+\t\ttest -s \"$TMP-patch\" ||\n+\t\tdie \"$(gettext \"No changes selected\")\"\n+\n+\t\trm -f \"$TMP-index\" ||\n+\t\tdie \"$(gettext \"Cannot remove temporary index (can't happen)\")\"\n+\n+\tfi\n+\n+\t# create the stash\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tstash_msg=$(printf 'WIP on %s' \"$msg\")\n+\telse\n+\t\tstash_msg=$(printf 'On %s: %s' \"$branch\" \"$stash_msg\")\n+\tfi\n+\tw_commit=$(printf '%s\\n' \"$stash_msg\" |\n+\tgit commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||\n+\tdie \"$(gettext \"Cannot record working tree state\")\"\n+}\n+\n+store_stash () {\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\tstash_msg=\"$1\"\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t-q|--quiet)\n+\t\t\tquiet=t\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\ttest $# = 1 ||\n+\tdie \"$(eval_gettext \"\\\"$dashless store\\\" requires one <commit> argument\")\"\n+\n+\tw_commit=\"$1\"\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tstash_msg=\"Created via \\\"git stash store\\\".\"\n+\tfi\n+\n+\tgit update-ref --create-reflog -m \"$stash_msg\" $ref_stash $w_commit\n+\tret=$?\n+\ttest $ret != 0 && test -z \"$quiet\" &&\n+\tdie \"$(eval_gettext \"Cannot update \\$ref_stash with \\$w_commit\")\"\n+\treturn $ret\n+}\n+\n+push_stash () {\n+\tkeep_index=\n+\tpatch_mode=\n+\tuntracked=\n+\tstash_msg=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t-k|--keep-index)\n+\t\t\tkeep_index=t\n+\t\t\t;;\n+\t\t--no-keep-index)\n+\t\t\tkeep_index=n\n+\t\t\t;;\n+\t\t-p|--patch)\n+\t\t\tpatch_mode=t\n+\t\t\t# only default to keep if we don't already have an override\n+\t\t\ttest -z \"$keep_index\" && keep_index=t\n+\t\t\t;;\n+\t\t-q|--quiet)\n+\t\t\tGIT_QUIET=t\n+\t\t\t;;\n+\t\t-u|--include-untracked)\n+\t\t\tuntracked=untracked\n+\t\t\t;;\n+\t\t-a|--all)\n+\t\t\tuntracked=all\n+\t\t\t;;\n+\t\t-m|--message)\n+\t\t\tshift\n+\t\t\ttest -z ${1+x} && usage\n+\t\t\tstash_msg=$1\n+\t\t\t;;\n+\t\t-m*)\n+\t\t\tstash_msg=${1#-m}\n+\t\t\t;;\n+\t\t--message=*)\n+\t\t\tstash_msg=${1#--message=}\n+\t\t\t;;\n+\t\t--help)\n+\t\t\tshow_help\n+\t\t\t;;\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\t-*)\n+\t\t\toption=\"$1\"\n+\t\t\teval_gettextln \"error: unknown option for 'stash push': \\$option\"\n+\t\t\tusage\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\teval \"set $(git rev-parse --sq --prefix \"$prefix\" -- \"$@\")\"\n+\n+\tif test -n \"$patch_mode\" && test -n \"$untracked\"\n+\tthen\n+\t\tdie \"$(gettext \"Can't use --patch and --include-untracked or --all at the same time\")\"\n+\tfi\n+\n+\ttest -n \"$untracked\" || git ls-files --error-unmatch -- \"$@\" >/dev/null || exit 1\n+\n+\tgit update-index -q --refresh\n+\tif no_changes \"$@\"\n+\tthen\n+\t\tsay \"$(gettext \"No local changes to save\")\"\n+\t\texit 0\n+\tfi\n+\n+\tgit reflog exists $ref_stash ||\n+\t\tclear_stash || die \"$(gettext \"Cannot initialize stash\")\"\n+\n+\tcreate_stash -m \"$stash_msg\" -u \"$untracked\" -- \"$@\"\n+\tstore_stash -m \"$stash_msg\" -q $w_commit ||\n+\tdie \"$(gettext \"Cannot save the current status\")\"\n+\tsay \"$(eval_gettext \"Saved working directory and index state \\$stash_msg\")\"\n+\n+\tif test -z \"$patch_mode\"\n+\tthen\n+\t\ttest \"$untracked\" = \"all\" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=\n+\t\tif test -n \"$untracked\" && test $# = 0\n+\t\tthen\n+\t\t\tgit clean --force --quiet -d $CLEAN_X_OPTION\n+\t\tfi\n+\n+\t\tif test $# != 0\n+\t\tthen\n+\t\t\ttest -z \"$untracked\" && UPDATE_OPTION=\"-u\" || UPDATE_OPTION=\n+\t\t\ttest \"$untracked\" = \"all\" && FORCE_OPTION=\"--force\" || FORCE_OPTION=\n+\t\t\tgit add $UPDATE_OPTION $FORCE_OPTION -- \"$@\"\n+\t\t\tgit diff-index -p --cached --binary HEAD -- \"$@\" |\n+\t\t\tgit apply --index -R\n+\t\telse\n+\t\t\tgit reset --hard -q\n+\t\tfi\n+\n+\t\tif test \"$keep_index\" = \"t\" && test -n \"$i_tree\"\n+\t\tthen\n+\t\t\tgit read-tree --reset $i_tree\n+\t\t\tgit ls-files -z --modified -- \"$@\" |\n+\t\t\tgit checkout-index -z --force --stdin\n+\t\tfi\n+\telse\n+\t\tgit apply -R < \"$TMP-patch\" ||\n+\t\tdie \"$(gettext \"Cannot remove worktree changes\")\"\n+\n+\t\tif test \"$keep_index\" != \"t\"\n+\t\tthen\n+\t\t\tgit reset -q -- \"$@\"\n+\t\tfi\n+\tfi\n+}\n+\n+save_stash () {\n+\tpush_options=\n+\twhile test $# != 0\n+\tdo\n+\t\tcase \"$1\" in\n+\t\t--)\n+\t\t\tshift\n+\t\t\tbreak\n+\t\t\t;;\n+\t\t-*)\n+\t\t\t# pass all options through to push_stash\n+\t\t\tpush_options=\"$push_options $1\"\n+\t\t\t;;\n+\t\t*)\n+\t\t\tbreak\n+\t\t\t;;\n+\t\tesac\n+\t\tshift\n+\tdone\n+\n+\tstash_msg=\"$*\"\n+\n+\tif test -z \"$stash_msg\"\n+\tthen\n+\t\tpush_stash $push_options\n+\telse\n+\t\tpush_stash $push_options -m \"$stash_msg\"\n+\tfi\n+}\n+\n+have_stash () {\n+\tgit rev-parse --verify --quiet $ref_stash >/dev/null\n+}\n+\n+list_stash () {\n+\thave_stash || return 0\n+\tgit log --format=\"%gd: %gs\" -g --first-parent -m \"$@\" $ref_stash --\n+}\n+\n+show_stash () {\n+\tALLOW_UNKNOWN_FLAGS=t\n+\tassert_stash_like \"$@\"\n+\n+\tif test -z \"$FLAGS\"\n+\tthen\n+\t\tif test \"$(git config --bool stash.showStat || echo true)\" = \"true\"\n+\t\tthen\n+\t\t\tFLAGS=--stat\n+\t\tfi\n+\n+\t\tif test \"$(git config --bool stash.showPatch || echo false)\" = \"true\"\n+\t\tthen\n+\t\t\tFLAGS=${FLAGS}${FLAGS:+ }-p\n+\t\tfi\n+\n+\t\tif test -z \"$FLAGS\"\n+\t\tthen\n+\t\t\treturn 0\n+\t\tfi\n+\tfi\n+\n+\tgit diff ${FLAGS} $b_commit $w_commit\n+}\n+\n+show_help () {\n+\texec git help stash\n+\texit 1\n+}\n+\n+#\n+# Parses the remaining options looking for flags and\n+# at most one revision defaulting to ${ref_stash}@{0}\n+# if none found.\n+#\n+# Derives related tree and commit objects from the\n+# revision, if one is found.\n+#\n+# stash records the work tree, and is a merge between the\n+# base commit (first parent) and the index tree (second parent).\n+#\n+#   REV is set to the symbolic version of the specified stash-like commit\n+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash\n+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref\n+#   s is set to the SHA1 of the stash commit\n+#   w_commit is set to the commit containing the working tree\n+#   b_commit is set to the base commit\n+#   i_commit is set to the commit containing the index tree\n+#   u_commit is set to the commit containing the untracked files tree\n+#   w_tree is set to the working tree\n+#   b_tree is set to the base tree\n+#   i_tree is set to the index tree\n+#   u_tree is set to the untracked files tree\n+#\n+#   GIT_QUIET is set to t if -q is specified\n+#   INDEX_OPTION is set to --index if --index is specified.\n+#   FLAGS is set to the remaining flags (if allowed)\n+#\n+# dies if:\n+#   * too many revisions specified\n+#   * no revision is specified and there is no stash stack\n+#   * a revision is specified which cannot be resolve to a SHA1\n+#   * a non-existent stash reference is specified\n+#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not \"t\"\n+#\n+\n+parse_flags_and_rev()\n+{\n+\ttest \"$PARSE_CACHE\" = \"$*\" && return 0 # optimisation\n+\tPARSE_CACHE=\"$*\"\n+\n+\tIS_STASH_LIKE=\n+\tIS_STASH_REF=\n+\tINDEX_OPTION=\n+\ts=\n+\tw_commit=\n+\tb_commit=\n+\ti_commit=\n+\tu_commit=\n+\tw_tree=\n+\tb_tree=\n+\ti_tree=\n+\tu_tree=\n+\n+\tFLAGS=\n+\tREV=\n+\tfor opt\n+\tdo\n+\t\tcase \"$opt\" in\n+\t\t\t-q|--quiet)\n+\t\t\t\tGIT_QUIET=-t\n+\t\t\t;;\n+\t\t\t--index)\n+\t\t\t\tINDEX_OPTION=--index\n+\t\t\t;;\n+\t\t\t--help)\n+\t\t\t\tshow_help\n+\t\t\t;;\n+\t\t\t-*)\n+\t\t\t\ttest \"$ALLOW_UNKNOWN_FLAGS\" = t ||\n+\t\t\t\t\tdie \"$(eval_gettext \"unknown option: \\$opt\")\"\n+\t\t\t\tFLAGS=\"${FLAGS}${FLAGS:+ }$opt\"\n+\t\t\t;;\n+\t\t\t*)\n+\t\t\t\tREV=\"${REV}${REV:+ }'$opt'\"\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+\n+\teval set -- $REV\n+\n+\tcase $# in\n+\t\t0)\n+\t\t\thave_stash || die \"$(gettext \"No stash entries found.\")\"\n+\t\t\tset -- ${ref_stash}@{0}\n+\t\t;;\n+\t\t1)\n+\t\t\t:\n+\t\t;;\n+\t\t*)\n+\t\t\tdie \"$(eval_gettext \"Too many revisions specified: \\$REV\")\"\n+\t\t;;\n+\tesac\n+\n+\tcase \"$1\" in\n+\t\t*[!0-9]*)\n+\t\t\t:\n+\t\t;;\n+\t\t*)\n+\t\t\tset -- \"${ref_stash}@{$1}\"\n+\t\t;;\n+\tesac\n+\n+\tREV=$(git rev-parse --symbolic --verify --quiet \"$1\") || {\n+\t\treference=\"$1\"\n+\t\tdie \"$(eval_gettext \"\\$reference is not a valid reference\")\"\n+\t}\n+\n+\ti_commit=$(git rev-parse --verify --quiet \"$REV^2\") &&\n+\tset -- $(git rev-parse \"$REV\" \"$REV^1\" \"$REV:\" \"$REV^1:\" \"$REV^2:\" 2>/dev/null) &&\n+\ts=$1 &&\n+\tw_commit=$1 &&\n+\tb_commit=$2 &&\n+\tw_tree=$3 &&\n+\tb_tree=$4 &&\n+\ti_tree=$5 &&\n+\tIS_STASH_LIKE=t &&\n+\ttest \"$ref_stash\" = \"$(git rev-parse --symbolic-full-name \"${REV%@*}\")\" &&\n+\tIS_STASH_REF=t\n+\n+\tu_commit=$(git rev-parse --verify --quiet \"$REV^3\") &&\n+\tu_tree=$(git rev-parse \"$REV^3:\" 2>/dev/null)\n+}\n+\n+is_stash_like()\n+{\n+\tparse_flags_and_rev \"$@\"\n+\ttest -n \"$IS_STASH_LIKE\"\n+}\n+\n+assert_stash_like() {\n+\tis_stash_like \"$@\" || {\n+\t\targs=\"$*\"\n+\t\tdie \"$(eval_gettext \"'\\$args' is not a stash-like commit\")\"\n+\t}\n+}\n+\n+is_stash_ref() {\n+\tis_stash_like \"$@\" && test -n \"$IS_STASH_REF\"\n+}\n+\n+assert_stash_ref() {\n+\tis_stash_ref \"$@\" || {\n+\t\targs=\"$*\"\n+\t\tdie \"$(eval_gettext \"'\\$args' is not a stash reference\")\"\n+\t}\n+}\n+\n+apply_stash () {\n+\n+\tassert_stash_like \"$@\"\n+\n+\tgit update-index -q --refresh || die \"$(gettext \"unable to refresh index\")\"\n+\n+\t# current index state\n+\tc_tree=$(git write-tree) ||\n+\t\tdie \"$(gettext \"Cannot apply a stash in the middle of a merge\")\"\n+\n+\tunstashed_index_tree=\n+\tif test -n \"$INDEX_OPTION\" && test \"$b_tree\" != \"$i_tree\" &&\n+\t\t\ttest \"$c_tree\" != \"$i_tree\"\n+\tthen\n+\t\tgit diff-tree --binary $s^2^..$s^2 | git apply --cached\n+\t\ttest $? -ne 0 &&\n+\t\t\tdie \"$(gettext \"Conflicts in index. Try without --index.\")\"\n+\t\tunstashed_index_tree=$(git write-tree) ||\n+\t\t\tdie \"$(gettext \"Could not save index tree\")\"\n+\t\tgit reset\n+\tfi\n+\n+\tif test -n \"$u_tree\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git read-tree \"$u_tree\" &&\n+\t\tGIT_INDEX_FILE=\"$TMPindex\" git checkout-index --all &&\n+\t\trm -f \"$TMPindex\" ||\n+\t\tdie \"$(gettext \"Could not restore untracked files from stash entry\")\"\n+\tfi\n+\n+\teval \"\n+\t\tGITHEAD_$w_tree='Stashed changes' &&\n+\t\tGITHEAD_$c_tree='Updated upstream' &&\n+\t\tGITHEAD_$b_tree='Version stash was based on' &&\n+\t\texport GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree\n+\t\"\n+\n+\tif test -n \"$GIT_QUIET\"\n+\tthen\n+\t\tGIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY\n+\tfi\n+\tif git merge-recursive $b_tree -- $c_tree $w_tree\n+\tthen\n+\t\t# No conflict\n+\t\tif test -n \"$unstashed_index_tree\"\n+\t\tthen\n+\t\t\tgit read-tree \"$unstashed_index_tree\"\n+\t\telse\n+\t\t\ta=\"$TMP-added\" &&\n+\t\t\tgit diff-index --cached --name-only --diff-filter=A $c_tree >\"$a\" &&\n+\t\t\tgit read-tree --reset $c_tree &&\n+\t\t\tgit update-index --add --stdin <\"$a\" ||\n+\t\t\t\tdie \"$(gettext \"Cannot unstage modified files\")\"\n+\t\t\trm -f \"$a\"\n+\t\tfi\n+\t\tsquelch=\n+\t\tif test -n \"$GIT_QUIET\"\n+\t\tthen\n+\t\t\tsquelch='>/dev/null 2>&1'\n+\t\tfi\n+\t\t(cd \"$START_DIR\" && eval \"git status $squelch\") || :\n+\telse\n+\t\t# Merge conflict; keep the exit status from merge-recursive\n+\t\tstatus=$?\n+\t\tgit rerere\n+\t\tif test -n \"$INDEX_OPTION\"\n+\t\tthen\n+\t\t\tgettextln \"Index was not unstashed.\" >&2\n+\t\tfi\n+\t\texit $status\n+\tfi\n+}\n+\n+pop_stash() {\n+\tassert_stash_ref \"$@\"\n+\n+\tif apply_stash \"$@\"\n+\tthen\n+\t\tdrop_stash \"$@\"\n+\telse\n+\t\tstatus=$?\n+\t\tsay \"$(gettext \"The stash entry is kept in case you need it again.\")\"\n+\t\texit $status\n+\tfi\n+}\n+\n+drop_stash () {\n+\tassert_stash_ref \"$@\"\n+\n+\tgit reflog delete --updateref --rewrite \"${REV}\" &&\n+\t\tsay \"$(eval_gettext \"Dropped \\${REV} (\\$s)\")\" ||\n+\t\tdie \"$(eval_gettext \"\\${REV}: Could not drop stash entry\")\"\n+\n+\t# clear_stash if we just dropped the last stash entry\n+\tgit rev-parse --verify --quiet \"$ref_stash@{0}\" >/dev/null ||\n+\tclear_stash\n+}\n+\n+apply_to_branch () {\n+\ttest -n \"$1\" || die \"$(gettext \"No branch name specified\")\"\n+\tbranch=$1\n+\tshift 1\n+\n+\tset -- --index \"$@\"\n+\tassert_stash_like \"$@\"\n+\n+\tgit checkout -b $branch $REV^ &&\n+\tapply_stash \"$@\" && {\n+\t\ttest -z \"$IS_STASH_REF\" || drop_stash \"$@\"\n+\t}\n+}\n+\n+test \"$1\" = \"-p\" && set \"push\" \"$@\"\n+\n+PARSE_CACHE='--not-parsed'\n+# The default command is \"push\" if nothing but options are given\n+seen_non_option=\n+for opt\n+do\n+\tcase \"$opt\" in\n+\t--) break ;;\n+\t-*) ;;\n+\t*) seen_non_option=t; break ;;\n+\tesac\n+done\n+\n+test -n \"$seen_non_option\" || set \"push\" \"$@\"\n+\n+# Main command set\n+case \"$1\" in\n+list)\n+\tshift\n+\tlist_stash \"$@\"\n+\t;;\n+show)\n+\tshift\n+\tshow_stash \"$@\"\n+\t;;\n+save)\n+\tshift\n+\tsave_stash \"$@\"\n+\t;;\n+push)\n+\tshift\n+\tpush_stash \"$@\"\n+\t;;\n+apply)\n+\tshift\n+\tapply_stash \"$@\"\n+\t;;\n+clear)\n+\tshift\n+\tclear_stash \"$@\"\n+\t;;\n+create)\n+\tshift\n+\tcreate_stash -m \"$*\" && echo \"$w_commit\"\n+\t;;\n+store)\n+\tshift\n+\tstore_stash \"$@\"\n+\t;;\n+drop)\n+\tshift\n+\tdrop_stash \"$@\"\n+\t;;\n+pop)\n+\tshift\n+\tpop_stash \"$@\"\n+\t;;\n+branch)\n+\tshift\n+\tapply_to_branch \"$@\"\n+\t;;\n+*)\n+\tcase $# in\n+\t0)\n+\t\tpush_stash &&\n+\t\tsay \"$(gettext \"(To restore them type \\\"git stash apply\\\")\")\"\n+\t\t;;\n+\t*)\n+\t\tusage\n+\tesac\n+\t;;\n+esac\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370184","messageId":"20190225231631.30507-28-t.gummerer@gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"[PATCH v13 27/27] tests: add a special setup where stash.useBuiltin is off","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-25T23:16:31Z","receivedAt":"2019-02-25T23:17:37Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAdd a GIT_TEST_STASH_USE_BUILTIN=false test mode which is equivalent\nto running with stash.useBuiltin=false. This is needed to spot that\nwe're not introducing any regressions in the legacy stash version\nwhile we're carrying both it and the new built-in version.\n\nThis imitates the equivalent treatment for the built-in rebase in\n62c23938fae5 (tests: add a special setup where rebase.useBuiltin is off,\n2018-11-14).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash.c | 5 ++++-\n t/README        | 4 ++++\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 49c6d7948a..1bfa24030c 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1515,7 +1515,10 @@ static int use_builtin_stash(void)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct strbuf out = STRBUF_INIT;\n-\tint ret;\n+\tint ret, env = git_env_bool(\"GIT_TEST_STASH_USE_BUILTIN\", -1);\n+\n+\tif (env != -1)\n+\t\treturn env;\n \n \targv_array_pushl(&cp.args,\n \t\t\t \"config\", \"--bool\", \"stash.usebuiltin\", NULL);\ndiff --git a/t/README b/t/README\nindex 28711cc508..9187eeea8e 100644\n--- a/t/README\n+++ b/t/README\n@@ -349,6 +349,10 @@ GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the\n builtin version of git-rebase. See 'rebase.useBuiltin' in\n git-config(1).\n \n+GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the\n+built-in version of git-stash. See 'stash.useBuiltin' in\n+git-config(1).\n+\n GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading\n of the index for the whole test suite by bypassing the default number of\n cache entries and thread minimums. Setting this to 1 will make the\n-- \n2.21.0.rc2.291.g17236886c5\n\n"},{"id":"370233","messageId":"nycvar.QRO.7.76.6.1902261330560.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-02-26T12:40:22Z","receivedAt":"2019-02-26T12:40:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Mon, 25 Feb 2019, Thomas Gummerer wrote:\n\n> As I was advocating for this series to go into 'next' without a large\n> refactor of this series, I'll put my money were my mouth is and try to\n> make the cleanups and fixes required, though without trying to avoid\n> further external process calls, or changing the series around too much.\n\nThank you.\n\n> range-diff below:\n> \n> [...]\n>  9:  f6bbd78127 ! 10:  45670448e8 stash: convert apply to builtin\n>     @@ -17,7 +17,7 @@\n>      \n>          Signed-off-by: Joel Teichroeb <joel@teichroeb.net>\n>          Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n>     -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n>     +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n>      \n>       diff --git a/.gitignore b/.gitignore\n>       --- a/.gitignore\n>     @@ -96,7 +96,6 @@\n>      + * i_tree is set to the index tree\n>      + * u_tree is set to the untracked files tree\n>      + */\n>     -+\n>      +struct stash_info {\n>      +\tstruct object_id w_commit;\n>      +\tstruct object_id b_commit;\n>     @@ -357,7 +356,7 @@\n>      +\tif (refresh_cache(REFRESH_QUIET))\n>      +\t\treturn -1;\n>      +\n>     -+\tif (write_cache_as_tree(&c_tree, 0, NULL) || reset_tree(&c_tree, 0, 0))\n>     ++\tif (write_cache_as_tree(&c_tree, 0, NULL))\n\nThank you for picking this up.\n\n>      +\t\treturn error(_(\"cannot apply a stash in the middle of a merge\"));\n>      +\n>      +\tif (index) {\n>\n> [...]\n> 22:  51809c70ca ! 23:  56a5ce2aeb stash: convert `stash--helper.c` into `stash.c`\n>     @@ -13,7 +13,7 @@\n>          called directly and not by a shell script.\n>      \n>          Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n>     -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n>     +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n>      \n>       diff --git a/.gitignore b/.gitignore\n>       --- a/.gitignore\n>     @@ -273,9 +273,11 @@\n>       \t\treturn 0;\n>       \n>      -\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n>     - \tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n>     +-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n>     ++\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n>     + \t\t\t      NULL, 0);\n\nI do not quite understand this change, though. The end result seems to\nlook similar enough to the previous iteration (except for the kept line\nwrapping which I would have undone in this patch), but how come that the\n`include_untracked` crept into some earlier patch in this iteration?\n\nThe rest was obvious enough, thank you.\n\nAs I said before, I was very much in favor of getting this `stash-in-c`\nbusiness moving again by advancing it to `next`.\n\nFrom my perspective (which is not informed by any official statement about\nthe role of `pu` or `next` because there is none as far as I know), the\npurpose of `pu` is to get patches into a shape where the original author\nis no longer the only one working on them. In my mind, that moment has\nlong passed, and the fact that `ps/stash-in-c` is still stuck in `pu`\nreally held me back on working more on this.\n\nSo if you manage to get this finally cooking again, all hail to you!\n\nThanks,\nDscho\n"},{"id":"370272","messageId":"20190226204828.GD27711@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1902261330560.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-02-26T20:48:28Z","receivedAt":"2019-02-26T20:48:34Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 02/26, Johannes Schindelin wrote:\n> Hi Thomas,\n> \n> On Mon, 25 Feb 2019, Thomas Gummerer wrote:\n> > 22:  51809c70ca ! 23:  56a5ce2aeb stash: convert `stash--helper.c` into `stash.c`\n> >     @@ -13,7 +13,7 @@\n> >          called directly and not by a shell script.\n> >      \n> >          Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> >     -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> >     +    Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> >      \n> >       diff --git a/.gitignore b/.gitignore\n> >       --- a/.gitignore\n> >     @@ -273,9 +273,11 @@\n> >       \t\treturn 0;\n> >       \n> >      -\tstrbuf_addstr(&stash_msg_buf, stash_msg);\n> >     - \tif (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))\n> >     +-\tret = do_create_stash(ps, &stash_msg_buf, include_untracked, 0, &info,\n> >     ++\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n> >     + \t\t\t      NULL, 0);\n> \n> I do not quite understand this change, though. The end result seems to\n> look similar enough to the previous iteration (except for the kept line\n> wrapping which I would have undone in this patch), but how come that the\n> `include_untracked` crept into some earlier patch in this iteration?\n\nThe reason for this is that previously we stopped passing\ninclude_untracked through in patch 17.  As the option parsing in the\ncreate_stash function was still in place at that point, it felt\nincorrect to not pass it through to 'do_create_stash()'.\n\nNone of the tests failed at that point though, as nothing was actually\npassing the command line options through anymore.  The last user of\nthat was removed in the \"convert save to builtin\" patch.\n\nArguably that would also be the right place to remove the option\nparsing, rather than in this patch (convert `stash--helper.c` into\n`stash.c`), but I tried to refrain from too much re-ordering to make\nthe changes more easily reviewable through range-diff.\n\nMaybe I should have held back on these changes as well, or made the\nother changes, dunno.  I wasn't always sure where to draw the line\nbetween changing too much, which might hurt the chances of advancing\nto next, because of the review work required, or doing too little,\nwhich might hurt the chances because the series is not deemed in good\nenough shape overall.\n\nSo what I ended up with is what I felt were the changes necessary to\nadvance the series, so we can start working on top, without the fear\nof stepping on others peoples toes as much.\n\n> The rest was obvious enough, thank you.\n> \n> As I said before, I was very much in favor of getting this `stash-in-c`\n> business moving again by advancing it to `next`.\n> \n> From my perspective (which is not informed by any official statement about\n> the role of `pu` or `next` because there is none as far as I know), the\n> purpose of `pu` is to get patches into a shape where the original author\n> is no longer the only one working on them. In my mind, that moment has\n> long passed, and the fact that `ps/stash-in-c` is still stuck in `pu`\n> really held me back on working more on this.\n\nI mainly held back on doing much with it, because I didn't want to\nstep on Paul-Sebastians toes (and I thought we'd be okay with\nadvancing the series with the fixups on top).  But hopefully this is\nnow getting us into a state where we get this to next, and get further\nwork on top unblocked.\n\n> So if you manage to get this finally cooking again, all hail to you!\n\nI hope we can :)\n\n> Thanks,\n> Dscho\n"},{"id":"370278","messageId":"87r2buusud.fsf@evledraar.gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-02-26T21:45:30Z","receivedAt":"2019-02-26T21:45:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 26 2019, Thomas Gummerer wrote:\n\n> As I was advocating for this series to go into 'next' without a large\n> refactor of this series, I'll put my money were my mouth is and try to\n> make the cleanups and fixes required, though without trying to avoid\n> further external process calls, or changing the series around too\n> much.\n>\n> One thing to consider here is that we have a GSoC project planned\n> based on 'git stash'.  If we can't get this to 'next' soon, I'd vote\n> for taking that project out of this years GSoC, and maybe try again\n> next year, if nobody implemented the feature in the meantime.\n\nFWIW I'd like to +1 getting it into \"next\" so this can be given thorough\ntesting in the 2.22\n\nIf there's still bugs or other regressions I think it's better sorted\nout without the cognitive load of reviewing it all again.\n\nWorst case we can always add something on top to flip the default of\nstash.useBuiltin.\n"},{"id":"370280","messageId":"nycvar.QRO.7.76.6.1902262335270.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"87r2buusud.fsf@evledraar.gmail.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-02-26T22:37:10Z","receivedAt":"2019-02-26T22:37:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 26 Feb 2019, Ævar Arnfjörð Bjarmason wrote:\n\n> \n> On Tue, Feb 26 2019, Thomas Gummerer wrote:\n> \n> > As I was advocating for this series to go into 'next' without a large\n> > refactor of this series, I'll put my money were my mouth is and try to\n> > make the cleanups and fixes required, though without trying to avoid\n> > further external process calls, or changing the series around too\n> > much.\n> >\n> > One thing to consider here is that we have a GSoC project planned\n> > based on 'git stash'.  If we can't get this to 'next' soon, I'd vote\n> > for taking that project out of this years GSoC, and maybe try again\n> > next year, if nobody implemented the feature in the meantime.\n> \n> FWIW I'd like to +1 getting it into \"next\" so this can be given thorough\n> testing in the 2.22\n\nIndeed. It is time.\n\n> If there's still bugs or other regressions I think it's better sorted\n> out without the cognitive load of reviewing it all again.\n\nFully agree!\n\n> Worst case we can always add something on top to flip the default of\n> stash.useBuiltin.\n\nI actually don't think that will be necessary. If my track record with\nfixing all kinds of built-in rebase corner-case bugs leading up to v2.20.0\nis any indication, the built-in stash will be cooking well.\n\nCiao,\nDscho"},{"id":"370491","messageId":"xmqq8sxw4uny.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-03T01:24:17Z","receivedAt":"2019-03-03T01:24:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> One thing that came up in the latest reviews, was to keep the stash\n> script intact throughout the series, and to not re-introduce it after\n> deleting it.  I did however not do that, as that would make the\n> range-diff quite a bit harder to read.\n\nOf course, if you start from a suboptimal ordering and reorder to\nmake it right, the range-diff that tries to match and compare the\nsteps will become larger and harder to follow.  What else is new?\n\nThat is refusing to think clearly hiding behind tautology.  \n\n> In addition removing the\n> script bit by bit also allowed us to find the precise commit in which\n> the missing 'discard_cache()' bug was introduced, which made it a bit\n> easier to pinpoint where the bug comes from imo.\n\nIn an ideal ordering, you would do a command line parser first and\ndispatch the rewritten commands piece by piece to the C\nreimplementation while diverting the remaining unwritten parts to\nscripted \"legacy\" one.  That would allow us to find the precise\ncommit that was faulty the same way.\n\nHaving said all that.\n\nI do not care too deeply about seeing this particular series done in\nthe right order anymore.  Everybody's eyes are tired of looking at\nit, and I do not mind to see you guys declare \"nobody can review\nthis, so let's keep it in 'next' and patch breakages up as they are\nfound out\", which seems to be happening here.  \n\nWe can divert our review cycles to other topics that haven't faced\nreviewer fatigue; they still have chance to be done right ;-).\n"},{"id":"370493","messageId":"xmqqtvgk3g1u.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190225231631.30507-1-t.gummerer@gmail.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-03T01:25:17Z","receivedAt":"2019-03-03T01:25:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> As I was advocating for this series to go into 'next' without a large\n> refactor of this series, I'll put my money were my mouth is and try to\n> make the cleanups and fixes required, though without trying to avoid\n> further external process calls, or changing the series around too\n> much.\n\nThanks for a well-written summary.  One thing that is missing in the\nwrite-up is if you kept the same base (i.e. on top of eacdb4d24f) or\nrebased the series on top of somewhere else.  I'd assume you built\non the same base as before in the meantime (I'll know soon enough,\nwhen I sit down on a terminal and resume working on the code ;-)\n\n\n\n\n"},{"id":"370504","messageId":"20190303100353.GV6085@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"xmqqtvgk3g1u.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v13 00/27] Convert \"git stash\" to C builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-03T10:03:53Z","receivedAt":"2019-03-03T10:04:01Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/03, Junio C Hamano wrote:\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> > As I was advocating for this series to go into 'next' without a large\n> > refactor of this series, I'll put my money were my mouth is and try to\n> > make the cleanups and fixes required, though without trying to avoid\n> > further external process calls, or changing the series around too\n> > much.\n> \n> Thanks for a well-written summary.  One thing that is missing in the\n> write-up is if you kept the same base (i.e. on top of eacdb4d24f) or\n> rebased the series on top of somewhere else.  I'd assume you built\n> on the same base as before in the meantime (I'll know soon enough,\n> when I sit down on a terminal and resume working on the code ;-)\n\nRight, I forgot mentioning that.  Yes it is still based on eacdb4d24f,\nas there was no good reason to change that.\n"},{"id":"370920","messageId":"20190307191533.GA29221@sigill.intra.peff.net","threadId":"50081","inReplyTo":"20190225231631.30507-13-t.gummerer@gmail.com","subject":"Re: [PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-03-07T19:15:34Z","receivedAt":"2019-03-07T19:15:38Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Feb 25, 2019 at 11:16:16PM +0000, Thomas Gummerer wrote:\n\n> +static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n\nThis series hit next recently, so I started building it merged with my\n-Wunused-parameters series. This \"prefix\" parameter is not ever used.\nSkimming through the function, I don't see anything that _should_ be\nusing it, so I think it's just cruft, and not indicative of a bug.\n\nThe same is true of create_stash() elsewhere in the series. But there it\nmight be worth keeping for consistency with the other top-level action\nfunctions. The other ones pass \"prefix\" to parse_options(), but\ncreate_stash() doesn't actually parse any options (and intentionally so,\nsince even \"--help\" should be taken as part of the stash message).\n\n-Peff\n"},{"id":"370921","messageId":"20190307191836.GB29221@sigill.intra.peff.net","threadId":"50081","inReplyTo":"20190225231631.30507-19-t.gummerer@gmail.com","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-03-07T19:18:36Z","receivedAt":"2019-03-07T19:18:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Feb 25, 2019 at 11:16:22PM +0000, Thomas Gummerer wrote:\n\n> +static void add_pathspecs(struct argv_array *args,\n> +\t\t\t  struct pathspec ps) {\n\nHere and elsewhere in the series, I notice that we pass the pathspec\nstruct by value, which is quite unusual for our codebase (and\npotentially confusing, if any of the callers were to mutate the pointers\nin the struct).\n\nIs there any reason this shouldn't be \"const struct pathspec *ps\" pretty\nmuch throughout the file?\n\n-Peff\n"},{"id":"370988","messageId":"nycvar.QRO.7.76.6.1903081630040.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"20190307191836.GB29221@sigill.intra.peff.net","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-08T15:30:36Z","receivedAt":"2019-03-08T15:31:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Thu, 7 Mar 2019, Jeff King wrote:\n\n> On Mon, Feb 25, 2019 at 11:16:22PM +0000, Thomas Gummerer wrote:\n> \n> > +static void add_pathspecs(struct argv_array *args,\n> > +\t\t\t  struct pathspec ps) {\n> \n> Here and elsewhere in the series, I notice that we pass the pathspec\n> struct by value, which is quite unusual for our codebase (and\n> potentially confusing, if any of the callers were to mutate the pointers\n> in the struct).\n> \n> Is there any reason this shouldn't be \"const struct pathspec *ps\" pretty\n> much throughout the file?\n\nI am quite certain that this is merely an oversight. It totes slipped\nby my review, for example.\n\nThanks for catching!\nDscho\n"},{"id":"371040","messageId":"20190309182610.GD31533@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1903081630040.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-09T18:26:10Z","receivedAt":"2019-03-09T18:26:16Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/08, Johannes Schindelin wrote:\n> Hi Peff,\n> \n> On Thu, 7 Mar 2019, Jeff King wrote:\n> \n> > On Mon, Feb 25, 2019 at 11:16:22PM +0000, Thomas Gummerer wrote:\n> > \n> > > +static void add_pathspecs(struct argv_array *args,\n> > > +\t\t\t  struct pathspec ps) {\n> > \n> > Here and elsewhere in the series, I notice that we pass the pathspec\n> > struct by value, which is quite unusual for our codebase (and\n> > potentially confusing, if any of the callers were to mutate the pointers\n> > in the struct).\n> > \n> > Is there any reason this shouldn't be \"const struct pathspec *ps\" pretty\n> > much throughout the file?\n> \n> I am quite certain that this is merely an oversight. It totes slipped\n> by my review, for example.\n\nYep, it slipped by me as well.  Here's a patch to fix it:\n\n--- >8 ---\nSubject: [PATCH 1/2] stash: pass pathspec as pointer\n\nPassing the pathspec by value is potentially confusing, as the copy is\nonly a shallow copy, so save the overhead of the copy, and pass the\npathspec struct as a pointer.\n\nIn addition use copy_pathspec to copy the pathspec into\nrev.prune_data, so the copy is a proper deep copy, and owned by the\nrevision API, as that's what the API expects.\n\nReported-by: Jeff King <peff@peff.net>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash.c | 50 ++++++++++++++++++++++++-------------------------\n 1 file changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 1bfa24030c..6eb67c75c3 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -826,11 +826,11 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n }\n \n static void add_pathspecs(struct argv_array *args,\n-\t\t\t  struct pathspec ps) {\n+\t\t\t  const struct pathspec *ps) {\n \tint i;\n \n-\tfor (i = 0; i < ps.nr; i++)\n-\t\targv_array_push(args, ps.items[i].match);\n+\tfor (i = 0; i < ps->nr; i++)\n+\t\targv_array_push(args, ps->items[i].match);\n }\n \n /*\n@@ -840,7 +840,7 @@ static void add_pathspecs(struct argv_array *args,\n  * = 0 if there are not any untracked files\n  * > 0 if there are untracked files\n  */\n-static int get_untracked_files(struct pathspec ps, int include_untracked,\n+static int get_untracked_files(const struct pathspec *ps, int include_untracked,\n \t\t\t       struct strbuf *untracked_files)\n {\n \tint i;\n@@ -853,12 +853,12 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n \tif (include_untracked != INCLUDE_ALL_FILES)\n \t\tsetup_standard_excludes(&dir);\n \n-\tseen = xcalloc(ps.nr, 1);\n+\tseen = xcalloc(ps->nr, 1);\n \n-\tmax_len = fill_directory(&dir, the_repository->index, &ps);\n+\tmax_len = fill_directory(&dir, the_repository->index, ps);\n \tfor (i = 0; i < dir.nr; i++) {\n \t\tstruct dir_entry *ent = dir.entries[i];\n-\t\tif (dir_path_match(&the_index, ent, &ps, max_len, seen)) {\n+\t\tif (dir_path_match(&the_index, ent, ps, max_len, seen)) {\n \t\t\tfound++;\n \t\t\tstrbuf_addstr(untracked_files, ent->name);\n \t\t\t/* NUL-terminate: will be fed to update-index -z */\n@@ -881,7 +881,7 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n  * = 0 if there are no changes.\n  * > 0 if there are changes.\n  */\n-static int check_changes_tracked_files(struct pathspec ps)\n+static int check_changes_tracked_files(const struct pathspec *ps)\n {\n \tint result;\n \tstruct rev_info rev;\n@@ -895,7 +895,7 @@ static int check_changes_tracked_files(struct pathspec ps)\n \t\treturn -1;\n \n \tinit_revisions(&rev, NULL);\n-\trev.prune_data = ps;\n+\tcopy_pathspec(&rev.prune_data, ps);\n \n \trev.diffopt.flags.quick = 1;\n \trev.diffopt.flags.ignore_submodules = 1;\n@@ -920,7 +920,7 @@ static int check_changes_tracked_files(struct pathspec ps)\n  * The function will fill `untracked_files` with the names of untracked files\n  * It will return 1 if there were any changes and 0 if there were not.\n  */\n-static int check_changes(struct pathspec ps, int include_untracked,\n+static int check_changes(const struct pathspec *ps, int include_untracked,\n \t\t\t struct strbuf *untracked_files)\n {\n \tint ret = 0;\n@@ -974,7 +974,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \treturn ret;\n }\n \n-static int stash_patch(struct stash_info *info, struct pathspec ps,\n+static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n \t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n@@ -1033,7 +1033,7 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \treturn ret;\n }\n \n-static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n+static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)\n {\n \tint ret = 0;\n \tstruct rev_info rev;\n@@ -1050,7 +1050,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \t}\n \tset_alternate_index_output(NULL);\n \n-\trev.prune_data = ps;\n+\tcopy_pathspec(&rev.prune_data, ps);\n \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = add_diff_to_buf;\n \trev.diffopt.format_callback_data = &diff_output;\n@@ -1094,7 +1094,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \treturn ret;\n }\n \n-static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n+static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n \t\t\t   struct stash_info *info, struct strbuf *patch,\n \t\t\t   int quiet)\n@@ -1226,10 +1226,10 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tstrbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n-\tif (!check_changes_tracked_files(ps))\n+\tif (!check_changes_tracked_files(&ps))\n \t\treturn 0;\n \n-\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n+\tret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,\n \t\t\t      NULL, 0);\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n@@ -1238,7 +1238,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n-static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n+static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,\n \t\t\t int keep_index, int patch_mode, int include_untracked)\n {\n \tint ret = 0;\n@@ -1258,15 +1258,15 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t}\n \n \tread_cache_preload(NULL);\n-\tif (!include_untracked && ps.nr) {\n+\tif (!include_untracked && ps->nr) {\n \t\tint i;\n-\t\tchar *ps_matched = xcalloc(ps.nr, 1);\n+\t\tchar *ps_matched = xcalloc(ps->nr, 1);\n \n \t\tfor (i = 0; i < active_nr; i++)\n-\t\t\tce_path_match(&the_index, active_cache[i], &ps,\n+\t\t\tce_path_match(&the_index, active_cache[i], ps,\n \t\t\t\t      ps_matched);\n \n-\t\tif (report_path_error(ps_matched, &ps, NULL)) {\n+\t\tif (report_path_error(ps_matched, ps, NULL)) {\n \t\t\tfprintf_ln(stderr, _(\"Did you forget to 'git add'?\"));\n \t\t\tret = -1;\n \t\t\tfree(ps_matched);\n@@ -1313,7 +1313,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\t\t  stash_msg_buf.buf);\n \n \tif (!patch_mode) {\n-\t\tif (include_untracked && !ps.nr) {\n+\t\tif (include_untracked && !ps->nr) {\n \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\t\tcp.git_cmd = 1;\n@@ -1327,7 +1327,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\t\t}\n \t\t}\n \t\tdiscard_cache();\n-\t\tif (ps.nr) {\n+\t\tif (ps->nr) {\n \t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n \t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n \t\t\tstruct child_process cp_apply = CHILD_PROCESS_INIT;\n@@ -1467,7 +1467,7 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\t\t\t     0);\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);\n-\treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n+\treturn do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,\n \t\t\t     include_untracked);\n }\n \n@@ -1504,7 +1504,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \t\tstash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n-\tret = do_push_stash(ps, stash_msg, quiet, keep_index,\n+\tret = do_push_stash(&ps, stash_msg, quiet, keep_index,\n \t\t\t    patch_mode, include_untracked);\n \n \tstrbuf_release(&stash_msg_buf);\n-- \n2.21.0.474.g541d9dca55\n"},{"id":"371042","messageId":"20190309183021.GE31533@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"20190307191533.GA29221@sigill.intra.peff.net","subject":"Re: [PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-09T18:30:21Z","receivedAt":"2019-03-09T18:30:27Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/07, Jeff King wrote:\n> On Mon, Feb 25, 2019 at 11:16:16PM +0000, Thomas Gummerer wrote:\n> \n> > +static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n> \n> This series hit next recently, so I started building it merged with my\n> -Wunused-parameters series. This \"prefix\" parameter is not ever used.\n> Skimming through the function, I don't see anything that _should_ be\n> using it, so I think it's just cruft, and not indicative of a bug.\n\nAgreed, I think it's only cruft, and shouldn't be used anywhere in the\nfunction.  Below is a patch to remove the parameter.\n\n> The same is true of create_stash() elsewhere in the series. But there it\n> might be worth keeping for consistency with the other top-level action\n> functions. The other ones pass \"prefix\" to parse_options(), but\n> create_stash() doesn't actually parse any options (and intentionally so,\n> since even \"--help\" should be taken as part of the stash message).\n\nAgreed, I'd be happy to keep the parameter there.  Looking at your\nfork, you seem to have some WIP patches to introduce a UNUSED macro\nfor parameters like this, which I don't think I've seen on the list\nyet (though I may have just missed them).\n\nI guess it's probably best for you to mark this parameter as UNUSED as\npart of your series, but if you have a different preference on how to\nhandle it, let me know.\n\n--- >8 ---\nSubject: [PATCH 2/2] stash: drop unused parameter\n\nDrop the unused prefix parameter in do_drop_stash.\n\nWe also have an unused \"prefix\" parameter in the 'create_stash'\nfunction, however we leave that in place for symmetry with the other\ntop-level functions.\n\nReported-by: Jeff King <peff@peff.net>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n builtin/stash.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 6eb67c75c3..069bf14846 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -527,7 +527,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n-static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n+static int do_drop_stash(struct stash_info *info, int quiet)\n {\n \tint ret;\n \tstruct child_process cp_reflog = CHILD_PROCESS_INIT;\n@@ -597,7 +597,7 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \n \tassert_stash_ref(&info);\n \n-\tret = do_drop_stash(prefix, &info, quiet);\n+\tret = do_drop_stash(&info, quiet);\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -626,7 +626,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t\tprintf_ln(_(\"The stash entry is kept in case \"\n \t\t\t    \"you need it again.\"));\n \telse\n-\t\tret = do_drop_stash(prefix, &info, quiet);\n+\t\tret = do_drop_stash(&info, quiet);\n \n \tfree_stash_info(&info);\n \treturn ret;\n@@ -663,7 +663,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tret = do_apply_stash(prefix, &info, 1, 0);\n \tif (!ret && info.is_stash_ref)\n-\t\tret = do_drop_stash(prefix, &info, 0);\n+\t\tret = do_drop_stash(&info, 0);\n \n \tfree_stash_info(&info);\n \n-- \n2.21.0.474.g541d9dca55\n"},{"id":"371090","messageId":"20190310232639.GA3059@sigill.intra.peff.net","threadId":"50081","inReplyTo":"20190309183021.GE31533@hank.intra.tgummerer.com","subject":"Re: [PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-03-10T23:26:39Z","receivedAt":"2019-03-10T23:26:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Mar 09, 2019 at 06:30:21PM +0000, Thomas Gummerer wrote:\n\n> On 03/07, Jeff King wrote:\n> > On Mon, Feb 25, 2019 at 11:16:16PM +0000, Thomas Gummerer wrote:\n> > \n> > > +static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n> > \n> > This series hit next recently, so I started building it merged with my\n> > -Wunused-parameters series. This \"prefix\" parameter is not ever used.\n> > Skimming through the function, I don't see anything that _should_ be\n> > using it, so I think it's just cruft, and not indicative of a bug.\n> \n> Agreed, I think it's only cruft, and shouldn't be used anywhere in the\n> function.  Below is a patch to remove the parameter.\n\nThanks. I did something similar temporarily for my own tree, but I'll\nassume yours will go on top of the stash topic upstream. The patch below\nlooks good (and matches what I did locally).\n\n> > The same is true of create_stash() elsewhere in the series. But there it\n> > might be worth keeping for consistency with the other top-level action\n> > functions. The other ones pass \"prefix\" to parse_options(), but\n> > create_stash() doesn't actually parse any options (and intentionally so,\n> > since even \"--help\" should be taken as part of the stash message).\n> \n> Agreed, I'd be happy to keep the parameter there.  Looking at your\n> fork, you seem to have some WIP patches to introduce a UNUSED macro\n> for parameters like this, which I don't think I've seen on the list\n> yet (though I may have just missed them).\n> \n> I guess it's probably best for you to mark this parameter as UNUSED as\n> part of your series, but if you have a different preference on how to\n> handle it, let me know.\n\nYep, that sounds good; I'll eventually mark this UNUSED when I send\nthose patches. The \"mark unused\" ones haven't hit the list yet. I've\nbeen trickling the patches out, 10 or so at a time, but I'm still on the\n\"drop ones that we can\" patches, and haven't even gotten to the \"mark\nones we want to keep\" patches. :)\n\n-Peff\n"},{"id":"371092","messageId":"xmqqmum2mbn4.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190309183021.GE31533@hank.intra.tgummerer.com","subject":"Re: [PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-11T01:40:31Z","receivedAt":"2019-03-11T01:40:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> Agreed, I'd be happy to keep the parameter there.  Looking at your\n> fork, you seem to have some WIP patches to introduce a UNUSED macro\n> for parameters like this, which I don't think I've seen on the list\n> yet (though I may have just missed them).\n>\n> I guess it's probably best for you to mark this parameter as UNUSED as\n> part of your series, but if you have a different preference on how to\n> handle it, let me know.\n\nI agree that the uniformity among near-toplevel helpers like\ncreate_stash() is a good thing to have.\n\nIn the meantime, you want the patch you sent (below) on top of the\nstash-in-c topic to address do_drop_stash()?\n\nThanks for working well together.\n\n> --- >8 ---\n> Subject: [PATCH 2/2] stash: drop unused parameter\n>\n> Drop the unused prefix parameter in do_drop_stash.\n>\n> We also have an unused \"prefix\" parameter in the 'create_stash'\n> function, however we leave that in place for symmetry with the other\n> top-level functions.\n>\n> Reported-by: Jeff King <peff@peff.net>\n> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> ---\n>  builtin/stash.c | 8 ++++----\n>  1 file changed, 4 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index 6eb67c75c3..069bf14846 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -527,7 +527,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n>  \treturn ret;\n>  }\n>  \n> -static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n> +static int do_drop_stash(struct stash_info *info, int quiet)\n>  {\n>  \tint ret;\n>  \tstruct child_process cp_reflog = CHILD_PROCESS_INIT;\n> @@ -597,7 +597,7 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n>  \n>  \tassert_stash_ref(&info);\n>  \n> -\tret = do_drop_stash(prefix, &info, quiet);\n> +\tret = do_drop_stash(&info, quiet);\n>  \tfree_stash_info(&info);\n>  \treturn ret;\n>  }\n> @@ -626,7 +626,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n>  \t\tprintf_ln(_(\"The stash entry is kept in case \"\n>  \t\t\t    \"you need it again.\"));\n>  \telse\n> -\t\tret = do_drop_stash(prefix, &info, quiet);\n> +\t\tret = do_drop_stash(&info, quiet);\n>  \n>  \tfree_stash_info(&info);\n>  \treturn ret;\n> @@ -663,7 +663,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n>  \tif (!ret)\n>  \t\tret = do_apply_stash(prefix, &info, 1, 0);\n>  \tif (!ret && info.is_stash_ref)\n> -\t\tret = do_drop_stash(prefix, &info, 0);\n> +\t\tret = do_drop_stash(&info, 0);\n>  \n>  \tfree_stash_info(&info);\n"},{"id":"371093","messageId":"xmqqimwqmbba.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190309182610.GD31533@hank.intra.tgummerer.com","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-11T01:47:37Z","receivedAt":"2019-03-11T01:47:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> Subject: [PATCH 1/2] stash: pass pathspec as pointer\n>\n> Passing the pathspec by value is potentially confusing, as the copy is\n> only a shallow copy, so save the overhead of the copy, and pass the\n> pathspec struct as a pointer.\n>\n> In addition use copy_pathspec to copy the pathspec into\n> rev.prune_data, so the copy is a proper deep copy, and owned by the\n> revision API, as that's what the API expects.\n\nIt does make quite a lot of sense, but do we need clear_pathspec()\nat strategic places after we are done using these copied instances?\n"},{"id":"371126","messageId":"xmqqd0mxkgvy.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"xmqqimwqmbba.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-11T07:30:09Z","receivedAt":"2019-03-11T07:30:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n>\n>> Subject: [PATCH 1/2] stash: pass pathspec as pointer\n>>\n>> Passing the pathspec by value is potentially confusing, as the copy is\n>> only a shallow copy, so save the overhead of the copy, and pass the\n>> pathspec struct as a pointer.\n>>\n>> In addition use copy_pathspec to copy the pathspec into\n>> rev.prune_data, so the copy is a proper deep copy, and owned by the\n>> revision API, as that's what the API expects.\n>\n> It does make quite a lot of sense, but do we need clear_pathspec()\n> at strategic places after we are done using these copied instances?\n\nAnother thing is that this also needs Dscho's fix to pass down the\npathspec that was originally given, not the parsed part.\n"},{"id":"371194","messageId":"20190311214029.GA16414@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"xmqqmum2mbn4.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v13 12/27] stash: convert drop and clear to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-11T21:40:29Z","receivedAt":"2019-03-11T21:40:35Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/11, Junio C Hamano wrote:\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n> \n> > Agreed, I'd be happy to keep the parameter there.  Looking at your\n> > fork, you seem to have some WIP patches to introduce a UNUSED macro\n> > for parameters like this, which I don't think I've seen on the list\n> > yet (though I may have just missed them).\n> >\n> > I guess it's probably best for you to mark this parameter as UNUSED as\n> > part of your series, but if you have a different preference on how to\n> > handle it, let me know.\n> \n> I agree that the uniformity among near-toplevel helpers like\n> create_stash() is a good thing to have.\n> \n> In the meantime, you want the patch you sent (below) on top of the\n> stash-in-c topic to address do_drop_stash()?\n\nYes, I think that would be good, thanks! (And if I read Peff's reply\nin this thread correctly, I think that's his preference as well).\n\nI also just realized that this is patch 2/2 as I created it on the\nsame branch as the pathspec fixups.  But they are really independent,\neven though they should both end up on top of ps/stash-in-c.\n\n> Thanks for working well together.\n> \n> > --- >8 ---\n> > Subject: [PATCH 2/2] stash: drop unused parameter\n> >\n> > Drop the unused prefix parameter in do_drop_stash.\n> >\n> > We also have an unused \"prefix\" parameter in the 'create_stash'\n> > function, however we leave that in place for symmetry with the other\n> > top-level functions.\n> >\n> > Reported-by: Jeff King <peff@peff.net>\n> > Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n> > ---\n> >  builtin/stash.c | 8 ++++----\n> >  1 file changed, 4 insertions(+), 4 deletions(-)\n> >\n> > diff --git a/builtin/stash.c b/builtin/stash.c\n> > index 6eb67c75c3..069bf14846 100644\n> > --- a/builtin/stash.c\n> > +++ b/builtin/stash.c\n> > @@ -527,7 +527,7 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n> >  \treturn ret;\n> >  }\n> >  \n> > -static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)\n> > +static int do_drop_stash(struct stash_info *info, int quiet)\n> >  {\n> >  \tint ret;\n> >  \tstruct child_process cp_reflog = CHILD_PROCESS_INIT;\n> > @@ -597,7 +597,7 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n> >  \n> >  \tassert_stash_ref(&info);\n> >  \n> > -\tret = do_drop_stash(prefix, &info, quiet);\n> > +\tret = do_drop_stash(&info, quiet);\n> >  \tfree_stash_info(&info);\n> >  \treturn ret;\n> >  }\n> > @@ -626,7 +626,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n> >  \t\tprintf_ln(_(\"The stash entry is kept in case \"\n> >  \t\t\t    \"you need it again.\"));\n> >  \telse\n> > -\t\tret = do_drop_stash(prefix, &info, quiet);\n> > +\t\tret = do_drop_stash(&info, quiet);\n> >  \n> >  \tfree_stash_info(&info);\n> >  \treturn ret;\n> > @@ -663,7 +663,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n> >  \tif (!ret)\n> >  \t\tret = do_apply_stash(prefix, &info, 1, 0);\n> >  \tif (!ret && info.is_stash_ref)\n> > -\t\tret = do_drop_stash(prefix, &info, 0);\n> > +\t\tret = do_drop_stash(&info, 0);\n> >  \n> >  \tfree_stash_info(&info);\n"},{"id":"371195","messageId":"20190311214244.GB16414@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"xmqqd0mxkgvy.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v13 18/27] stash: convert create to builtin","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-11T21:42:44Z","receivedAt":"2019-03-11T21:42:48Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/11, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Thomas Gummerer <t.gummerer@gmail.com> writes:\n> >\n> >> Subject: [PATCH 1/2] stash: pass pathspec as pointer\n> >>\n> >> Passing the pathspec by value is potentially confusing, as the copy is\n> >> only a shallow copy, so save the overhead of the copy, and pass the\n> >> pathspec struct as a pointer.\n> >>\n> >> In addition use copy_pathspec to copy the pathspec into\n> >> rev.prune_data, so the copy is a proper deep copy, and owned by the\n> >> revision API, as that's what the API expects.\n> >\n> > It does make quite a lot of sense, but do we need clear_pathspec()\n> > at strategic places after we are done using these copied instances?\n> \n> Another thing is that this also needs Dscho's fix to pass down the\n> pathspec that was originally given, not the parsed part.\n\nGood catch on both acconts.  I'll send a new patch soon, adding the\nclear_pathspec() calls and rebasing it on top of Dscho's fix.\n"},{"id":"371198","messageId":"20190311221624.GC16414@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"20190311214244.GB16414@hank.intra.tgummerer.com","subject":"[PATCH v2] stash: pass pathspec as pointer","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-11T22:16:32Z","receivedAt":"2019-03-11T22:16:38Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"Passing the pathspec by value is potentially confusing, as the copy is\nonly a shallow copy, so save the overhead of the copy, and pass the\npathspec struct as a pointer.\n\nIn addition use copy_pathspec to copy the pathspec into\nrev.prune_data, so the copy is a proper deep copy, and owned by the\nrevision API, as that's what the API expects.\n\nReported-by: Jeff King <peff@peff.net>\nSigned-off-by: Thomas Gummerer <t.gummerer@gmail.com>\n---\n\n> Good catch on both acconts.  I'll send a new patch soon, adding the\n> clear_pathspec() calls and rebasing it on top of Dscho's fix.\n\nHere it is.  Thanks for the review of the first round Junio!\n\nThis is on top of Dscho's series at\n<pull.159.git.gitgitgadget@gmail.com> applied to ps/stash-in-c.\n\n builtin/stash.c | 68 +++++++++++++++++++++++++++----------------------\n 1 file changed, 38 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 2f29d037c8..e0528d4cc8 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -826,11 +826,11 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n }\n \n static void add_pathspecs(struct argv_array *args,\n-\t\t\t  struct pathspec ps) {\n+\t\t\t  const struct pathspec *ps) {\n \tint i;\n \n-\tfor (i = 0; i < ps.nr; i++)\n-\t\targv_array_push(args, ps.items[i].original);\n+\tfor (i = 0; i < ps->nr; i++)\n+\t\targv_array_push(args, ps->items[i].original);\n }\n \n /*\n@@ -840,7 +840,7 @@ static void add_pathspecs(struct argv_array *args,\n  * = 0 if there are not any untracked files\n  * > 0 if there are untracked files\n  */\n-static int get_untracked_files(struct pathspec ps, int include_untracked,\n+static int get_untracked_files(const struct pathspec *ps, int include_untracked,\n \t\t\t       struct strbuf *untracked_files)\n {\n \tint i;\n@@ -853,12 +853,12 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n \tif (include_untracked != INCLUDE_ALL_FILES)\n \t\tsetup_standard_excludes(&dir);\n \n-\tseen = xcalloc(ps.nr, 1);\n+\tseen = xcalloc(ps->nr, 1);\n \n-\tmax_len = fill_directory(&dir, the_repository->index, &ps);\n+\tmax_len = fill_directory(&dir, the_repository->index, ps);\n \tfor (i = 0; i < dir.nr; i++) {\n \t\tstruct dir_entry *ent = dir.entries[i];\n-\t\tif (dir_path_match(&the_index, ent, &ps, max_len, seen)) {\n+\t\tif (dir_path_match(&the_index, ent, ps, max_len, seen)) {\n \t\t\tfound++;\n \t\t\tstrbuf_addstr(untracked_files, ent->name);\n \t\t\t/* NUL-terminate: will be fed to update-index -z */\n@@ -881,11 +881,12 @@ static int get_untracked_files(struct pathspec ps, int include_untracked,\n  * = 0 if there are no changes.\n  * > 0 if there are changes.\n  */\n-static int check_changes_tracked_files(struct pathspec ps)\n+static int check_changes_tracked_files(const struct pathspec *ps)\n {\n \tint result;\n \tstruct rev_info rev;\n \tstruct object_id dummy;\n+\tint ret = 0;\n \n \t/* No initial commit. */\n \tif (get_oid(\"HEAD\", &dummy))\n@@ -895,7 +896,7 @@ static int check_changes_tracked_files(struct pathspec ps)\n \t\treturn -1;\n \n \tinit_revisions(&rev, NULL);\n-\trev.prune_data = ps;\n+\tcopy_pathspec(&rev.prune_data, ps);\n \n \trev.diffopt.flags.quick = 1;\n \trev.diffopt.flags.ignore_submodules = 1;\n@@ -905,22 +906,28 @@ static int check_changes_tracked_files(struct pathspec ps)\n \tdiff_setup_done(&rev.diffopt);\n \n \tresult = run_diff_index(&rev, 1);\n-\tif (diff_result_code(&rev.diffopt, result))\n-\t\treturn 1;\n+\tif (diff_result_code(&rev.diffopt, result)) {\n+\t\tret = 1;\n+\t\tgoto done;\n+\t}\n \n \tobject_array_clear(&rev.pending);\n \tresult = run_diff_files(&rev, 0);\n-\tif (diff_result_code(&rev.diffopt, result))\n-\t\treturn 1;\n+\tif (diff_result_code(&rev.diffopt, result)) {\n+\t\tret = 1;\n+\t\tgoto done;\n+\t}\n \n-\treturn 0;\n+done:\n+\tclear_pathspec(&rev.prune_data);\n+\treturn ret;\n }\n \n /*\n  * The function will fill `untracked_files` with the names of untracked files\n  * It will return 1 if there were any changes and 0 if there were not.\n  */\n-static int check_changes(struct pathspec ps, int include_untracked,\n+static int check_changes(const struct pathspec *ps, int include_untracked,\n \t\t\t struct strbuf *untracked_files)\n {\n \tint ret = 0;\n@@ -974,7 +981,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,\n \treturn ret;\n }\n \n-static int stash_patch(struct stash_info *info, struct pathspec ps,\n+static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n \t\t       struct strbuf *out_patch, int quiet)\n {\n \tint ret = 0;\n@@ -1033,7 +1040,7 @@ static int stash_patch(struct stash_info *info, struct pathspec ps,\n \treturn ret;\n }\n \n-static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n+static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)\n {\n \tint ret = 0;\n \tstruct rev_info rev;\n@@ -1042,6 +1049,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \tstruct index_state istate = { NULL };\n \n \tinit_revisions(&rev, NULL);\n+\tcopy_pathspec(&rev.prune_data, ps);\n \n \tset_alternate_index_output(stash_index_path.buf);\n \tif (reset_tree(&info->i_tree, 0, 0)) {\n@@ -1050,7 +1058,6 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \t}\n \tset_alternate_index_output(NULL);\n \n-\trev.prune_data = ps;\n \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = add_diff_to_buf;\n \trev.diffopt.format_callback_data = &diff_output;\n@@ -1089,12 +1096,13 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n \tdiscard_index(&istate);\n \tUNLEAK(rev);\n \tobject_array_clear(&rev.pending);\n+\tclear_pathspec(&rev.prune_data);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\n }\n \n-static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,\n+static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,\n \t\t\t   int include_untracked, int patch_mode,\n \t\t\t   struct stash_info *info, struct strbuf *patch,\n \t\t\t   int quiet)\n@@ -1226,10 +1234,10 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tstrbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n-\tif (!check_changes_tracked_files(ps))\n+\tif (!check_changes_tracked_files(&ps))\n \t\treturn 0;\n \n-\tret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,\n+\tret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,\n \t\t\t      NULL, 0);\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n@@ -1238,7 +1246,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \treturn ret;\n }\n \n-static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n+static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,\n \t\t\t int keep_index, int patch_mode, int include_untracked)\n {\n \tint ret = 0;\n@@ -1258,15 +1266,15 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t}\n \n \tread_cache_preload(NULL);\n-\tif (!include_untracked && ps.nr) {\n+\tif (!include_untracked && ps->nr) {\n \t\tint i;\n-\t\tchar *ps_matched = xcalloc(ps.nr, 1);\n+\t\tchar *ps_matched = xcalloc(ps->nr, 1);\n \n \t\tfor (i = 0; i < active_nr; i++)\n-\t\t\tce_path_match(&the_index, active_cache[i], &ps,\n+\t\t\tce_path_match(&the_index, active_cache[i], ps,\n \t\t\t\t      ps_matched);\n \n-\t\tif (report_path_error(ps_matched, &ps, NULL)) {\n+\t\tif (report_path_error(ps_matched, ps, NULL)) {\n \t\t\tfprintf_ln(stderr, _(\"Did you forget to 'git add'?\"));\n \t\t\tret = -1;\n \t\t\tfree(ps_matched);\n@@ -1313,7 +1321,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\t\t  stash_msg_buf.buf);\n \n \tif (!patch_mode) {\n-\t\tif (include_untracked && !ps.nr) {\n+\t\tif (include_untracked && !ps->nr) {\n \t\t\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \t\t\tcp.git_cmd = 1;\n@@ -1327,7 +1335,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,\n \t\t\t}\n \t\t}\n \t\tdiscard_cache();\n-\t\tif (ps.nr) {\n+\t\tif (ps->nr) {\n \t\t\tstruct child_process cp_add = CHILD_PROCESS_INIT;\n \t\t\tstruct child_process cp_diff = CHILD_PROCESS_INIT;\n \t\t\tstruct child_process cp_apply = CHILD_PROCESS_INIT;\n@@ -1468,7 +1476,7 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n-\treturn do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,\n+\treturn do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,\n \t\t\t     include_untracked);\n }\n \n@@ -1505,7 +1513,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)\n \t\tstash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');\n \n \tmemset(&ps, 0, sizeof(ps));\n-\tret = do_push_stash(ps, stash_msg, quiet, keep_index,\n+\tret = do_push_stash(&ps, stash_msg, quiet, keep_index,\n \t\t\t    patch_mode, include_untracked);\n \n \tstrbuf_release(&stash_msg_buf);\n-- \n2.21.0.474.g541d9dca55\n"},{"id":"371223","messageId":"xmqqsgvsh9hk.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190311221624.GC16414@hank.intra.tgummerer.com","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-12T06:50:31Z","receivedAt":"2019-03-12T06:50:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n>> Good catch on both acconts.  I'll send a new patch soon, adding the\n>> clear_pathspec() calls and rebasing it on top of Dscho's fix.\n>\n> Here it is.  Thanks for the review of the first round Junio!\n>\n> This is on top of Dscho's series at\n> <pull.159.git.gitgitgadget@gmail.com> applied to ps/stash-in-c.\n\nThat's called js/stash-in-c-pathspec-fix; as this patch is also\nabout pathspec, I guess it is a good idea to just keep them in a\nsingle topic, so I'll apply it there.\n\n"},{"id":"371305","messageId":"nycvar.QRO.7.76.6.1903122323460.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"20190311221624.GC16414@hank.intra.tgummerer.com","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-12T22:35:44Z","receivedAt":"2019-03-12T22:36:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Mon, 11 Mar 2019, Thomas Gummerer wrote:\n\n> Passing the pathspec by value is potentially confusing, as the copy is\n> only a shallow copy, so save the overhead of the copy, and pass the\n> pathspec struct as a pointer.\n\nNot only confusing, but also wasteful ;-)\n\n> In addition use copy_pathspec to copy the pathspec into\n> rev.prune_data, so the copy is a proper deep copy, and owned by the\n> revision API, as that's what the API expects.\n\nGood.\n\n> [...]\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index 2f29d037c8..e0528d4cc8 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -826,11 +826,11 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n>  }\n>  \n>  static void add_pathspecs(struct argv_array *args,\n> -\t\t\t  struct pathspec ps) {\n> +\t\t\t  const struct pathspec *ps) {\n\nI see that you added the `const` keyword. While it does not hurt, I would\nprobably not have bothered...\n\n> [...]\n> @@ -1042,6 +1049,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n>  \tstruct index_state istate = { NULL };\n>  \n>  \tinit_revisions(&rev, NULL);\n> +\tcopy_pathspec(&rev.prune_data, ps);\n\nThis moved here... because...\n\n>  \n>  \tset_alternate_index_output(stash_index_path.buf);\n>  \tif (reset_tree(&info->i_tree, 0, 0)) {\n\n... this `if` block could jump to...\n\n\n> @@ -1050,7 +1058,6 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n>  \t}\n>  \tset_alternate_index_output(NULL);\n>  \n> -\trev.prune_data = ps;\n>  \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n>  \trev.diffopt.format_callback = add_diff_to_buf;\n>  \trev.diffopt.format_callback_data = &diff_output;\n> @@ -1089,12 +1096,13 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n\n... this point (the `done:` label is *just* one line further up, and this\nis a static diff, so we cannot just increase the context when we need to\nsee more, unlike, say, GitHub PRs) and...\n\n>  \tdiscard_index(&istate);\n>  \tUNLEAK(rev);\n>  \tobject_array_clear(&rev.pending);\n> +\tclear_pathspec(&rev.prune_data);\n\n... we add this call here.\n\nHowever, we would not have needed to move the initialization of\n`rev.prune_data`, I don't think, because `init_revision()` zeros the\nentire struct, including `prune_data`, which would have made\n`clear_pathspec()` safe to call, too.\n\nBoth of my comments need no action, and the rest of the patch looks good\nto me.\n\nThank you for going through this!\nDscho\n"},{"id":"371311","messageId":"20190312234046.GF16414@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1903122323460.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-12T23:40:46Z","receivedAt":"2019-03-12T23:40:52Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/12, Johannes Schindelin wrote:\n> Hi Thomas,\n> \n> On Mon, 11 Mar 2019, Thomas Gummerer wrote:\n> \n> > Passing the pathspec by value is potentially confusing, as the copy is\n> > only a shallow copy, so save the overhead of the copy, and pass the\n> > pathspec struct as a pointer.\n> \n> Not only confusing, but also wasteful ;-)\n> \n> > In addition use copy_pathspec to copy the pathspec into\n> > rev.prune_data, so the copy is a proper deep copy, and owned by the\n> > revision API, as that's what the API expects.\n> \n> Good.\n> \n> > [...]\n> > diff --git a/builtin/stash.c b/builtin/stash.c\n> > index 2f29d037c8..e0528d4cc8 100644\n> > --- a/builtin/stash.c\n> > +++ b/builtin/stash.c\n> > @@ -826,11 +826,11 @@ static int store_stash(int argc, const char **argv, const char *prefix)\n> >  }\n> >  \n> >  static void add_pathspecs(struct argv_array *args,\n> > -\t\t\t  struct pathspec ps) {\n> > +\t\t\t  const struct pathspec *ps) {\n> \n> I see that you added the `const` keyword. While it does not hurt, I would\n> probably not have bothered...\n\nThat's fair, I went with what seemed most common in the codebase.\nMore than half the parameters seem to be using \"const struct\npathspec\", so that seems to be the more common way if we don't require\nthe parameter to be modifyable.\n\n$ git grep -F \"struct pathspec *\" | wc -l\n81\n$ git grep -F \"const struct pathspec *\" | wc -l\n67\n\n\n> > [...]\n> > @@ -1042,6 +1049,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> >  \tstruct index_state istate = { NULL };\n> >  \n> >  \tinit_revisions(&rev, NULL);\n> > +\tcopy_pathspec(&rev.prune_data, ps);\n> \n> This moved here... because...\n> \n> >  \n> >  \tset_alternate_index_output(stash_index_path.buf);\n> >  \tif (reset_tree(&info->i_tree, 0, 0)) {\n> \n> ... this `if` block could jump to...\n> \n> \n> > @@ -1050,7 +1058,6 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> >  \t}\n> >  \tset_alternate_index_output(NULL);\n> >  \n> > -\trev.prune_data = ps;\n> >  \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n> >  \trev.diffopt.format_callback = add_diff_to_buf;\n> >  \trev.diffopt.format_callback_data = &diff_output;\n> > @@ -1089,12 +1096,13 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> \n> ... this point (the `done:` label is *just* one line further up, and this\n> is a static diff, so we cannot just increase the context when we need to\n> see more, unlike, say, GitHub PRs) and...\n> \n> >  \tdiscard_index(&istate);\n> >  \tUNLEAK(rev);\n> >  \tobject_array_clear(&rev.pending);\n> > +\tclear_pathspec(&rev.prune_data);\n> \n> ... we add this call here.\n> \n> However, we would not have needed to move the initialization of\n> `rev.prune_data`, I don't think, because `init_revision()` zeros the\n> entire struct, including `prune_data`, which would have made\n> `clear_pathspec()` safe to call, too.\n\n'clear_pathspec()' doesn't actually check whether the parameter passed\nto it is NULL or not before dereferencing it.  The first few lines of\nthe function are:\n\n\tvoid clear_pathspec(struct pathspec *pathspec)\n\t{\n\t\tint i, j;\n\n\t\tfor (i = 0; i < pathspec->nr; i++) {\n\t\t[...]\n\nSo I think moving the 'copy_pathspec()' earlier is actually required.\nIt may make sense to make 'clear_pathspec()' safe to call with a NULL\npointer, dunno.\n\n> Both of my comments need no action, and the rest of the patch looks good\n> to me.\n\nThanks for your review!\n\n> Thank you for going through this!\n> Dscho\n"},{"id":"371316","messageId":"xmqqwol3fsv6.fsf@gitster-ct.c.googlers.com","threadId":"50081","inReplyTo":"20190312234046.GF16414@hank.intra.tgummerer.com","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-13T01:47:09Z","receivedAt":"2019-03-13T01:47:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n>> I see that you added the `const` keyword. While it does not hurt, I would\n>> probably not have bothered...\n>\n> That's fair, I went with what seemed most common in the codebase.\n> More than half the parameters seem to be using \"const struct\n> pathspec\", so that seems to be the more common way if we don't require\n> the parameter to be modifyable.\n\nYes, when you prepare a struct at a callsite and pass it thru a long\ncallchain, it is very helpful to both humans and compilers reading\nthe code to declare that the structure would not be modified, if the\ncode indeed keeps it constant.  A caller that used to passed the\nstructure by value certainly hasn't been expecting the callee would\nmodify its contents and it needs to read back the updated value, so\nI find that most of these constifing, if not all, very much in line\nwith the original's spirit.\n"},{"id":"371406","messageId":"nycvar.QRO.7.76.6.1903132311200.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"20190312234046.GF16414@hank.intra.tgummerer.com","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-13T22:14:50Z","receivedAt":"2019-03-13T22:15:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Thomas,\n\nOn Tue, 12 Mar 2019, Thomas Gummerer wrote:\n\n> On 03/12, Johannes Schindelin wrote:\n> \n> > On Mon, 11 Mar 2019, Thomas Gummerer wrote:\n> > > [...]\n> > > @@ -1042,6 +1049,7 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> > >  \tstruct index_state istate = { NULL };\n> > >  \n> > >  \tinit_revisions(&rev, NULL);\n> > > +\tcopy_pathspec(&rev.prune_data, ps);\n> > \n> > This moved here... because...\n> > \n> > >  \n> > >  \tset_alternate_index_output(stash_index_path.buf);\n> > >  \tif (reset_tree(&info->i_tree, 0, 0)) {\n> > \n> > ... this `if` block could jump to...\n> > \n> > \n> > > @@ -1050,7 +1058,6 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> > >  \t}\n> > >  \tset_alternate_index_output(NULL);\n> > >  \n> > > -\trev.prune_data = ps;\n> > >  \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n> > >  \trev.diffopt.format_callback = add_diff_to_buf;\n> > >  \trev.diffopt.format_callback_data = &diff_output;\n> > > @@ -1089,12 +1096,13 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)\n> > \n> > ... this point (the `done:` label is *just* one line further up, and this\n> > is a static diff, so we cannot just increase the context when we need to\n> > see more, unlike, say, GitHub PRs) and...\n> > \n> > >  \tdiscard_index(&istate);\n> > >  \tUNLEAK(rev);\n> > >  \tobject_array_clear(&rev.pending);\n> > > +\tclear_pathspec(&rev.prune_data);\n> > \n> > ... we add this call here.\n> > \n> > However, we would not have needed to move the initialization of\n> > `rev.prune_data`, I don't think, because `init_revision()` zeros the\n> > entire struct, including `prune_data`, which would have made\n> > `clear_pathspec()` safe to call, too.\n> \n> 'clear_pathspec()' doesn't actually check whether the parameter passed\n> to it is NULL or not before dereferencing it.\n\nIn this case, it does not need to check for NULL, as `&rev.prune_data`\nwill always be non-NULL: `rev`'s `prune_data` field is of type `struct\npatchspec`, i.e. *not* a pointer (in which case the type would be `struct\npathspec *`). See for yourself:\n\n\thttps://github.com/git/git/blob/v2.21.0/revision.h#L91\n\nCiao,\nDscho\n"},{"id":"371496","messageId":"87ftrpbnkg.fsf@evledraar.gmail.com","threadId":"50081","inReplyTo":"20190225231631.30507-12-t.gummerer@gmail.com","subject":"regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-03-14T13:19:43Z","receivedAt":"2019-03-14T13:19:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 26 2019, Thomas Gummerer wrote:\n\n> From: Joel Teichroeb <joel@teichroeb.net>\n>\n> Add a builtin helper for performing stash commands. Converting\n> all at once proved hard to review, so starting with just apply\n> lets conversion get started without the other commands being\n> finished.\n>\n> The helper is being implemented as a drop in replacement for\n> stash so that when it is complete it can simply be renamed and\n> the shell script deleted.\n>\n> Delete the contents of the apply_stash shell function and replace\n> it with a call to stash--helper apply until pop is also\n> converted.\n\nThis\n\n    GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all ./t3420-rebase-autostash.sh\n\nNow fails, which bisects to 8a0fc8d19d (\"stash: convert apply to\nbuiltin\", 2019-02-25).\n\nTested on both a CentOS 6 & modern Debian testing machine:\n\n    + git rebase -i --autostash HEAD^\n    Created autostash: 5cd734b\n    HEAD is now at 0c4d2f1 third commit\n    hint: Waiting for your editor to close the file...\n    error: There was a problem with the editor '\"$FAKE_EDITOR\"'.\n    Applied autostash.\n    + exit_code=1\n    + test 1 -eq 0\n    + test_match_signal 13 1\n    + test 1 = 141\n    + test 1 = 269\n    + return 1\n    + test 1 -gt 129\n    + test 1 -eq 127\n    + test 1 -eq 126\n    + return 0\n    + rm -f abort-editor.sh\n    + echo conflicting-content\n    + test_cmp expected file0\n    + diff -u expected file0\n    --- expected    2019-03-14 13:19:08.212215263 +0000\n    +++ file0       2019-03-14 13:19:08.196215250 +0000\n    @@ -1 +1 @@\n    -conflicting-content\n    +uncommitted-content\n    error: last command exited with $?=1\n    not ok 36 - autostash is saved on editor failure with conflict\n\nAre you able to reproduce this? And if so I suggest running the test\nsuite with some of the other GIT_TEST_* modes documented in\nt/README. Maybe it'll turn up something else...\n"},{"id":"371513","messageId":"nycvar.QRO.7.76.6.1903141600020.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"87ftrpbnkg.fsf@evledraar.gmail.com","subject":"Re: regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-14T15:20:46Z","receivedAt":"2019-03-14T15:21:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n\n> On Tue, Feb 26 2019, Thomas Gummerer wrote:\n> \n> > From: Joel Teichroeb <joel@teichroeb.net>\n> >\n> > Add a builtin helper for performing stash commands. Converting\n> > all at once proved hard to review, so starting with just apply\n> > lets conversion get started without the other commands being\n> > finished.\n> >\n> > The helper is being implemented as a drop in replacement for\n> > stash so that when it is complete it can simply be renamed and\n> > the shell script deleted.\n> >\n> > Delete the contents of the apply_stash shell function and replace\n> > it with a call to stash--helper apply until pop is also\n> > converted.\n> \n> This\n> \n>     GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all ./t3420-rebase-autostash.sh\n> \n> Now fails, which bisects to 8a0fc8d19d (\"stash: convert apply to\n> builtin\", 2019-02-25).\n> \n> Tested on both a CentOS 6 & modern Debian testing machine:\n> \n>     + git rebase -i --autostash HEAD^\n>     Created autostash: 5cd734b\n>     HEAD is now at 0c4d2f1 third commit\n>     hint: Waiting for your editor to close the file...\n>     error: There was a problem with the editor '\"$FAKE_EDITOR\"'.\n>     Applied autostash.\n>     + exit_code=1\n>     + test 1 -eq 0\n>     + test_match_signal 13 1\n>     + test 1 = 141\n>     + test 1 = 269\n>     + return 1\n>     + test 1 -gt 129\n>     + test 1 -eq 127\n>     + test 1 -eq 126\n>     + return 0\n>     + rm -f abort-editor.sh\n>     + echo conflicting-content\n>     + test_cmp expected file0\n>     + diff -u expected file0\n>     --- expected    2019-03-14 13:19:08.212215263 +0000\n>     +++ file0       2019-03-14 13:19:08.196215250 +0000\n>     @@ -1 +1 @@\n>     -conflicting-content\n>     +uncommitted-content\n>     error: last command exited with $?=1\n>     not ok 36 - autostash is saved on editor failure with conflict\n> \n> Are you able to reproduce this? And if so I suggest running the test\n> suite with some of the other GIT_TEST_* modes documented in\n> t/README. Maybe it'll turn up something else...\n\nYep, totally can reproduce it :-(\n\nI'll try to investigate a bit,\nDscho"},{"id":"371520","messageId":"87d0mtbh1w.fsf@evledraar.gmail.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1903141600020.41@tvgsbejvaqbjf.bet","subject":"Re: regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-03-14T15:40:27Z","receivedAt":"2019-03-14T15:40:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 14 2019, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Tue, Feb 26 2019, Thomas Gummerer wrote:\n>>\n>> > From: Joel Teichroeb <joel@teichroeb.net>\n>> >\n>> > Add a builtin helper for performing stash commands. Converting\n>> > all at once proved hard to review, so starting with just apply\n>> > lets conversion get started without the other commands being\n>> > finished.\n>> >\n>> > The helper is being implemented as a drop in replacement for\n>> > stash so that when it is complete it can simply be renamed and\n>> > the shell script deleted.\n>> >\n>> > Delete the contents of the apply_stash shell function and replace\n>> > it with a call to stash--helper apply until pop is also\n>> > converted.\n>>\n>> This\n>>\n>>     GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all ./t3420-rebase-autostash.sh\n>>\n>> Now fails, which bisects to 8a0fc8d19d (\"stash: convert apply to\n>> builtin\", 2019-02-25).\n>>\n>> Tested on both a CentOS 6 & modern Debian testing machine:\n>>\n>>     + git rebase -i --autostash HEAD^\n>>     Created autostash: 5cd734b\n>>     HEAD is now at 0c4d2f1 third commit\n>>     hint: Waiting for your editor to close the file...\n>>     error: There was a problem with the editor '\"$FAKE_EDITOR\"'.\n>>     Applied autostash.\n>>     + exit_code=1\n>>     + test 1 -eq 0\n>>     + test_match_signal 13 1\n>>     + test 1 = 141\n>>     + test 1 = 269\n>>     + return 1\n>>     + test 1 -gt 129\n>>     + test 1 -eq 127\n>>     + test 1 -eq 126\n>>     + return 0\n>>     + rm -f abort-editor.sh\n>>     + echo conflicting-content\n>>     + test_cmp expected file0\n>>     + diff -u expected file0\n>>     --- expected    2019-03-14 13:19:08.212215263 +0000\n>>     +++ file0       2019-03-14 13:19:08.196215250 +0000\n>>     @@ -1 +1 @@\n>>     -conflicting-content\n>>     +uncommitted-content\n>>     error: last command exited with $?=1\n>>     not ok 36 - autostash is saved on editor failure with conflict\n>>\n>> Are you able to reproduce this? And if so I suggest running the test\n>> suite with some of the other GIT_TEST_* modes documented in\n>> t/README. Maybe it'll turn up something else...\n>\n> Yep, totally can reproduce it :-(\n\nIn the meantime I did a build with \"next\" (so stash-in-C) using the\nstandard test mode and these:\n\n    (cd t && GIT_TEST_GETTEXT_POISON=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all GIT_SKIP_TESTS=\"t3404.8 t3420.36\" /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_SPLIT_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_FULL_IN_PACK_ARRAY=true GIT_TEST_OE_SIZE=10 /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_COMMIT_GRAPH=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_MULTI_PACK_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_STASH_USE_BUILTIN=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n    (cd t && GIT_TEST_CHECK_COLLISIONS=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n\nOnly this specific test failed.\n"},{"id":"371550","messageId":"nycvar.QRO.7.76.6.1903142058130.41@tvgsbejvaqbjf.bet","threadId":"50081","inReplyTo":"87d0mtbh1w.fsf@evledraar.gmail.com","subject":"Re: regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-03-14T22:45:09Z","receivedAt":"2019-03-14T22:45:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n\n> On Thu, Mar 14 2019, Johannes Schindelin wrote:\n> \n> > On Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n> >\n> >> On Tue, Feb 26 2019, Thomas Gummerer wrote:\n> >>\n> >> > From: Joel Teichroeb <joel@teichroeb.net>\n> >> >\n> >> > Add a builtin helper for performing stash commands. Converting\n> >> > all at once proved hard to review, so starting with just apply\n> >> > lets conversion get started without the other commands being\n> >> > finished.\n> >> >\n> >> > The helper is being implemented as a drop in replacement for\n> >> > stash so that when it is complete it can simply be renamed and\n> >> > the shell script deleted.\n> >> >\n> >> > Delete the contents of the apply_stash shell function and replace\n> >> > it with a call to stash--helper apply until pop is also\n> >> > converted.\n> >>\n> >> This\n> >>\n> >>     GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all ./t3420-rebase-autostash.sh\n> >>\n> >> Now fails, which bisects to 8a0fc8d19d (\"stash: convert apply to\n> >> builtin\", 2019-02-25).\n> >>\n> >> Tested on both a CentOS 6 & modern Debian testing machine:\n> >>\n> >>     + git rebase -i --autostash HEAD^\n> >>     Created autostash: 5cd734b\n> >>     HEAD is now at 0c4d2f1 third commit\n> >>     hint: Waiting for your editor to close the file...\n> >>     error: There was a problem with the editor '\"$FAKE_EDITOR\"'.\n> >>     Applied autostash.\n> >>     + exit_code=1\n> >>     + test 1 -eq 0\n> >>     + test_match_signal 13 1\n> >>     + test 1 = 141\n> >>     + test 1 = 269\n> >>     + return 1\n> >>     + test 1 -gt 129\n> >>     + test 1 -eq 127\n> >>     + test 1 -eq 126\n> >>     + return 0\n> >>     + rm -f abort-editor.sh\n> >>     + echo conflicting-content\n> >>     + test_cmp expected file0\n> >>     + diff -u expected file0\n> >>     --- expected    2019-03-14 13:19:08.212215263 +0000\n> >>     +++ file0       2019-03-14 13:19:08.196215250 +0000\n> >>     @@ -1 +1 @@\n> >>     -conflicting-content\n> >>     +uncommitted-content\n> >>     error: last command exited with $?=1\n> >>     not ok 36 - autostash is saved on editor failure with conflict\n> >>\n> >> Are you able to reproduce this? And if so I suggest running the test\n> >> suite with some of the other GIT_TEST_* modes documented in\n> >> t/README. Maybe it'll turn up something else...\n> >\n> > Yep, totally can reproduce it :-(\n\nWell, isn't this exciting: we found not a bug in the built-in stash (even\nif Junio probably expected yet another one), but an fsmonitor one! Even\nbetter, I think this might be the bug that Alex Vandiver was chasing and\nthat he talked about at the Contributors' Summit last year in Barcelona.\n\nThe symptom is that cache entries are sometimes considered up to date,\nwhen they really are not.\n\nAnd the reason is that the fsmonitor has this honking global flag\n`has_run_once` (it is not really global, it is `static` to\n`refresh_fsmonitor()`, but that's the same for all practical purposes, as\nit is *not* specific to one `struct index_state`), which was kind of okay\nas long as `the_index` was used implicitly by everything.\n\nExcept it was not okay when `discard_index()` (or `discard_cache()`) was\ncalled: in that case, the flag was not re-set. And re-set it needs to be,\nin that case, otherwise the fsmonitor is not asked which entries need to\nbe updated.\n\nI saw this pretty early on in my investigation and marked it up for a\nfollow-up task, wasting hours of investigation by not believing that this\ncould be the culprit of the bug you described. I did not believe it\nbecause `git stash apply` is *spawned*, so there is not even an index that\nneeds to be discarded (I thought; more on that one later).\n\nIt is quite curious that this is the only occasion that our test suite\ncovers that particular part of the fsmonitor...\n\nI do not really want to rely on implementation details of the rebase to verify\nthat the fsmonitor is queried again (and, crucially, resets the\nFSMONITOR_VALID flag of the file(s) indicated as out of date) after the index\nis discarded and re-read.\n\nI guess the best bet is to extend `t/helper/test-read-cache.c` to optionally\noutput information about a specific cache entry, then refresh it, and then run\nthat test helper with fsmonitor-all (which should mark anything as modified,\nall the time). That should verify that the fix works.\n\nI did exactly that, and pushed the result to https://github.com/dscho/git\nas `fix-fsmonitor` branch.\n\nCould I ask you to test that one (it is based off of Git for Windows'\n`master`, but that should compile cleanly for you)?\n\nFor now, I am a bit spent, so I'll leave the rest for tomorrow.\n\n> In the meantime I did a build with \"next\" (so stash-in-C) using the\n> standard test mode and these:\n> \n>     (cd t && GIT_TEST_GETTEXT_POISON=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all GIT_SKIP_TESTS=\"t3404.8 t3420.36\" /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_SPLIT_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_FULL_IN_PACK_ARRAY=true GIT_TEST_OE_SIZE=10 /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_COMMIT_GRAPH=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_MULTI_PACK_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_STASH_USE_BUILTIN=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>     (cd t && GIT_TEST_CHECK_COLLISIONS=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> \n> Only this specific test failed.\n\nWell, good!\n\nThank you for getting the ball rolling!\nDscho"},{"id":"371551","messageId":"878sxhauvh.fsf@evledraar.gmail.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1903142058130.41@tvgsbejvaqbjf.bet","subject":"Re: regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-03-14T23:39:30Z","receivedAt":"2019-03-14T23:39:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 14 2019, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n>\n>> On Thu, Mar 14 2019, Johannes Schindelin wrote:\n>>\n>> > On Thu, 14 Mar 2019, Ævar Arnfjörð Bjarmason wrote:\n>> >\n>> >> On Tue, Feb 26 2019, Thomas Gummerer wrote:\n>> >>\n>> >> > From: Joel Teichroeb <joel@teichroeb.net>\n>> >> >\n>> >> > Add a builtin helper for performing stash commands. Converting\n>> >> > all at once proved hard to review, so starting with just apply\n>> >> > lets conversion get started without the other commands being\n>> >> > finished.\n>> >> >\n>> >> > The helper is being implemented as a drop in replacement for\n>> >> > stash so that when it is complete it can simply be renamed and\n>> >> > the shell script deleted.\n>> >> >\n>> >> > Delete the contents of the apply_stash shell function and replace\n>> >> > it with a call to stash--helper apply until pop is also\n>> >> > converted.\n>> >>\n>> >> This\n>> >>\n>> >>     GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all ./t3420-rebase-autostash.sh\n>> >>\n>> >> Now fails, which bisects to 8a0fc8d19d (\"stash: convert apply to\n>> >> builtin\", 2019-02-25).\n>> >>\n>> >> Tested on both a CentOS 6 & modern Debian testing machine:\n>> >>\n>> >>     + git rebase -i --autostash HEAD^\n>> >>     Created autostash: 5cd734b\n>> >>     HEAD is now at 0c4d2f1 third commit\n>> >>     hint: Waiting for your editor to close the file...\n>> >>     error: There was a problem with the editor '\"$FAKE_EDITOR\"'.\n>> >>     Applied autostash.\n>> >>     + exit_code=1\n>> >>     + test 1 -eq 0\n>> >>     + test_match_signal 13 1\n>> >>     + test 1 = 141\n>> >>     + test 1 = 269\n>> >>     + return 1\n>> >>     + test 1 -gt 129\n>> >>     + test 1 -eq 127\n>> >>     + test 1 -eq 126\n>> >>     + return 0\n>> >>     + rm -f abort-editor.sh\n>> >>     + echo conflicting-content\n>> >>     + test_cmp expected file0\n>> >>     + diff -u expected file0\n>> >>     --- expected    2019-03-14 13:19:08.212215263 +0000\n>> >>     +++ file0       2019-03-14 13:19:08.196215250 +0000\n>> >>     @@ -1 +1 @@\n>> >>     -conflicting-content\n>> >>     +uncommitted-content\n>> >>     error: last command exited with $?=1\n>> >>     not ok 36 - autostash is saved on editor failure with conflict\n>> >>\n>> >> Are you able to reproduce this? And if so I suggest running the test\n>> >> suite with some of the other GIT_TEST_* modes documented in\n>> >> t/README. Maybe it'll turn up something else...\n>> >\n>> > Yep, totally can reproduce it :-(\n>\n> Well, isn't this exciting: we found not a bug in the built-in stash (even\n> if Junio probably expected yet another one), but an fsmonitor one! Even\n> better, I think this might be the bug that Alex Vandiver was chasing and\n> that he talked about at the Contributors' Summit last year in Barcelona.\n>\n> The symptom is that cache entries are sometimes considered up to date,\n> when they really are not.\n>\n> And the reason is that the fsmonitor has this honking global flag\n> `has_run_once` (it is not really global, it is `static` to\n> `refresh_fsmonitor()`, but that's the same for all practical purposes, as\n> it is *not* specific to one `struct index_state`), which was kind of okay\n> as long as `the_index` was used implicitly by everything.\n>\n> Except it was not okay when `discard_index()` (or `discard_cache()`) was\n> called: in that case, the flag was not re-set. And re-set it needs to be,\n> in that case, otherwise the fsmonitor is not asked which entries need to\n> be updated.\n>\n> I saw this pretty early on in my investigation and marked it up for a\n> follow-up task, wasting hours of investigation by not believing that this\n> could be the culprit of the bug you described. I did not believe it\n> because `git stash apply` is *spawned*, so there is not even an index that\n> needs to be discarded (I thought; more on that one later).\n>\n> It is quite curious that this is the only occasion that our test suite\n> covers that particular part of the fsmonitor...\n>\n> I do not really want to rely on implementation details of the rebase to verify\n> that the fsmonitor is queried again (and, crucially, resets the\n> FSMONITOR_VALID flag of the file(s) indicated as out of date) after the index\n> is discarded and re-read.\n>\n> I guess the best bet is to extend `t/helper/test-read-cache.c` to optionally\n> output information about a specific cache entry, then refresh it, and then run\n> that test helper with fsmonitor-all (which should mark anything as modified,\n> all the time). That should verify that the fix works.\n>\n> I did exactly that, and pushed the result to https://github.com/dscho/git\n> as `fix-fsmonitor` branch.\n>\n> Could I ask you to test that one (it is based off of Git for Windows'\n> `master`, but that should compile cleanly for you)?\n>\n> For now, I am a bit spent, so I'll leave the rest for tomorrow.\n\nIt fixes not just this issue, but now the whole test suite passes with\nGIT_TEST_FSMONITOR, i.e. this test that's been failing for ~2 years also\nworks now:\nhttps://public-inbox.org/git/87k1vwn9qe.fsf@evledraar.gmail.com/\n\n>> In the meantime I did a build with \"next\" (so stash-in-C) using the\n>> standard test mode and these:\n>>\n>>     (cd t && GIT_TEST_GETTEXT_POISON=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all GIT_SKIP_TESTS=\"t3404.8 t3420.36\" /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_SPLIT_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_FULL_IN_PACK_ARRAY=true GIT_TEST_OE_SIZE=10 /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_COMMIT_GRAPH=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_MULTI_PACK_INDEX=true /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_STASH_USE_BUILTIN=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>     (cd t && GIT_TEST_CHECK_COLLISIONS=false /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n>>\n>> Only this specific test failed.\n>\n> Well, good!\n>\n> Thank you for getting the ball rolling!\n> Dscho\n"},{"id":"371555","messageId":"BL0PR2101MB091483F7B267CD57A37499B6F4440@BL0PR2101MB0914.namprd21.prod.outlook.com","threadId":"50081","inReplyTo":"878sxhauvh.fsf@evledraar.gmail.com","subject":"RE: regression in new built-in stash + fsmonitor (was Re: [PATCH v13 11/27] stash: convert apply to builtin)","fromName":"Ben Peart","fromEmail":"ben.peart@microsoft.com","sentAt":"2019-03-15T02:23:39Z","receivedAt":"2019-03-15T02:23:44Z","isPatch":true,"sender":{"key":"benpeart@microsoft.com","avatar":"https://avatars.githubusercontent.com/u/15252029?v=4"},"body":"> It fixes not just this issue, but now the whole test suite passes with\n> GIT_TEST_FSMONITOR, i.e. this test that's been failing for ~2 years also\n> works now:\n> https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpubli\n> c-\n> inbox.org%2Fgit%2F87k1vwn9qe.fsf%40evledraar.gmail.com%2F&amp;data=\n> 02%7C01%7CBen.Peart%40microsoft.com%7C758f6272cb9741291c0208d6a8\n> d650b4%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6368820357\n> 67290383&amp;sdata=H4J7xguBJVxanMfn4y0I0HsXBNYcrMS9IrwHn3aWwO\n> U%3D&amp;reserved=0\n> \n> >> In the meantime I did a build with \"next\" (so stash-in-C) using the\n> >> standard test mode and these:\n> >>\n> >>     (cd t && GIT_TEST_GETTEXT_POISON=true /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all\n> GIT_SKIP_TESTS=\"t3404.8 t3420.36\" /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_SPLIT_INDEX=true /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_FULL_IN_PACK_ARRAY=true GIT_TEST_OE_SIZE=10\n> /usr/bin/prove $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_COMMIT_GRAPH=true /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_MULTI_PACK_INDEX=true /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_STASH_USE_BUILTIN=false /usr/bin/prove\n> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>     (cd t && GIT_TEST_CHECK_COLLISIONS=false /usr/bin/prove\n> >> $BKNG_GIT_HARNESS_OPTIONS t[0-9]*.sh)\n> >>\n> >> Only this specific test failed.\n> >\n> > Well, good!\n> >\n> > Thank you for getting the ball rolling!\n\nAwesome find and fix.  Thanks for chasing this down and CC'ing me so I am aware.\n\n> > Dscho\n"},{"id":"371632","messageId":"20190315223320.GH16414@hank.intra.tgummerer.com","threadId":"50081","inReplyTo":"nycvar.QRO.7.76.6.1903132311200.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v2] stash: pass pathspec as pointer","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2019-03-15T22:33:20Z","receivedAt":"2019-03-15T22:33:25Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 03/13, Johannes Schindelin wrote:\n> Hi Thomas,\n> \n> On Tue, 12 Mar 2019, Thomas Gummerer wrote:\n> \n> > On 03/12, Johannes Schindelin wrote:\n> > > However, we would not have needed to move the initialization of\n> > > `rev.prune_data`, I don't think, because `init_revision()` zeros the\n> > > entire struct, including `prune_data`, which would have made\n> > > `clear_pathspec()` safe to call, too.\n> > \n> > 'clear_pathspec()' doesn't actually check whether the parameter passed\n> > to it is NULL or not before dereferencing it.\n> \n> In this case, it does not need to check for NULL, as `&rev.prune_data`\n> will always be non-NULL: `rev`'s `prune_data` field is of type `struct\n> patchspec`, i.e. *not* a pointer (in which case the type would be `struct\n> pathspec *`). See for yourself:\n> \n> \thttps://github.com/git/git/blob/v2.21.0/revision.h#L91\n\nDoh, you're right of course, I totally missed that.  Thanks for the\npointer, and sorry for the noise!\n"}]}