223

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?
Andrew Grimm
  • 70,470
  • 47
  • 186
  • 310

12 Answers12

77

Note: I've just rewritten this answer with 24 hours more git-fu under my belt :) In my shell history, the whole shebang is now three one-liners. However, I've uncondensed them for your convenience.

This way, I hope you will be able to see how I did things, instead of just having to blindly copy/paste stuff.


Here is step by step.

Assume is source in ~/OLDREPO containing stashes. Create a TEST clone containing no stashes:

cd ~/OLDREPO
git clone . /tmp/TEST

Push all the stashes as temp branches:

git send-pack /tmp/TEST $(for sha in $(git rev-list -g stash); \
    do echo $sha:refs/heads/stash_$sha; done)

Loop on the receiving end to transform back into stashes:

cd /tmp/TEST/
for a in $(git rev-list --no-walk --glob='refs/heads/stash_*'); 
do 
    git checkout $a && 
    git reset HEAD^ && 
    git stash save "$(git log --format='%s' -1 HEAD@{1})"
done

Cleanup your temporary branches if you will

git branch -D $(git branch|cut -c3-|grep ^stash_)

Do a git stash list and you will something like this:

stash@{0}: On (no branch): On testing: openmp import
stash@{1}: On (no branch): On testing: zfsrc
stash@{2}: On (no branch): WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue
stash@{3}: On (no branch): WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{4}: On (no branch): WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{5}: On (no branch): WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{6}: On (no branch): WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{7}: On (no branch): WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{8}: On (no branch): WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{9}: On (no branch): WIP on emmanuel: bee6660 avoid unrelated changes

On the original repository, the same looked like

stash@{0}: WIP on emmanuel: bee6660 avoid unrelated changes
stash@{1}: WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{2}: WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{3}: WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{4}: WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{5}: WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{6}: WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{7}: WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue #57)
stash@{8}: On testing: zfsrc
stash@{9}: On testing: openmp import
sehe
  • 328,274
  • 43
  • 416
  • 565
  • 1
    I'm learning a lot in little time, and I feel I should probably simply many command usages in my earlier approach, which I will try to do later. – sehe Mar 13 '11 at 02:00
  • 10
    This worked well for me except that I needed a `git add .` before the `git stash save ...` step since `git stash` refuses to stash new files unless they've been staged. Also, piping the result of `git rev-list ...` through `tac` reverses the order of the stashes so they come out in the same order. – Alan Krueger Dec 04 '12 at 22:35
  • @AlanKrueger I did't try this answer, but `git stash save -u` may help. – hiroshi May 28 '14 at 05:58
  • @PietroSaccardi Alan mentioned this a few years ago :) – sehe Mar 02 '16 at 10:30
  • @PietroSaccardi Frankly I feel the answer is complicated enough. Also, people will likely put on some more finishing touches of their own (like me, last week when I revisited this answer to push my stashes to a backup repo) – sehe Mar 02 '16 at 10:33
  • 2
    @sehe Excellent script!! Two suggestions: 1) --reverse the final ref-list so that stashes are in the same order in target repo as in original. 2) End final `for` loop with `git branch -D stash_$a` (clean up as stashes are created) so that if something goes wrong and we retry, we don't reprocess commits already successfully stashed. – Keith Robertson Jul 11 '16 at 20:12
  • 1
    Thank you so much for taking the time to explain what you did instead of "just posting the solution". – Marjan Venema Jul 14 '16 at 11:02
  • @KeithRobertson Good ideas. How do you reverse the loop? Thanks. – Ryan Dec 22 '18 at 14:22
  • @sehe This answer looks really promising, but what I'm confused about is that the question is about copying all stashes from one computer to another (not a different repo on the same computer). Does your code here do that? If not, what could I change to enable it to help me copy all my stashes from one computer to another? Thanks. – Ryan Dec 22 '18 at 14:23
  • 2
    The solution can be improved further: If you replace `git stash save "$(git log --format='%s' -1 HEAD@{1})"` with `git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $rev` you get the original stash message (`update-ref` is what `git stash save` behind the scenes). – Sebastian Schrader Jan 14 '20 at 16:39
71

It's not possible to get it via fetch or so, the mirror refspec is fetch = +refs/*:refs/*, and even though stash is refs/stash it doesn't get sent. An explicit refs/stash:refs/stash has no effect either!

It would only be confusing anyway since that wouldn't fetch all stashes, only the latest one; the list of stashes is the reflog of the ref refs/stashes.

u0b34a0f6ae
  • 42,509
  • 13
  • 86
  • 97
  • 4
    You can fetch the latest stash from a git remote, but not into your stash, only into another ref. Something like `git fetch some-remote +refs/stash:refs/remotes/some-remote/stash` the `git stash apply some-remote/stash`. But you can't get older stashes because they're stored in the reflog which isn't fetchable. See http://stackoverflow.com/questions/2248680/can-i-fetch-a-stash-from-a-remote-repo-into-a-local-branch/29839687#answer-29839687 – sj26 Feb 05 '16 at 03:56
32

I'm a little late to the party, but I believe I found something that works for me regarding this and it might for you too if your circumstances are the same or similar.

I'm working on a feature in its own branch. The branch isn't merged into master and pushed until its finished or I've made commits that I feel comfortable showing to the public. So what I do when I want to transfer non-staged changes to another computer is:

  • Make a commit, with a commit message like "[non-commit] FOR TRANSFER ONLY", featuring the content you want transfered.
  • Login to the other computer.
  • Then do:

    git pull ssh+git://<username>@<domain>/path/to/project/ rb:lb

    The URL might differ for you if you access your repository in a different way. This will pull changes from that URL from the remote branch "rb" into the local branch "lb". Note that I have an ssh server running on my own computer, and am able to access the repository that way.

  • git reset HEAD^ (implies --mixed)

    This resets the HEAD to point to the state before the "[non-commit]" commit.

From git-reset(1): "--mixed: Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) [...]"

So you will have your changes to the files in the end, but no commits are made to master and no need for a stash.

This will however require you to git reset --hard HEAD^ in the repository in which you made the "[non-commit]", since that commit is garbage.

Victor Zamanian
  • 2,861
  • 21
  • 28
  • 2
    This is a lot dirtier then just creating a new feature branch then deleting it afterwards.... – Taegost Apr 18 '19 at 18:51
  • @Taegost depends on your environment I guess. Might be some CI/CD stuff preventing just pushing branches upstream willy nilly. But yes, depending on what you prefer, you might want to just create a branch to accomplish the same thing. – Victor Zamanian Apr 18 '19 at 20:54
26

It's a little late, but this answer might help someone. I wanted to know this because I wanted to be able to push an in-progress feature/bug/whatever and work from the same point on another computer.

What works for me is to commit my in-progress code (in a branch that I'm working on alone). When I get to my other computer, do a pull, then undo the commit with:

git reset --soft HEAD^

Continue working as you were, with all your in-progress changes there, uncommitted, and unstaged.

Hope it helps.

Sir Robert
  • 4,078
  • 6
  • 30
  • 47
  • When I try to do this, the Origin still maintains the Commit, that was uncommitted. Close but no cigar for me. – rezwits Jul 16 '16 at 18:48
  • @rezwits Yeah, the remote keeps it, but it's easy enough to simply delete the temporary branch from origin. – Sir Robert Sep 01 '17 at 04:39
  • as a matter of fact that's what I have been doing! – rezwits Sep 02 '17 at 06:55
  • It technically answers the OP question, since he didn’t state that it should be invisible in commit history… Anyway, you can always `--force` or `--force-with-lease` to overwrite the "committed stash" afterwards. – Yahya Abou Imran Apr 29 '21 at 17:10
19

There seems to be a very neat trick to solve this. you can use git diff > file.diff (and commit the file) , then restore the changes using git apply file.diff (from anywhere) to achieve the same result.

This was explained here as well.

Community
  • 1
  • 1
Daniel Dubovski
  • 1,854
  • 23
  • 28
  • 5
    if you have untracked files: 1. git add . 2. git diff HEAD > file.diff – trickpatty Sep 13 '17 at 22:08
  • Messaging the diff to yourself allows for no commits/footprint on the repo at all! (eg: Note-to-self via Signal desktop app), or email. – John Mee Jun 16 '20 at 05:24
9

I'd go with second approach although no idea why you can't commit it to master/featured branch . It is possible to do cherry-picking too.

Eimantas
  • 47,394
  • 15
  • 127
  • 164
  • 29
    There's no technical reason not to commit to master/featured, just that I want to say "This isn't a real commit, it's just saving my work so I can get it on another machine". – Andrew Grimm Oct 11 '09 at 23:01
8

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.

Scott Weldon
  • 8,032
  • 6
  • 43
  • 60
6

AFAIK the whole idea of stash is to hide something not-so-important under the local carpet. Nobody should know about your favorite crap ;-) The only "but" is: But if I develop on a couple of workstations? Then scp is way better.

argent_smith
  • 476
  • 6
  • 11
  • 9
    Something this funny ought to be a comment. ;-) – Andrew Grimm Sep 22 '11 at 08:10
  • 2
    Total git-ssh-newbie here but can you use scp with github then? – Koen Mar 16 '12 at 10:23
  • No, github's git-ssh frontend is programmed so you don't ever have an ssh shell/console. It can only run server-side git process. – argent_smith Mar 16 '12 at 13:50
  • 1
    So scp is not really an option for this scenario if your master branch is on github? Any other suggestions to transfer a stash in that case? – Koen Mar 16 '12 at 13:53
  • 1
    I've tried to emphasize that stash transfer isn't possible at all, AFAIK. – argent_smith Mar 17 '12 at 19:55
  • Wikipedia: Secure copy protocol (SCP) is a means of securely transferring computer files between a local host and a remote host or between two remote hosts. It is based on the Secure Shell (SSH) protocol.[1] "SCP" commonly refers to both the Secure Copy Protocol and the program itself.[2] According to OpenSSH developers in April 2019, SCP is outdated, inflexible and not readily fixed; they recommend the use of more modern protocols like sftp and rsync for file transfer.[3] – generic-user Mar 26 '21 at 14:41
  • Regardless of the precise mechanism you use for remote copying, the fundamental problem with this approach is that copying around random work in progress messes up the organization of your files. The reason we use version control in thee first place is the ability to say "these bytes are here for this reason"; a stash nicely packages this up so you can stow away a particular set of changes and then perhaps pick it up again and continue working on it later. – tripleee Apr 19 '21 at 12:37
1

The following does not work with the stash, but with the uncommitted changes in the working dir. It creates a branch, autocommits all current changes, and pushes to the remote:

commit_and_push_ ( ) {
    # This will:
    #  1. checkout a new branch stash-XXX
    #  2. commit the current changes in that branch
    #  3. push the branch to the remote
    local locbr=${1:-autostash-XXX}
    git checkout -b $locbr
    git add .
    git commit -a -m "Automatically created commit"
    git push origin $locbr
    echo "Autocommitted changes in branch $locbr ..."
}

Use like:

commit_and_push_ my-temp-branch
commit_and_push_
blueFast
  • 33,335
  • 48
  • 165
  • 292
0

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

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
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 when not needed from local
git push -d origin stash-branch // Delete when not needed from remote
Bhojendra Rauniyar
  • 73,156
  • 29
  • 131
  • 187
0

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.

hacker1024
  • 958
  • 4
  • 14
-10

Just use Dropbox like this guy did. That way you don't have to worry about pushing stashes since all your code would be backed up.

http://blog.sapegin.me/all/github-vs-dropbox

NYC Tech Engineer
  • 1,677
  • 2
  • 20
  • 23
  • 2
    Before anyone has a knee-jerk reaction, I'm not saying to use Dropbox instead of Github, but to store code that is not ready for a commit in Dropbox which would still be under version control locally. – NYC Tech Engineer Apr 24 '17 at 08:20
  • 4
    its will take too much time to copy all the project to remote cloud. – Stav Alfi Nov 19 '17 at 20:40
  • 1
    Regardless of possible merit of the material behind the link, an answer which basically says "don't read this answer; click this link instead" is not acceptable on Stack Overflow. At a minimum, the answer should summarize the actual content behind the link so that we can assess whether we want to try our luck, if the link even works any longer. – tripleee Apr 19 '21 at 12:30