From 1118bd68ac98f19c2401e36250fd44f058f8cb69 Mon Sep 17 00:00:00 2001 From: Miroma Date: Wed, 8 Oct 2025 14:16:19 +0200 Subject: [PATCH] stash: show correct entries count Currently, 'stash apply' internally calls 'status' to show a summary of what the command did. This also happens with 'stash pop', which roughly corresponds to 'apply' + 'drop'. When the configuration 'status.showStash' is set, 'status' shows a count of stashed entries. This leads to the following, confusing, output: ```sh $ # assuming there are two stashed entries $ git stash pop ... Your stash currently has 2 entries Dropped refs/stash@{0} (abc123...) $ # when actually... $ git status --show-stash ... Your stash currently has 1 entry ``` This patch changes the output format to the following, no matter the value of 'status.showStash': ```sh $ # assuming there's a stashed entry $ git stash pop ... Dropped refs/stash@{0} (abc123...) Your stash now has 0 entries ``` This new output is only shown if the stash count has changed, which can happen with the following subcommands: 'drop', 'pop', 'branch', 'store', 'push', 'save'. Helped-by: Karthik Nayak Helped-by: Junio C Hamano Signed-off-by: Miroma --- builtin/stash.c | 31 ++++++++++++++++++-- t/t3903-stash.sh | 75 ++++++++++++++++++++++++++++-------------------- wt-status.c | 2 +- wt-status.h | 1 + 4 files changed, 75 insertions(+), 34 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index 1977e50df27fc5..1bee391fb2d5d6 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -32,6 +32,7 @@ #include "add-interactive.h" #include "oid-array.h" #include "commit.h" +#include "wt-status.h" #define INCLUDE_ALL_FILES 2 @@ -585,6 +586,20 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) die(_("could not write index")); } +static int previous_stash_count = -1; + +static void print_stash_status(bool quiet) +{ + int stash_count; + stash_count = count_stash_entries(); + + if (quiet || stash_count == previous_stash_count || previous_stash_count == -1) + return; + + printf_ln(Q_("Your stash now has %d entry", + "Your stash now has %d entries", stash_count), stash_count); +} + static int do_apply_stash(const char *prefix, struct stash_info *info, int index, int quiet) { @@ -705,7 +720,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, absolute_path(repo_get_work_tree(the_repository))); strvec_pushf(&cp.env, GIT_DIR_ENVIRONMENT"=%s", absolute_path(repo_get_git_dir(the_repository))); - strvec_push(&cp.args, "status"); + strvec_pushl(&cp.args, "status", "--no-show-stash", NULL); run_command(&cp); } @@ -756,6 +771,8 @@ static int reflog_is_empty(const char *refname) static int do_drop_stash(struct stash_info *info, int quiet) { + previous_stash_count = count_stash_entries(); + if (!reflog_delete(info->revision.buf, EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF, 0)) { @@ -805,6 +822,8 @@ static int drop_stash(int argc, const char **argv, const char *prefix, goto cleanup; ret = do_drop_stash(&info, quiet); + print_stash_status(quiet); + cleanup: free_stash_info(&info); return ret; @@ -836,6 +855,8 @@ static int pop_stash(int argc, const char **argv, const char *prefix, else ret = do_drop_stash(&info, quiet); + print_stash_status(quiet); + cleanup: free_stash_info(&info); return ret; @@ -875,6 +896,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix, if (!ret && info.is_stash_ref) ret = do_drop_stash(&info, 0); + print_stash_status(0); + cleanup: free_stash_info(&info); return ret; @@ -1062,6 +1085,7 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms struct stash_info info; char revision[GIT_MAX_HEXSZ]; + previous_stash_count = count_stash_entries(); oid_to_hex_r(revision, w_commit); assert_stash_like(&info, revision); @@ -1119,6 +1143,7 @@ static int store_stash(int argc, const char **argv, const char *prefix, } ret = do_store_stash(&obj, stash_msg, quiet); + print_stash_status(quiet); out: object_context_release(&dummy); @@ -1914,6 +1939,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, &add_p_opt, include_untracked, only_staged); + print_stash_status(quiet); clear_pathspec(&ps); free(pathspec_from_file); @@ -1982,6 +2008,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, &add_p_opt, include_untracked, only_staged); + print_stash_status(quiet); strbuf_release(&stash_msg_buf); return ret; @@ -2291,7 +2318,7 @@ static int do_export_stash(struct repository *r, .r = r, .items = iter, }; if (refs_for_each_reflog_ent_reverse(get_main_ref_store(r), - "refs/stash", + ref_stash, collect_stash_entries, &cb_data) && cb_data.count) goto out; diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 0bb4648e3639b2..d3af50e40412a1 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -49,7 +49,7 @@ diff_cmp () { rm -f "$1.compare" "$2.compare" } -setup_stash() { +setup_stash () { echo 1 >file && git add file && echo unrelated >other-file && @@ -60,13 +60,15 @@ setup_stash() { git add file && echo 3 >file && test_tick && - git stash && + git stash | tail -n 1 >actual && git diff-files --quiet && git diff-index --cached --quiet HEAD } -test_expect_success 'stash some dirty working directory' ' - setup_stash +test_expect_success 'stash reports stash count' ' + setup_stash && + echo "Your stash now has 1 entry" >expect && + test_cmp expect actual ' cat >expect <expected && + git stash list >expect && echo 7 >file && git stash && - git stash drop && + git stash drop | tail -n 1 >msg-actual && + echo "Your stash now has 1 entry" >msg-expect && + test_cmp msg-expect msg-actual && git stash list >actual && - test_cmp expected actual && + test_cmp expect actual && git stash apply && test 3 = $(cat file) && test 1 = $(git show :file) && @@ -236,14 +240,18 @@ test_expect_success 'drop stash reflog updates refs/stash with rewrite' ' test_expect_success 'stash pop' ' git reset --hard && - git stash pop && + git stash pop | tail -n 1 >actual && + echo "Your stash now has 0 entries" >expect && + test_cmp expect actual && test 3 = $(cat file) && test 1 = $(git show :file) && test 1 = $(git show HEAD:file) && test 0 = $(git stash list | wc -l) ' -cat >expect <expect + +cat >expect1 <expect1 <expect2 <expect2 <expect3 <file && git commit file -m second && - git stash branch stashbranch && + git stash branch stashbranch | tail -n 1 >actual && + test_cmp expect actual && test refs/heads/stashbranch = $(git symbolic-ref HEAD) && test $(git rev-parse HEAD) = $(git rev-parse main^) && git diff --cached >output && - diff_cmp expect output && - git diff >output && diff_cmp expect1 output && + git diff >output && + diff_cmp expect2 output && git add file && git commit -m alternate\ second && git diff main..stashbranch >output && - diff_cmp output expect2 && + diff_cmp output expect3 && test 0 = $(git stash list | wc -l) ' @@ -427,7 +436,9 @@ test_expect_success 'stash an added file' ' git reset --hard && echo new >file3 && git add file3 && - git stash save "added file" && + git stash save "added file" | tail -n 1 >actual && + echo "Your stash now has 6 entries" >expect && + test_cmp expect actual && ! test -r file3 && git stash apply && test new = "$(cat file3)" @@ -683,12 +694,12 @@ test_expect_success 'stash show format defaults to --stat' ' echo bar >>file && STASH_ID=$(git stash create) && git reset --hard && - cat >expected <<-EOF && + cat >expect <<-EOF && file | 1 + 1 file changed, 1 insertion(+) EOF git stash show ${STASH_ID} >actual && - test_cmp expected actual + test_cmp expect actual ' test_expect_success 'stash show - stashes on stack, stash-like argument' ' @@ -701,9 +712,9 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' ' echo bar >>file && STASH_ID=$(git stash create) && git reset --hard && - echo "1 0 file" >expected && + echo "1 0 file" >expect && git stash show --numstat ${STASH_ID} >actual && - test_cmp expected actual + test_cmp expect actual ' test_expect_success 'stash show -p - stashes on stack, stash-like argument' ' @@ -716,7 +727,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' ' echo bar >>file && STASH_ID=$(git stash create) && git reset --hard && - cat >expected <<-EOF && + cat >expect <<-EOF && diff --git a/file b/file index 7601807..935fbd3 100644 --- a/file @@ -726,7 +737,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' ' +bar EOF git stash show -p ${STASH_ID} >actual && - diff_cmp expected actual + diff_cmp expect actual ' test_expect_success 'stash show - no stashes on stack, stash-like argument' ' @@ -736,9 +747,9 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' ' echo foo >>file && STASH_ID=$(git stash create) && git reset --hard && - echo "1 0 file" >expected && + echo "1 0 file" >expect && git stash show --numstat ${STASH_ID} >actual && - test_cmp expected actual + test_cmp expect actual ' test_expect_success 'stash show -p - no stashes on stack, stash-like argument' ' @@ -748,7 +759,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' ' echo foo >>file && STASH_ID=$(git stash create) && git reset --hard && - cat >expected <<-EOF && + cat >expect <<-EOF && diff --git a/file b/file index 7601807..71b52c4 100644 --- a/file @@ -758,7 +769,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' ' +foo EOF git stash show -p ${STASH_ID} >actual && - diff_cmp expected actual + diff_cmp expect actual ' test_expect_success 'stash show --patience shows diff' ' @@ -766,7 +777,7 @@ test_expect_success 'stash show --patience shows diff' ' echo foo >>file && STASH_ID=$(git stash create) && git reset --hard && - cat >expected <<-EOF && + cat >expect <<-EOF && diff --git a/file b/file index 7601807..71b52c4 100644 --- a/file @@ -776,7 +787,7 @@ test_expect_success 'stash show --patience shows diff' ' +foo EOF git stash show --patience ${STASH_ID} >actual && - diff_cmp expected actual + diff_cmp expect actual ' test_expect_success 'drop: fail early if specified stash is not a stash ref' ' @@ -915,7 +926,7 @@ test_expect_success 'apply: show same status as git status (relative to ./)' ' sane_unset GIT_MERGE_VERBOSITY && git stash apply ) | - sed -e 1d >actual && # drop "Saved..." + sed -e 1,2d >actual && # drop "Your stash now has 1 entry" and "Saved..." test_cmp expect actual ' @@ -959,9 +970,11 @@ test_expect_success 'store updates stash ref and reflog' ' STASH_ID=$(git stash create) && git reset --hard && test_path_is_missing bazzy && - git stash store -m quuxery $STASH_ID && + git stash store -m quuxery $STASH_ID | tail -n 1 >actual && + echo "Your stash now has 1 entry" >expect && + test_cmp expect actual && test $(git rev-parse stash) = $STASH_ID && - git reflog --format=%H stash| grep $STASH_ID && + git reflog --format=%H stash | grep $STASH_ID && git stash pop && grep quux bazzy ' diff --git a/wt-status.c b/wt-status.c index 454601afa15a95..6bdf71efee9f66 100644 --- a/wt-status.c +++ b/wt-status.c @@ -983,7 +983,7 @@ static int stash_count_refs(struct object_id *ooid UNUSED, return 0; } -static int count_stash_entries(void) +int count_stash_entries(void) { int n = 0; refs_for_each_reflog_ent(get_main_ref_store(the_repository), diff --git a/wt-status.h b/wt-status.h index 4e377ce62b8b28..daccde0b6ec160 100644 --- a/wt-status.h +++ b/wt-status.h @@ -151,6 +151,7 @@ size_t wt_status_locate_end(const char *s, size_t len); void wt_status_append_cut_line(struct strbuf *buf); void wt_status_add_cut_line(struct wt_status *s); void wt_status_prepare(struct repository *r, struct wt_status *s); +int count_stash_entries(void); void wt_status_print(struct wt_status *s); void wt_status_collect(struct wt_status *s); /*