Subject: [PATCH/RFC] stash: add --include-untracked support to git stash create

From: "Shabbir Bhojani via GitGitGadget" <[email-masked]>
Date: Sun, 29 Mar 2026 07:16:20 +0000

From: SBhojani <[email-masked]>

The `git stash create` command now supports the `--include-untracked` flag,
allowing users to include untracked files in the stash entry. This brings
parity with `git stash push`, which already supports this option.

Previously, `git stash create` would only stash tracked changes. With this
change, users can optionally include untracked files by specifying
`--include-untracked`. The implementation involves parsing the new option
in `create_stash` and passing it to `do_create_stash`, which handles the
creation of the stash entry.

The check for tracked changes is skipped when `--include-untracked` is
set, as `do_create_stash` performs its own check internally. Test cases
were added to `t3903-stash.sh` to ensure the correct behavior, and the
documentation was updated accordingly.

Signed-off-by: SBhojani <[email-masked]>
---
    stash: add --include-untracked support to git stash create
    
    This is a single-commit pull request, and the commit message already
    describes the changes.
    
    I just want to add context for where I felt the need for this. While
    working on a logical single commit task, my working tree is often in a
    state where certain changes are staged and others are not. I use this
    staging strategy as a conceptual model of which changes I intend to
    mostly keep as they are and which I intend to change, rewrite, refactor,
    discard, or not commit. Some of the changes in the latter category might
    be new untracked files. Now, I often use (my git GUI tool's equivalent
    of) git stash --include-untracked followed by git stash apply to take a
    snapshot of these changes to be able to come back to the state if I mess
    things up too much from that point on. Doing that results in the working
    directory being cleaned momentarily and then being repopulated again. My
    development tools often detect this as the files having been changed and
    then prompt me to reload them or cause rebuilds and so on. git stash
    store $(git stash create) has the nice feature that it doesn't touch the
    working directory, but it was missing the ability to also stash
    untracked files. This change fixes that.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1892%2FSBhojani%2Fstash-create-untracked-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1892/SBhojani/stash-create-untracked-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1892

 Documentation/git-stash.adoc |  9 +++++++--
 builtin/stash.c              | 25 +++++++++++++++++++------
 t/t3903-stash.sh             | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc
index 235d57ddd8..c379062110 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] [<message>]
 git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
 git stash export (--print | --to-ref <ref>) [<stash>...]
 git stash import <commit>
@@ -140,10 +140,12 @@ with no conflicts.
 `drop [-q | --quiet] [<stash>]`::
 	Remove a single stash entry from the list of stash entries.
 
-`create`::
+`create [-u | --include-untracked]`::
 	Create a stash entry (which is a regular commit object) and
 	return its object name, without storing it anywhere in the ref
 	namespace.
+	If the `--include-untracked` option is used, all untracked files are
+	also included in the stash entry.
 	This is intended to be useful for scripts.  It is probably not
 	the command you want to use; see "push" above.
 
@@ -181,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, all untracked files are also included
+in the stash entry.
++
 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 95c5005b0b..419f70ea39 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -58,7 +58,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] [<message>]")
 #define BUILTIN_STASH_EXPORT_USAGE \
 	N_("git stash export (--print | --to-ref <ref>) [<stash>...]")
 #define BUILTIN_STASH_IMPORT_USAGE \
@@ -118,6 +118,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
@@ -1570,22 +1575,30 @@ done:
 	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 include_untracked = 0;
+	struct option options[] = {
+		OPT_BOOL('u', "include-untracked", &include_untracked,
+			N_("include untracked 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))
+	if (!include_untracked && !check_changes_tracked_files(&ps))
 		return 0;
 
-	ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
+	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));
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 70879941c2..4e0243e315 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -639,6 +639,39 @@ test_expect_success 'stash create - no changes' '
 	test_must_be_empty actual
 '
 
+test_expect_success 'stash create with --include-untracked' '
+	test_when_finished "git reset --hard && git clean -fd" &&
+	test_commit stash-create-untracked-1 file1 committed &&
+	echo staged >file2 &&
+	git add file2 &&
+	echo unstaged >file3 &&
+	echo untracked >untracked_file &&
+	STASH_ID=$(git stash create --include-untracked "test message") &&
+	git cat-file -p $STASH_ID >stash_commit &&
+	grep "test message" stash_commit &&
+	grep parent stash_commit >parents &&
+	test_line_count = 3 parents &&
+	UNTRACKED_TREE=$(git rev-parse $STASH_ID^3^{tree}) &&
+	git ls-tree $UNTRACKED_TREE >files &&
+	grep untracked_file files &&
+	test_path_is_file untracked_file
+'
+
+test_expect_success 'stash create without --include-untracked does not include untracked files' '
+	test_when_finished "git reset --hard && git clean -fd" &&
+	test_commit stash-create-no-untracked-1 file4 committed &&
+	echo staged >file5 &&
+	git add file5 &&
+	echo unstaged >file6 &&
+	echo untracked >untracked_file2 &&
+	STASH_ID=$(git stash create "test message") &&
+	git cat-file -p $STASH_ID >stash_commit &&
+	grep "test message" stash_commit &&
+	grep parent stash_commit >parents &&
+	test_line_count = 2 parents &&
+	test_path_is_file untracked_file2
+'
+
 test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
 	git stash clear &&
 	test_when_finished "git reset --hard HEAD" &&

base-commit: 5361983c075154725be47b65cca9a2421789e410
-- 
gitgitgadget