public inbox for git@vger.kernel.org 
 help / color / mirror / Atom feed
* Git Stash Synchronization - Best Workflow?
@ 2025-08-31 23:25 Brooke Kuhlmann
  2025-09-01 10:10 ` Phillip Wood
  0 siblings, 1 reply; 9+ messages in thread
From: Brooke Kuhlmann @ 2025-08-31 23:25 UTC (permalink / raw)
  To: git

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"

git stash push --include-untracked --message "Demo II"
git stash export --to-ref "refs/stashes/$USER"
git push origin "refs/stashes/$USER"

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.

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!

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-08-31 23:25 Git Stash Synchronization - Best Workflow? Brooke Kuhlmann
@ 2025-09-01 10:10 ` Phillip Wood
  2025-09-01 20:48   ` Brooke Kuhlmann
  0 siblings, 1 reply; 9+ messages in thread
From: Phillip Wood @ 2025-09-01 10:10 UTC (permalink / raw)
  To: Brooke Kuhlmann, git; +Cc: brian m . carlson

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:
> 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

> 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
> 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

> 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!


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-01 10:10 ` Phillip Wood
@ 2025-09-01 20:48   ` Brooke Kuhlmann
  2025-09-05 14:03     ` Phillip Wood
  0 siblings, 1 reply; 9+ messages in thread
From: Brooke Kuhlmann @ 2025-09-01 20:48 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, brian m . carlson

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 <phillip.wood123@gmail•com> 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:
>> 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
> 
>> 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
>> 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
> 
>> 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!
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-01 20:48   ` Brooke Kuhlmann
@ 2025-09-05 14:03     ` Phillip Wood
  2025-09-06 12:50       ` Brooke Kuhlmann
  0 siblings, 1 reply; 9+ messages in thread
From: Phillip Wood @ 2025-09-05 14:03 UTC (permalink / raw)
  To: Brooke Kuhlmann, phillip.wood; +Cc: git, brian m . carlson

Hi Brooke

On 01/09/2025 21:48, Brooke Kuhlmann wrote:
> 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.
That sounds like a bug if you're getting the rejected message above but 
the ref on the remote is still being updated. I'll try and take a look 
at that next week.

 > [...]> 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).
You need to pass the name of the ref whose reflog you want to look at, 
otherwise it defaults to showing the reflog for HEAD. You should be able 
to see the reflog for you exported stashes with

     git reflog refs/stashes/$USER

and the reflog for the remote tracking ref with

     git reflog refs/remote/origin/stashes/$USER

> 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.
Let's try and find why the remote update say's it rejected when it isn't 
and then we can think about the best way to document pushing and pulling 
exported stashes.

Thanks

Phillip


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-05 14:03     ` Phillip Wood
@ 2025-09-06 12:50       ` Brooke Kuhlmann
  2025-09-10  9:52         ` Phillip Wood
  0 siblings, 1 reply; 9+ messages in thread
From: Brooke Kuhlmann @ 2025-09-06 12:50 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, brian m . carlson

>  That sounds like a bug if you're getting the rejected message above but the ref on the remote is still being updated. I'll try and take a look at that next week.

OK, sounds good.

> You need to pass the name of the ref whose reflog you want to look at, otherwise it defaults to showing the reflog for HEAD. You should be able to see the reflog for you exported stashes.

I gave this a try and every time I use `git reflog refs/stashes/$USER`, I always get a blank response. No errors and no output.

> Let's try and find why the remote update say's it rejected when it isn't and then we can think about the best way to document pushing and pulling exported stashes.

Sounds good. Happy to test more of this when you're ready.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-06 12:50       ` Brooke Kuhlmann
@ 2025-09-10  9:52         ` Phillip Wood
  2025-09-11  2:22           ` Brooke Kuhlmann
  0 siblings, 1 reply; 9+ messages in thread
From: Phillip Wood @ 2025-09-10  9:52 UTC (permalink / raw)
  To: Brooke Kuhlmann, phillip.wood; +Cc: git, brian m . carlson

Hi Brooke

On 06/09/2025 13:50, Brooke Kuhlmann wrote:
>> That sounds like a bug if you're getting the rejected message above
>> but the ref on the remote is still being updated. I'll try and take
>> a look at that next week.

I'm unable to reproduce this. In the script below the final push succeeds.

     set -ex
     dir="$(mktemp -d)"
     cd "$dir"
     git init --bare origin
     git init repo
     cd repo
     git remote add origin "file://${PWD%/*}/origin"
     git config core.logAllRefUpdates always
     git config remote.origin.fetch refs/stashes/*:refs/remote/origin/stashes/*
     echo a >a
     git add a
     git commit -m a
     echo b >a
     git stash push
     echo c >a
     git stash push
     git stash export --to-ref refs/stashes/test
     git push origin refs/stashes/test
     git stash pop
     git stash push -m message
     git stash export --to-ref refs/stashes/test
     git push --force-with-lease --force-if-includes  origin refs/stashes/test

>> You need to pass the name of the ref whose reflog you want to look at,
>> otherwise it defaults to showing the reflog for HEAD. You should be
>> able to see the reflog for you exported stashes.> 
> I gave this a try and every time I use `git reflog refs/stashes/$USER`,
>  I always get a blank response. No errors and no output.
Ah, I wonder if core.logAllRefUpdates only affects the creation of new
refs. You can force the creation of a reflog by running

	oid=$(git rev-parse --verify refs/stashes/$USER) &&
	git update-ref -d refs/stashes/$USER &&
	git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid

the same applies to refs/remote/stashes/origin/$USER

>> Let's try and find why the remote update say's it rejected when it isn't
>> and then we can think about the best way to document pushing and
>> pulling exported stashes.
I haven't thought much about the pulling side of this. "git stash import"
appends to the existing stashes so I'm not sure how we'd cope with forced
updates - have got got any experience of handling this from your
experiments?

Thanks

Phillip

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-10  9:52         ` Phillip Wood
@ 2025-09-11  2:22           ` Brooke Kuhlmann
  2025-09-19 14:04             ` Phillip Wood
  0 siblings, 1 reply; 9+ messages in thread
From: Brooke Kuhlmann @ 2025-09-11  2:22 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, brian m . carlson

Hey Phillip

> I'm unable to reproduce this. In the script below the final push succeeds.

That's because you need to export every time before you push. Like this:

touch one.txt
git stash push --include-untracked --message "One"

git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"

git stash pop
git stash push --include-untracked --message "One II"

git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"

The above will yield the following error:

 ! [rejected]                  refs/stashes/bkuhlmann -> refs/stashes/bkuhlmann (remote ref updated since checkout)
error: failed to push some refs to 'https://github.com/bkuhlmann/test'
hint: Updates were rejected because the tip of the remote-tracking branch has
hint: been updated since the last checkout. If you want to integrate the
hint: remote changes, use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

However, if you perform the above with only the single "git stash export" then you won't get the error as you discovered in your workflow.

The only way I've been able to make this work is to do this:

touch one.txt
git stash push --include-untracked --message "One"

git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force origin "refs/stashes/$USER"

git stash pop
git stash push --include-untracked --message "One II"

git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force origin "refs/stashes/$USER"

Notice that I always export before the push AND that I'm using `--force` each time. That's the only way to ensure your local stash is in sync with the remote stash.

You can always verify that the remote stash is being updated by always clearing your local stash and then immediately importing to check if your stash message was updated properly. Example:

git stash clear
git stash import "refs/stashes/$USER"
git stash list

Once you perform the import, and immediately list what's in your stash, you should see something similar to the following:

stash@{0} 6ba4eaea3751 On main: One II

When your remote stash isn't updated, you'll see this:

stash@{0} 6ba4eaea3751 On main: One

(Notice the difference between the message of "One" versus "One II")

> You can force the creation of a reflog

I tried that too which makes the error go away but doesn't update the remote stash at all. Example:

touch one.txt
git stash push --include-untracked --message "One"

git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"

git stash pop
git stash push --include-untracked --message "One II"

oid=$(git rev-parse --verify refs/stashes/$USER) &&
  git update-ref -d refs/stashes/$USER &&
  git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid

git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"

The above works but if I run `git stash clear && git stash import "refs/stashes/$USER"`, I find that my local stash doesn't have the message change (still using "One" instead of "One II" which means the remote stash never got updated).

Sadly, I can only seem to make this work when using a force push but would definitely be nice to not have to use a force push.

> 
>    set -ex
>    dir="$(mktemp -d)"
>    cd "$dir"
>    git init --bare origin
>    git init repo
>    cd repo
>    git remote add origin "file://${PWD%/*}/origin"
>    git config core.logAllRefUpdates always
>    git config remote.origin.fetch refs/stashes/*:refs/remote/origin/stashes/*
>    echo a >a
>    git add a
>    git commit -m a
>    echo b >a
>    git stash push
>    echo c >a
>    git stash push
>    git stash export --to-ref refs/stashes/test
>    git push origin refs/stashes/test
>    git stash pop
>    git stash push -m message
>    git stash export --to-ref refs/stashes/test
>    git push --force-with-lease --force-if-includes  origin refs/stashes/test
> 
>>> You need to pass the name of the ref whose reflog you want to look at,
>>> otherwise it defaults to showing the reflog for HEAD. You should be
>>> able to see the reflog for you exported stashes.>
>> I gave this a try and every time I use `git reflog refs/stashes/$USER`,
>> I always get a blank response. No errors and no output.
> Ah, I wonder if core.logAllRefUpdates only affects the creation of new
> refs. You can force the creation of a reflog by running
> 
> 	oid=$(git rev-parse --verify refs/stashes/$USER) &&
> 	git update-ref -d refs/stashes/$USER &&
> 	git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid
> 
> the same applies to refs/remote/stashes/origin/$USER
> 
>>> Let's try and find why the remote update say's it rejected when it isn't
>>> and then we can think about the best way to document pushing and
>>> pulling exported stashes.
> I haven't thought much about the pulling side of this. "git stash import"
> appends to the existing stashes so I'm not sure how we'd cope with forced
> updates - have got got any experience of handling this from your
> experiments?
> 
> Thanks
> 
> Phillip


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
  2025-09-11  2:22           ` Brooke Kuhlmann
@ 2025-09-19 14:04             ` Phillip Wood
       [not found]               ` <A42DC91A-91F2-4AB6-B0EE-52DE5135E99E@alchemists.io>
  0 siblings, 1 reply; 9+ messages in thread
From: Phillip Wood @ 2025-09-19 14:04 UTC (permalink / raw)
  To: Brooke Kuhlmann, phillip.wood; +Cc: git, brian m . carlson

Hi Brooke

On 11/09/2025 03:22, Brooke Kuhlmann wrote:
> 
>> I'm unable to reproduce this. In the script below the final push succeeds.
> 
> That's because you need to export every time before you push. Like this:
> 
> touch one.txt
> git stash push --include-untracked --message "One"
> 
> git stash export --to-ref "refs/stashes/$USER"
> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
> 
> git stash pop
> git stash push --include-untracked --message "One II"
> 
> git stash export --to-ref "refs/stashes/$USER"
> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
> 
> The above will yield the following error:
> 
>   ! [rejected]                  refs/stashes/bkuhlmann -> refs/stashes/bkuhlmann (remote ref updated since checkout)
> error: failed to push some refs to 'https://github.com/bkuhlmann/test'
> hint: Updates were rejected because the tip of the remote-tracking branch has
> hint: been updated since the last checkout. If you want to integrate the
> hint: remote changes, use 'git pull' before pushing again.
> hint: See the 'Note about fast-forwards' in 'git push --help' for details.
> 
> However, if you perform the above with only the single "git stash export" then you won't get the error as you discovered in your workflow.

I'm confused by this, here is the relevant part of my script again

 >>     git stash push
 >>     git stash export --to-ref refs/stashes/test

This is the first export before pushing

 >>     git push origin refs/stashes/test
 >>     git stash pop
 >>     git stash push -m message
 >>     git stash export --to-ref refs/stashes/test

This is the second export before pushing

 >>     git push --force-with-lease --force-if-includes  origin 
refs/stashes/test

I'm afraid I'm struggling to see what the difference is.

Thanks

Phillip

> The only way I've been able to make this work is to do this:
> 
> touch one.txt
> git stash push --include-untracked --message "One"
> 
> git stash export --to-ref "refs/stashes/$USER"
> git push --no-verify --force origin "refs/stashes/$USER"
> 
> git stash pop
> git stash push --include-untracked --message "One II"
> 
> git stash export --to-ref "refs/stashes/$USER"
> git push --no-verify --force origin "refs/stashes/$USER"
> 
> Notice that I always export before the push AND that I'm using `--force` each time. That's the only way to ensure your local stash is in sync with the remote stash.
> 
> You can always verify that the remote stash is being updated by always clearing your local stash and then immediately importing to check if your stash message was updated properly. Example:
> 
> git stash clear
> git stash import "refs/stashes/$USER"
> git stash list
> 
> Once you perform the import, and immediately list what's in your stash, you should see something similar to the following:
> 
> stash@{0} 6ba4eaea3751 On main: One II
> 
> When your remote stash isn't updated, you'll see this:
> 
> stash@{0} 6ba4eaea3751 On main: One
> 
> (Notice the difference between the message of "One" versus "One II")
> 
>> You can force the creation of a reflog
> 
> I tried that too which makes the error go away but doesn't update the remote stash at all. Example:
> 
> touch one.txt
> git stash push --include-untracked --message "One"
> 
> git stash export --to-ref "refs/stashes/$USER"
> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
> 
> git stash pop
> git stash push --include-untracked --message "One II"
> 
> oid=$(git rev-parse --verify refs/stashes/$USER) &&
>    git update-ref -d refs/stashes/$USER &&
>    git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid
> 
> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
> 
> The above works but if I run `git stash clear && git stash import "refs/stashes/$USER"`, I find that my local stash doesn't have the message change (still using "One" instead of "One II" which means the remote stash never got updated).
> 
> Sadly, I can only seem to make this work when using a force push but would definitely be nice to not have to use a force push.
> 
>>
>>     set -ex
>>     dir="$(mktemp -d)"
>>     cd "$dir"
>>     git init --bare origin
>>     git init repo
>>     cd repo
>>     git remote add origin "file://${PWD%/*}/origin"
>>     git config core.logAllRefUpdates always
>>     git config remote.origin.fetch refs/stashes/*:refs/remote/origin/stashes/*
>>     echo a >a
>>     git add a
>>     git commit -m a
>>     echo b >a
>>     git stash push
>>     echo c >a
>>     git stash push
>>     git stash export --to-ref refs/stashes/test
>>     git push origin refs/stashes/test
>>     git stash pop
>>     git stash push -m message
>>     git stash export --to-ref refs/stashes/test
>>     git push --force-with-lease --force-if-includes  origin refs/stashes/test
>>
>>>> You need to pass the name of the ref whose reflog you want to look at,
>>>> otherwise it defaults to showing the reflog for HEAD. You should be
>>>> able to see the reflog for you exported stashes.>
>>> I gave this a try and every time I use `git reflog refs/stashes/$USER`,
>>> I always get a blank response. No errors and no output.
>> Ah, I wonder if core.logAllRefUpdates only affects the creation of new
>> refs. You can force the creation of a reflog by running
>>
>> 	oid=$(git rev-parse --verify refs/stashes/$USER) &&
>> 	git update-ref -d refs/stashes/$USER &&
>> 	git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid
>>
>> the same applies to refs/remote/stashes/origin/$USER
>>
>>>> Let's try and find why the remote update say's it rejected when it isn't
>>>> and then we can think about the best way to document pushing and
>>>> pulling exported stashes.
>> I haven't thought much about the pulling side of this. "git stash import"
>> appends to the existing stashes so I'm not sure how we'd cope with forced
>> updates - have got got any experience of handling this from your
>> experiments?
>>
>> Thanks
>>
>> Phillip
> 
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Git Stash Synchronization - Best Workflow?
       [not found]               ` <A42DC91A-91F2-4AB6-B0EE-52DE5135E99E@alchemists.io>
@ 2025-09-19 19:54                 ` Brooke Kuhlmann
  0 siblings, 0 replies; 9+ messages in thread
From: Brooke Kuhlmann @ 2025-09-19 19:54 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, brian m . carlson

It's subtle but you have to use `--force` instead of `--force-with-lease --force-if-includes` after you make a modification to an existing stash. This means you must export AND push each time you make a change to the stash (i.e. pushing, popping).

So what I was trying to illustrate is that you must export and push each time. In order to do that -- and keep the remote up-to-date -- you have to use a force push. That's the only way I've found that I can ensure my remote stash stays in sync with my local stash. Otherwise, if I clear my local stash (to simulate losing it) then import the remote stash, I can ensure everything is restored with all changes restored.

I'm also using GitHub as my remote. Not sure if that matters, though.

> 
>> On Sep 19, 2025, at 8:04 AM, Phillip Wood <phillip.wood123@gmail•com> wrote:
>> 
>> Hi Brooke
>> 
>> On 11/09/2025 03:22, Brooke Kuhlmann wrote:
>>>> I'm unable to reproduce this. In the script below the final push succeeds.
>>> That's because you need to export every time before you push. Like this:
>>> touch one.txt
>>> git stash push --include-untracked --message "One"
>>> git stash export --to-ref "refs/stashes/$USER"
>>> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
>>> git stash pop
>>> git stash push --include-untracked --message "One II"
>>> git stash export --to-ref "refs/stashes/$USER"
>>> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
>>> The above will yield the following error:
>>>  ! [rejected]                  refs/stashes/bkuhlmann -> refs/stashes/bkuhlmann (remote ref updated since checkout)
>>> error: failed to push some refs to 'https://github.com/bkuhlmann/test'
>>> hint: Updates were rejected because the tip of the remote-tracking branch has
>>> hint: been updated since the last checkout. If you want to integrate the
>>> hint: remote changes, use 'git pull' before pushing again.
>>> hint: See the 'Note about fast-forwards' in 'git push --help' for details.
>>> However, if you perform the above with only the single "git stash export" then you won't get the error as you discovered in your workflow.
>> 
>> I'm confused by this, here is the relevant part of my script again
>> 
>> >>     git stash push
>> >>     git stash export --to-ref refs/stashes/test
>> 
>> This is the first export before pushing
>> 
>> >>     git push origin refs/stashes/test
>> >>     git stash pop
>> >>     git stash push -m message
>> >>     git stash export --to-ref refs/stashes/test
>> 
>> This is the second export before pushing
>> 
>> >>     git push --force-with-lease --force-if-includes  origin refs/stashes/test
>> 
>> I'm afraid I'm struggling to see what the difference is.
>> 
>> Thanks
>> 
>> Phillip
>> 
>>> The only way I've been able to make this work is to do this:
>>> touch one.txt
>>> git stash push --include-untracked --message "One"
>>> git stash export --to-ref "refs/stashes/$USER"
>>> git push --no-verify --force origin "refs/stashes/$USER"
>>> git stash pop
>>> git stash push --include-untracked --message "One II"
>>> git stash export --to-ref "refs/stashes/$USER"
>>> git push --no-verify --force origin "refs/stashes/$USER"
>>> Notice that I always export before the push AND that I'm using `--force` each time. That's the only way to ensure your local stash is in sync with the remote stash.
>>> You can always verify that the remote stash is being updated by always clearing your local stash and then immediately importing to check if your stash message was updated properly. Example:
>>> git stash clear
>>> git stash import "refs/stashes/$USER"
>>> git stash list
>>> Once you perform the import, and immediately list what's in your stash, you should see something similar to the following:
>>> stash@{0} 6ba4eaea3751 On main: One II
>>> When your remote stash isn't updated, you'll see this:
>>> stash@{0} 6ba4eaea3751 On main: One
>>> (Notice the difference between the message of "One" versus "One II")
>>>> You can force the creation of a reflog
>>> I tried that too which makes the error go away but doesn't update the remote stash at all. Example:
>>> touch one.txt
>>> git stash push --include-untracked --message "One"
>>> git stash export --to-ref "refs/stashes/$USER"
>>> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
>>> git stash pop
>>> git stash push --include-untracked --message "One II"
>>> oid=$(git rev-parse --verify refs/stashes/$USER) &&
>>>   git update-ref -d refs/stashes/$USER &&
>>>   git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid
>>> git push --no-verify --force-with-lease --force-if-includes origin "refs/stashes/$USER"
>>> The above works but if I run `git stash clear && git stash import "refs/stashes/$USER"`, I find that my local stash doesn't have the message change (still using "One" instead of "One II" which means the remote stash never got updated).
>>> Sadly, I can only seem to make this work when using a force push but would definitely be nice to not have to use a force push.
>>>> 
>>>>    set -ex
>>>>    dir="$(mktemp -d)"
>>>>    cd "$dir"
>>>>    git init --bare origin
>>>>    git init repo
>>>>    cd repo
>>>>    git remote add origin "file://${PWD%/*}/origin"
>>>>    git config core.logAllRefUpdates always
>>>>    git config remote.origin.fetch refs/stashes/*:refs/remote/origin/stashes/*
>>>>    echo a >a
>>>>    git add a
>>>>    git commit -m a
>>>>    echo b >a
>>>>    git stash push
>>>>    echo c >a
>>>>    git stash push
>>>>    git stash export --to-ref refs/stashes/test
>>>>    git push origin refs/stashes/test
>>>>    git stash pop
>>>>    git stash push -m message
>>>>    git stash export --to-ref refs/stashes/test
>>>>    git push --force-with-lease --force-if-includes  origin refs/stashes/test
>>>> 
>>>>>> You need to pass the name of the ref whose reflog you want to look at,
>>>>>> otherwise it defaults to showing the reflog for HEAD. You should be
>>>>>> able to see the reflog for you exported stashes.>
>>>>> I gave this a try and every time I use `git reflog refs/stashes/$USER`,
>>>>> I always get a blank response. No errors and no output.
>>>> Ah, I wonder if core.logAllRefUpdates only affects the creation of new
>>>> refs. You can force the creation of a reflog by running
>>>> 
>>>> oid=$(git rev-parse --verify refs/stashes/$USER) &&
>>>> git update-ref -d refs/stashes/$USER &&
>>>> git update-ref --create-reflog -m 'export stashes' refs/stashes/$USER $oid
>>>> 
>>>> the same applies to refs/remote/stashes/origin/$USER
>>>> 
>>>>>> Let's try and find why the remote update say's it rejected when it isn't
>>>>>> and then we can think about the best way to document pushing and
>>>>>> pulling exported stashes.
>>>> I haven't thought much about the pulling side of this. "git stash import"
>>>> appends to the existing stashes so I'm not sure how we'd cope with forced
>>>> updates - have got got any experience of handling this from your
>>>> experiments?
>>>> 
>>>> Thanks
>>>> 
>>>> Phillip



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-09-19 19:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-31 23:25 Git Stash Synchronization - Best Workflow? Brooke Kuhlmann
2025-09-01 10:10 ` Phillip Wood
2025-09-01 20:48   ` Brooke Kuhlmann
2025-09-05 14:03     ` Phillip Wood
2025-09-06 12:50       ` Brooke Kuhlmann
2025-09-10  9:52         ` Phillip Wood
2025-09-11  2:22           ` Brooke Kuhlmann
2025-09-19 14:04             ` Phillip Wood
     [not found]               ` <A42DC91A-91F2-4AB6-B0EE-52DE5135E99E@alchemists.io>
2025-09-19 19:54                 ` Brooke Kuhlmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox