开发者

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

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 th开发者_StackOverflow社区e other computer, or
  • Create a minor branch and commit the incomplete work to that branch?


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


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.


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.


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.


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.


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

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 bash 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'd go with second approach although no idea why you can't commit it to main/feature branch. It is possible to do cherry-picking too.


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.


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.


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_


See below two one-liners I successfully used to push all stashes from multiple source repo's to a single destination repo on a remote host.

This is based on sehe's original answer merged with Alan Krueger's comment, Keith Robertson's comment, Sebastian Schrader's comment and by adding the remote host:dir.

# on each src_host:repo_dir
git send-pack <dst_host>:<dst_repo_dir> $(for sha in $(git rev-list -g stash); do echo $sha:refs/heads/stash_$sha; done)

# on the dst_host:repo_dir
for a in $(git rev-list --no-walk --glob='refs/heads/stash_*' | tac); do git checkout $a && git reset HEAD^ && git update-ref --create-reflog -m "$(git show -s --format=%B $a)" refs/stash $a && git restore . && git branch -D stash_$a; done; git checkout master

(I don't have enough reputation points yet, otherwise I would have added a comment under sehe's original answer)


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜