* [RFC] builtin/stash: data loss from reset --hard
@ 2026-01-04 11:05 Tsahi Elkayam
2026-01-05 1:47 ` Junio C Hamano
0 siblings, 1 reply; 2+ messages in thread
From: Tsahi Elkayam @ 2026-01-04 11:05 UTC (permalink / raw)
To: git@vger•kernel.org
Hi,
I am a beginner C developer exploring the Git codebase and came across
something I would like to understand better.
In builtin/stash.c line 1747, there is a comment:
/* BUG: this nukes untracked files in the way */
strvec_pushl(&cp.args, "reset", "--hard", "-q",
"--no-recurse-submodules", NULL);
Steps to reproduce:
$ git init test && cd test
$ echo "tracked" > foo && git add foo && git commit -m "init"
$ git rm foo
$ mkdir foo && echo "precious" > foo/file
$ git stash
$ cat foo/file
cat: foo/file: Not a directory # precious data is lost
The reset --hard restores the original tracked file "foo" from HEAD,
destroying the untracked directory "foo/" and its contents.
There is also a test_expect_failure test in t/t2500-untracked-overwriting.sh
that documents this behavior.
I am not sure if this is considered a bug to be fixed, or intentional
behavior that is simply documented.
If it is a bug, would this fix be reasonable:
- /* BUG: this nukes untracked files in the way */
- strvec_pushl(&cp.args, "reset", "--hard", "-q",
+ strvec_pushl(&cp.args, "reset", "--merge", "-q",
"--no-recurse-submodules", NULL);
I understand --merge would fail instead of silently overwriting,
which seems safer.
I would appreciate any feedback or guidance.
Thanks,
Tsahi
Sent with Proton Mail secure email.
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [RFC] builtin/stash: data loss from reset --hard
2026-01-04 11:05 [RFC] builtin/stash: data loss from reset --hard Tsahi Elkayam
@ 2026-01-05 1:47 ` Junio C Hamano
0 siblings, 0 replies; 2+ messages in thread
From: Junio C Hamano @ 2026-01-05 1:47 UTC (permalink / raw)
To: Tsahi Elkayam; +Cc: git@vger•kernel.org
Tsahi Elkayam <Tsahi.Elkayam@protonmail•com> writes:
> Hi,
>
> I am a beginner C developer exploring the Git codebase and came across
> something I would like to understand better.
>
> In builtin/stash.c line 1747, there is a comment:
>
> /* BUG: this nukes untracked files in the way */
> strvec_pushl(&cp.args, "reset", "--hard", "-q",
> "--no-recurse-submodules", NULL);
>
> Steps to reproduce:
>
> $ git init test && cd test
> $ echo "tracked" > foo && git add foo && git commit -m "init"
> $ git rm foo
> $ mkdir foo && echo "precious" > foo/file
> $ git stash
> $ cat foo/file
> cat: foo/file: Not a directory # precious data is lost
As far as I can see, everything I see in the above is working as
intended. "stash" is a way to get rid of your work in progress and
take you back to the pristine state quickly (after all, it is
"panic! the boss is here and tells me to work on something else,
clear the desk real quick now" option), so after "git stash",
whatever change you made relative to the pristine state (that is, a
regular file 'foo' exists and has "tracked" in it) should go away,
if it gets in a way in order to get us back to the pristine state.
After the above sequence, if you "git stash pop", it should get you
back to the state immediately before you did "git stash [push]", but
depending on what you did between push and pop, it is possible that
the changes conflict and "stash pop" may fail without popping the
stash entry. This is to allow you to attempt to pop it again later
after cleaning up the mess (like, perhaps going back to the state
before you did "stash push" on a new branch). The point to note
here is that the "push" operation cannot afford to fail at the
"panic! the boss is here" moment, but at the "stash pop" stage, aka
"the crisis is over, now let's get back to where we were", the user
can afford to see a failure and spend time on conflict resolution.
Another thing to note is that the new foo/file in the above example
is not tracked, and IIUC, "git stash" by default will not save
random untracked cruft found in the working tree. I wasn't heavily
involved in the design of this optional feature of saving away the
untracked cruft ("git stash push -u"), so I do not offhand know if
the contents of foo/file is saved away correctly in the above
sequence of yours if you changed your "git stash" to "git stash
[push] -u", or if it is recovered when you later say "git stash pop"
(and if it doesn't, then you have found a bug there). But it does
not change the fact that the "cat" in the above sequence immediately
after "git stash [push]" with or without "-u" should notice that
foo/file is now gone, once you got back to the pristine state.
I do not know what "BUG:" comment refers to in the above. Without
"-u", getting rid of untracked files that get in the way of going
back to the pristine state is absolutely the right thing to do, so
there is no bug there. It is possible that it is done way too early
even when "-u" is given, making it impossible to save away such an
untracked files that are in the way, but I didn't check.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-01-05 1:47 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-04 11:05 [RFC] builtin/stash: data loss from reset --hard Tsahi Elkayam
2026-01-05 1:47 ` Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox