* [PATCH] stash: expose untracked modes in create
@ 2026-09-29 7:42 Kazumasa Shigeta
2026-09-29 16:08 ` Phillip Wood
2026-10-01 4:21 ` [PATCH v2] " Kazumasa Shigeta
0 siblings, 2 replies; 20+ messages in thread
From: Kazumasa Shigeta @ 2026-09-29 7:42 UTC (permalink / raw)
To: git; +Cc: Shabbir Bhojani, Phillip Wood
`git stash create` always passes zero for the include_untracked parameter
of do_create_stash(), even though that helper already supports untracked
and ignored files and stash push/save expose those modes as
-u/--include-untracked and -a/--all.
Teach create to accept the same options and pass the existing mode
through. Unlike push/save, create continues to only create objects: it
does not update refs/stash or modify the index or working tree.
When the selected mode finds no changes, do_create_stash() returns 1.
Translate that to success so create keeps its existing no-object, empty
output behavior.
Use normal parse-options semantics, so options may appear after message
arguments. A message that begins with a dash can be disambiguated with
--.
9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
internal include-untracked path while intentionally leaving the user
interface for "git stash create" unchanged. Reuse that machinery and
the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
creation path.
Add coverage for both short and long aliases, the untracked/ignored
boundary, option/message parsing, no-change behavior, and preservation
of refs/stash, the index, and the working tree.
Signed-off-by: Kazumasa Shigeta <kazumasa.shigeta@kanamei.com>
---
Related work:
I proposed adding both --include-untracked and --all to
"git stash create" in 2014:
<1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp>
I should also apologize for dropping that thread after receiving review.
I did not follow up on the comments at the time. Thanks to those who
reviewed it then.
Separately, in 2017, Thomas Gummerer added an internal -u path while
refactoring stash_create in 9ca6326dff29 (stash: refactor stash_create).
That change explicitly kept the user interface of "git stash create"
unchanged.
When "stash create" was later converted to the builtin C implementation
in d4788af875cc (stash: convert create to builtin), the untracked-file
handling was carried into the new implementation and remains there today.
More recently, Shabbir Bhojani proposed exposing --include-untracked:
<pull.1892.git.1774768580147.gitgitgadget@gmail.com>
This patch exposes both existing untracked modes, --include-untracked and
--all, to "git stash create".
Documentation/git-stash.adoc | 18 ++++++----
builtin/stash.c | 36 ++++++++++++++-----
t/t3903-stash.sh | 70 ++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+), 15 deletions(-)
diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
index fc6a9a0..32f0fd5 100644
--- a/Documentation/git-stash.adoc
+++ b/Documentation/git-stash.adoc
@@ -21,7 +21,7 @@ git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | -
git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
[-u | --include-untracked] [-a | --all] [<message>]
git stash clear
-git stash create [<message>]
+git stash create [-u | --include-untracked] [-a | --all] [<message>]
git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
git stash export (--print | --to-ref <ref>) [<stash>...]
git stash import <commit>
@@ -138,10 +138,12 @@ with no conflicts.
`drop [-q | --quiet] [<stash>]`::
Remove a single stash entry from the list of stash entries.
-`create`::
+`create [-u | --include-untracked] [-a | --all]`::
Create a stash entry (which is a regular commit object) and
return its object name, without storing it anywhere in the ref
- namespace.
+ namespace. The `--include-untracked` option includes untracked
+ files, while `--all` also includes ignored files, without modifying
+ the working tree.
This is intended to be useful for scripts. It is probably not
the command you want to use; see "push" above.
@@ -167,10 +169,11 @@ OPTIONS
-------
`-a`::
`--all`::
- This option is only valid for `push` and `save` commands.
+ When used with the `push` and `save` commands, all ignored and
+ untracked files are also stashed and then cleaned up with `git clean`.
+
-All ignored and untracked files are also stashed and then cleaned
-up with `git clean`.
+When used with the `create` command, ignored and untracked files are included
+in the stash entry without modifying the working tree.
`-u`::
`--include-untracked`::
@@ -179,6 +182,9 @@ up with `git clean`.
all untracked files are also stashed and then cleaned up with
`git clean`.
+
+When used with the `create` command, untracked files are included in the
+stash entry without modifying the working tree.
++
When used with the `show` command, show the untracked files in the stash
entry as part of the diff.
diff --git a/builtin/stash.c b/builtin/stash.c
index 7a98434..57a4750 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -59,7 +59,7 @@
N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
" [-u | --include-untracked] [-a | --all] [<message>]")
#define BUILTIN_STASH_CREATE_USAGE \
- N_("git stash create [<message>]")
+ N_("git stash create [-u | --include-untracked] [-a | --all] [<message>]")
#define BUILTIN_STASH_EXPORT_USAGE \
N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
#define BUILTIN_STASH_IMPORT_USAGE \
@@ -119,6 +119,11 @@ static const char * const git_stash_clear_usage[] = {
NULL
};
+static const char * const git_stash_create_usage[] = {
+ BUILTIN_STASH_CREATE_USAGE,
+ NULL
+};
+
static const char * const git_stash_store_usage[] = {
BUILTIN_STASH_STORE_USAGE,
NULL
@@ -1643,26 +1648,39 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
return ret;
}
-static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
+static int create_stash(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
- int ret;
+ int ret = 0;
+ int include_untracked = 0;
+ struct option options[] = {
+ OPT_BOOL('u', "include-untracked", &include_untracked,
+ N_("include untracked files in stash")),
+ OPT_SET_INT('a', "all", &include_untracked,
+ N_("include ignored files in stash"),
+ INCLUDE_ALL_FILES),
+ OPT_END()
+ };
struct strbuf stash_msg_buf = STRBUF_INIT;
struct stash_info info = STASH_INFO_INIT;
struct pathspec ps;
- /* Starting with argv[1], since argv[0] is "create" */
- strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_create_usage, 0);
+ strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
memset(&ps, 0, sizeof(ps));
- if (!check_changes_tracked_files(&ps))
- return 0;
+ if (!include_untracked && !check_changes_tracked_files(&ps))
+ goto done;
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
- NULL, 0);
+ ret = do_create_stash(&ps, &stash_msg_buf, include_untracked, 0, NULL,
+ 0, &info, NULL, 0);
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
+ else if (ret == 1)
+ ret = 0;
+done:
free_stash_info(&info);
strbuf_release(&stash_msg_buf);
return ret;
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 7211586..fe34879 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -640,6 +640,76 @@ test_expect_success 'stash create - no changes' '
test_must_be_empty actual
'
+# --all observes every untracked and ignored path in the worktree. Use one
+# isolated repository for these checks so unrelated test state is not captured.
+test_expect_success 'stash create with untracked options' '
+ test_when_finished "rm -rf stash-create-options" &&
+ test_create_repo stash-create-options &&
+ (
+ cd stash-create-options &&
+ test_commit base tracked base &&
+ echo create-ignored >.gitignore &&
+ git add .gitignore &&
+ git commit -m ignore &&
+
+ git stash create -u >.git/actual &&
+ test_must_be_empty .git/actual &&
+ git stash create -a >.git/actual &&
+ test_must_be_empty .git/actual &&
+
+ echo untracked >create-untracked &&
+ git stash create "without untracked" >.git/actual &&
+ test_must_be_empty .git/actual &&
+ short=$(git stash create "create untracked" -u) &&
+ long=$(git stash create --include-untracked "create untracked") &&
+ test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
+ echo untracked >.git/expect &&
+ git show "$short^3:create-untracked" >.git/actual &&
+ test_cmp .git/expect .git/actual &&
+ branch=$(git symbolic-ref --short HEAD) &&
+ echo "On $branch: create untracked" >.git/expect &&
+ git show --pretty=%s -s "$short" >.git/actual &&
+ test_cmp .git/expect .git/actual &&
+ test_path_is_file create-untracked &&
+
+ echo ignored >create-ignored &&
+ with_untracked=$(git stash create -u "create options") &&
+ test_must_fail git cat-file -e "$with_untracked^3:create-ignored" &&
+ short=$(git stash create "create options" -a) &&
+ long=$(git stash create --all "create options") &&
+ test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
+ echo ignored >.git/expect &&
+ git show "$short^3:create-ignored" >.git/actual &&
+ test_cmp .git/expect .git/actual &&
+ test_path_is_file create-untracked &&
+ test_path_is_file create-ignored &&
+
+ echo staged >staged &&
+ git add staged &&
+ echo modified >>tracked &&
+ git diff >.git/before-worktree &&
+ git diff --cached >.git/before-index &&
+ git status --porcelain=v1 --ignored >.git/before-status &&
+ test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
+ STASH_ID=$(git stash create -a -- -create-message) &&
+ git diff >.git/after-worktree &&
+ git diff --cached >.git/after-index &&
+ git status --porcelain=v1 --ignored >.git/after-status &&
+ test_cmp .git/before-worktree .git/after-worktree &&
+ test_cmp .git/before-index .git/after-index &&
+ test_cmp .git/before-status .git/after-status &&
+ test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
+ echo "On $branch: -create-message" >.git/expect &&
+ git show --pretty=%s -s "$STASH_ID" >.git/actual &&
+ test_cmp .git/expect .git/actual
+ )
+'
+
+test_expect_success 'stash create rejects unknown options' '
+ test_expect_code 129 git stash create --unknown-option 2>err &&
+ test_grep "unknown option" err
+'
+
test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH] stash: expose untracked modes in create
2026-09-29 7:42 [PATCH] stash: expose untracked modes in create Kazumasa Shigeta
@ 2026-09-29 16:08 ` Phillip Wood
2026-10-01 17:44 ` 重田一聖
2026-10-01 4:21 ` [PATCH v2] " Kazumasa Shigeta
1 sibling, 1 reply; 20+ messages in thread
From: Phillip Wood @ 2026-09-29 16:08 UTC (permalink / raw)
To: Kazumasa Shigeta, git; +Cc: Shabbir Bhojani, Phillip Wood
Hi Kazumasa
On 29/09/2026 08:42, Kazumasa Shigeta wrote:
> `git stash create` always passes zero for the include_untracked parameter
> of do_create_stash(), even though that helper already supports untracked
> and ignored files and stash push/save expose those modes as
> -u/--include-untracked and -a/--all.
>
> Teach create to accept the same options and pass the existing mode
> through. Unlike push/save, create continues to only create objects: it
> does not update refs/stash or modify the index or working tree.
>
> When the selected mode finds no changes, do_create_stash() returns 1.
> Translate that to success so create keeps its existing no-object, empty
> output behavior.
This doesn't seem to match the code changes. The code that prints the
object id when the stash is successfully created is unchanged, as far as
I can see what this patch does is change the exit status for "git stash
create" when there are no changes to stash. Instead of exiting 1, it
exits 0 even though it does not create a stash. That does not seem like
a good idea.
> Use normal parse-options semantics, so options may appear after message
> arguments. A message that begins with a dash can be disambiguated with
As "git stash create" concatenates excess arguments to use as the stash
message we should not be permuting options. "git stash create handle new
-u flag" should continue to create a stash with the message "handle new
-u flag" - it should not start stashing untracked files. You should pass
PARSE_OPT_STOP_AT_NON_OPTION to parse_options() to prevent that.
> I proposed adding both --include-untracked and --all to
> "git stash create" in 2014:
> <1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp>
>
> I should also apologize for dropping that thread after receiving review.
> I did not follow up on the comments at the time. Thanks to those who
> reviewed it then.
Better late than never! I think the idea is fine, but the implementation
could do with a couple of tweaks so it is as backward compatible as
possible.
Thanks
Phillip
> Separately, in 2017, Thomas Gummerer added an internal -u path while
> refactoring stash_create in 9ca6326dff29 (stash: refactor stash_create).
> That change explicitly kept the user interface of "git stash create"
> unchanged.
>
> When "stash create" was later converted to the builtin C implementation
> in d4788af875cc (stash: convert create to builtin), the untracked-file
> handling was carried into the new implementation and remains there today.
>
> More recently, Shabbir Bhojani proposed exposing --include-untracked:
> <pull.1892.git.1774768580147.gitgitgadget@gmail.com>
>
> This patch exposes both existing untracked modes, --include-untracked and
> --all, to "git stash create".
>
> Documentation/git-stash.adoc | 18 ++++++----
> builtin/stash.c | 36 ++++++++++++++-----
> t/t3903-stash.sh | 70 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 109 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
> index fc6a9a0..32f0fd5 100644
> --- a/Documentation/git-stash.adoc
> +++ b/Documentation/git-stash.adoc
> @@ -21,7 +21,7 @@ git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | -
> git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
> [-u | --include-untracked] [-a | --all] [<message>]
> git stash clear
> -git stash create [<message>]
> +git stash create [-u | --include-untracked] [-a | --all] [<message>]
> git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
> git stash export (--print | --to-ref <ref>) [<stash>...]
> git stash import <commit>
> @@ -138,10 +138,12 @@ with no conflicts.
> `drop [-q | --quiet] [<stash>]`::
> Remove a single stash entry from the list of stash entries.
>
> -`create`::
> +`create [-u | --include-untracked] [-a | --all]`::
> Create a stash entry (which is a regular commit object) and
> return its object name, without storing it anywhere in the ref
> - namespace.
> + namespace. The `--include-untracked` option includes untracked
> + files, while `--all` also includes ignored files, without modifying
> + the working tree.
> This is intended to be useful for scripts. It is probably not
> the command you want to use; see "push" above.
>
> @@ -167,10 +169,11 @@ OPTIONS
> -------
> `-a`::
> `--all`::
> - This option is only valid for `push` and `save` commands.
> + When used with the `push` and `save` commands, all ignored and
> + untracked files are also stashed and then cleaned up with `git clean`.
> +
> -All ignored and untracked files are also stashed and then cleaned
> -up with `git clean`.
> +When used with the `create` command, ignored and untracked files are included
> +in the stash entry without modifying the working tree.
>
> `-u`::
> `--include-untracked`::
> @@ -179,6 +182,9 @@ up with `git clean`.
> all untracked files are also stashed and then cleaned up with
> `git clean`.
> +
> +When used with the `create` command, untracked files are included in the
> +stash entry without modifying the working tree.
> ++
> When used with the `show` command, show the untracked files in the stash
> entry as part of the diff.
>
> diff --git a/builtin/stash.c b/builtin/stash.c
> index 7a98434..57a4750 100644
> --- a/builtin/stash.c
> +++ b/builtin/stash.c
> @@ -59,7 +59,7 @@
> N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
> " [-u | --include-untracked] [-a | --all] [<message>]")
> #define BUILTIN_STASH_CREATE_USAGE \
> - N_("git stash create [<message>]")
> + N_("git stash create [-u | --include-untracked] [-a | --all] [<message>]")
> #define BUILTIN_STASH_EXPORT_USAGE \
> N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
> #define BUILTIN_STASH_IMPORT_USAGE \
> @@ -119,6 +119,11 @@ static const char * const git_stash_clear_usage[] = {
> NULL
> };
>
> +static const char * const git_stash_create_usage[] = {
> + BUILTIN_STASH_CREATE_USAGE,
> + NULL
> +};
> +
> static const char * const git_stash_store_usage[] = {
> BUILTIN_STASH_STORE_USAGE,
> NULL
> @@ -1643,26 +1648,39 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
> return ret;
> }
>
> -static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
> +static int create_stash(int argc, const char **argv, const char *prefix,
> struct repository *repo UNUSED)
> {
> - int ret;
> + int ret = 0;
> + int include_untracked = 0;
> + struct option options[] = {
> + OPT_BOOL('u', "include-untracked", &include_untracked,
> + N_("include untracked files in stash")),
> + OPT_SET_INT('a', "all", &include_untracked,
> + N_("include ignored files in stash"),
> + INCLUDE_ALL_FILES),
> + OPT_END()
> + };
> struct strbuf stash_msg_buf = STRBUF_INIT;
> struct stash_info info = STASH_INFO_INIT;
> struct pathspec ps;
>
> - /* Starting with argv[1], since argv[0] is "create" */
> - strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
> + argc = parse_options(argc, argv, prefix, options,
> + git_stash_create_usage, 0);
> + strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
>
> memset(&ps, 0, sizeof(ps));
> - if (!check_changes_tracked_files(&ps))
> - return 0;
> + if (!include_untracked && !check_changes_tracked_files(&ps))
> + goto done;
>
> - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
> - NULL, 0);
> + ret = do_create_stash(&ps, &stash_msg_buf, include_untracked, 0, NULL,
> + 0, &info, NULL, 0);
> if (!ret)
> printf_ln("%s", oid_to_hex(&info.w_commit));
> + else if (ret == 1)
> + ret = 0;
>
> +done:
> free_stash_info(&info);
> strbuf_release(&stash_msg_buf);
> return ret;
> diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
> index 7211586..fe34879 100755
> --- a/t/t3903-stash.sh
> +++ b/t/t3903-stash.sh
> @@ -640,6 +640,76 @@ test_expect_success 'stash create - no changes' '
> test_must_be_empty actual
> '
>
> +# --all observes every untracked and ignored path in the worktree. Use one
> +# isolated repository for these checks so unrelated test state is not captured.
> +test_expect_success 'stash create with untracked options' '
> + test_when_finished "rm -rf stash-create-options" &&
> + test_create_repo stash-create-options &&
> + (
> + cd stash-create-options &&
> + test_commit base tracked base &&
> + echo create-ignored >.gitignore &&
> + git add .gitignore &&
> + git commit -m ignore &&
> +
> + git stash create -u >.git/actual &&
> + test_must_be_empty .git/actual &&
> + git stash create -a >.git/actual &&
> + test_must_be_empty .git/actual &&
> +
> + echo untracked >create-untracked &&
> + git stash create "without untracked" >.git/actual &&
> + test_must_be_empty .git/actual &&
> + short=$(git stash create "create untracked" -u) &&
> + long=$(git stash create --include-untracked "create untracked") &&
> + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
> + echo untracked >.git/expect &&
> + git show "$short^3:create-untracked" >.git/actual &&
> + test_cmp .git/expect .git/actual &&
> + branch=$(git symbolic-ref --short HEAD) &&
> + echo "On $branch: create untracked" >.git/expect &&
> + git show --pretty=%s -s "$short" >.git/actual &&
> + test_cmp .git/expect .git/actual &&
> + test_path_is_file create-untracked &&
> +
> + echo ignored >create-ignored &&
> + with_untracked=$(git stash create -u "create options") &&
> + test_must_fail git cat-file -e "$with_untracked^3:create-ignored" &&
> + short=$(git stash create "create options" -a) &&
> + long=$(git stash create --all "create options") &&
> + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
> + echo ignored >.git/expect &&
> + git show "$short^3:create-ignored" >.git/actual &&
> + test_cmp .git/expect .git/actual &&
> + test_path_is_file create-untracked &&
> + test_path_is_file create-ignored &&
> +
> + echo staged >staged &&
> + git add staged &&
> + echo modified >>tracked &&
> + git diff >.git/before-worktree &&
> + git diff --cached >.git/before-index &&
> + git status --porcelain=v1 --ignored >.git/before-status &&
> + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
> + STASH_ID=$(git stash create -a -- -create-message) &&
> + git diff >.git/after-worktree &&
> + git diff --cached >.git/after-index &&
> + git status --porcelain=v1 --ignored >.git/after-status &&
> + test_cmp .git/before-worktree .git/after-worktree &&
> + test_cmp .git/before-index .git/after-index &&
> + test_cmp .git/before-status .git/after-status &&
> + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
> + echo "On $branch: -create-message" >.git/expect &&
> + git show --pretty=%s -s "$STASH_ID" >.git/actual &&
> + test_cmp .git/expect .git/actual
> + )
> +'
> +
> +test_expect_success 'stash create rejects unknown options' '
> + test_expect_code 129 git stash create --unknown-option 2>err &&
> + test_grep "unknown option" err
> +'
> +
> test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
> git stash clear &&
> test_when_finished "git reset --hard HEAD" &&
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2] stash: expose untracked modes in create
2026-09-29 7:42 [PATCH] stash: expose untracked modes in create Kazumasa Shigeta
2026-09-29 16:08 ` Phillip Wood
@ 2026-10-01 4:21 ` Kazumasa Shigeta
2026-10-01 11:32 ` Patrick Steinhardt
2026-10-01 17:03 ` Junio C Hamano
1 sibling, 2 replies; 20+ messages in thread
From: Kazumasa Shigeta @ 2026-10-01 4:21 UTC (permalink / raw)
To: git; +Cc: Shabbir Bhojani, Phillip Wood, Kazumasa Shigeta
`git stash create` always passes zero for the include_untracked parameter
of do_create_stash(), even though that helper already supports untracked
and ignored files and stash push/save expose those modes as
-u/--include-untracked and -a/--all.
Teach create to accept the same options and pass the existing mode
through. Unlike push/save, create continues to only create objects: it
does not update refs/stash, reset the index, or clean the working tree.
Use parse_options() for the new options and stop parsing at the first
non-option message word. This keeps option-like tokens after the message
as message text, while leading option-like arguments now follow Git's
normal option parsing. In particular, unknown or malformed leading
options are rejected instead of silently becoming a message, short
options may be combined, and `--` can be used when a message itself
begins with a dash.
Keep create's existing no-change behavior: detect the usual no-change
case before do_create_stash() refreshes and writes the index, and return
success without printing an object name. If do_create_stash() still
reports its internal "nothing to create" result, map that to create's
public success status.
This follows the stash subcommand exit-status convention established by
786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
subcommands return 0 on success, negative values on failure, and status 1
when applying a stash results in conflicts. cmd_stash() maps negative
subcommand failures to 128.
9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
internal include-untracked path while intentionally leaving the user
interface for "git stash create" unchanged. Reuse that machinery and
the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
creation path.
Add coverage for short and long aliases, combined short options, the
untracked/ignored boundary including an ignored-only worktree, option
parsing and dash-leading messages, no-change behavior, and preservation
of refs/stash, the index state, and the working tree.
Signed-off-by: Kazumasa Shigeta <kazumasa.shigeta@kanamei.com>
---
Documentation/git-stash.adoc | 19 ++++++---
builtin/stash.c | 48 +++++++++++++++++----
t/t3903-stash.sh | 83 ++++++++++++++++++++++++++++++++++++
3 files changed, 135 insertions(+), 15 deletions(-)
diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
index fc6a9a0..d343a75 100644
--- a/Documentation/git-stash.adoc
+++ b/Documentation/git-stash.adoc
@@ -21,7 +21,7 @@ git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | -
git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
[-u | --include-untracked] [-a | --all] [<message>]
git stash clear
-git stash create [<message>]
+git stash create [-u | --include-untracked] [-a | --all] [--] [<message>]
git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
git stash export (--print | --to-ref <ref>) [<stash>...]
git stash import <commit>
@@ -138,10 +138,13 @@ with no conflicts.
`drop [-q | --quiet] [<stash>]`::
Remove a single stash entry from the list of stash entries.
-`create`::
+`create [-u | --include-untracked] [-a | --all] [--]`::
Create a stash entry (which is a regular commit object) and
return its object name, without storing it anywhere in the ref
- namespace.
+ namespace. The `--include-untracked` option includes untracked
+ files, while `--all` also includes ignored files, without modifying
+ the working tree. If `<message>` begins with a dash, use `--` to
+ separate it from the options.
This is intended to be useful for scripts. It is probably not
the command you want to use; see "push" above.
@@ -167,10 +170,11 @@ OPTIONS
-------
`-a`::
`--all`::
- This option is only valid for `push` and `save` commands.
+ When used with the `push` and `save` commands, all ignored and
+ untracked files are also stashed and then cleaned up with `git clean`.
+
-All ignored and untracked files are also stashed and then cleaned
-up with `git clean`.
+When used with the `create` command, ignored and untracked files are included
+in the stash entry without modifying the working tree.
`-u`::
`--include-untracked`::
@@ -179,6 +183,9 @@ up with `git clean`.
all untracked files are also stashed and then cleaned up with
`git clean`.
+
+When used with the `create` command, untracked files are included in the
+stash entry without modifying the working tree.
++
When used with the `show` command, show the untracked files in the stash
entry as part of the diff.
diff --git a/builtin/stash.c b/builtin/stash.c
index 7a98434..ec2b5e7 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -59,7 +59,7 @@
N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
" [-u | --include-untracked] [-a | --all] [<message>]")
#define BUILTIN_STASH_CREATE_USAGE \
- N_("git stash create [<message>]")
+ N_("git stash create [-u | --include-untracked] [-a | --all] [--] [<message>]")
#define BUILTIN_STASH_EXPORT_USAGE \
N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
#define BUILTIN_STASH_IMPORT_USAGE \
@@ -119,6 +119,11 @@ static const char * const git_stash_clear_usage[] = {
NULL
};
+static const char * const git_stash_create_usage[] = {
+ BUILTIN_STASH_CREATE_USAGE,
+ NULL
+};
+
static const char * const git_stash_store_usage[] = {
BUILTIN_STASH_STORE_USAGE,
NULL
@@ -1643,26 +1648,51 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
return ret;
}
-static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
+static int create_stash(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
- int ret;
+ int ret = 0;
+ int include_untracked = 0;
+ struct option options[] = {
+ OPT_BOOL('u', "include-untracked", &include_untracked,
+ N_("include untracked files in stash")),
+ OPT_SET_INT('a', "all", &include_untracked,
+ N_("include ignored files in stash"),
+ INCLUDE_ALL_FILES),
+ OPT_END()
+ };
struct strbuf stash_msg_buf = STRBUF_INIT;
+ struct strbuf untracked_files = STRBUF_INIT;
struct stash_info info = STASH_INFO_INIT;
struct pathspec ps;
- /* Starting with argv[1], since argv[0] is "create" */
- strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_create_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
memset(&ps, 0, sizeof(ps));
- if (!check_changes_tracked_files(&ps))
- return 0;
+ /*
+ * Preserve "stash create"'s successful no-change behavior before
+ * do_create_stash() refreshes and writes the index.
+ */
+ if (!check_changes(&ps, include_untracked, &untracked_files))
+ goto done;
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
- NULL, 0);
+ ret = do_create_stash(&ps, &stash_msg_buf, include_untracked, 0, NULL,
+ 0, &info, NULL, 0);
+ /*
+ * Status 1 is reserved for conflicts when applying a stash.
+ * do_create_stash() uses it internally for "nothing to create", so
+ * translate that sentinel to create's public success status.
+ */
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
+ else if (ret == 1)
+ ret = 0;
+done:
+ strbuf_release(&untracked_files);
free_stash_info(&info);
strbuf_release(&stash_msg_buf);
return ret;
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 7211586..1f660ca 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -1179,6 +1179,89 @@ test_expect_success 'create with multiple arguments for the message' '
test_cmp expect actual
'
+test_expect_success 'create with untracked options' '
+ test_when_finished "rm -rf create-options" &&
+ git init create-options &&
+ test_commit -C create-options base tracked base &&
+ test_commit -C create-options ignore .gitignore ignored &&
+
+ git -C create-options stash create -u >actual &&
+ test_must_be_empty actual &&
+ git -C create-options stash create -a >actual &&
+ test_must_be_empty actual &&
+
+ echo untracked >create-options/untracked &&
+ echo ignored >create-options/ignored &&
+ git -C create-options diff >before-worktree &&
+ git -C create-options diff --cached >before-index &&
+ git -C create-options status --porcelain=v1 --ignored >before-status &&
+
+ short=$(git -C create-options stash create -u "create options") &&
+ long=$(git -C create-options stash create --include-untracked "create options") &&
+ test "$(git -C create-options rev-parse "$short^3^{tree}")" = "$(git -C create-options rev-parse "$long^3^{tree}")" &&
+ echo untracked >expect &&
+ git -C create-options show "$short^3:untracked" >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -C create-options cat-file -e "$short^3:ignored" &&
+
+ short=$(git -C create-options stash create -a "create options") &&
+ long=$(git -C create-options stash create --all "create options") &&
+ cluster=$(git -C create-options stash create -ua "create options") &&
+ test "$(git -C create-options rev-parse "$short^3^{tree}")" = "$(git -C create-options rev-parse "$long^3^{tree}")" &&
+ test "$(git -C create-options rev-parse "$short^3^{tree}")" = "$(git -C create-options rev-parse "$cluster^3^{tree}")" &&
+ echo ignored >expect &&
+ git -C create-options show "$short^3:ignored" >actual &&
+ test_cmp expect actual &&
+
+ git -C create-options diff >after-worktree &&
+ git -C create-options diff --cached >after-index &&
+ git -C create-options status --porcelain=v1 --ignored >after-status &&
+ test_cmp before-worktree after-worktree &&
+ test_cmp before-index after-index &&
+ test_cmp before-status after-status &&
+ test_must_fail git -C create-options rev-parse --verify refs/stash >/dev/null 2>&1
+'
+
+test_expect_success 'create untracked modes with only ignored files' '
+ test_when_finished "rm -rf create-ignored-only" &&
+ git init create-ignored-only &&
+ test_commit -C create-ignored-only base tracked base &&
+ test_commit -C create-ignored-only ignore .gitignore ignored &&
+ echo ignored >create-ignored-only/ignored &&
+
+ git -C create-ignored-only stash create -u >actual &&
+ test_must_be_empty actual &&
+ stash=$(git -C create-ignored-only stash create -a) &&
+ test -n "$stash" &&
+ echo ignored >expect &&
+ git -C create-ignored-only show "$stash^3:ignored" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create option parsing and dash-leading messages' '
+ test_when_finished "rm -rf create-message-options" &&
+ git init create-message-options &&
+ test_commit -C create-message-options base tracked base &&
+ echo modified >>create-message-options/tracked &&
+ echo untracked >create-message-options/untracked &&
+
+ stash=$(git -C create-message-options stash create handle new -u flag) &&
+ echo "On main: handle new -u flag" >expect &&
+ git -C create-message-options show --pretty=%s -s "$stash" >actual &&
+ test_cmp expect actual &&
+ test_must_fail git -C create-message-options cat-file -e "$stash^3^{commit}" &&
+
+ test_must_fail git -C create-message-options stash create -f >out 2>err &&
+ test_grep "unknown switch" err &&
+ stash=$(git -C create-message-options stash create -- -f) &&
+ echo "On main: -f" >expect &&
+ git -C create-message-options show --pretty=%s -s "$stash" >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git -C create-message-options stash create \
+ --include-untracked=yes >out 2>err
+'
+
test_expect_success 'create in a detached state' '
test_when_finished "git checkout main" &&
git checkout HEAD~1 &&
--
2.47.3
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 4:21 ` [PATCH v2] " Kazumasa Shigeta
@ 2026-10-01 11:32 ` Patrick Steinhardt
2026-10-01 16:01 ` 重田一聖
2026-10-01 16:36 ` Junio C Hamano
2026-10-01 17:03 ` Junio C Hamano
1 sibling, 2 replies; 20+ messages in thread
From: Patrick Steinhardt @ 2026-10-01 11:32 UTC (permalink / raw)
To: Kazumasa Shigeta; +Cc: git, Shabbir Bhojani, Phillip Wood
On Thu, Oct 01, 2026 at 01:21:55PM +0900, Kazumasa Shigeta wrote:
When sending a v2 in response to review feedback it's a good idea to
both:
- Respond to the reviewer to acknowledge their feedback and/or engage
in a discussion.
- As part of v2, send a range-diff as well as some documentation what
has changed between the two versions.
This ensures some netiquette in an age where we're increasingly only
talking with AI, either directly or via a meat proxy. And makes it
easier for the reviewer to see how exactly you have honored their
feedback.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 11:32 ` Patrick Steinhardt
@ 2026-10-01 16:01 ` 重田一聖
2026-10-01 16:36 ` Junio C Hamano
1 sibling, 0 replies; 20+ messages in thread
From: 重田一聖 @ 2026-10-01 16:01 UTC (permalink / raw)
To: ps; +Cc: git, shabbir.r.bhojani, phillip.wood
Hi Patrick,
Thanks for pointing this out, and sorry I did not understand the
expected review process here and sent v2 before replying to Phillip.
I'll reply to Phillip first and follow the proper order.
Thanks,
Kazumasa Shigeta
On Thu, 1 Oct 2026 13:32:19 +0200, Patrick Steinhardt <ps@pks.im> wrote:
> On Thu, Oct 01, 2026 at 01:21:55PM +0900, Kazumasa Shigeta wrote:
>
> When sending a v2 in response to review feedback it's a good idea to
> both:
>
> - Respond to the reviewer to acknowledge their feedback and/or engage
> in a discussion.
>
> - As part of v2, send a range-diff as well as some documentation what
> has changed between the two versions.
>
> This ensures some netiquette in an age where we're increasingly only
> talking with AI, either directly or via a meat proxy. And makes it
> easier for the reviewer to see how exactly you have honored their
> feedback.
>
> Thanks!
>
> Patrick
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 11:32 ` Patrick Steinhardt
2026-10-01 16:01 ` 重田一聖
@ 2026-10-01 16:36 ` Junio C Hamano
1 sibling, 0 replies; 20+ messages in thread
From: Junio C Hamano @ 2026-10-01 16:36 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Kazumasa Shigeta, git, Shabbir Bhojani, Phillip Wood
Patrick Steinhardt <ps@pks.im> writes:
> On Thu, Oct 01, 2026 at 01:21:55PM +0900, Kazumasa Shigeta wrote:
>
> When sending a v2 in response to review feedback it's a good idea to
> both:
>
> - Respond to the reviewer to acknowledge their feedback and/or engage
> in a discussion.
>
> - As part of v2, send a range-diff as well as some documentation what
> has changed between the two versions.
>
> This ensures some netiquette in an age where we're increasingly only
> talking with AI, either directly or via a meat proxy. And makes it
> easier for the reviewer to see how exactly you have honored their
> feedback.
>
> Thanks!
>
> Patrick
Thanks for bringing this up.
A response to review on the first round should come _before_ sending
v2 round of patch(es). Some people send them after v2, or
immediately sending before v2, but the right time to respond is
actually soon after receiving reviews on v1 and you had enough time
to understand the review comments, before starting to work on v2.
And then after working on v2, you would send patches. So whenever I
see v1 responses come after v2 patches or soon before v2 patches, I
smell that something is fishy.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 4:21 ` [PATCH v2] " Kazumasa Shigeta
2026-10-01 11:32 ` Patrick Steinhardt
@ 2026-10-01 17:03 ` Junio C Hamano
2026-10-02 1:53 ` 重田一聖
2026-10-02 9:04 ` 重田一聖
1 sibling, 2 replies; 20+ messages in thread
From: Junio C Hamano @ 2026-10-01 17:03 UTC (permalink / raw)
To: Kazumasa Shigeta; +Cc: git, Shabbir Bhojani, Phillip Wood
Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
> `git stash create` always passes zero for the include_untracked parameter
> of do_create_stash(), even though that helper already supports untracked
> and ignored files and stash push/save expose those modes as
> -u/--include-untracked and -a/--all.
There may be no lies in what the above says, but we would prefer to
hear what the user visible implication of "passing 0" is more than
what mechanically is happening inside a program. For example:
"git stash create", "git stash push", and "git stash save" are
commands that create a new stash entry. The latter two are also
responsible for storing the resulting stash entry to the reflog
of the "refs/stash" ref, but have options to control what is
included in the stash entry. Among these options, "create" only
supports the equivalent of "-m <message." to record in the stash
entry. Most notably, "-u" and "-a" options are missing.
> Teach create to accept the same options and pass the existing mode
> through. Unlike push/save, create continues to only create objects: it
> does not update refs/stash, reset the index, or clean the working tree.
Sure. It is a very concise and good description of what we want to
do.
> Use parse_options() for the new options and stop parsing at the first
> non-option message word. This keeps option-like tokens after the message
> as message text, while leading option-like arguments now follow Git's
> normal option parsing. In particular, unknown or malformed leading
> options are rejected instead of silently becoming a message, short
> options may be combined, and `--` can be used when a message itself
> begins with a dash.
Why do we need to go into such a detail in the log message? What is
the above paragraph designed to convey to the reader? Again, it may
not be telling any lies, but it misses the point by being inconsiderate
to your readers. What you need to tell them is _WHY_ you chose to
use parse_options() in such a way. What were you trying to achieve?
I am guessing that something along this line ...
"git stash create" traditionally treated the rest of the command
line as a message. For example,
$ git stash create adding -u option
has always been a request to create a stash entry with the
string "adding -u option" as its message. We should not make it
trigger the "-u" (include untracked) behavior for backward
compatibility, by using parse_options() with stop-at-the-non-option
mode to forbid it from reordering the command line arguments.
... was what you wanted to say, but I am not sure.
How much of all these verbiage was written by AI by the way? You'd
need to spend effort to make it readable to humans.
> Keep create's existing no-change behavior: detect the usual no-change
> case before do_create_stash() refreshes and writes the index, and return
> success without printing an object name. If do_create_stash() still
> reports its internal "nothing to create" result, map that to create's
> public success status.
You already said that with "does not update, reset, or clean".
> This follows the stash subcommand exit-status convention established by
> 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> subcommands return 0 on success, negative values on failure, and status 1
> when applying a stash results in conflicts. cmd_stash() maps negative
> subcommand failures to 128.
Again, there may not be lies in here, but if you did not make a
breaking change to the established convention, is it worth saying?
> 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> internal include-untracked path while intentionally leaving the user
> interface for "git stash create" unchanged. Reuse that machinery and
> the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> creation path.
>
> Add coverage for short and long aliases, combined short options, the
> untracked/ignored boundary including an ignored-only worktree, option
> parsing and dash-leading messages, no-change behavior, and preservation
> of refs/stash, the index state, and the working tree.
Again, adding tests for comprehensive coverage is not something to
boast about. Is it worth saying?
Aren't -p/-S/-k/-q and pathspec support all about the creating half
of "git stash push" that are not available to "git stash create",
not just "-u" and "-a"? Why are we singling out only these two? It
may be more worthwhile to explain the rationale behind such a design
decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] stash: expose untracked modes in create
2026-09-29 16:08 ` Phillip Wood
@ 2026-10-01 17:44 ` 重田一聖
2026-10-05 16:37 ` Phillip Wood
0 siblings, 1 reply; 20+ messages in thread
From: 重田一聖 @ 2026-10-01 17:44 UTC (permalink / raw)
To: phillip.wood; +Cc: git, shabbir.r.bhojani
Hi Phillip,
Thanks for the review.
Sorry, I got a little carried away and sent v2 before replying.
> This doesn't seem to match the code changes.
For the no-change case, plain "git stash create" already checks for
tracked changes before calling do_create_stash(), and returns 0 with
empty output when there is nothing to create.
For -u and -a, I think we should follow that existing "create" behavior
as well, using check_changes() for the selected mode before calling
do_create_stash(), and returning 0 when it finds nothing to create.
I am also thinking of mapping do_create_stash()'s internal no-change
status of 1 to 0 in the unlikely case where the state changes between
these checks. That 1 is not STASH_APPLY_CONFLICT. Following 786fc390465f
("stash: reserve exit status 1 for conflicts"), I do not think it should
escape as public exit status 1, and would map it to 0 instead.
> You should pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()
> to prevent that.
I plan to use PARSE_OPT_STOP_AT_NON_OPTION as you suggested.
Thanks,
Kazumasa Shigeta
On Tue, 29 Sep 2026 17:08:08 +0100, Phillip Wood
<phillip.wood123@gmail.com> wrote:
> Hi Kazumasa
>
> On 29/09/2026 08:42, Kazumasa Shigeta wrote:
> > `git stash create` always passes zero for the include_untracked parameter
> > of do_create_stash(), even though that helper already supports untracked
> > and ignored files and stash push/save expose those modes as
> > -u/--include-untracked and -a/--all.
> >
> > Teach create to accept the same options and pass the existing mode
> > through. Unlike push/save, create continues to only create objects: it
> > does not update refs/stash or modify the index or working tree.
> >
> > When the selected mode finds no changes, do_create_stash() returns 1.
> > Translate that to success so create keeps its existing no-object, empty
> > output behavior.
>
> This doesn't seem to match the code changes. The code that prints the
> object id when the stash is successfully created is unchanged, as far as
> I can see what this patch does is change the exit status for "git stash
> create" when there are no changes to stash. Instead of exiting 1, it
> exits 0 even though it does not create a stash. That does not seem like
> a good idea.
>
> > Use normal parse-options semantics, so options may appear after message
> > arguments. A message that begins with a dash can be disambiguated with
>
> As "git stash create" concatenates excess arguments to use as the stash
> message we should not be permuting options. "git stash create handle new
> -u flag" should continue to create a stash with the message "handle new
> -u flag" - it should not start stashing untracked files. You should pass
> PARSE_OPT_STOP_AT_NON_OPTION to parse_options() to prevent that.
>
> > I proposed adding both --include-untracked and --all to
> > "git stash create" in 2014:
> > <1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp>
> >
> > I should also apologize for dropping that thread after receiving review.
> > I did not follow up on the comments at the time. Thanks to those who
> > reviewed it then.
>
> Better late than never! I think the idea is fine, but the implementation
> could do with a couple of tweaks so it is as backward compatible as
> possible.
>
> Thanks
>
> Phillip
>
> > Separately, in 2017, Thomas Gummerer added an internal -u path while
> > refactoring stash_create in 9ca6326dff29 (stash: refactor stash_create).
> > That change explicitly kept the user interface of "git stash create"
> > unchanged.
> >
> > When "stash create" was later converted to the builtin C implementation
> > in d4788af875cc (stash: convert create to builtin), the untracked-file
> > handling was carried into the new implementation and remains there today.
> >
> > More recently, Shabbir Bhojani proposed exposing --include-untracked:
> > <pull.1892.git.1774768580147.gitgitgadget@gmail.com>
> >
> > This patch exposes both existing untracked modes, --include-untracked and
> > --all, to "git stash create".
> >
> > Documentation/git-stash.adoc | 18 ++++++----
> > builtin/stash.c | 36 ++++++++++++++-----
> > t/t3903-stash.sh | 70 ++++++++++++++++++++++++++++++++++++
> > 3 files changed, 109 insertions(+), 15 deletions(-)
> >
> > diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
> > index fc6a9a0..32f0fd5 100644
> > --- a/Documentation/git-stash.adoc
> > +++ b/Documentation/git-stash.adoc
> > @@ -21,7 +21,7 @@ git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | -
> > git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
> > [-u | --include-untracked] [-a | --all] [<message>]
> > git stash clear
> > -git stash create [<message>]
> > +git stash create [-u | --include-untracked] [-a | --all] [<message>]
> > git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
> > git stash export (--print | --to-ref <ref>) [<stash>...]
> > git stash import <commit>
> > @@ -138,10 +138,12 @@ with no conflicts.
> > `drop [-q | --quiet] [<stash>]`::
> > Remove a single stash entry from the list of stash entries.
> >
> > -`create`::
> > +`create [-u | --include-untracked] [-a | --all]`::
> > Create a stash entry (which is a regular commit object) and
> > return its object name, without storing it anywhere in the ref
> > - namespace.
> > + namespace. The `--include-untracked` option includes untracked
> > + files, while `--all` also includes ignored files, without modifying
> > + the working tree.
> > This is intended to be useful for scripts. It is probably not
> > the command you want to use; see "push" above.
> >
> > @@ -167,10 +169,11 @@ OPTIONS
> > -------
> > `-a`::
> > `--all`::
> > - This option is only valid for `push` and `save` commands.
> > + When used with the `push` and `save` commands, all ignored and
> > + untracked files are also stashed and then cleaned up with `git clean`.
> > +
> > -All ignored and untracked files are also stashed and then cleaned
> > -up with `git clean`.
> > +When used with the `create` command, ignored and untracked files are included
> > +in the stash entry without modifying the working tree.
> >
> > `-u`::
> > `--include-untracked`::
> > @@ -179,6 +182,9 @@ up with `git clean`.
> > all untracked files are also stashed and then cleaned up with
> > `git clean`.
> > +
> > +When used with the `create` command, untracked files are included in the
> > +stash entry without modifying the working tree.
> > ++
> > When used with the `show` command, show the untracked files in the stash
> > entry as part of the diff.
> >
> > diff --git a/builtin/stash.c b/builtin/stash.c
> > index 7a98434..57a4750 100644
> > --- a/builtin/stash.c
> > +++ b/builtin/stash.c
> > @@ -59,7 +59,7 @@
> > N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
> > " [-u | --include-untracked] [-a | --all] [<message>]")
> > #define BUILTIN_STASH_CREATE_USAGE \
> > - N_("git stash create [<message>]")
> > + N_("git stash create [-u | --include-untracked] [-a | --all] [<message>]")
> > #define BUILTIN_STASH_EXPORT_USAGE \
> > N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
> > #define BUILTIN_STASH_IMPORT_USAGE \
> > @@ -119,6 +119,11 @@ static const char * const git_stash_clear_usage[] = {
> > NULL
> > };
> >
> > +static const char * const git_stash_create_usage[] = {
> > + BUILTIN_STASH_CREATE_USAGE,
> > + NULL
> > +};
> > +
> > static const char * const git_stash_store_usage[] = {
> > BUILTIN_STASH_STORE_USAGE,
> > NULL
> > @@ -1643,26 +1648,39 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
> > return ret;
> > }
> >
> > -static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
> > +static int create_stash(int argc, const char **argv, const char *prefix,
> > struct repository *repo UNUSED)
> > {
> > - int ret;
> > + int ret = 0;
> > + int include_untracked = 0;
> > + struct option options[] = {
> > + OPT_BOOL('u', "include-untracked", &include_untracked,
> > + N_("include untracked files in stash")),
> > + OPT_SET_INT('a', "all", &include_untracked,
> > + N_("include ignored files in stash"),
> > + INCLUDE_ALL_FILES),
> > + OPT_END()
> > + };
> > struct strbuf stash_msg_buf = STRBUF_INIT;
> > struct stash_info info = STASH_INFO_INIT;
> > struct pathspec ps;
> >
> > - /* Starting with argv[1], since argv[0] is "create" */
> > - strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
> > + argc = parse_options(argc, argv, prefix, options,
> > + git_stash_create_usage, 0);
> > + strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
> >
> > memset(&ps, 0, sizeof(ps));
> > - if (!check_changes_tracked_files(&ps))
> > - return 0;
> > + if (!include_untracked && !check_changes_tracked_files(&ps))
> > + goto done;
> >
> > - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
> > - NULL, 0);
> > + ret = do_create_stash(&ps, &stash_msg_buf, include_untracked, 0, NULL,
> > + 0, &info, NULL, 0);
> > if (!ret)
> > printf_ln("%s", oid_to_hex(&info.w_commit));
> > + else if (ret == 1)
> > + ret = 0;
> >
> > +done:
> > free_stash_info(&info);
> > strbuf_release(&stash_msg_buf);
> > return ret;
> > diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
> > index 7211586..fe34879 100755
> > --- a/t/t3903-stash.sh
> > +++ b/t/t3903-stash.sh
> > @@ -640,6 +640,76 @@ test_expect_success 'stash create - no changes' '
> > test_must_be_empty actual
> > '
> >
> > +# --all observes every untracked and ignored path in the worktree. Use one
> > +# isolated repository for these checks so unrelated test state is not captured.
> > +test_expect_success 'stash create with untracked options' '
> > + test_when_finished "rm -rf stash-create-options" &&
> > + test_create_repo stash-create-options &&
> > + (
> > + cd stash-create-options &&
> > + test_commit base tracked base &&
> > + echo create-ignored >.gitignore &&
> > + git add .gitignore &&
> > + git commit -m ignore &&
> > +
> > + git stash create -u >.git/actual &&
> > + test_must_be_empty .git/actual &&
> > + git stash create -a >.git/actual &&
> > + test_must_be_empty .git/actual &&
> > +
> > + echo untracked >create-untracked &&
> > + git stash create "without untracked" >.git/actual &&
> > + test_must_be_empty .git/actual &&
> > + short=$(git stash create "create untracked" -u) &&
> > + long=$(git stash create --include-untracked "create untracked") &&
> > + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
> > + echo untracked >.git/expect &&
> > + git show "$short^3:create-untracked" >.git/actual &&
> > + test_cmp .git/expect .git/actual &&
> > + branch=$(git symbolic-ref --short HEAD) &&
> > + echo "On $branch: create untracked" >.git/expect &&
> > + git show --pretty=%s -s "$short" >.git/actual &&
> > + test_cmp .git/expect .git/actual &&
> > + test_path_is_file create-untracked &&
> > +
> > + echo ignored >create-ignored &&
> > + with_untracked=$(git stash create -u "create options") &&
> > + test_must_fail git cat-file -e "$with_untracked^3:create-ignored" &&
> > + short=$(git stash create "create options" -a) &&
> > + long=$(git stash create --all "create options") &&
> > + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
> > + echo ignored >.git/expect &&
> > + git show "$short^3:create-ignored" >.git/actual &&
> > + test_cmp .git/expect .git/actual &&
> > + test_path_is_file create-untracked &&
> > + test_path_is_file create-ignored &&
> > +
> > + echo staged >staged &&
> > + git add staged &&
> > + echo modified >>tracked &&
> > + git diff >.git/before-worktree &&
> > + git diff --cached >.git/before-index &&
> > + git status --porcelain=v1 --ignored >.git/before-status &&
> > + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
> > + STASH_ID=$(git stash create -a -- -create-message) &&
> > + git diff >.git/after-worktree &&
> > + git diff --cached >.git/after-index &&
> > + git status --porcelain=v1 --ignored >.git/after-status &&
> > + test_cmp .git/before-worktree .git/after-worktree &&
> > + test_cmp .git/before-index .git/after-index &&
> > + test_cmp .git/before-status .git/after-status &&
> > + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
> > + echo "On $branch: -create-message" >.git/expect &&
> > + git show --pretty=%s -s "$STASH_ID" >.git/actual &&
> > + test_cmp .git/expect .git/actual
> > + )
> > +'
> > +
> > +test_expect_success 'stash create rejects unknown options' '
> > + test_expect_code 129 git stash create --unknown-option 2>err &&
> > + test_grep "unknown option" err
> > +'
> > +
> > test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
> > git stash clear &&
> > test_when_finished "git reset --hard HEAD" &&
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 17:03 ` Junio C Hamano
@ 2026-10-02 1:53 ` 重田一聖
2026-10-02 9:04 ` 重田一聖
1 sibling, 0 replies; 20+ messages in thread
From: 重田一聖 @ 2026-10-02 1:53 UTC (permalink / raw)
To: gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Junio,
Sorry for the crossed replies. As you also pointed out, I should have
replied to Phillip before sending v2. After Patrick raised that point, I
was writing my response to Phillip, and I did not notice that your
messages had arrived while I was doing so. I ended up sending my reply
to Phillip before seeing your comments.
Thank you for the detailed review. I will go through your comments
carefully before following up.
I am not very quick at writing these emails, so it takes me quite a
while to respond. Sorry about that. I will do my best to understand the
points properly and improve the next round.
Thanks,
Kazumasa Shigeta
On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
>
> > `git stash create` always passes zero for the include_untracked parameter
> > of do_create_stash(), even though that helper already supports untracked
> > and ignored files and stash push/save expose those modes as
> > -u/--include-untracked and -a/--all.
>
> There may be no lies in what the above says, but we would prefer to
> hear what the user visible implication of "passing 0" is more than
> what mechanically is happening inside a program. For example:
>
> "git stash create", "git stash push", and "git stash save" are
> commands that create a new stash entry. The latter two are also
> responsible for storing the resulting stash entry to the reflog
> of the "refs/stash" ref, but have options to control what is
> included in the stash entry. Among these options, "create" only
> supports the equivalent of "-m <message." to record in the stash
> entry. Most notably, "-u" and "-a" options are missing.
>
> > Teach create to accept the same options and pass the existing mode
> > through. Unlike push/save, create continues to only create objects: it
> > does not update refs/stash, reset the index, or clean the working tree.
>
> Sure. It is a very concise and good description of what we want to
> do.
>
> > Use parse_options() for the new options and stop parsing at the first
> > non-option message word. This keeps option-like tokens after the message
> > as message text, while leading option-like arguments now follow Git's
> > normal option parsing. In particular, unknown or malformed leading
> > options are rejected instead of silently becoming a message, short
> > options may be combined, and `--` can be used when a message itself
> > begins with a dash.
>
> Why do we need to go into such a detail in the log message? What is
> the above paragraph designed to convey to the reader? Again, it may
> not be telling any lies, but it misses the point by being inconsiderate
> to your readers. What you need to tell them is _WHY_ you chose to
> use parse_options() in such a way. What were you trying to achieve?
>
> I am guessing that something along this line ...
>
> "git stash create" traditionally treated the rest of the command
> line as a message. For example,
>
> $ git stash create adding -u option
>
> has always been a request to create a stash entry with the
> string "adding -u option" as its message. We should not make it
> trigger the "-u" (include untracked) behavior for backward
> compatibility, by using parse_options() with stop-at-the-non-option
> mode to forbid it from reordering the command line arguments.
>
> ... was what you wanted to say, but I am not sure.
>
> How much of all these verbiage was written by AI by the way? You'd
> need to spend effort to make it readable to humans.
>
> > Keep create's existing no-change behavior: detect the usual no-change
> > case before do_create_stash() refreshes and writes the index, and return
> > success without printing an object name. If do_create_stash() still
> > reports its internal "nothing to create" result, map that to create's
> > public success status.
>
> You already said that with "does not update, reset, or clean".
>
> > This follows the stash subcommand exit-status convention established by
> > 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> > subcommands return 0 on success, negative values on failure, and status 1
> > when applying a stash results in conflicts. cmd_stash() maps negative
> > subcommand failures to 128.
>
> Again, there may not be lies in here, but if you did not make a
> breaking change to the established convention, is it worth saying?
>
> > 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> > internal include-untracked path while intentionally leaving the user
> > interface for "git stash create" unchanged. Reuse that machinery and
> > the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> > creation path.
> >
> > Add coverage for short and long aliases, combined short options, the
> > untracked/ignored boundary including an ignored-only worktree, option
> > parsing and dash-leading messages, no-change behavior, and preservation
> > of refs/stash, the index state, and the working tree.
>
> Again, adding tests for comprehensive coverage is not something to
> boast about. Is it worth saying?
>
> Aren't -p/-S/-k/-q and pathspec support all about the creating half
> of "git stash push" that are not available to "git stash create",
> not just "-u" and "-a"? Why are we singling out only these two? It
> may be more worthwhile to explain the rationale behind such a design
> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-01 17:03 ` Junio C Hamano
2026-10-02 1:53 ` 重田一聖
@ 2026-10-02 9:04 ` 重田一聖
2026-10-05 5:55 ` 重田一聖
1 sibling, 1 reply; 20+ messages in thread
From: 重田一聖 @ 2026-10-02 9:04 UTC (permalink / raw)
To: gitster; +Cc: git, shabbir.r.bhojani, phillip.wood
Hi Junio,
> we would prefer to hear what the user visible implication of
> "passing 0" is more than what mechanically is happening inside a
> program.
The user-visible effect is that stash create cannot currently include
untracked or ignored files in the stash entry. If those are the only
changes, it creates no entry at all, while stash push and save can
include them with -u or -a as appropriate. I should have described that
difference directly instead of starting from the include_untracked
implementation detail.
> ... was what you wanted to say, but I am not sure.
Yes, exactly. I'll explain the backward-compatibility reason rather
than the mechanics of parse_options().
> You already said that with "does not update, reset, or clean".
I'll drop that paragraph.
> if you did not make a breaking change to the established convention,
> is it worth saying?
I don't think it adds anything here. I'll remove the exit-status
discussion from the commit message as well.
> adding tests for comprehensive coverage is not something to boast
> about. Is it worth saying?
I'll remove the test details from the commit message.
> Why are we singling out only these two?
I started by looking at the missing -u and -a support in create, and I
think that led me to focus too narrowly on those two when considering
the scope. I need to think more about whether this patch should remain
limited to those two.
Thanks,
Kazumasa Shigeta
On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
>
> > `git stash create` always passes zero for the include_untracked parameter
> > of do_create_stash(), even though that helper already supports untracked
> > and ignored files and stash push/save expose those modes as
> > -u/--include-untracked and -a/--all.
>
> There may be no lies in what the above says, but we would prefer to
> hear what the user visible implication of "passing 0" is more than
> what mechanically is happening inside a program. For example:
>
> "git stash create", "git stash push", and "git stash save" are
> commands that create a new stash entry. The latter two are also
> responsible for storing the resulting stash entry to the reflog
> of the "refs/stash" ref, but have options to control what is
> included in the stash entry. Among these options, "create" only
> supports the equivalent of "-m <message." to record in the stash
> entry. Most notably, "-u" and "-a" options are missing.
>
> > Teach create to accept the same options and pass the existing mode
> > through. Unlike push/save, create continues to only create objects: it
> > does not update refs/stash, reset the index, or clean the working tree.
>
> Sure. It is a very concise and good description of what we want to
> do.
>
> > Use parse_options() for the new options and stop parsing at the first
> > non-option message word. This keeps option-like tokens after the message
> > as message text, while leading option-like arguments now follow Git's
> > normal option parsing. In particular, unknown or malformed leading
> > options are rejected instead of silently becoming a message, short
> > options may be combined, and `--` can be used when a message itself
> > begins with a dash.
>
> Why do we need to go into such a detail in the log message? What is
> the above paragraph designed to convey to the reader? Again, it may
> not be telling any lies, but it misses the point by being inconsiderate
> to your readers. What you need to tell them is _WHY_ you chose to
> use parse_options() in such a way. What were you trying to achieve?
>
> I am guessing that something along this line ...
>
> "git stash create" traditionally treated the rest of the command
> line as a message. For example,
>
> $ git stash create adding -u option
>
> has always been a request to create a stash entry with the
> string "adding -u option" as its message. We should not make it
> trigger the "-u" (include untracked) behavior for backward
> compatibility, by using parse_options() with stop-at-the-non-option
> mode to forbid it from reordering the command line arguments.
>
> ... was what you wanted to say, but I am not sure.
>
> How much of all these verbiage was written by AI by the way? You'd
> need to spend effort to make it readable to humans.
>
> > Keep create's existing no-change behavior: detect the usual no-change
> > case before do_create_stash() refreshes and writes the index, and return
> > success without printing an object name. If do_create_stash() still
> > reports its internal "nothing to create" result, map that to create's
> > public success status.
>
> You already said that with "does not update, reset, or clean".
>
> > This follows the stash subcommand exit-status convention established by
> > 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> > subcommands return 0 on success, negative values on failure, and status 1
> > when applying a stash results in conflicts. cmd_stash() maps negative
> > subcommand failures to 128.
>
> Again, there may not be lies in here, but if you did not make a
> breaking change to the established convention, is it worth saying?
>
> > 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> > internal include-untracked path while intentionally leaving the user
> > interface for "git stash create" unchanged. Reuse that machinery and
> > the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> > creation path.
> >
> > Add coverage for short and long aliases, combined short options, the
> > untracked/ignored boundary including an ignored-only worktree, option
> > parsing and dash-leading messages, no-change behavior, and preservation
> > of refs/stash, the index state, and the working tree.
>
> Again, adding tests for comprehensive coverage is not something to
> boast about. Is it worth saying?
>
> Aren't -p/-S/-k/-q and pathspec support all about the creating half
> of "git stash push" that are not available to "git stash create",
> not just "-u" and "-a"? Why are we singling out only these two? It
> may be more worthwhile to explain the rationale behind such a design
> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-02 9:04 ` 重田一聖
@ 2026-10-05 5:55 ` 重田一聖
2026-10-05 16:38 ` Phillip Wood
2026-10-05 16:43 ` Junio C Hamano
0 siblings, 2 replies; 20+ messages in thread
From: 重田一聖 @ 2026-10-05 5:55 UTC (permalink / raw)
To: gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Junio,
> Why are we singling out only these two?
After looking more carefully at both the history and the current stash
code, I don't think there is a good reason to single out only `-u` and
`-a`.
Your question made me realize that I had focused too narrowly on the
untracked modes. The larger issue is not simply that `do_create_stash()`
has capabilities that `git stash create` does not expose. The existing
`git stash create <message>` grammar is a long-standing compatibility
contract that has been deliberately preserved.
Making more of those capabilities available through `create` would
therefore mean either changing that contract or designing around it.
That is a much larger interface decision than I appreciated when I sent
the patch.
I moved too quickly here and sent the patch before understanding that
constraint well enough. I am sorry about that. Thanks to you, Phillip,
Patrick, and everyone else who took the time to review it. I should
have investigated this compatibility history first.
Even so, I still think it would be useful to make more of the existing
`do_create_stash()` capabilities available through the public command
line.
So I no longer think the question is simply which additional options
`stash create` should expose. The broader question is how to make those
capabilities available through a public interface while dealing
appropriately with the existing `git stash create <message>`
compatibility contract.
From that perspective, I can see three possible directions.
1. Keep extending `stash create`.
We could expose more of the existing `do_create_stash()`
functionality through `stash create`, following the conventions of
`stash push` for the creation-related options they have in common.
This seems implementable, but even with
`PARSE_OPT_STOP_AT_NON_OPTION` it would change the handling of
messages that begin with an option-like argument. Those would need
explicit disambiguation, such as `--`.
There is also the pathspec question. If positional arguments
continue to be joined to form the message, pathspecs need some other
way to be distinguished from that message.
2. Add a new stash subcommand for the creation functionality.
This would leave the existing `stash create <message>` contract
unchanged. Because the new command would not inherit `create`'s
positional message grammar, its creation-related options and
pathspec handling could follow conventions similar to `stash push`.
This preserves the existing `create` grammar while avoiding the need
to fit additional creation capabilities into it. The trade-off is
adding another public stash subcommand and its long-term maintenance
cost.
3. Add something like `--create-only` to `git stash push`.
This would reuse the existing `push` option grammar without adding
another subcommand.
I also read the 2019 discussion around `git stash push --snapshot`.
One concern there was that approximately the same end state could
already be obtained with `git stash push && git stash apply`.
I do not think that particular concern carries over directly here.
`git stash create` already stops at object creation, but its public
interface does not expose more of the creation capabilities already
available in `do_create_stash()`. There is currently no public stash
command that exposes those capabilities while retaining that
create-only boundary.
That does not mean a similar result cannot be constructed by other
means. The missing piece is a public interface to the existing stash
creation machinery at that boundary.
Even so, there is still the separate question of whether `push` is
the right place for a creation-only operation in the first place.
The push-specific work around `do_create_stash()` would also need to
be separated carefully.
All three seem substantially broader than the original `-u` / `-a`
patch.
If this is worth pursuing further, which of these directions seems the
most plausible? Also, is this the right thread to continue that design
discussion, or would it be better to discuss it separately?
Thanks again for the guidance,
Kazumasa Shigeta
On Fri, 2 Oct 2026 05:04:26 -0400, "重田一聖" <kazumasa.shigeta@kanamei.com> wrote:
> Hi Junio,
>
> > we would prefer to hear what the user visible implication of
> > "passing 0" is more than what mechanically is happening inside a
> > program.
>
> The user-visible effect is that stash create cannot currently include
> untracked or ignored files in the stash entry. If those are the only
> changes, it creates no entry at all, while stash push and save can
> include them with -u or -a as appropriate. I should have described that
> difference directly instead of starting from the include_untracked
> implementation detail.
>
> > ... was what you wanted to say, but I am not sure.
>
> Yes, exactly. I'll explain the backward-compatibility reason rather
> than the mechanics of parse_options().
>
> > You already said that with "does not update, reset, or clean".
>
> I'll drop that paragraph.
>
> > if you did not make a breaking change to the established convention,
> > is it worth saying?
>
> I don't think it adds anything here. I'll remove the exit-status
> discussion from the commit message as well.
>
> > adding tests for comprehensive coverage is not something to boast
> > about. Is it worth saying?
>
> I'll remove the test details from the commit message.
>
> > Why are we singling out only these two?
>
> I started by looking at the missing -u and -a support in create, and I
> think that led me to focus too narrowly on those two when considering
> the scope. I need to think more about whether this patch should remain
> limited to those two.
>
> Thanks,
> Kazumasa Shigeta
>
> On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> > Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
> >
> > > `git stash create` always passes zero for the include_untracked parameter
> > > of do_create_stash(), even though that helper already supports untracked
> > > and ignored files and stash push/save expose those modes as
> > > -u/--include-untracked and -a/--all.
> >
> > There may be no lies in what the above says, but we would prefer to
> > hear what the user visible implication of "passing 0" is more than
> > what mechanically is happening inside a program. For example:
> >
> > "git stash create", "git stash push", and "git stash save" are
> > commands that create a new stash entry. The latter two are also
> > responsible for storing the resulting stash entry to the reflog
> > of the "refs/stash" ref, but have options to control what is
> > included in the stash entry. Among these options, "create" only
> > supports the equivalent of "-m <message." to record in the stash
> > entry. Most notably, "-u" and "-a" options are missing.
> >
> > > Teach create to accept the same options and pass the existing mode
> > > through. Unlike push/save, create continues to only create objects: it
> > > does not update refs/stash, reset the index, or clean the working tree.
> >
> > Sure. It is a very concise and good description of what we want to
> > do.
> >
> > > Use parse_options() for the new options and stop parsing at the first
> > > non-option message word. This keeps option-like tokens after the message
> > > as message text, while leading option-like arguments now follow Git's
> > > normal option parsing. In particular, unknown or malformed leading
> > > options are rejected instead of silently becoming a message, short
> > > options may be combined, and `--` can be used when a message itself
> > > begins with a dash.
> >
> > Why do we need to go into such a detail in the log message? What is
> > the above paragraph designed to convey to the reader? Again, it may
> > not be telling any lies, but it misses the point by being inconsiderate
> > to your readers. What you need to tell them is _WHY_ you chose to
> > use parse_options() in such a way. What were you trying to achieve?
> >
> > I am guessing that something along this line ...
> >
> > "git stash create" traditionally treated the rest of the command
> > line as a message. For example,
> >
> > $ git stash create adding -u option
> >
> > has always been a request to create a stash entry with the
> > string "adding -u option" as its message. We should not make it
> > trigger the "-u" (include untracked) behavior for backward
> > compatibility, by using parse_options() with stop-at-the-non-option
> > mode to forbid it from reordering the command line arguments.
> >
> > ... was what you wanted to say, but I am not sure.
> >
> > How much of all these verbiage was written by AI by the way? You'd
> > need to spend effort to make it readable to humans.
> >
> > > Keep create's existing no-change behavior: detect the usual no-change
> > > case before do_create_stash() refreshes and writes the index, and return
> > > success without printing an object name. If do_create_stash() still
> > > reports its internal "nothing to create" result, map that to create's
> > > public success status.
> >
> > You already said that with "does not update, reset, or clean".
> >
> > > This follows the stash subcommand exit-status convention established by
> > > 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> > > subcommands return 0 on success, negative values on failure, and status 1
> > > when applying a stash results in conflicts. cmd_stash() maps negative
> > > subcommand failures to 128.
> >
> > Again, there may not be lies in here, but if you did not make a
> > breaking change to the established convention, is it worth saying?
> >
> > > 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> > > internal include-untracked path while intentionally leaving the user
> > > interface for "git stash create" unchanged. Reuse that machinery and
> > > the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> > > creation path.
> > >
> > > Add coverage for short and long aliases, combined short options, the
> > > untracked/ignored boundary including an ignored-only worktree, option
> > > parsing and dash-leading messages, no-change behavior, and preservation
> > > of refs/stash, the index state, and the working tree.
> >
> > Again, adding tests for comprehensive coverage is not something to
> > boast about. Is it worth saying?
> >
> > Aren't -p/-S/-k/-q and pathspec support all about the creating half
> > of "git stash push" that are not available to "git stash create",
> > not just "-u" and "-a"? Why are we singling out only these two? It
> > may be more worthwhile to explain the rationale behind such a design
> > decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH] stash: expose untracked modes in create
2026-10-01 17:44 ` 重田一聖
@ 2026-10-05 16:37 ` Phillip Wood
0 siblings, 0 replies; 20+ messages in thread
From: Phillip Wood @ 2026-10-05 16:37 UTC (permalink / raw)
To: 重田一聖, phillip.wood; +Cc: git, shabbir.r.bhojani
Hi Kazumasa
On 01/10/2026 18:44, 重田一聖 wrote:
> Hi Phillip,
>
> Thanks for the review.
>
> Sorry, I got a little carried away and sent v2 before replying.
>
>> This doesn't seem to match the code changes.
>
> For the no-change case, plain "git stash create" already checks for
> tracked changes before calling do_create_stash(), and returns 0 with
> empty output when there is nothing to create.
>
> For -u and -a, I think we should follow that existing "create" behavior
> as well, using check_changes() for the selected mode before calling
> do_create_stash(), and returning 0 when it finds nothing to create.
I'm not really sure why that call to check_changes_tracked_files() is
there in create_stash() as do_create_stash() repeats the same check.
I've sent a couple of patches [1] to fix that.
> I am also thinking of mapping do_create_stash()'s internal no-change
> status of 1 to 0 in the unlikely case where the state changes between
> these checks.
I think that is worth doing but it is not related to adding new options
so should be a separate patch.
> That 1 is not STASH_APPLY_CONFLICT. Following 786fc390465f
> ("stash: reserve exit status 1 for conflicts"), I do not think it should
> escape as public exit status 1, and would map it to 0 instead.
I agree we should exit 0 in that case.
>> You should pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()
>> to prevent that.
>
> I plan to use PARSE_OPT_STOP_AT_NON_OPTION as you suggested.
That's great
Thanks
Phillip
[1]
https://lore.kernel.org/git/cover.1791218125.git.phillip.wood@dunelm.org.uk
> Thanks,
>
> Kazumasa Shigeta
>
>
> On Tue, 29 Sep 2026 17:08:08 +0100, Phillip Wood
> <phillip.wood123@gmail.com> wrote:
>> Hi Kazumasa
>>
>> On 29/09/2026 08:42, Kazumasa Shigeta wrote:
>>> `git stash create` always passes zero for the include_untracked parameter
>>> of do_create_stash(), even though that helper already supports untracked
>>> and ignored files and stash push/save expose those modes as
>>> -u/--include-untracked and -a/--all.
>>>
>>> Teach create to accept the same options and pass the existing mode
>>> through. Unlike push/save, create continues to only create objects: it
>>> does not update refs/stash or modify the index or working tree.
>>>
>>> When the selected mode finds no changes, do_create_stash() returns 1.
>>> Translate that to success so create keeps its existing no-object, empty
>>> output behavior.
>>
>> This doesn't seem to match the code changes. The code that prints the
>> object id when the stash is successfully created is unchanged, as far as
>> I can see what this patch does is change the exit status for "git stash
>> create" when there are no changes to stash. Instead of exiting 1, it
>> exits 0 even though it does not create a stash. That does not seem like
>> a good idea.
>>
>>> Use normal parse-options semantics, so options may appear after message
>>> arguments. A message that begins with a dash can be disambiguated with
>>
>> As "git stash create" concatenates excess arguments to use as the stash
>> message we should not be permuting options. "git stash create handle new
>> -u flag" should continue to create a stash with the message "handle new
>> -u flag" - it should not start stashing untracked files. You should pass
>> PARSE_OPT_STOP_AT_NON_OPTION to parse_options() to prevent that.
>>
>>> I proposed adding both --include-untracked and --all to
>>> "git stash create" in 2014:
>>> <1403856479-37421-1-git-send-email-shigeta@kanamei.co.jp>
>>>
>>> I should also apologize for dropping that thread after receiving review.
>>> I did not follow up on the comments at the time. Thanks to those who
>>> reviewed it then.
>>
>> Better late than never! I think the idea is fine, but the implementation
>> could do with a couple of tweaks so it is as backward compatible as
>> possible.
>>
>> Thanks
>>
>> Phillip
>>
>>> Separately, in 2017, Thomas Gummerer added an internal -u path while
>>> refactoring stash_create in 9ca6326dff29 (stash: refactor stash_create).
>>> That change explicitly kept the user interface of "git stash create"
>>> unchanged.
>>>
>>> When "stash create" was later converted to the builtin C implementation
>>> in d4788af875cc (stash: convert create to builtin), the untracked-file
>>> handling was carried into the new implementation and remains there today.
>>>
>>> More recently, Shabbir Bhojani proposed exposing --include-untracked:
>>> <pull.1892.git.1774768580147.gitgitgadget@gmail.com>
>>>
>>> This patch exposes both existing untracked modes, --include-untracked and
>>> --all, to "git stash create".
>>>
>>> Documentation/git-stash.adoc | 18 ++++++----
>>> builtin/stash.c | 36 ++++++++++++++-----
>>> t/t3903-stash.sh | 70 ++++++++++++++++++++++++++++++++++++
>>> 3 files changed, 109 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
>>> index fc6a9a0..32f0fd5 100644
>>> --- a/Documentation/git-stash.adoc
>>> +++ b/Documentation/git-stash.adoc
>>> @@ -21,7 +21,7 @@ git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | -
>>> git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
>>> [-u | --include-untracked] [-a | --all] [<message>]
>>> git stash clear
>>> -git stash create [<message>]
>>> +git stash create [-u | --include-untracked] [-a | --all] [<message>]
>>> git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
>>> git stash export (--print | --to-ref <ref>) [<stash>...]
>>> git stash import <commit>
>>> @@ -138,10 +138,12 @@ with no conflicts.
>>> `drop [-q | --quiet] [<stash>]`::
>>> Remove a single stash entry from the list of stash entries.
>>>
>>> -`create`::
>>> +`create [-u | --include-untracked] [-a | --all]`::
>>> Create a stash entry (which is a regular commit object) and
>>> return its object name, without storing it anywhere in the ref
>>> - namespace.
>>> + namespace. The `--include-untracked` option includes untracked
>>> + files, while `--all` also includes ignored files, without modifying
>>> + the working tree.
>>> This is intended to be useful for scripts. It is probably not
>>> the command you want to use; see "push" above.
>>>
>>> @@ -167,10 +169,11 @@ OPTIONS
>>> -------
>>> `-a`::
>>> `--all`::
>>> - This option is only valid for `push` and `save` commands.
>>> + When used with the `push` and `save` commands, all ignored and
>>> + untracked files are also stashed and then cleaned up with `git clean`.
>>> +
>>> -All ignored and untracked files are also stashed and then cleaned
>>> -up with `git clean`.
>>> +When used with the `create` command, ignored and untracked files are included
>>> +in the stash entry without modifying the working tree.
>>>
>>> `-u`::
>>> `--include-untracked`::
>>> @@ -179,6 +182,9 @@ up with `git clean`.
>>> all untracked files are also stashed and then cleaned up with
>>> `git clean`.
>>> +
>>> +When used with the `create` command, untracked files are included in the
>>> +stash entry without modifying the working tree.
>>> ++
>>> When used with the `show` command, show the untracked files in the stash
>>> entry as part of the diff.
>>>
>>> diff --git a/builtin/stash.c b/builtin/stash.c
>>> index 7a98434..57a4750 100644
>>> --- a/builtin/stash.c
>>> +++ b/builtin/stash.c
>>> @@ -59,7 +59,7 @@
>>> N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
>>> " [-u | --include-untracked] [-a | --all] [<message>]")
>>> #define BUILTIN_STASH_CREATE_USAGE \
>>> - N_("git stash create [<message>]")
>>> + N_("git stash create [-u | --include-untracked] [-a | --all] [<message>]")
>>> #define BUILTIN_STASH_EXPORT_USAGE \
>>> N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
>>> #define BUILTIN_STASH_IMPORT_USAGE \
>>> @@ -119,6 +119,11 @@ static const char * const git_stash_clear_usage[] = {
>>> NULL
>>> };
>>>
>>> +static const char * const git_stash_create_usage[] = {
>>> + BUILTIN_STASH_CREATE_USAGE,
>>> + NULL
>>> +};
>>> +
>>> static const char * const git_stash_store_usage[] = {
>>> BUILTIN_STASH_STORE_USAGE,
>>> NULL
>>> @@ -1643,26 +1648,39 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
>>> return ret;
>>> }
>>>
>>> -static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
>>> +static int create_stash(int argc, const char **argv, const char *prefix,
>>> struct repository *repo UNUSED)
>>> {
>>> - int ret;
>>> + int ret = 0;
>>> + int include_untracked = 0;
>>> + struct option options[] = {
>>> + OPT_BOOL('u', "include-untracked", &include_untracked,
>>> + N_("include untracked files in stash")),
>>> + OPT_SET_INT('a', "all", &include_untracked,
>>> + N_("include ignored files in stash"),
>>> + INCLUDE_ALL_FILES),
>>> + OPT_END()
>>> + };
>>> struct strbuf stash_msg_buf = STRBUF_INIT;
>>> struct stash_info info = STASH_INFO_INIT;
>>> struct pathspec ps;
>>>
>>> - /* Starting with argv[1], since argv[0] is "create" */
>>> - strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
>>> + argc = parse_options(argc, argv, prefix, options,
>>> + git_stash_create_usage, 0);
>>> + strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
>>>
>>> memset(&ps, 0, sizeof(ps));
>>> - if (!check_changes_tracked_files(&ps))
>>> - return 0;
>>> + if (!include_untracked && !check_changes_tracked_files(&ps))
>>> + goto done;
>>>
>>> - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
>>> - NULL, 0);
>>> + ret = do_create_stash(&ps, &stash_msg_buf, include_untracked, 0, NULL,
>>> + 0, &info, NULL, 0);
>>> if (!ret)
>>> printf_ln("%s", oid_to_hex(&info.w_commit));
>>> + else if (ret == 1)
>>> + ret = 0;
>>>
>>> +done:
>>> free_stash_info(&info);
>>> strbuf_release(&stash_msg_buf);
>>> return ret;
>>> diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
>>> index 7211586..fe34879 100755
>>> --- a/t/t3903-stash.sh
>>> +++ b/t/t3903-stash.sh
>>> @@ -640,6 +640,76 @@ test_expect_success 'stash create - no changes' '
>>> test_must_be_empty actual
>>> '
>>>
>>> +# --all observes every untracked and ignored path in the worktree. Use one
>>> +# isolated repository for these checks so unrelated test state is not captured.
>>> +test_expect_success 'stash create with untracked options' '
>>> + test_when_finished "rm -rf stash-create-options" &&
>>> + test_create_repo stash-create-options &&
>>> + (
>>> + cd stash-create-options &&
>>> + test_commit base tracked base &&
>>> + echo create-ignored >.gitignore &&
>>> + git add .gitignore &&
>>> + git commit -m ignore &&
>>> +
>>> + git stash create -u >.git/actual &&
>>> + test_must_be_empty .git/actual &&
>>> + git stash create -a >.git/actual &&
>>> + test_must_be_empty .git/actual &&
>>> +
>>> + echo untracked >create-untracked &&
>>> + git stash create "without untracked" >.git/actual &&
>>> + test_must_be_empty .git/actual &&
>>> + short=$(git stash create "create untracked" -u) &&
>>> + long=$(git stash create --include-untracked "create untracked") &&
>>> + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
>>> + echo untracked >.git/expect &&
>>> + git show "$short^3:create-untracked" >.git/actual &&
>>> + test_cmp .git/expect .git/actual &&
>>> + branch=$(git symbolic-ref --short HEAD) &&
>>> + echo "On $branch: create untracked" >.git/expect &&
>>> + git show --pretty=%s -s "$short" >.git/actual &&
>>> + test_cmp .git/expect .git/actual &&
>>> + test_path_is_file create-untracked &&
>>> +
>>> + echo ignored >create-ignored &&
>>> + with_untracked=$(git stash create -u "create options") &&
>>> + test_must_fail git cat-file -e "$with_untracked^3:create-ignored" &&
>>> + short=$(git stash create "create options" -a) &&
>>> + long=$(git stash create --all "create options") &&
>>> + test_cmp_rev "$short^3^{tree}" "$long^3^{tree}" &&
>>> + echo ignored >.git/expect &&
>>> + git show "$short^3:create-ignored" >.git/actual &&
>>> + test_cmp .git/expect .git/actual &&
>>> + test_path_is_file create-untracked &&
>>> + test_path_is_file create-ignored &&
>>> +
>>> + echo staged >staged &&
>>> + git add staged &&
>>> + echo modified >>tracked &&
>>> + git diff >.git/before-worktree &&
>>> + git diff --cached >.git/before-index &&
>>> + git status --porcelain=v1 --ignored >.git/before-status &&
>>> + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
>>> + STASH_ID=$(git stash create -a -- -create-message) &&
>>> + git diff >.git/after-worktree &&
>>> + git diff --cached >.git/after-index &&
>>> + git status --porcelain=v1 --ignored >.git/after-status &&
>>> + test_cmp .git/before-worktree .git/after-worktree &&
>>> + test_cmp .git/before-index .git/after-index &&
>>> + test_cmp .git/before-status .git/after-status &&
>>> + test_must_fail git rev-parse --verify refs/stash >/dev/null 2>&1 &&
>>> + echo "On $branch: -create-message" >.git/expect &&
>>> + git show --pretty=%s -s "$STASH_ID" >.git/actual &&
>>> + test_cmp .git/expect .git/actual
>>> + )
>>> +'
>>> +
>>> +test_expect_success 'stash create rejects unknown options' '
>>> + test_expect_code 129 git stash create --unknown-option 2>err &&
>>> + test_grep "unknown option" err
>>> +'
>>> +
>>> test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
>>> git stash clear &&
>>> test_when_finished "git reset --hard HEAD" &&
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-05 5:55 ` 重田一聖
@ 2026-10-05 16:38 ` Phillip Wood
2026-10-06 9:25 ` 重田一聖
2026-10-05 16:43 ` Junio C Hamano
1 sibling, 1 reply; 20+ messages in thread
From: Phillip Wood @ 2026-10-05 16:38 UTC (permalink / raw)
To: 重田一聖, gitster
Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Kazumasa
On 05/10/2026 06:55, 重田一聖 wrote:
>
> From that perspective, I can see three possible directions.
>
> 1. Keep extending `stash create`.
>
> We could expose more of the existing `do_create_stash()`
> functionality through `stash create`, following the conventions of
> `stash push` for the creation-related options they have in common.
>
> This seems implementable, but even with
> `PARSE_OPT_STOP_AT_NON_OPTION` it would change the handling of
> messages that begin with an option-like argument. Those would need
> explicit disambiguation, such as `--`.
>
> There is also the pathspec question. If positional arguments
> continue to be joined to form the message, pathspecs need some other
> way to be distinguished from that message.
It is worth thinking about which options from "push" make sense with
"create" as the latter is really aimed at scripts rather than users. I
can see a script wanting to stash untracked files, but it may not make
sense to add interactive options like "--patch" which sometimes [1]
fails to clear the stashed changes from the worktree, that would be
problematic for scripts. I wonder if we really need pathspec support, or
if we do is "--pathspec-from-file" sufficient? I think it is fairly
unlikely that the message is going to start with '-' so using
PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way forward to me.
Adding "-m/--message" to match other commands that take a message would
certainly make sense.
Thanks
Phillip
[1] This happens when a user edits a hunk that looks like
@@ -1 +1,4 @@
-A
+a
+b
+c
+d
to
@@ -1 +1,3 @@
-A
+a
+b
+d
To clear the stashed changes, we apply the hunk in reverse, so we
try to apply
@@ -1,3 +1 @@
-a
-b
-d
+A
to a file that looks like
a
b
c
d
which fails because the '-' lines do not match the content of the
file.
>
> 2. Add a new stash subcommand for the creation functionality.
>
> This would leave the existing `stash create <message>` contract
> unchanged. Because the new command would not inherit `create`'s
> positional message grammar, its creation-related options and
> pathspec handling could follow conventions similar to `stash push`.
>
> This preserves the existing `create` grammar while avoiding the need
> to fit additional creation capabilities into it. The trade-off is
> adding another public stash subcommand and its long-term maintenance
> cost.
>
> 3. Add something like `--create-only` to `git stash push`.
>
> This would reuse the existing `push` option grammar without adding
> another subcommand.
>
> I also read the 2019 discussion around `git stash push --snapshot`.
> One concern there was that approximately the same end state could
> already be obtained with `git stash push && git stash apply`.
>
> I do not think that particular concern carries over directly here.
> `git stash create` already stops at object creation, but its public
> interface does not expose more of the creation capabilities already
> available in `do_create_stash()`. There is currently no public stash
> command that exposes those capabilities while retaining that
> create-only boundary.
>
> That does not mean a similar result cannot be constructed by other
> means. The missing piece is a public interface to the existing stash
> creation machinery at that boundary.
>
> Even so, there is still the separate question of whether `push` is
> the right place for a creation-only operation in the first place.
> The push-specific work around `do_create_stash()` would also need to
> be separated carefully.
>
> All three seem substantially broader than the original `-u` / `-a`
> patch.
>
> If this is worth pursuing further, which of these directions seems the
> most plausible? Also, is this the right thread to continue that design
> discussion, or would it be better to discuss it separately?
>
> Thanks again for the guidance,
> Kazumasa Shigeta
>
> On Fri, 2 Oct 2026 05:04:26 -0400, "重田一聖" <kazumasa.shigeta@kanamei.com> wrote:
>> Hi Junio,
>>
>>> we would prefer to hear what the user visible implication of
>>> "passing 0" is more than what mechanically is happening inside a
>>> program.
>>
>> The user-visible effect is that stash create cannot currently include
>> untracked or ignored files in the stash entry. If those are the only
>> changes, it creates no entry at all, while stash push and save can
>> include them with -u or -a as appropriate. I should have described that
>> difference directly instead of starting from the include_untracked
>> implementation detail.
>>
>>> ... was what you wanted to say, but I am not sure.
>>
>> Yes, exactly. I'll explain the backward-compatibility reason rather
>> than the mechanics of parse_options().
>>
>>> You already said that with "does not update, reset, or clean".
>>
>> I'll drop that paragraph.
>>
>>> if you did not make a breaking change to the established convention,
>>> is it worth saying?
>>
>> I don't think it adds anything here. I'll remove the exit-status
>> discussion from the commit message as well.
>>
>>> adding tests for comprehensive coverage is not something to boast
>>> about. Is it worth saying?
>>
>> I'll remove the test details from the commit message.
>>
>>> Why are we singling out only these two?
>>
>> I started by looking at the missing -u and -a support in create, and I
>> think that led me to focus too narrowly on those two when considering
>> the scope. I need to think more about whether this patch should remain
>> limited to those two.
>>
>> Thanks,
>> Kazumasa Shigeta
>>
>> On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
>>> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
>>>
>>>> `git stash create` always passes zero for the include_untracked parameter
>>>> of do_create_stash(), even though that helper already supports untracked
>>>> and ignored files and stash push/save expose those modes as
>>>> -u/--include-untracked and -a/--all.
>>>
>>> There may be no lies in what the above says, but we would prefer to
>>> hear what the user visible implication of "passing 0" is more than
>>> what mechanically is happening inside a program. For example:
>>>
>>> "git stash create", "git stash push", and "git stash save" are
>>> commands that create a new stash entry. The latter two are also
>>> responsible for storing the resulting stash entry to the reflog
>>> of the "refs/stash" ref, but have options to control what is
>>> included in the stash entry. Among these options, "create" only
>>> supports the equivalent of "-m <message." to record in the stash
>>> entry. Most notably, "-u" and "-a" options are missing.
>>>
>>>> Teach create to accept the same options and pass the existing mode
>>>> through. Unlike push/save, create continues to only create objects: it
>>>> does not update refs/stash, reset the index, or clean the working tree.
>>>
>>> Sure. It is a very concise and good description of what we want to
>>> do.
>>>
>>>> Use parse_options() for the new options and stop parsing at the first
>>>> non-option message word. This keeps option-like tokens after the message
>>>> as message text, while leading option-like arguments now follow Git's
>>>> normal option parsing. In particular, unknown or malformed leading
>>>> options are rejected instead of silently becoming a message, short
>>>> options may be combined, and `--` can be used when a message itself
>>>> begins with a dash.
>>>
>>> Why do we need to go into such a detail in the log message? What is
>>> the above paragraph designed to convey to the reader? Again, it may
>>> not be telling any lies, but it misses the point by being inconsiderate
>>> to your readers. What you need to tell them is _WHY_ you chose to
>>> use parse_options() in such a way. What were you trying to achieve?
>>>
>>> I am guessing that something along this line ...
>>>
>>> "git stash create" traditionally treated the rest of the command
>>> line as a message. For example,
>>>
>>> $ git stash create adding -u option
>>>
>>> has always been a request to create a stash entry with the
>>> string "adding -u option" as its message. We should not make it
>>> trigger the "-u" (include untracked) behavior for backward
>>> compatibility, by using parse_options() with stop-at-the-non-option
>>> mode to forbid it from reordering the command line arguments.
>>>
>>> ... was what you wanted to say, but I am not sure.
>>>
>>> How much of all these verbiage was written by AI by the way? You'd
>>> need to spend effort to make it readable to humans.
>>>
>>>> Keep create's existing no-change behavior: detect the usual no-change
>>>> case before do_create_stash() refreshes and writes the index, and return
>>>> success without printing an object name. If do_create_stash() still
>>>> reports its internal "nothing to create" result, map that to create's
>>>> public success status.
>>>
>>> You already said that with "does not update, reset, or clean".
>>>
>>>> This follows the stash subcommand exit-status convention established by
>>>> 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
>>>> subcommands return 0 on success, negative values on failure, and status 1
>>>> when applying a stash results in conflicts. cmd_stash() maps negative
>>>> subcommand failures to 128.
>>>
>>> Again, there may not be lies in here, but if you did not make a
>>> breaking change to the established convention, is it worth saying?
>>>
>>>> 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
>>>> internal include-untracked path while intentionally leaving the user
>>>> interface for "git stash create" unchanged. Reuse that machinery and
>>>> the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
>>>> creation path.
>>>>
>>>> Add coverage for short and long aliases, combined short options, the
>>>> untracked/ignored boundary including an ignored-only worktree, option
>>>> parsing and dash-leading messages, no-change behavior, and preservation
>>>> of refs/stash, the index state, and the working tree.
>>>
>>> Again, adding tests for comprehensive coverage is not something to
>>> boast about. Is it worth saying?
>>>
>>> Aren't -p/-S/-k/-q and pathspec support all about the creating half
>>> of "git stash push" that are not available to "git stash create",
>>> not just "-u" and "-a"? Why are we singling out only these two? It
>>> may be more worthwhile to explain the rationale behind such a design
>>> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-05 5:55 ` 重田一聖
2026-10-05 16:38 ` Phillip Wood
@ 2026-10-05 16:43 ` Junio C Hamano
2026-10-08 13:58 ` 重田一聖
1 sibling, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2026-10-05 16:43 UTC (permalink / raw)
To: 重田一聖; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
重田一聖 <kazumasa.shigeta@kanamei.com> writes:
> Your question made me realize that I had focused too narrowly on the
> untracked modes. The larger issue is not simply that `do_create_stash()`
> has capabilities that `git stash create` does not expose.
Brilliant. I agree that is the right way to frame the issue.
> Making more of those capabilities available through `create` would
> therefore mean either changing that contract or designing around it.
> That is a much larger interface decision than I appreciated when I sent
> the patch.
Perhaps, but I do not think it is too huge a backward-compatibility
breakage to forbid giving a message lazily (i.e., all strings in
argv[] after 'git stash create' gets concatenated and becomes a
single message) that begins with "-", with an escape hatch that a
leading "-m" will take the next argv[] element as the message, for
example.
> 2. Add a new stash subcommand for the creation functionality.
This is essentially how 'git stash save' came about, to give us ways
to control how a new stash entry is created and how the working tree
is cleared with command line options. In the beginning, you did not
even have to say 'save', because 'git stash <message>' was invented
as a way to say "the boss is here and tells me to work on something
unrelated. clear the slate with minimum number of keystrokes to
continue working on what I have been working on later." And that
later became 'git stash push'.
> 3. Add something like `--create-only` to `git stash push`.
This also would work and sounds the safest.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-05 16:38 ` Phillip Wood
@ 2026-10-06 9:25 ` 重田一聖
2026-10-06 9:57 ` Phillip Wood
0 siblings, 1 reply; 20+ messages in thread
From: 重田一聖 @ 2026-10-06 9:25 UTC (permalink / raw)
To: phillip.wood123, gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Phillip,
Thanks for the two patches removing the duplicate changes checks. I'll
wait for those to settle before revisiting the exit-status and no-change
handling.
> I can see a script wanting to stash untracked files, but it may not make
> sense to add interactive options like "--patch" which sometimes [1]
> fails to clear the stashed changes from the worktree, that would be
> problematic for scripts.
For `stash create`, I don't think the issue in [1] should apply, since
it does not remove the selected changes from the worktree. I still need
to think about whether `--patch` is worth supporting for `stash create`,
even though it is primarily aimed at scripts.
> I wonder if we really need pathspec support, or if we do is
> "--pathspec-from-file" sufficient?
I agree that positional pathspec support probably isn't necessary.
Since `create` is primarily aimed at scripts, `--pathspec-from-file`
seems sufficient. It also avoids giving positional arguments another
meaning while we are already dealing with the message ambiguity.
> I think it is fairly unlikely that the message is going to start with
> '-' so using PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way
> forward to me. Adding "-m/--message" to match other commands that take
> a message would certainly make sense.
Thanks for confirming those points.
Thanks,
Kazumasa
On Mon, 5 Oct 2026 17:38:07 +0100, Phillip Wood
<phillip.wood123@gmail.com> wrote:
> Hi Kazumasa
>
> On 05/10/2026 06:55, 重田一聖 wrote:
> >
> > From that perspective, I can see three possible directions.
> >
> > 1. Keep extending `stash create`.
> >
> > We could expose more of the existing `do_create_stash()`
> > functionality through `stash create`, following the conventions of
> > `stash push` for the creation-related options they have in common.
> >
> > This seems implementable, but even with
> > `PARSE_OPT_STOP_AT_NON_OPTION` it would change the handling of
> > messages that begin with an option-like argument. Those would need
> > explicit disambiguation, such as `--`.
> >
> > There is also the pathspec question. If positional arguments
> > continue to be joined to form the message, pathspecs need some other
> > way to be distinguished from that message.
>
> It is worth thinking about which options from "push" make sense with
> "create" as the latter is really aimed at scripts rather than users. I
> can see a script wanting to stash untracked files, but it may not make
> sense to add interactive options like "--patch" which sometimes [1]
> fails to clear the stashed changes from the worktree, that would be
> problematic for scripts. I wonder if we really need pathspec support, or
> if we do is "--pathspec-from-file" sufficient? I think it is fairly
> unlikely that the message is going to start with '-' so using
> PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way forward to me.
> Adding "-m/--message" to match other commands that take a message would
> certainly make sense.
>
> Thanks
>
> Phillip
>
> [1] This happens when a user edits a hunk that looks like
> @@ -1 +1,4 @@
> -A
> +a
> +b
> +c
> +d
>
> to
>
> @@ -1 +1,3 @@
> -A
> +a
> +b
> +d
>
> To clear the stashed changes, we apply the hunk in reverse, so we
> try to apply
>
> @@ -1,3 +1 @@
> -a
> -b
> -d
> +A
>
> to a file that looks like
>
> a
> b
> c
> d
>
> which fails because the '-' lines do not match the content of the
> file.
>
> >
> > 2. Add a new stash subcommand for the creation functionality.
> >
> > This would leave the existing `stash create <message>` contract
> > unchanged. Because the new command would not inherit `create`'s
> > positional message grammar, its creation-related options and
> > pathspec handling could follow conventions similar to `stash push`.
> >
> > This preserves the existing `create` grammar while avoiding the need
> > to fit additional creation capabilities into it. The trade-off is
> > adding another public stash subcommand and its long-term maintenance
> > cost.
> >
> > 3. Add something like `--create-only` to `git stash push`.
> >
> > This would reuse the existing `push` option grammar without adding
> > another subcommand.
> >
> > I also read the 2019 discussion around `git stash push --snapshot`.
> > One concern there was that approximately the same end state could
> > already be obtained with `git stash push && git stash apply`.
> >
> > I do not think that particular concern carries over directly here.
> > `git stash create` already stops at object creation, but its public
> > interface does not expose more of the creation capabilities already
> > available in `do_create_stash()`. There is currently no public stash
> > command that exposes those capabilities while retaining that
> > create-only boundary.
> >
> > That does not mean a similar result cannot be constructed by other
> > means. The missing piece is a public interface to the existing stash
> > creation machinery at that boundary.
> >
> > Even so, there is still the separate question of whether `push` is
> > the right place for a creation-only operation in the first place.
> > The push-specific work around `do_create_stash()` would also need to
> > be separated carefully.
> >
> > All three seem substantially broader than the original `-u` / `-a`
> > patch.
> >
> > If this is worth pursuing further, which of these directions seems the
> > most plausible? Also, is this the right thread to continue that design
> > discussion, or would it be better to discuss it separately?
> >
> > Thanks again for the guidance,
> > Kazumasa Shigeta
> >
> > On Fri, 2 Oct 2026 05:04:26 -0400, "重田一聖" <kazumasa.shigeta@kanamei.com> wrote:
> >> Hi Junio,
> >>
> >>> we would prefer to hear what the user visible implication of
> >>> "passing 0" is more than what mechanically is happening inside a
> >>> program.
> >>
> >> The user-visible effect is that stash create cannot currently include
> >> untracked or ignored files in the stash entry. If those are the only
> >> changes, it creates no entry at all, while stash push and save can
> >> include them with -u or -a as appropriate. I should have described that
> >> difference directly instead of starting from the include_untracked
> >> implementation detail.
> >>
> >>> ... was what you wanted to say, but I am not sure.
> >>
> >> Yes, exactly. I'll explain the backward-compatibility reason rather
> >> than the mechanics of parse_options().
> >>
> >>> You already said that with "does not update, reset, or clean".
> >>
> >> I'll drop that paragraph.
> >>
> >>> if you did not make a breaking change to the established convention,
> >>> is it worth saying?
> >>
> >> I don't think it adds anything here. I'll remove the exit-status
> >> discussion from the commit message as well.
> >>
> >>> adding tests for comprehensive coverage is not something to boast
> >>> about. Is it worth saying?
> >>
> >> I'll remove the test details from the commit message.
> >>
> >>> Why are we singling out only these two?
> >>
> >> I started by looking at the missing -u and -a support in create, and I
> >> think that led me to focus too narrowly on those two when considering
> >> the scope. I need to think more about whether this patch should remain
> >> limited to those two.
> >>
> >> Thanks,
> >> Kazumasa Shigeta
> >>
> >> On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> >>> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
> >>>
> >>>> `git stash create` always passes zero for the include_untracked parameter
> >>>> of do_create_stash(), even though that helper already supports untracked
> >>>> and ignored files and stash push/save expose those modes as
> >>>> -u/--include-untracked and -a/--all.
> >>>
> >>> There may be no lies in what the above says, but we would prefer to
> >>> hear what the user visible implication of "passing 0" is more than
> >>> what mechanically is happening inside a program. For example:
> >>>
> >>> "git stash create", "git stash push", and "git stash save" are
> >>> commands that create a new stash entry. The latter two are also
> >>> responsible for storing the resulting stash entry to the reflog
> >>> of the "refs/stash" ref, but have options to control what is
> >>> included in the stash entry. Among these options, "create" only
> >>> supports the equivalent of "-m <message." to record in the stash
> >>> entry. Most notably, "-u" and "-a" options are missing.
> >>>
> >>>> Teach create to accept the same options and pass the existing mode
> >>>> through. Unlike push/save, create continues to only create objects: it
> >>>> does not update refs/stash, reset the index, or clean the working tree.
> >>>
> >>> Sure. It is a very concise and good description of what we want to
> >>> do.
> >>>
> >>>> Use parse_options() for the new options and stop parsing at the first
> >>>> non-option message word. This keeps option-like tokens after the message
> >>>> as message text, while leading option-like arguments now follow Git's
> >>>> normal option parsing. In particular, unknown or malformed leading
> >>>> options are rejected instead of silently becoming a message, short
> >>>> options may be combined, and `--` can be used when a message itself
> >>>> begins with a dash.
> >>>
> >>> Why do we need to go into such a detail in the log message? What is
> >>> the above paragraph designed to convey to the reader? Again, it may
> >>> not be telling any lies, but it misses the point by being inconsiderate
> >>> to your readers. What you need to tell them is _WHY_ you chose to
> >>> use parse_options() in such a way. What were you trying to achieve?
> >>>
> >>> I am guessing that something along this line ...
> >>>
> >>> "git stash create" traditionally treated the rest of the command
> >>> line as a message. For example,
> >>>
> >>> $ git stash create adding -u option
> >>>
> >>> has always been a request to create a stash entry with the
> >>> string "adding -u option" as its message. We should not make it
> >>> trigger the "-u" (include untracked) behavior for backward
> >>> compatibility, by using parse_options() with stop-at-the-non-option
> >>> mode to forbid it from reordering the command line arguments.
> >>>
> >>> ... was what you wanted to say, but I am not sure.
> >>>
> >>> How much of all these verbiage was written by AI by the way? You'd
> >>> need to spend effort to make it readable to humans.
> >>>
> >>>> Keep create's existing no-change behavior: detect the usual no-change
> >>>> case before do_create_stash() refreshes and writes the index, and return
> >>>> success without printing an object name. If do_create_stash() still
> >>>> reports its internal "nothing to create" result, map that to create's
> >>>> public success status.
> >>>
> >>> You already said that with "does not update, reset, or clean".
> >>>
> >>>> This follows the stash subcommand exit-status convention established by
> >>>> 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> >>>> subcommands return 0 on success, negative values on failure, and status 1
> >>>> when applying a stash results in conflicts. cmd_stash() maps negative
> >>>> subcommand failures to 128.
> >>>
> >>> Again, there may not be lies in here, but if you did not make a
> >>> breaking change to the established convention, is it worth saying?
> >>>
> >>>> 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> >>>> internal include-untracked path while intentionally leaving the user
> >>>> interface for "git stash create" unchanged. Reuse that machinery and
> >>>> the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> >>>> creation path.
> >>>>
> >>>> Add coverage for short and long aliases, combined short options, the
> >>>> untracked/ignored boundary including an ignored-only worktree, option
> >>>> parsing and dash-leading messages, no-change behavior, and preservation
> >>>> of refs/stash, the index state, and the working tree.
> >>>
> >>> Again, adding tests for comprehensive coverage is not something to
> >>> boast about. Is it worth saying?
> >>>
> >>> Aren't -p/-S/-k/-q and pathspec support all about the creating half
> >>> of "git stash push" that are not available to "git stash create",
> >>> not just "-u" and "-a"? Why are we singling out only these two? It
> >>> may be more worthwhile to explain the rationale behind such a design
> >>> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-06 9:25 ` 重田一聖
@ 2026-10-06 9:57 ` Phillip Wood
2026-10-08 3:43 ` 重田一聖
0 siblings, 1 reply; 20+ messages in thread
From: Phillip Wood @ 2026-10-06 9:57 UTC (permalink / raw)
To: 重田一聖, gitster
Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Kazumasa
On 06/10/2026 10:25, 重田一聖 wrote:
> Hi Phillip,
>
> Thanks for the two patches removing the duplicate changes checks. I'll
> wait for those to settle before revisiting the exit-status and no-change
> handling.
>
>> I can see a script wanting to stash untracked files, but it may not make
>> sense to add interactive options like "--patch" which sometimes [1]
>> fails to clear the stashed changes from the worktree, that would be
>> problematic for scripts.
>
> For `stash create`, I don't think the issue in [1] should apply, since
> it does not remove the selected changes from the worktree.
Oh, good point, I'd completely forgotten that when I was writing
yesterday. If the script wants to remove the changes from the worktree
it still faces the same problem. As "git stash create" does not remove
the stashed changes from the worktree it would probably be simplest to
not support "--patch" or "pathspecs" so that the script can easily
remove the stashed changes with "git read-tree -m -u HEAD" (together
with "git clean" if it is stashing untracked files). If someone has a
use for pathspec support we can think about adding it but "git stash
create" has existed for 20 years without anyone requesting it.
Thanks
Phillip
> I still need
> to think about whether `--patch` is worth supporting for `stash create`,
> even though it is primarily aimed at scripts.
>
>> I wonder if we really need pathspec support, or if we do is
>> "--pathspec-from-file" sufficient?
>
> I agree that positional pathspec support probably isn't necessary.
> Since `create` is primarily aimed at scripts, `--pathspec-from-file`
> seems sufficient. It also avoids giving positional arguments another
> meaning while we are already dealing with the message ambiguity.
>
>> I think it is fairly unlikely that the message is going to start with
>> '-' so using PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way
>> forward to me. Adding "-m/--message" to match other commands that take
>> a message would certainly make sense.
>
> Thanks for confirming those points.
>
> Thanks,
> Kazumasa
>
> On Mon, 5 Oct 2026 17:38:07 +0100, Phillip Wood
> <phillip.wood123@gmail.com> wrote:
>> Hi Kazumasa
>>
>> On 05/10/2026 06:55, 重田一聖 wrote:
>>>
>>> From that perspective, I can see three possible directions.
>>>
>>> 1. Keep extending `stash create`.
>>>
>>> We could expose more of the existing `do_create_stash()`
>>> functionality through `stash create`, following the conventions of
>>> `stash push` for the creation-related options they have in common.
>>>
>>> This seems implementable, but even with
>>> `PARSE_OPT_STOP_AT_NON_OPTION` it would change the handling of
>>> messages that begin with an option-like argument. Those would need
>>> explicit disambiguation, such as `--`.
>>>
>>> There is also the pathspec question. If positional arguments
>>> continue to be joined to form the message, pathspecs need some other
>>> way to be distinguished from that message.
>>
>> It is worth thinking about which options from "push" make sense with
>> "create" as the latter is really aimed at scripts rather than users. I
>> can see a script wanting to stash untracked files, but it may not make
>> sense to add interactive options like "--patch" which sometimes [1]
>> fails to clear the stashed changes from the worktree, that would be
>> problematic for scripts. I wonder if we really need pathspec support, or
>> if we do is "--pathspec-from-file" sufficient? I think it is fairly
>> unlikely that the message is going to start with '-' so using
>> PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way forward to me.
>> Adding "-m/--message" to match other commands that take a message would
>> certainly make sense.
>>
>> Thanks
>>
>> Phillip
>>
>> [1] This happens when a user edits a hunk that looks like
>> @@ -1 +1,4 @@
>> -A
>> +a
>> +b
>> +c
>> +d
>>
>> to
>>
>> @@ -1 +1,3 @@
>> -A
>> +a
>> +b
>> +d
>>
>> To clear the stashed changes, we apply the hunk in reverse, so we
>> try to apply
>>
>> @@ -1,3 +1 @@
>> -a
>> -b
>> -d
>> +A
>>
>> to a file that looks like
>>
>> a
>> b
>> c
>> d
>>
>> which fails because the '-' lines do not match the content of the
>> file.
>>
>>>
>>> 2. Add a new stash subcommand for the creation functionality.
>>>
>>> This would leave the existing `stash create <message>` contract
>>> unchanged. Because the new command would not inherit `create`'s
>>> positional message grammar, its creation-related options and
>>> pathspec handling could follow conventions similar to `stash push`.
>>>
>>> This preserves the existing `create` grammar while avoiding the need
>>> to fit additional creation capabilities into it. The trade-off is
>>> adding another public stash subcommand and its long-term maintenance
>>> cost.
>>>
>>> 3. Add something like `--create-only` to `git stash push`.
>>>
>>> This would reuse the existing `push` option grammar without adding
>>> another subcommand.
>>>
>>> I also read the 2019 discussion around `git stash push --snapshot`.
>>> One concern there was that approximately the same end state could
>>> already be obtained with `git stash push && git stash apply`.
>>>
>>> I do not think that particular concern carries over directly here.
>>> `git stash create` already stops at object creation, but its public
>>> interface does not expose more of the creation capabilities already
>>> available in `do_create_stash()`. There is currently no public stash
>>> command that exposes those capabilities while retaining that
>>> create-only boundary.
>>>
>>> That does not mean a similar result cannot be constructed by other
>>> means. The missing piece is a public interface to the existing stash
>>> creation machinery at that boundary.
>>>
>>> Even so, there is still the separate question of whether `push` is
>>> the right place for a creation-only operation in the first place.
>>> The push-specific work around `do_create_stash()` would also need to
>>> be separated carefully.
>>>
>>> All three seem substantially broader than the original `-u` / `-a`
>>> patch.
>>>
>>> If this is worth pursuing further, which of these directions seems the
>>> most plausible? Also, is this the right thread to continue that design
>>> discussion, or would it be better to discuss it separately?
>>>
>>> Thanks again for the guidance,
>>> Kazumasa Shigeta
>>>
>>> On Fri, 2 Oct 2026 05:04:26 -0400, "重田一聖" <kazumasa.shigeta@kanamei.com> wrote:
>>>> Hi Junio,
>>>>
>>>>> we would prefer to hear what the user visible implication of
>>>>> "passing 0" is more than what mechanically is happening inside a
>>>>> program.
>>>>
>>>> The user-visible effect is that stash create cannot currently include
>>>> untracked or ignored files in the stash entry. If those are the only
>>>> changes, it creates no entry at all, while stash push and save can
>>>> include them with -u or -a as appropriate. I should have described that
>>>> difference directly instead of starting from the include_untracked
>>>> implementation detail.
>>>>
>>>>> ... was what you wanted to say, but I am not sure.
>>>>
>>>> Yes, exactly. I'll explain the backward-compatibility reason rather
>>>> than the mechanics of parse_options().
>>>>
>>>>> You already said that with "does not update, reset, or clean".
>>>>
>>>> I'll drop that paragraph.
>>>>
>>>>> if you did not make a breaking change to the established convention,
>>>>> is it worth saying?
>>>>
>>>> I don't think it adds anything here. I'll remove the exit-status
>>>> discussion from the commit message as well.
>>>>
>>>>> adding tests for comprehensive coverage is not something to boast
>>>>> about. Is it worth saying?
>>>>
>>>> I'll remove the test details from the commit message.
>>>>
>>>>> Why are we singling out only these two?
>>>>
>>>> I started by looking at the missing -u and -a support in create, and I
>>>> think that led me to focus too narrowly on those two when considering
>>>> the scope. I need to think more about whether this patch should remain
>>>> limited to those two.
>>>>
>>>> Thanks,
>>>> Kazumasa Shigeta
>>>>
>>>> On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
>>>>> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
>>>>>
>>>>>> `git stash create` always passes zero for the include_untracked parameter
>>>>>> of do_create_stash(), even though that helper already supports untracked
>>>>>> and ignored files and stash push/save expose those modes as
>>>>>> -u/--include-untracked and -a/--all.
>>>>>
>>>>> There may be no lies in what the above says, but we would prefer to
>>>>> hear what the user visible implication of "passing 0" is more than
>>>>> what mechanically is happening inside a program. For example:
>>>>>
>>>>> "git stash create", "git stash push", and "git stash save" are
>>>>> commands that create a new stash entry. The latter two are also
>>>>> responsible for storing the resulting stash entry to the reflog
>>>>> of the "refs/stash" ref, but have options to control what is
>>>>> included in the stash entry. Among these options, "create" only
>>>>> supports the equivalent of "-m <message." to record in the stash
>>>>> entry. Most notably, "-u" and "-a" options are missing.
>>>>>
>>>>>> Teach create to accept the same options and pass the existing mode
>>>>>> through. Unlike push/save, create continues to only create objects: it
>>>>>> does not update refs/stash, reset the index, or clean the working tree.
>>>>>
>>>>> Sure. It is a very concise and good description of what we want to
>>>>> do.
>>>>>
>>>>>> Use parse_options() for the new options and stop parsing at the first
>>>>>> non-option message word. This keeps option-like tokens after the message
>>>>>> as message text, while leading option-like arguments now follow Git's
>>>>>> normal option parsing. In particular, unknown or malformed leading
>>>>>> options are rejected instead of silently becoming a message, short
>>>>>> options may be combined, and `--` can be used when a message itself
>>>>>> begins with a dash.
>>>>>
>>>>> Why do we need to go into such a detail in the log message? What is
>>>>> the above paragraph designed to convey to the reader? Again, it may
>>>>> not be telling any lies, but it misses the point by being inconsiderate
>>>>> to your readers. What you need to tell them is _WHY_ you chose to
>>>>> use parse_options() in such a way. What were you trying to achieve?
>>>>>
>>>>> I am guessing that something along this line ...
>>>>>
>>>>> "git stash create" traditionally treated the rest of the command
>>>>> line as a message. For example,
>>>>>
>>>>> $ git stash create adding -u option
>>>>>
>>>>> has always been a request to create a stash entry with the
>>>>> string "adding -u option" as its message. We should not make it
>>>>> trigger the "-u" (include untracked) behavior for backward
>>>>> compatibility, by using parse_options() with stop-at-the-non-option
>>>>> mode to forbid it from reordering the command line arguments.
>>>>>
>>>>> ... was what you wanted to say, but I am not sure.
>>>>>
>>>>> How much of all these verbiage was written by AI by the way? You'd
>>>>> need to spend effort to make it readable to humans.
>>>>>
>>>>>> Keep create's existing no-change behavior: detect the usual no-change
>>>>>> case before do_create_stash() refreshes and writes the index, and return
>>>>>> success without printing an object name. If do_create_stash() still
>>>>>> reports its internal "nothing to create" result, map that to create's
>>>>>> public success status.
>>>>>
>>>>> You already said that with "does not update, reset, or clean".
>>>>>
>>>>>> This follows the stash subcommand exit-status convention established by
>>>>>> 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
>>>>>> subcommands return 0 on success, negative values on failure, and status 1
>>>>>> when applying a stash results in conflicts. cmd_stash() maps negative
>>>>>> subcommand failures to 128.
>>>>>
>>>>> Again, there may not be lies in here, but if you did not make a
>>>>> breaking change to the established convention, is it worth saying?
>>>>>
>>>>>> 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
>>>>>> internal include-untracked path while intentionally leaving the user
>>>>>> interface for "git stash create" unchanged. Reuse that machinery and
>>>>>> the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
>>>>>> creation path.
>>>>>>
>>>>>> Add coverage for short and long aliases, combined short options, the
>>>>>> untracked/ignored boundary including an ignored-only worktree, option
>>>>>> parsing and dash-leading messages, no-change behavior, and preservation
>>>>>> of refs/stash, the index state, and the working tree.
>>>>>
>>>>> Again, adding tests for comprehensive coverage is not something to
>>>>> boast about. Is it worth saying?
>>>>>
>>>>> Aren't -p/-S/-k/-q and pathspec support all about the creating half
>>>>> of "git stash push" that are not available to "git stash create",
>>>>> not just "-u" and "-a"? Why are we singling out only these two? It
>>>>> may be more worthwhile to explain the rationale behind such a design
>>>>> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-06 9:57 ` Phillip Wood
@ 2026-10-08 3:43 ` 重田一聖
0 siblings, 0 replies; 20+ messages in thread
From: 重田一聖 @ 2026-10-08 3:43 UTC (permalink / raw)
To: phillip.wood123, gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Phillip,
> As "git stash create" does not remove the stashed changes from the
> worktree it would probably be simplest to not support "--patch" or
> "pathspecs" so that the script can easily remove the stashed changes
> with "git read-tree -m -u HEAD" (together with "git clean" if it is
> stashing untracked files). If someone has a use for pathspec support
> we can think about adding it but "git stash create" has existed for 20
> years without anyone requesting it.
That makes sense. If nobody has asked for pathspec support in all that
time, maybe I was overthinking it a little.
I had started to think that if we are going to make `stash create`
parse options, it might be better to expose everything that
`do_create_stash()` can already handle at the same time.
But even if we do not expose all the options now, I think introducing
option parsing to `stash create` has a clear benefit on its own. If we
make that change now, adding more script-oriented options later should
not require us to revisit the basic decision to make `stash create`
parse options, or the backward-compatibility discussion around that
change.
So, at least for now, I am thinking of leaving `--patch` and pathspecs
out.
Thanks,
Kazumasa Shigeta
On Tue, 6 Oct 2026 10:57:58 +0100, Phillip Wood
<phillip.wood123@gmail.com> wrote:
> Hi Kazumasa
>
> On 06/10/2026 10:25, 重田一聖 wrote:
> > Hi Phillip,
> >
> > Thanks for the two patches removing the duplicate changes checks. I'll
> > wait for those to settle before revisiting the exit-status and no-change
> > handling.
> >
> >> I can see a script wanting to stash untracked files, but it may not make
> >> sense to add interactive options like "--patch" which sometimes [1]
> >> fails to clear the stashed changes from the worktree, that would be
> >> problematic for scripts.
> >
> > For `stash create`, I don't think the issue in [1] should apply, since
> > it does not remove the selected changes from the worktree.
>
> Oh, good point, I'd completely forgotten that when I was writing
> yesterday. If the script wants to remove the changes from the worktree
> it still faces the same problem. As "git stash create" does not remove
> the stashed changes from the worktree it would probably be simplest to
> not support "--patch" or "pathspecs" so that the script can easily
> remove the stashed changes with "git read-tree -m -u HEAD" (together
> with "git clean" if it is stashing untracked files). If someone has a
> use for pathspec support we can think about adding it but "git stash
> create" has existed for 20 years without anyone requesting it.
>
> Thanks
>
> Phillip
> > I still need
> > to think about whether `--patch` is worth supporting for `stash create`,
> > even though it is primarily aimed at scripts.
> >
> >> I wonder if we really need pathspec support, or if we do is
> >> "--pathspec-from-file" sufficient?
> >
> > I agree that positional pathspec support probably isn't necessary.
> > Since `create` is primarily aimed at scripts, `--pathspec-from-file`
> > seems sufficient. It also avoids giving positional arguments another
> > meaning while we are already dealing with the message ambiguity.
> >
> >> I think it is fairly unlikely that the message is going to start with
> >> '-' so using PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way
> >> forward to me. Adding "-m/--message" to match other commands that take
> >> a message would certainly make sense.
> >
> > Thanks for confirming those points.
> >
> > Thanks,
> > Kazumasa
> >
> > On Mon, 5 Oct 2026 17:38:07 +0100, Phillip Wood
> > <phillip.wood123@gmail.com> wrote:
> >> Hi Kazumasa
> >>
> >> On 05/10/2026 06:55, 重田一聖 wrote:
> >>>
> >>> From that perspective, I can see three possible directions.
> >>>
> >>> 1. Keep extending `stash create`.
> >>>
> >>> We could expose more of the existing `do_create_stash()`
> >>> functionality through `stash create`, following the conventions of
> >>> `stash push` for the creation-related options they have in common.
> >>>
> >>> This seems implementable, but even with
> >>> `PARSE_OPT_STOP_AT_NON_OPTION` it would change the handling of
> >>> messages that begin with an option-like argument. Those would need
> >>> explicit disambiguation, such as `--`.
> >>>
> >>> There is also the pathspec question. If positional arguments
> >>> continue to be joined to form the message, pathspecs need some other
> >>> way to be distinguished from that message.
> >>
> >> It is worth thinking about which options from "push" make sense with
> >> "create" as the latter is really aimed at scripts rather than users. I
> >> can see a script wanting to stash untracked files, but it may not make
> >> sense to add interactive options like "--patch" which sometimes [1]
> >> fails to clear the stashed changes from the worktree, that would be
> >> problematic for scripts. I wonder if we really need pathspec support, or
> >> if we do is "--pathspec-from-file" sufficient? I think it is fairly
> >> unlikely that the message is going to start with '-' so using
> >> PARSE_OPT_STOP_AT_NON_OPTION seems like a reasonable way forward to me.
> >> Adding "-m/--message" to match other commands that take a message would
> >> certainly make sense.
> >>
> >> Thanks
> >>
> >> Phillip
> >>
> >> [1] This happens when a user edits a hunk that looks like
> >> @@ -1 +1,4 @@
> >> -A
> >> +a
> >> +b
> >> +c
> >> +d
> >>
> >> to
> >>
> >> @@ -1 +1,3 @@
> >> -A
> >> +a
> >> +b
> >> +d
> >>
> >> To clear the stashed changes, we apply the hunk in reverse, so we
> >> try to apply
> >>
> >> @@ -1,3 +1 @@
> >> -a
> >> -b
> >> -d
> >> +A
> >>
> >> to a file that looks like
> >>
> >> a
> >> b
> >> c
> >> d
> >>
> >> which fails because the '-' lines do not match the content of the
> >> file.
> >>
> >>>
> >>> 2. Add a new stash subcommand for the creation functionality.
> >>>
> >>> This would leave the existing `stash create <message>` contract
> >>> unchanged. Because the new command would not inherit `create`'s
> >>> positional message grammar, its creation-related options and
> >>> pathspec handling could follow conventions similar to `stash push`.
> >>>
> >>> This preserves the existing `create` grammar while avoiding the need
> >>> to fit additional creation capabilities into it. The trade-off is
> >>> adding another public stash subcommand and its long-term maintenance
> >>> cost.
> >>>
> >>> 3. Add something like `--create-only` to `git stash push`.
> >>>
> >>> This would reuse the existing `push` option grammar without adding
> >>> another subcommand.
> >>>
> >>> I also read the 2019 discussion around `git stash push --snapshot`.
> >>> One concern there was that approximately the same end state could
> >>> already be obtained with `git stash push && git stash apply`.
> >>>
> >>> I do not think that particular concern carries over directly here.
> >>> `git stash create` already stops at object creation, but its public
> >>> interface does not expose more of the creation capabilities already
> >>> available in `do_create_stash()`. There is currently no public stash
> >>> command that exposes those capabilities while retaining that
> >>> create-only boundary.
> >>>
> >>> That does not mean a similar result cannot be constructed by other
> >>> means. The missing piece is a public interface to the existing stash
> >>> creation machinery at that boundary.
> >>>
> >>> Even so, there is still the separate question of whether `push` is
> >>> the right place for a creation-only operation in the first place.
> >>> The push-specific work around `do_create_stash()` would also need to
> >>> be separated carefully.
> >>>
> >>> All three seem substantially broader than the original `-u` / `-a`
> >>> patch.
> >>>
> >>> If this is worth pursuing further, which of these directions seems the
> >>> most plausible? Also, is this the right thread to continue that design
> >>> discussion, or would it be better to discuss it separately?
> >>>
> >>> Thanks again for the guidance,
> >>> Kazumasa Shigeta
> >>>
> >>> On Fri, 2 Oct 2026 05:04:26 -0400, "重田一聖" <kazumasa.shigeta@kanamei.com> wrote:
> >>>> Hi Junio,
> >>>>
> >>>>> we would prefer to hear what the user visible implication of
> >>>>> "passing 0" is more than what mechanically is happening inside a
> >>>>> program.
> >>>>
> >>>> The user-visible effect is that stash create cannot currently include
> >>>> untracked or ignored files in the stash entry. If those are the only
> >>>> changes, it creates no entry at all, while stash push and save can
> >>>> include them with -u or -a as appropriate. I should have described that
> >>>> difference directly instead of starting from the include_untracked
> >>>> implementation detail.
> >>>>
> >>>>> ... was what you wanted to say, but I am not sure.
> >>>>
> >>>> Yes, exactly. I'll explain the backward-compatibility reason rather
> >>>> than the mechanics of parse_options().
> >>>>
> >>>>> You already said that with "does not update, reset, or clean".
> >>>>
> >>>> I'll drop that paragraph.
> >>>>
> >>>>> if you did not make a breaking change to the established convention,
> >>>>> is it worth saying?
> >>>>
> >>>> I don't think it adds anything here. I'll remove the exit-status
> >>>> discussion from the commit message as well.
> >>>>
> >>>>> adding tests for comprehensive coverage is not something to boast
> >>>>> about. Is it worth saying?
> >>>>
> >>>> I'll remove the test details from the commit message.
> >>>>
> >>>>> Why are we singling out only these two?
> >>>>
> >>>> I started by looking at the missing -u and -a support in create, and I
> >>>> think that led me to focus too narrowly on those two when considering
> >>>> the scope. I need to think more about whether this patch should remain
> >>>> limited to those two.
> >>>>
> >>>> Thanks,
> >>>> Kazumasa Shigeta
> >>>>
> >>>> On Thu, 01 Oct 2026 10:03:13 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> >>>>> Kazumasa Shigeta <kazumasa.shigeta@kanamei.com> writes:
> >>>>>
> >>>>>> `git stash create` always passes zero for the include_untracked parameter
> >>>>>> of do_create_stash(), even though that helper already supports untracked
> >>>>>> and ignored files and stash push/save expose those modes as
> >>>>>> -u/--include-untracked and -a/--all.
> >>>>>
> >>>>> There may be no lies in what the above says, but we would prefer to
> >>>>> hear what the user visible implication of "passing 0" is more than
> >>>>> what mechanically is happening inside a program. For example:
> >>>>>
> >>>>> "git stash create", "git stash push", and "git stash save" are
> >>>>> commands that create a new stash entry. The latter two are also
> >>>>> responsible for storing the resulting stash entry to the reflog
> >>>>> of the "refs/stash" ref, but have options to control what is
> >>>>> included in the stash entry. Among these options, "create" only
> >>>>> supports the equivalent of "-m <message." to record in the stash
> >>>>> entry. Most notably, "-u" and "-a" options are missing.
> >>>>>
> >>>>>> Teach create to accept the same options and pass the existing mode
> >>>>>> through. Unlike push/save, create continues to only create objects: it
> >>>>>> does not update refs/stash, reset the index, or clean the working tree.
> >>>>>
> >>>>> Sure. It is a very concise and good description of what we want to
> >>>>> do.
> >>>>>
> >>>>>> Use parse_options() for the new options and stop parsing at the first
> >>>>>> non-option message word. This keeps option-like tokens after the message
> >>>>>> as message text, while leading option-like arguments now follow Git's
> >>>>>> normal option parsing. In particular, unknown or malformed leading
> >>>>>> options are rejected instead of silently becoming a message, short
> >>>>>> options may be combined, and `--` can be used when a message itself
> >>>>>> begins with a dash.
> >>>>>
> >>>>> Why do we need to go into such a detail in the log message? What is
> >>>>> the above paragraph designed to convey to the reader? Again, it may
> >>>>> not be telling any lies, but it misses the point by being inconsiderate
> >>>>> to your readers. What you need to tell them is _WHY_ you chose to
> >>>>> use parse_options() in such a way. What were you trying to achieve?
> >>>>>
> >>>>> I am guessing that something along this line ...
> >>>>>
> >>>>> "git stash create" traditionally treated the rest of the command
> >>>>> line as a message. For example,
> >>>>>
> >>>>> $ git stash create adding -u option
> >>>>>
> >>>>> has always been a request to create a stash entry with the
> >>>>> string "adding -u option" as its message. We should not make it
> >>>>> trigger the "-u" (include untracked) behavior for backward
> >>>>> compatibility, by using parse_options() with stop-at-the-non-option
> >>>>> mode to forbid it from reordering the command line arguments.
> >>>>>
> >>>>> ... was what you wanted to say, but I am not sure.
> >>>>>
> >>>>> How much of all these verbiage was written by AI by the way? You'd
> >>>>> need to spend effort to make it readable to humans.
> >>>>>
> >>>>>> Keep create's existing no-change behavior: detect the usual no-change
> >>>>>> case before do_create_stash() refreshes and writes the index, and return
> >>>>>> success without printing an object name. If do_create_stash() still
> >>>>>> reports its internal "nothing to create" result, map that to create's
> >>>>>> public success status.
> >>>>>
> >>>>> You already said that with "does not update, reset, or clean".
> >>>>>
> >>>>>> This follows the stash subcommand exit-status convention established by
> >>>>>> 786fc390465f (stash: reserve exit status 1 for conflicts, 2026-09-03):
> >>>>>> subcommands return 0 on success, negative values on failure, and status 1
> >>>>>> when applying a stash results in conflicts. cmd_stash() maps negative
> >>>>>> subcommand failures to 128.
> >>>>>
> >>>>> Again, there may not be lies in here, but if you did not make a
> >>>>> breaking change to the established convention, is it worth saying?
> >>>>>
> >>>>>> 9ca6326dff29 (stash: refactor stash_create, 2017-02-19) added the
> >>>>>> internal include-untracked path while intentionally leaving the user
> >>>>>> interface for "git stash create" unchanged. Reuse that machinery and
> >>>>>> the existing INCLUDE_ALL_FILES mode rather than adding a separate stash
> >>>>>> creation path.
> >>>>>>
> >>>>>> Add coverage for short and long aliases, combined short options, the
> >>>>>> untracked/ignored boundary including an ignored-only worktree, option
> >>>>>> parsing and dash-leading messages, no-change behavior, and preservation
> >>>>>> of refs/stash, the index state, and the working tree.
> >>>>>
> >>>>> Again, adding tests for comprehensive coverage is not something to
> >>>>> boast about. Is it worth saying?
> >>>>>
> >>>>> Aren't -p/-S/-k/-q and pathspec support all about the creating half
> >>>>> of "git stash push" that are not available to "git stash create",
> >>>>> not just "-u" and "-a"? Why are we singling out only these two? It
> >>>>> may be more worthwhile to explain the rationale behind such a design
> >>>>> decision.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-05 16:43 ` Junio C Hamano
@ 2026-10-08 13:58 ` 重田一聖
2026-10-09 19:05 ` Junio C Hamano
0 siblings, 1 reply; 20+ messages in thread
From: 重田一聖 @ 2026-10-08 13:58 UTC (permalink / raw)
To: gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
Hi Junio,
> Perhaps, but I do not think it is too huge a backward-compatibility
> breakage to forbid giving a message lazily (i.e., all strings in
> argv[] after 'git stash create' gets concatenated and becomes a
> single message) that begins with "-", with an escape hatch that a
> leading "-m" will take the next argv[] element as the message, for
> example.
If the compatibility change for messages beginning with `-` is
acceptable with an `-m` escape hatch like the one you mentioned,
I think a new subcommand is unnecessary.
>> 3. Add something like `--create-only` to `git stash push`.
>
> This also would work and sounds the safest.
I think so too, at least for the immediate change.
But `stash create` is already intended for scripts, and I think we
may want to add more script-oriented options to it in the future.
If we put the create-only interface under `push`, each future option
for that interface would also need a decision about whether it
applies to ordinary `push`, `--create-only`, or both.
That would add another set of option rules inside `push` that future
additions would have to account for.
So I currently think extending `stash create` directly is the better
long-term approach.
Phillip's comments about which options make sense for scripts also
made me reconsider the scope of this patch.
I realized that I had been treating two decisions as one:
whether to introduce option parsing in `stash create`, and how much
of what `do_create_stash()` already supports to expose now.
I think adding option parsing and `-m/--message` now would be useful,
even if we only expose a few options. That is because we should not
need to revisit the basic parsing and message compatibility question
just to add another script-oriented option later.
That makes me think we don't need to expose everything
`do_create_stash()` already supports in this patch. I now think
we don't need to settle the public rules for other options and
their interactions until there is a concrete need.
In particular, I would prioritize leaving out options for which
I haven't found a concrete request and whose interaction rules
might make future additions harder if fixed now.
So my conclusion for this patch is to expose `-m/-q/-u/-a`.
The untracked modes address the original gap, and I think
`-q/--quiet` makes sense for a script-oriented command. It would
use the existing creation helper's quiet behavior without
suppressing the object name returned on success.
For now, I would leave `--patch` and pathspec support out.
I also checked `-k/--keep-index`. It concerns the cleanup and
preservation behavior of `push`, rather than creation of the stash
object itself, so I would leave it out.
I still need to think a little more about `--staged`. It does affect
what goes into the stash object, but its interactions with other
options are less straightforward. For example, `stash push` does not
allow it with `-u/-a` or `--pathspec-from-file`, while patch mode
takes precedence over it.
So far I haven't found a concrete request specifically for
`stash create --staged`, but I'm still checking. Unless I find one,
I would like to leave `--staged` out of this patch rather than
decide those interaction rules now.
Thanks,
Kazumasa Shigeta
On Mon, 05 Oct 2026 09:43:03 -0700, Junio C Hamano <gitster@pobox.com> wrote:
> 重田一聖 <kazumasa.shigeta@kanamei.com> writes:
>
> > Your question made me realize that I had focused too narrowly on the
> > untracked modes. The larger issue is not simply that `do_create_stash()`
> > has capabilities that `git stash create` does not expose.
>
> Brilliant. I agree that is the right way to frame the issue.
>
> > Making more of those capabilities available through `create` would
> > therefore mean either changing that contract or designing around it.
> > That is a much larger interface decision than I appreciated when I sent
> > the patch.
>
> Perhaps, but I do not think it is too huge a backward-compatibility
> breakage to forbid giving a message lazily (i.e., all strings in
> argv[] after 'git stash create' gets concatenated and becomes a
> single message) that begins with "-", with an escape hatch that a
> leading "-m" will take the next argv[] element as the message, for
> example.
>
> > 2. Add a new stash subcommand for the creation functionality.
>
> This is essentially how 'git stash save' came about, to give us ways
> to control how a new stash entry is created and how the working tree
> is cleared with command line options. In the beginning, you did not
> even have to say 'save', because 'git stash <message>' was invented
> as a way to say "the boss is here and tells me to work on something
> unrelated. clear the slate with minimum number of keystrokes to
> continue working on what I have been working on later." And that
> later became 'git stash push'.
>
> > 3. Add something like `--create-only` to `git stash push`.
>
> This also would work and sounds the safest.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-08 13:58 ` 重田一聖
@ 2026-10-09 19:05 ` Junio C Hamano
2026-10-10 6:34 ` 重田一聖
0 siblings, 1 reply; 20+ messages in thread
From: Junio C Hamano @ 2026-10-09 19:05 UTC (permalink / raw)
To: 重田一聖; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
重田一聖 <kazumasa.shigeta@kanamei.com> writes:
> I realized that I had been treating two decisions as one:
> whether to introduce option parsing in `stash create`, and how much
> of what `do_create_stash()` already supports to expose now.
>
> I think adding option parsing and `-m/--message` now would be useful,
> even if we only expose a few options. That is because we should not
> need to revisit the basic parsing and message compatibility question
> just to add another script-oriented option later.
>
> That makes me think we don't need to expose everything
> `do_create_stash()` already supports in this patch. I now think
> we don't need to settle the public rules for other options and
> their interactions until there is a concrete need.
>
> In particular, I would prioritize leaving out options for which
> I haven't found a concrete request and whose interaction rules
> might make future additions harder if fixed now.
Stepping back a bit, I think the long-term goal should be to extend
the 'create' and 'store' pair sufficiently to allow script writers
to write their own 'git stash push' on top of them if they wanted
to. 'git stash create' does not have to be fully capable of doing
so with the current topic alone, but do you agree that improving
'create' in such a way should be our long-term goal?
With that future vision in mind, I am not sure I follow what you
said above. Shouldn't 'stash create --foo' work the same way as
'stash push --foo' while creating the stash entry, if '--foo' is
not an option relevant only to 'stash store'? Under what
circumstances does a '--foo' option that 'push' has (and for which
you have not seen a request) have to behave differently when added
to 'create', leaving a stash entry of a different shape from the
one 'push --foo' would create?
If the wish is "we want to start small because thinking about
each and every one of them and making sure they work correctly
is too much work for my liking", I would understand. But I
do not understand how "we worry we may overspecify without
knowing the need" would apply to this particular case, even
though it is a good thing to keep in mind in other situations.
Thanks.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2] stash: expose untracked modes in create
2026-10-09 19:05 ` Junio C Hamano
@ 2026-10-10 6:34 ` 重田一聖
0 siblings, 0 replies; 20+ messages in thread
From: 重田一聖 @ 2026-10-10 6:34 UTC (permalink / raw)
To: gitster; +Cc: git, shabbir.r.bhojani, phillip.wood, ps
> 'git stash create' does not have to be fully capable of doing
> so with the current topic alone, but do you agree that improving
> 'create' in such a way should be our long-term goal?
Yes, I agree.
> Shouldn't 'stash create --foo' work the same way as
> 'stash push --foo' while creating the stash entry, if '--foo' is
> not an option relevant only to 'stash store'?
Yes, that's exactly what I had in mind.
> If the wish is "we want to start small because thinking about
> each and every one of them and making sure they work correctly
> is too much work for my liking", I would understand.
Yes, that is what I meant. When you asked why I was singling out
`-u` and `-a`, I went back and examined the other options. I'm glad
I did. It helped me understand `stash create` better. Thank you for
raising that question.
But when I tried to explain why I wanted to leave the other options
out, I gave a poor reason. Sorry for the confusion.
With that in mind, I'd like to limit this patch to `-m/-q/-u/-a`
and leave `--patch`, `--staged`, and pathspec support for follow-up
patches. Does that sound reasonable?
Thanks,
Kazumasa Shigeta
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-10-10 6:34 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-29 7:42 [PATCH] stash: expose untracked modes in create Kazumasa Shigeta
2026-09-29 16:08 ` Phillip Wood
2026-10-01 17:44 ` 重田一聖
2026-10-05 16:37 ` Phillip Wood
2026-10-01 4:21 ` [PATCH v2] " Kazumasa Shigeta
2026-10-01 11:32 ` Patrick Steinhardt
2026-10-01 16:01 ` 重田一聖
2026-10-01 16:36 ` Junio C Hamano
2026-10-01 17:03 ` Junio C Hamano
2026-10-02 1:53 ` 重田一聖
2026-10-02 9:04 ` 重田一聖
2026-10-05 5:55 ` 重田一聖
2026-10-05 16:38 ` Phillip Wood
2026-10-06 9:25 ` 重田一聖
2026-10-06 9:57 ` Phillip Wood
2026-10-08 3:43 ` 重田一聖
2026-10-05 16:43 ` Junio C Hamano
2026-10-08 13:58 ` 重田一聖
2026-10-09 19:05 ` Junio C Hamano
2026-10-10 6:34 ` 重田一聖
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox