Is it possible to push a git stash to a remote repository?

Viewed 98846

In git, is it possible to create a stash, push the stash to a remote repository, retrieve the stash on another computer, and apply the stash?

Or are my options:

  • Create a patch and copy the patch to the other computer, or
  • Create a minor branch and commit the incomplete work to that branch?
12 Answers

The currently accepted answer is technically correct, you can't directly tell Git to push all your stashes to a remote, and then pull everything into your local stashes on another computer.

And while the currently top-upvoted answer should work, I didn't like that it creates a bunch of temporary branches, and that it requires manually checking out the stash commit and saving it as a stash, which can lead to issues like this comment mentioned, and leads to a duplicate On (no branch): On testing:. Surely there must be a better way!

So while you can't directly push stashes, a stash is just a commit (actually two commits), and per the git push man page you can push commits:

The <src> is often the name of the branch you would want to push, but it can be any arbitrary "SHA-1 expression"...

I chose to push the stashes to refs/stashes/* so that I wouldn't clutter up my remote with extra branches. So I can do that with:

git push origin stash@{0}:refs/stashes/$(git rev-parse --short stash@{0})

(The rev-parse command gets the short hash of the stash, which will be unique for the repo.)

Next, I need to fetch the stash from the other computer. Git only fetches branches by default, so I need to fetch the stashes specifically:

git fetch origin refs/stashes/*:refs/stashes/*

Now to convert the stash commit back into an actual stash. As mentioned, while I could just check out the stash commit, reset, and stash as usual, I don't like that it requires extra steps, or that it might not maintain the index state for the stash. I was looking online for a way to do that automatically, but my search-fu failed me. Finally I looked through the man page for git stash, where I found this:

create
Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the ref namespace. This is intended to be useful for scripts. It is probably not the command you want to use; see "save" above.

store
Store a given stash created via git stash create (which is a dangling merge commit) in the stash ref, updating the stash reflog. This is intended to be useful for scripts. It is probably not the command you want to use; see "save" above.

Since I already have the commit, store sounds like what I want. So I can do:

git stash store --message "$(git show --no-patch --format=format:%s <SHA>)" <SHA>

Replacing <SHA> with the stash that was just fetched.

(The git show command gets the commit message from the stash commit, to use as the message for the stash log.)

The stash now shows up as normal in my local repo:

$ git stash list
stash@{0}: On master: temp
...

To clean up the remote, the stashes can be deleted from the remote like so:

git push origin :refs/stashes/<SHA>

This method also has the benefit of being idempotent: if you run the push command again, it will report Everything up-to-date. The fetch command can also be safely run repeatedly. While the stash store will skip storing the stash if it is the same as the most recent stash, it doesn't prevent duplicates of older stashes. This can be worked around though, as I do in my git-rstash script, see below.


For completion, you can also easily push all stashes (with ):

for i in $(seq 0 $(expr $(git rev-list --walk-reflogs --count stash) - 1))
do
  git push origin stash@{$i}:refs/stashes/$(git rev-parse --short stash@{$i})
done

or import all fetched stashes:

for stash in $(ls .git/refs/stashes)
do
  git stash store --message "$(git show --no-patch --format=format:%s $stash)" $stash
done

I've created a script that can be called as a subcommand (e.g. git rstash push 0) so I don't have to remember all this. git-rstash can be found here.

I would simply create a new stash branch and the remove whenever that branch is not required.

On Machine 1:

git add . // Add work-in-progress job
git checkout -b stash-branch // Create and checkout to stash-branch
git commit -m 'WIP: job description' // Commit message
git push origin stash-branch // Push to remote

On Machine 2:

git pull origin stash-branch // Pull the stash-branch
git checkout master // Checkout to working branch
git rebase stash-branch // Rebase the stash-branch
git reset --soft // Equivalent to stash!!
git branch -d stash-branch // Delete branch when not needed from local
git push -d origin stash-branch // Delete branch when not needed from remote

I am going to mix 2 answers from above just to share how I do it with zsh shell.

A huge thanks to @Scott Weldon (answer-link) and @sehe (answer-link) for their answers to this question! I learned a lot from them!! Also I learned about shell scripting extensively thanks to this problem!


A very simple explanation of what the codes does

Please refer to above answer-links for better (recommended) understanding.:

What the code does:

Stash goes from Machine 1 --> to remote --> to Machine 2

Steps On Machine 1:

1. Push the stash to remote (using for loop for multiple stashes)

Steps On Machine 2:

1. Check stash(es) in remote. (This is just to check whether there are  previous stashes in remote. If there are, you must delete them if you don't want them in you stash list. Command for deleting remote stashes are given in bonus)
2. Fetch the stash(es) to a local ref folder named "ref/stashes" in your .git folder in your local repository. (Its like downloading the stashes on your PC)
3. Convert the fetched stash(es) to proper data. (Its like extracting or installing the stashes on your PC)

bonus:

1. Check stash(es) in remote
2. Delete stash(es) from remote 

Codes:

On Machine 1:

1. git push origin $(for sha in $(git rev-list -g stash); do echo $sha:"refs/stashes/$(git rev-parse --short $sha)"; done)

On Machine 2:

1. git ls-remote
2. git fetch origin "refs/stashes/*":"refs/stashes/*"
3. for sha in $(git rev-list --no-walk --reverse --glob='refs/stashes/*'); do git stash store --message "$(git show --no-patch --format=format:%s $sha)" $sha; done

bonus:

1. git ls-remote
2. git push origin :refs/stashes/<stashFile-1> :refs/stashes/<stashFile-2>

The above codes are for multiple stashes and can be used for one stash too. Just make sure that your remote ref/stashes folder has only the stashes you want in your local repo.

Building on other answers, if you have a public repository on a platform like GitHub, but don't want your in-progress changes to be public, you can create a private repository and add it as a remote.

To sync changes: commit, push to a branch on the private remote, pull on the target device, and do a soft/mixed reset.

Related