Thread (8 messages) 8 messages, 2 authors, 2025-09-19

Re: Git Stash Synchronization - Best Workflow?

From: Brooke Kuhlmann <hidden>
Date: 2025-09-01 20:48:42

Hey Phillip, thanks!

I applied what you've suggested and still was only able to make push and popping my stash with `git push --force` work. Actually, `git push --force-with-lease` does work but I get this error still:

To https://github.com/bkuhlmann/test
 ! [rejected]                  refs/stashes/bkuhlmann -> refs/stashes/bkuhlmann (remote ref updated since checkout)
error: failed to push some refs to 'https://github.com/bkuhlmann/test'

Despite that error showing up -- and the fact that I've applied your changes -- the stash DOES get updated properly both locally and via the remote. That didn't happen before.

Good callout on the use of `--force-if-includes`. I didn't mention that I have this enabled earlier because I always forget I have it configured via my global configuration as:

[push]
  useForceIfIncludes = true

...but I did update my global configuration, per your suggestion, as follows:

[core]
  logAllrefUpdates = always

[remote "origin"]
  fetch = refs/stashes/*:refs/remote/origin/stashes/*

With the above enabled, my reflog ended up showing this (using my "test" repository):

9305680c9afb (HEAD -> main, tag: 0.0.0, origin/main, origin/HEAD) HEAD@{6 minutes ago}: reset: moving to HEAD
9305680c9afb (HEAD -> main, tag: 0.0.0, origin/main, origin/HEAD) HEAD@{7 minutes ago}: reset: moving to HEAD
9305680c9afb (HEAD -> main, tag: 0.0.0, origin/main, origin/HEAD) HEAD@{8 minutes ago}: reset: moving to HEAD

I'm only seeing "reset: moving to HEAD" in my reflog when performing the push on my stash (in case that helps).

Yeah, having the documentation reflect this would be nice in terms of informing folks that you should enable what I've shown above. Ensuring any change to the stash would also update the reflog would be helpful too so folks can be implicit instead of explicit.

You can use --force-with-lease=refs/stashes/$USER:$expect where $expect is the value of refs/stashes/$USER when you last pushed. The problem is that there is no easy way to find that as by default refs/stashes/$USER does not have a reflog and there is no remote tracking ref set up for it either. If you add a fetch refspec like

   refs/stashes/*:refs/remote/origin/stashes/*

(note "remote" rather than "remotes" to avoid clashing with the default refspec for branches) then refs/remote/origin/stashes/$USER should be updated when you push to or pull from refs/stashes/* and I think a bare --force-with-lease will work. In general --force-with-lease without explicitly specifying $expect is not that safe as it will happily overwrite the remote ref if you fetch and do not incorporate the remote changes into your local changes before pushing. Using --force-if-includes is safer if you don't want to give $expect explicitly. That requires a reflog for the local ref though which you can enable by setting core.logAllrefUpdates=always. We should perhaps change the export code to create a reflog for the ref we're exporting the stashes to and maybe expand the documentation to mention setting up a fetch refspec.



On Sep 1, 2025, at 4:10 AM, Phillip Wood [off-list ref] wrote:

Hi Brooke

[I've cc'd brian to see what he thinks about setting up a reflog by default when exporting stashes]

On 01/09/2025 00:25, Brooke Kuhlmann wrote:
quoted
Hello.
When using Git 2.51.0, what is the correct way to safely export
your stash and then keep that stash up-to-date? Here's an example
workflow:>
touch demo.txt
git stash push --include-untracked --message "Demo"
git stash export --to-ref "refs/stashes/$USER"
git push origin "refs/stashes/$USER"
git stash pop stash@{0}
git push origin "refs/stashes/$USER"
This push doesn't do anything because refs/stashes/$USER is unchanged since the last push
quoted
git stash push --include-untracked --message "Demo II"
git stash export --to-ref "refs/stashes/$USER"
git push origin "refs/stashes/$USER"
This push fails because you've popped and then pushed a stash since the last export so refs/stashes/$USER on the remote cannot fast-forward
quoted
Notice, in the middle, I pop the stash only to rename it. Upon
pushing these changes back up, I get the following error:>
To https://github.com/bkuhlmann/test
 ! [rejected]                  refs/stashes/bkuhlmann -> refs/stashes/bkuhlmann (non-fast-forward)
error: failed to push some refs to 'https://github.com/bkuhlmann/test'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. If you want to integrate the remote changes, use 'git pull'
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
The work around is to use `git push --force` when pushing updates.
I'd like to use `git push --force-with-lease` but that doesn't work.
You can use --force-with-lease=refs/stashes/$USER:$expect where $expect is the value of refs/stashes/$USER when you last pushed. The problem is that there is no easy way to find that as by default refs/stashes/$USER does not have a reflog and there is no remote tracking ref set up for it either. If you add a fetch refspec like

   refs/stashes/*:refs/remote/origin/stashes/*

(note "remote" rather than "remotes" to avoid clashing with the default refspec for branches) then refs/remote/origin/stashes/$USER should be updated when you push to or pull from refs/stashes/* and I think a bare --force-with-lease will work. In general --force-with-lease without explicitly specifying $expect is not that safe as it will happily overwrite the remote ref if you fetch and do not incorporate the remote changes into your local changes before pushing. Using --force-if-includes is safer if you don't want to give $expect explicitly. That requires a reflog for the local ref though which you can enable by setting core.logAllrefUpdates=always. We should perhaps change the export code to create a reflog for the ref we're exporting the stashes to and maybe expand the documentation to mention setting up a fetch refspec.

Thanks

Phillip
quoted
I realize that force pushing over your remote stash makes a lot more sense since you typically never share a stash with folks but was thinking it would be nice to ensure you don't accidentally override your remote stash when working on different machine when you forgot to import first. Basically, wanting to protect myself from myself. :)
Is force push the only way to handle this use case or is there a better approach? Thanks!
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help