É possível enviar um stash git para um repositório remoto?

205

No git, é possível criar um stash, enviá-lo para um repositório remoto, recuperá-lo em outro computador e aplicá-lo?

Ou são minhas opções:

  • Crie um patch e copie-o para outro computador ou
  • Criar um ramo menor e confirmar o trabalho incompleto nesse ramo?
Andrew Grimm
fonte

Respostas:

68

Não é possível obtê-lo por meio de busca, o espelho refspec é fetch = +refs/*:refs/*e, mesmo que oculto , refs/stashele não é enviado. Um explícito refs/stash:refs/stashtambém não tem efeito!

Seria apenas confuso de qualquer maneira, uma vez que isso não pegaria todos os esconderijos, apenas o mais recente; a lista de esconderijos é o reflog da ref refs/stashes.

u0b34a0f6ae
fonte
4
Você pode buscar o stash mais recente em um controle remoto git, mas não no stash, apenas em outra ref. Algo como git fetch some-remote +refs/stash:refs/remotes/some-remote/stasho git stash apply some-remote/stash. Mas você não pode obter stashes mais antigos porque eles são armazenados no reflog que não é possível de ser obtido. Veja stackoverflow.com/questions/2248680/…
sj26
75

Nota: Acabei de reescrever esta resposta com mais 24 horas de git-fu no meu currículo :) Na minha história de shell, todo o shebang agora é de três linhas. No entanto, não os condensamos para sua conveniência.

Dessa forma, espero que você possa ver como eu fiz as coisas, em vez de apenas ter que copiar / colar cegamente as coisas.


Aqui está passo a passo.

Suponha que a fonte seja ~ / OLDREPO contendo stashes. Crie um clone TEST que não contém stashes:

cd ~/OLDREPO
git clone . /tmp/TEST

Empurre todos os stashes como ramificações temporárias:

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

Faça um loop na extremidade de recebimento para transformar novamente em 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

Limpe suas filiais temporárias se desejar

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

Faça uma lista stash do git e você terá algo assim:

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

No repositório original, parecia o mesmo

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
ver
fonte
1
Estou aprendendo muito em pouco tempo e acho que provavelmente deveria simplesmente usar muitos comandos na minha abordagem anterior, o que tentarei fazer mais tarde.
Veja
9
Isso funcionou bem para mim, exceto que eu precisava de um git add .antes da git stash save ...etapa, pois git stashse recusa a esconder novos arquivos, a menos que eles tenham sido testados. Além disso, canalizando o resultado git rev-list ...através tacinverte a ordem dos esconderijos assim que saem na mesma ordem.
Alan Krueger
1
Excelente script !! Duas sugestões: 1) - inverta a lista de ref final para que os stashes estejam na mesma ordem no repositório de destino e no original. 2) Finalize o forloop final com git branch -D stash_$a(limpar à medida que os stashes são criados) para que, se algo der errado e tentarmos novamente, não reprocessarmos os commit já armazenados com êxito.
21116 Keith Robertson
1
Muito obrigado por dedicar um tempo para explicar o que você fez em vez de "apenas postar a solução".
Marjan Venema
1
A solução pode ser melhorada ainda mais: Se você substituir git stash save "$(git log --format='%s' -1 HEAD@{1})"com git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $revvocê receber a mensagem esconderijo original ( update-refé o que git stash savenos bastidores).
Sebastian Schrader
31

Estou um pouco atrasado para a festa, mas acredito que encontrei algo que funciona para mim em relação a isso e que pode também para você se suas circunstâncias forem iguais ou semelhantes.

Estou trabalhando em um recurso em seu próprio ramo. O ramo não é mesclado no master e enviado até o término ou fiz compromissos que me sinto à vontade para mostrar ao público. Então, o que faço quando quero transferir alterações não-estágios para outro computador é:

  • Faça uma confirmação, com uma mensagem de confirmação como " [non-commit] FOR TRANSFER ONLY", apresentando o conteúdo que você deseja transferir.
  • Faça login no outro computador.
  • Então faça:

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

    A URL pode ser diferente para você se você acessar seu repositório de uma maneira diferente. Isso puxará as alterações desse URL da ramificação remota "rb" para a ramificação local "lb". Observe que eu tenho um servidor ssh em execução no meu próprio computador e posso acessar o repositório dessa maneira.

  • git reset HEAD^(implica --mixed)

    Isso redefine o HEAD para apontar para o estado antes do commit "[não confirmado]".

From git-reset (1): " --mixed: redefine o índice, mas não a árvore de trabalho (ou seja, os arquivos alterados são preservados, mas não marcados para confirmação) [...]"

Portanto, você terá suas alterações nos arquivos no final, mas nenhuma confirmação será feita no domínio e não haverá necessidade de esconderijo.

No entanto, isso exigirá que você faça git reset --hard HEAD^o repositório no qual você fez o "[non-commit]", pois esse commit é um lixo.

Victor Zamanian
fonte
Esta é uma mais suja muito, então apenas criar um novo ramo de funcionalidade, em seguida, excluí-lo depois ....
Taegost
@ Taegost depende do seu ambiente, eu acho. Pode haver algumas coisas de CI / CD, impedindo apenas empurrar galhos rio acima, por muito tempo. Mas sim, dependendo do que você preferir, você pode apenas criar um ramo para realizar a mesma coisa.
Victor Zamanian
22

É um pouco tarde, mas esta resposta pode ajudar alguém. Eu queria saber disso porque queria poder enviar um recurso em andamento / bug / qualquer coisa e trabalhar a partir do mesmo ponto em outro computador.

O que funciona para mim é confirmar meu código em andamento (em um ramo em que estou trabalhando sozinho). Quando chego ao meu outro computador, puxe e desfaça a confirmação com:

git reset --soft HEAD^

Continue trabalhando como estava, com todas as alterações em andamento lá, não confirmadas e sem estágio.

Espero que ajude.

Sir Robert
fonte
Quando tento fazer isso, o Origin ainda mantém o Commit, que não foi confirmado. Perto, mas sem charuto para mim.
Rezwits 16/07/2016
@rezwits Sim, o controle remoto o mantém, mas é fácil o suficiente simplesmente excluir a ramificação temporária da origem.
Sir Robert
de fato, é isso que tenho feito!
Rezwits 02/09/19
19

Parece haver um truque muito interessante para resolver isso. você pode usar git diff > file.diff(e confirmar o arquivo) e, em seguida, restaurar as alterações usando git apply file.diff(de qualquer lugar) para obter o mesmo resultado.

Isso foi explicado aqui também.

Daniel Dubovski
fonte
5
se você possui arquivos não rastreados: 1. git add. 2. git diff HEAD> file.diff
trickpatty 13/17
Enviar uma mensagem para o diff não permite nenhuma confirmação / pegada no repositório! (por exemplo: Nota para si mesmo via aplicativo de desktop Signal) ou e-mail.
John Mee
9

Eu iria com a segunda abordagem, embora não tenha idéia de por que você não pode comprometê-la no ramo principal / em destaque. Também é possível fazer a colheita de cerejas.

Eimantas
fonte
27
Não há nenhuma razão técnica para não comprometer-se com o mestre / destaque, apenas que eu quero dizer "Este não é um comprometimento real, está apenas salvando meu trabalho para que eu possa colocá-lo em outra máquina".
22813 Andrew Grimm
4

AFAIK, toda a idéia do esconderijo é esconder algo não tão importante sob o tapete local . Ninguém deve saber sobre sua porcaria favorita ;-) O único "mas" é: Mas se eu desenvolvo em algumas estações de trabalho? Então scpé muito melhor.

argent_smith
fonte
9
Algo tão engraçado deve ser um comentário. ;-)
Andrew Grimm
2
Total git-ssh-newbie aqui, mas você pode usar o scp com o github?
Koen
Não, o frontend git-ssh do github está programado para que você nunca tenha um console / shell ssh. Só pode executar o processo git do lado do servidor.
23812 argent_smith
1
Então o scp não é realmente uma opção para esse cenário se o seu ramo principal estiver no github? Alguma outra sugestão para transferir um estoque nesse caso?
Koen
1
Tentei enfatizar que a transferência de stash não é possível, AFAIK.
23412 argent_smith
2

A resposta atualmente aceita é tecnicamente correta, você não pode dizer diretamente ao Git para enviar todos os seus stashes para um controle remoto e, em seguida, colocar tudo nos stashes locais em outro computador.

E embora a resposta atualmente com a votação superior deva funcionar, eu não gostei que ela crie várias ramificações temporárias e que exija verificar manualmente o commit do stash e salvá-lo como stash, o que pode levar a problemas como este comentário mencionado e leva a uma duplicata On (no branch): On testing:. Certamente deve haver uma maneira melhor!

Portanto, embora você não possa enviar stash diretamente, um stash é apenas uma confirmação (na verdade, duas confirmações) e, de acordo com a git pushpágina de manual, você pode enviar confirmações por push:

A <src>é muitas vezes o nome do ramo que você gostaria de empurrar, mas pode ser qualquer arbitrária "SHA-1 expressão" ...

Eu escolhi empurrar os esconderijos para refs/stashes/*não bagunçar meu controle remoto com galhos extras. Então eu posso fazer isso com:

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

(O rev-parsecomando obtém o pequeno hash do stash, que será exclusivo para o repositório.)

Em seguida, preciso buscar o esconderijo do outro computador. O Git apenas busca ramificações por padrão, então eu preciso buscar as stashes especificamente:

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

Agora, para converter o commit do stash novamente em um stash real. Como mencionado, embora eu pudesse verificar o commit, redefinição e armazenamento do stash normalmente, não gosto que exija etapas extras ou que não mantenha o estado do índice do stash. Eu estava procurando on-line uma maneira de fazer isso automaticamente, mas minha busca-falhou comigo. Finalmente, procurei na página de manual git stash, onde encontrei o seguinte:

create
Crie um stash (que é um objeto de confirmação regular) e retorne seu nome de objeto, sem armazená-lo em qualquer lugar no espaço para nome ref. Isso pretende ser útil para scripts. Provavelmente não é o comando que você deseja usar; veja "salvar" acima.

store
Armazene um determinado stash criado via git stash create (que é uma confirmação de mesclagem pendente) na ref stash, atualizando o reflog do stash. Isso pretende ser útil para scripts. Provavelmente não é o comando que você deseja usar; veja "salvar" acima.

Desde que eu já tenho o commit, storesoa como o que eu quero. Então eu posso fazer:

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

Substituindo <SHA>pelo esconderijo que acabou de ser buscado.

(O git showcomando obtém a mensagem de confirmação do stash commit, para ser usada como a mensagem do log stash.)

O stash agora aparece normalmente no meu repositório local:

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

Para limpar o controle remoto, os stashes podem ser excluídos do controle da seguinte maneira:

git push origin :refs/stashes/<SHA>

Este método também tem o benefício de ser idempotente: se você executar o pushcomando novamente, ele será reportado Everything up-to-date. O fetchcomando também pode ser executado com segurança repetidamente. Embora o stash storearquivo pule o armazenamento do stash, se for o mesmo que o stash mais recente, ele não impede duplicatas de stash mais antigos. Isso pode ser contornado, como faço no meu git-rstashscript, veja abaixo.


Para concluir, você também pode enviar facilmente todos os stashes (com ):

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

ou importe todos os stashes buscados:

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

Eu criei um script que pode ser chamado como subcomando (por exemplo git rstash push 0), para que eu não precise me lembrar de tudo isso. git-rstashpode ser encontrado aqui.

Scott Weldon
fonte
1

O seguinte não funciona com o stash, mas com as alterações não confirmadas no diretório de trabalho. Ele cria uma ramificação, confirma automaticamente todas as alterações atuais e envia para o controle remoto:

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

commit_and_push_ my-temp-branch
commit_and_push_
blueFast
fonte
0

Eu simplesmente criaria um novo ramo stash e o removeria sempre que esse ramo não for necessário.

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

Basta usar o Dropbox como esse cara. Dessa forma, você não precisa se preocupar em enviar stashes, pois todo o seu código seria copiado.

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

Engenheiro de tecnologia de Nova York
fonte
2
Antes que alguém tenha uma reação instintiva, não estou dizendo para usar o Dropbox em vez do Github, mas para armazenar código que não está pronto para uma confirmação no Dropbox que ainda estaria sob controle de versão localmente.
engenharia
4
levará muito tempo para copiar todo o projeto para a nuvem remota.
Stav Alfi