Afaste temporariamente as alterações não confirmadas no Subversion (à la "git-stash")

312

Ao programar o software armazenado em um repositório do Subversion, geralmente modifico alguns arquivos, depois percebo que gostaria de fazer algumas alterações preparatórias para o meu trabalho principal. Por exemplo, ao implementar novas funcionalidades, noto alguma refatoração que pode me ajudar.

Para não misturar duas alterações não relacionadas, nesses casos, eu gostaria de "ocultar" minhas alterações, ou seja, reverter para a versão do repositório, fazer outras alterações, confirmar essas alterações e "recuperar" minhas alterações.

O git-stash permite fazer exatamente isso. Existe alguma maneira de fazer isso com o Subversion, diretamente ou com algum plugin ou script. Os plugins do Eclipse também ficariam bem.

sleske
fonte
6
apenas curioso, mas por que não usar git-svn?
amigos estão dizendo sobre cmcginty
3
Algumas notícias relevantes: infoworld.com/d/application-development/… (citando: "Ele também observa que o próximo lançamento do Subversion 1.8 deve aproximar os recursos do Git, com recursos como o Git stash, nos quais um desenvolvedor pode fazer alterações localmente e depois retirá-las, e comete off-line, que registra concluídas alterações quando um desenvolvedor está offline e move o para o repositório principal quando os reconecta desenvolvedores ".
Sebastiaan van den Broek
1
Atualização (em 26/04/2012): As prateleiras estão agendadas para 1.9, sem ETA. Por isso pode levar algum tempo ...
sleske
10
Atualização (a partir de 17/11/2012): as prateleiras estão agendadas para a 1.10. Talvez esteja sempre agendado para <próximo lançamento +1>? ;-)
sleske 17/11/2012
3
Atualização (a partir de 23/03/2015, 2 anos e meio depois): Boas notícias são que as prateleiras ainda estão agendadas para a 1.10. As más notícias são a ETA: Q2 2015 (provisória), versão 1.9.0 / 2017? (especulativa na melhor das hipóteses) Solte 1.10.0 ( subversion.apache.org/roadmap.html )
Ribamar

Respostas:

69

Quando tenho alterações não confirmadas de uma tarefa na minha cópia de trabalho e preciso mudar para outra tarefa, faço uma de duas coisas:

  1. Confira uma nova cópia de trabalho para a segunda tarefa.

    ou

  2. Inicie uma filial:

    workingcopy$ svn copy CURRENT_URL_OF_WORKING_COPY SOME_BRANCH
    workingcopy$ svn switch SOME_BRANCH
    workingcopy$ svn commit -m "work in progress"
    workingcoyp$ svn switch WHATEVER_I_WAS_WORKING_ON_BEFORE
    

Eu tenho alguns scripts que ajudam a automatizar isso.

Bendin
fonte
65
isso irá resultar em um monte de lixo em seu subversão servidor
knittl
3
@knittl: Não, não vai. E o que é ainda mais importante: não resultará em alterações perdidas, como sua sugestão. Isso, e ter outra cópia do tronco / mesma ramificação, são as únicas duas maneiras confiáveis ​​de fazer isso que eu conheço. Se você se sentir desconfortável com isso, verifique outra cópia e trabalhe nela em paralelo.
Sb12
2
@knittl: a ramificação pode ser criada em um caminho discreto que fica fora da localização padrão das ramificações ou tags do projeto. Por exemplo, uma equipe pode designar project\temp\<creationdate-reason>ou project\personal\<creationdate-reason>para esse fim.
rwong 21/05
12
Ainda é lamentável que a ramificação tenha que ser criada no servidor. Não é que essas ramificações duplicem muitos dados, mas elas criam muitas referências desnecessárias que um sistema como o git dispensa.
thepeer 11/07/12
8
isso não é útil em um repositório grande. Isso absolutamente não é uma opção no meu ambiente de trabalho. E embora eu desejasse que nosso repositório fosse menor e melhor organizado, e francamente, um repositório git em vez de svn, estou confinado aos limites de como nosso código é organizado em nossa organização.
AdrianVeidt 24/06
337

Esta postagem do blog aconselha o uso de diff e patch.

  • git stash torna-se aproximadamente svn diff > patch_name.patch; svn revert -R .
  • git stash apply torna-se patch -p0 < patch_name.patch

Observe que isso não esconde alterações de metadados ou (eu acho) o diretório cria / exclui. (Sim, o svn rastreia aqueles separadamente do conteúdo do diretório, ao contrário do git.)

Walter Mundt
fonte
13
Esta é uma duplicata acidental de stackoverflow.com/questions/1554278/… - envie upvotes para lá.
Walter Mundt
2
Também não parece incluir arquivos binários, o que é irritante. Pelo menos ao usar o TortoiseSVN para gerar o patch.
Angularsen
1
stackoverflow.com/questions/159853/… pode ajudar com isso.
Walter Mundt
6
Você pode ter mais ou menos os metadados rastreados se usar em svn patch patch_name.patchvez de patch -p0, porque eles estão no arquivo de correção, e o svn patch os entende.
mat
Isso não inclui alterações externas.
congusbongus
182

Você pode armazenar suas alterações atuais svn diffem um arquivo de correção e reverter sua cópia de trabalho:

svn diff > stash.patch
svn revert -R .

Depois de implementar seu recurso preparatório, você poderá aplicar seu patch com o utilitário de patch:

patch < stash.patch

Como outros observaram, isso não funcionará com svn:propertiesoperações em árvore (adicione, remova, renomeie arquivos e diretórios).

Arquivos binários também podem causar problemas, não sei como o patch (ou o TortoiseSVN, neste caso, os trata).

knittl
fonte
4
Provavelmente isso não funciona muito bem com arquivos removidos / renomeados, eu acho.
JesperE
7
Veja a caixa intitulada "Por que não usar patches?" em svnbook.red-bean.com/en/1.5/… para entender por que essa é uma má ideia.
Sb12
4
@sbi: Eu não acho que isso seja uma justificativa válida para um voto negativo. Não é uma "resposta ruim". Não é apenas a resposta perfeita, é tudo. Eu não acho que essa pessoa merece punição por sua sugestão. Você prefere que ele não responda? Se sim, então sim, você deve fazer voto negativo. Caso contrário, isso está punindo boas intenções.
Sedat Kapanoglu 5/11/2009
5
no caso de alguém, como eu, achar que essa é a solução mais leve e decidir experimentá-la, tive que usar o patch -p0 <stash.patch - caso contrário, ele reclamaria por não conseguir encontrar os arquivos para corrigir
CupawnTae
4
Esse conselho ajuda especialmente se você é oriundo de um git e é forçado a usar o SVN por vários motivos. Uma pequena melhoria no conselho já dado aos usuários iniciantes do patch: $ patch --strip=0 < stash.patch Isso garantirá que o patch não pergunte o nome do arquivo quando você estiver aplicando o patch.
ksinkar
43

A maneira mais fácil seria usar uma ramificação temporária, assim:

$ svn copy ^/trunk ^/branches/tempbranch
$ svn switch ^/branches/tempbranch
$ svn commit -m "Stashed"
$ svn switch ^/trunk
$ ... hack away in trunk ...
$ svn commit -m "..."
$ svn merge ^/branches/tempbranch .
$ svn rm ^/branches/tempbranch
$ ... continue hacking

Isso poderia (e provavelmente deveria) ser colocado em um script se feito com mais regularidade.

JesperE
fonte
2
Por que isso é recusado, enquanto as "soluções" são votadas que nem funcionam quando você exclui / adicionou arquivos ou alterou propriedades? Sim, isso não é a coisa mais fácil de fazer quando você faz pela primeira vez, mas, além de ter outra cópia retirada para funcionar em paralelo, esta é a única solução que funciona em todos os casos.
Sb12
5
Bom uso da sintaxe ^ para o repo root (desde o svn 1.6). Boa solução quando o seu repositório tem troncos / tags / ramos no nível superior.
22410
4
Eu realmente não gosto de colocar todas essas ramificações temporárias no servidor. Eu sinto que isso deve ser feito localmente, em vez de sobrecarregar o servidor (e gerar spurios emails de check-in, se você gerar emails no check-in). Ainda assim, uma opção que vale a pena lembrar.
Sleske # 14/09
3
@lesleske: sim, você está enviando seu esconderijo temporário para o servidor, mas o próprio ramo é excluído. Enfim, acho que essa é a maneira mais rápida e robusta de fazer isso.
JesperE
5
@lesleske: SVN não é um VCS distribuído, então tudo tem que estar no servidor. É assim que as coisas são.
Sbi 14/10/2009
24

A partir de 1.10.0 (13-04-2018), você tem um svn shelvecomando experimental . (O TortoiseSVN suporta o comando ) Não é senão um auxiliar salvar um patch e aplicar novamente, por isso possui as mesmas limitações do svn diff+ patch(ou seja, não pode lidar com arquivos binários e renomear). ( Edit : Parece que o suporte binário está chegando na próxima versão 1.11.0 )

Editar ^ 2: com 1.11.0 (lançado em 30-10-2018), os arquivos binários são suportados . Arquivar arquivos renomeados permaneceu sem suporte. As prateleiras no 1.11 são incompatíveis com as prateleiras criadas pelo 1.10.

Editar ^ 3: com 1.12.0 (lançado em 24/04/2019), a cópia e a renomeação são suportadas . As prateleiras na versão 1.12 são incompatíveis com as prateleiras criadas por versões anteriores.

Editar ^ 4: Não há alterações em relação às prateleiras com 1.13.0 e 1.14.0 . Os comandos ainda estão marcados como experimentais e você precisa definir SVN_EXPERIMENTAL_COMMANDS=shelf3para ativar o recurso. Parece que o recurso não tem triagem no momento .

As notas de design podem ser encontradas no Wiki dos desenvolvedores .

$ svn x-shelve --help
x-shelve: Move local changes onto a shelf.
usage: x-shelve [--keep-local] SHELF [PATH...]

  Save the local changes in the given PATHs to a new or existing SHELF.
  Revert those changes from the WC unless '--keep-local' is given.
  The shelf's log message can be set with -m, -F, etc.

  'svn shelve --keep-local' is the same as 'svn shelf-save'.

  The kinds of change you can shelve are committable changes to files and
  properties, except the following kinds which are not yet supported:
     * copies and moves
     * mkdir and rmdir
  Uncommittable states such as conflicts, unversioned and missing cannot
  be shelved.

  To bring back shelved changes, use 'svn unshelve SHELF'.

  Shelves are currently stored under <WC>/.svn/experimental/shelves/ .
  (In Subversion 1.10, shelves were stored under <WC>/.svn/shelves/ as
  patch files. To recover a shelf created by 1.10, either use a 1.10
  client to find and unshelve it, or find the patch file and use any
  1.10 or later 'svn patch' to apply it.)

  The shelving feature is EXPERIMENTAL. This command is likely to change
  in the next release, and there is no promise of backward compatibility.

Valid options:
  -q [--quiet]             : print nothing, or only summary information
  --dry-run                : try operation but make no changes
  --keep-local             : keep path in working copy

(...)

$ svn x-unshelve --help
x-unshelve: Copy shelved changes back into the WC.
usage: x-unshelve [--drop] [SHELF [VERSION]]

  Apply the changes stored in SHELF to the working copy.
  SHELF defaults to the newest shelf.

  Apply the newest version of the shelf, by default. If VERSION is
  specified, apply that version and discard all versions newer than that.
  In any case, retain the unshelved version and versions older than that
  (unless --drop is specified).

  With --drop, delete the entire shelf (like 'svn shelf-drop') after
  successfully unshelving with no conflicts.

  The working files involved should be in a clean, unmodified state
  before using this command. To roll back to an older version of the
  shelf, first ensure any current working changes are removed, such as
  by shelving or reverting them, and then unshelve the desired version.

  Unshelve normally refuses to apply any changes if any path involved is
  already modified (or has any other abnormal status) in the WC. With
  --force, it does not check and may error out and/or produce partial or
  unexpected results.

  The shelving feature is EXPERIMENTAL. This command is likely to change
  in the next release, and there is no promise of backward compatibility.

Valid options:
  --drop                   : drop shelf after successful unshelve
(...)

$ svn help | grep x-
 x-shelf-diff
 x-shelf-drop
 x-shelf-list (x-shelves)
 x-shelf-list-by-paths
 x-shelf-log
 x-shelf-save
 x-shelve
 x-unshelve
snipsnipsnip
fonte
9

Eu não sei de uma maneira fácil de fazer isso com apenas svn. Honestamente, eu aconselho usar git-svnpara criar um repositório git que funcione como uma cópia de trabalho svn, e apenas usá git stash-lo. Basta substituir git pullpor git svn rebasee git pushcom git svn dcommite você poderá manter 90% do seu fluxo de trabalho git e ainda estar conversando com um servidor svn.

Walter Mundt
fonte
Mas o link stackoverflow.com/questions/1554278/… que mencionei nos comentários acima propõe uma solução prática para fazer um stash apenas no svn.
VonC 02/08/19
Justo; Na verdade, o Google me levou a essa solução em um blog agora. Ainda mantenho que, para esse questionador, o git-svn é uma solução natural.
Walter Mundt
Duvido que a solução siga o nome do arquivo, pois o git não.
NO_NAME
4

Há um pequeno script Python 2 chamado svn-stashdisponível na GPL 3: https://github.com/frankcortes/svn-stash .

Funciona como as svn diff/patchsoluções mencionadas e oferece o envio e a liberação de alterações conforme as diferenças em algum diretório local. Infelizmente, os stashes não podem ser nomeados e apenas o último pode ser exibido (bem, sim, é uma pilha, mas não há motivo real para essa limitação.) Mas, então, você sempre pode criar os recursos ausentes no fonte.

Ele foi escrito para * ix, mas depois de substituir todos os "/" por os.sepele também funciona bem no Windows.

Se você usa o svn 1.7 ou superior, é necessário alterar is_a_current_stash(): remova a linha if ".svn" in os.listdir(CURRENT_DIR):, pois existe apenas um subdiretório .svn de nível superior nos 1.7 WCs.

cxxl
fonte
Não é para mim sob as janelas! :(
Antonio Petricca
4

Você pode fazer isso facilmente usando o Intellij IDEA - Shelve Changes

lili
fonte
Esta maneira pode segurar metadata changese directory creates/deletes? Como exatamente o que git stashfaz?
wonsuc
3

outra opção é copiar seu checkout atual para um novo diretório e reverter todas as suas alterações. Dessa maneira, você evitará o incômodo de criar uma ramificação temporária no servidor - afinal, o armazenamento em cache é uma operação local, que nem todo mundo deve ver e pode ser feito com bastante frequência.

depois de confirmar seu hotfix, você pode atualizar sua cópia principal de trabalho e excluir sua "área de armazenamento"

knittl
fonte
Nota: Isso é essencialmente o mesmo que fazer o check-out de uma segunda cópia de trabalho - apenas sem o checkout :-).
27411 sleske
6
@sleske: sim, sem a enorme quantidade de largura de banda necessária para um novo check-out
knittl
Goste ou não, esta é a resposta que mais reflete o comportamento do "git stash". Criar uma filial É legal, mas está mais relacionado às prateleiras TFS.
Charles Roberto Canato
1

Eu também queria esse recurso. Atualmente, uso o TortoiseSVN.

Eu não encontrei uma solução mais rígida, exceto para exportar a árvore, reverter novamente para o repositório, fazer minhas alterações e confirmar e comparar as alterações da árvore exportada para o meu diretório controlado de origem usando uma ferramenta como Beyond Compare.

Ou, outra solução pode ser ramificar do HEAD para outro diretório, fazer suas alterações e confirmar. Quando estiver pronto para mesclar esses itens à sua outra cópia de trabalho, faça uma atualização e mescle suas alterações.

Anthony Shaw
fonte
1

Eu sempre mantenho um segundo checkout, que chamo de "trunk_clean". Sempre que eu preciso fazer uma alteração rápida e isolada relacionada ao que estou fazendo, eu apenas faço o checkout.

angularsen
fonte
0

As idéias de ramificação e correção acima são ótimas, mas elas não funcionam bem para mim. Eu uso uma ferramenta visual diff, portanto, a execução git diffnão produz patches baseados em texto. Nosso sistema de criação cria um novo ambiente toda vez que uma ramificação é criada, portanto, a criação de ramificações temporárias "stash" seria uma bagunça.

Em vez disso, escrevi um pequeno script de shell que copia um arquivo para um diretório "shelf", adiciona um carimbo de data e hora e reverte a alteração. Não é tão robusto quanto as soluções acima, mas também evita algumas das armadilhas que encontrei.

Ryan DeBeasi
fonte
0

Com base na resposta de Walter, criei os seguintes aliases no meu arquivo bashrc:

alias svn.stash='read -p "saving local changes in raq.patch. Existing stash in raq.patch will be overwritten. Continue?[y/N]" && [[ $REPLY =~ ^[yY] ]] && rm -f raq.patch && svn diff > raq.patch && svn revert -R .'
alias svn.stash.apply='patch -p0 < raq.patch; rm -f raq.patch'

Esses aliases são muito mais fáceis de usar e lembrar.

Uso:

svn.stash para esconder alterações e svn.stash.apply para aplicar stash.

Raheel
fonte
0

Na minha prática, eu uso git initpara criar um repositório Git no trunkdiretório do meu repositório Subversion, e depois adiciono *.gitaos padrões de ignição do Suctions.

Após modificar alguns arquivos, se eu quiser continuar meu trabalho com a linha principal do Subversion, apenas o uso git stashpara esconder meu trabalho. Após me comprometer com o repositório Subversion, eu uso git stash poppara restaurar minhas modificações.

yhluo
fonte
2
Esta é realmente uma boa solução! Muitas outras soluções usam ferramentas de terceiros para resolver o problema; este usa o Git como uma ferramenta de terceiros. Isso tem várias vantagens: 1) Git é muito geral e poderoso. 2) Muitas pessoas já têm o Git instalado.
Lii
Estou curioso para saber como isso funciona se você também não fizer um commit do git.
B2K 22/10
0

Usar:

svn cp --parents . ^/trash-stash/my-stash

Ele criará uma ramificação a partir do local atual e da revisão atual e, em seguida, confirmará as alterações na cópia de trabalho para essa ramificação sem mudar para ela.

uso: copiar SRC [@REV] ... DST

SRC e DST podem ser um caminho ou URL da cópia de trabalho (WC):

WC  -> URL:  immediately commit a copy of WC to URL

Observe que as alterações na cópia de trabalho não serão revertidas automaticamente ( cpapenas copiando as alterações para uma nova ramificação) e você precisará revertê-las manualmente.

Para restaurar as alterações, basta mesclar as alterações da ramificação recém-criada à sua cópia de trabalho.

svn merge --ignore-ancestry ^/trash-stash/my-stash -c <commited revision>

--ignore-ancestry é usado para não atualizar as informações de mesclagem na cópia de trabalho.

Usar:

svn ls -v ^/trash-stash/

para ver o que você tem no caminho do esconderijo. Revisões confirmadas também são impressas.

Se você não precisar mais do stash, basta executar:

svn rm ^/trash-stash/my-stash

Essa solução é melhor do que usar o patch, pois se novas alterações na cópia de trabalho ou na ramificação atual entrarem em conflito com as alterações no stash, você poderá resolver os conflitos usando o svn significa, enquanto patchem alguns casos apenas falhará ou até aplicará o patch incorretamente.

anton_rh
fonte
-1

Como o Subversion não suporta stashrecursos perfeitamente,
eu apenas faço da maneira manual assim.

Coloque Developmente Production(release)projete em um caminho separado.

source\code\MyApp         -- Development
release\MyApp(release)    -- Production(release)

Você pode trabalhar com todos os novos recursos do seu projeto no caminho de desenvolvimento
e só comprometeria um progresso significativo ou algo deveria ser liberado para o estábulo.

Quando você tiver que liberá-lo para produção, abra o projeto de produção, atualize o svn e faça coisas para liberar (compilar, exportar ... etc).

Sei que isso torna um pouco problemático, mas liberar progresso não acontece com frequência (não acontece para mim, mas eu sei que alguns projetos) se comparam para desenvolver progresso, dessa forma se encaixa para mim.

Estou usando o svn para projetos específicos, já que os membros da equipe do projeto o usam, então tenho que segui-lo.
A melhor solução é usar gitum sistema de controle de versão perfeito e melhor que svn.

wonsuc
fonte
Não está claro o que você está fazendo (que versão está registrada nos diretórios mencionados?), Mas parece uma duplicata da resposta mais votada ("Confira uma nova cópia de trabalho").
sleske
@sleske Desculpe, eu não li os detalhes do seu caso. No meu caso, eu só preciso deve prod, 2 situações. Desenvolver funcionalidades completamente novas seria complicado com o svn. Não tenho certeza se existe um método claro para resolver seu caso no svn world.
21719