Metti via temporaneamente le modifiche non confermate in Subversion (a la "git-stash")


312

Durante la programmazione del software archiviato in un repository Subversion, modifico spesso alcuni file, quindi noto che vorrei apportare alcune modifiche preparatorie al mio lavoro principale. Ad esempio durante l'implementazione di nuove funzionalità, noto alcuni refactoring che potrebbero aiutarmi.

Al fine di non mescolare due modifiche non correlate, in questi casi mi piacerebbe "riporre" le mie modifiche, ovvero ripristinare la versione del repository, apportare alcune altre modifiche, eseguire il commit di queste, quindi "recuperare" le mie modifiche.

git-stash permette di fare proprio questo. C'è un modo per farlo con Subversion, direttamente o con alcuni plugin o script. Anche i plugin di Eclipse andrebbero bene.


6
solo curioso, ma perché non usare git-svn?
cmcginty

3
Alcune notizie rilevanti: infoworld.com/d/application-development/… (citando: "Nota anche che la prossima versione di Subversion 1.8 dovrebbe avvicinarla alle capacità di Git, con funzionalità come Git stash, in cui uno sviluppatore può apportare modifiche localmente e quindi metterli da parte, e offline si impegna, che registra le modifiche completate quando uno sviluppatore è offline e sposta il repository principale quando lo sviluppatore si riconnette. "
Sebastiaan van den Broek,

1
Aggiornamento (dal 26/04/2012): la scaffalatura è ora programmata per 1.9, senza ETA. Quindi potrebbe volerci un po 'di tempo ...
sleske,

10
Aggiornamento (dal 17/11/2012): la scaffalatura è ora prevista per l'1.10. Forse è sempre programmato per <prossima versione +1>? ;-)
sleske,

3
Aggiornamento (dal 23-03-2015, 2 anni e mezzo dopo): la buona notizia è che la scaffalatura è ancora prevista per l'1.10. Cattive notizie sono l'ETA: Q2 2015 (provvisorio) Release 1.9.0 / 2017? (nella migliore delle ipotesi) Rilascio 1.10.0 ( subversion.apache.org/roadmap.html )
ribamar

Risposte:


69

Quando ho modifiche non confermate da un'attività nella mia copia di lavoro e devo passare a un'altra attività, faccio una delle due cose:

  1. Scopri una nuova copia di lavoro per la seconda attività.

    o

  2. Inizia una filiale:

    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
    

Ho alcuni script che aiutano ad automatizzare questo.


65
questo comporterà un sacco di spazzatura sul tuo server subversion
knittl

3
@knittl: No, non lo farà. E ciò che è ancora più importante: non comporterà la perdita di modifiche, come il tuo suggerimento. Questo e avere un'altra copia verificata del trunk / stesso ramo, sono gli unici due modi affidabili per farlo che conosco. Se ti senti a disagio con questo, dai un'occhiata a un'altra copia e lavoraci su in parallelo.
sbi,

2
@knittl: il ramo può essere creato in un percorso poco appariscente che si trova fuori dalla posizione dei rami o tag predefinita del progetto. Ad esempio, una squadra può designare project\temp\<creationdate-reason>o project\personal\<creationdate-reason>per questo scopo.
rwong

12
È ancora sfortunato che il ramo debba essere creato sul server. Non è che tali rami duplicano molti dati, ma creano molti riferimenti non necessari di cui un sistema come git non ha bisogno.
thepeer,

8
questo non è utile con un repository di grandi dimensioni. Questa non è assolutamente un'opzione nel mio ambiente di lavoro. E mentre vorrei che il nostro repository fosse più piccolo e meglio organizzato, e francamente, un repository git invece di svn, sono limitato ai limiti di come il nostro codice è organizzato nella nostra organizzazione.
AdrianVeidt,

337

Questo post sul blog consiglia di utilizzare diff e patch.

  • git stash diventa approssimativamente svn diff > patch_name.patch; svn revert -R .
  • git stash apply diventa patch -p0 < patch_name.patch

Nota che questo non nasconde le modifiche ai metadati o (penso) che la directory crei / elimini. (Sì, svn tiene traccia di questi separatamente dal contenuto della directory, a differenza di git.)


13
Si tratta di un duplicato accidentale di stackoverflow.com/questions/1554278/... - invio upvotes lì.
Walter Mundt,

2
Inoltre, non sembra includere file binari, il che è fastidioso. Almeno quando si utilizza TortoiseSVN per generare la patch.
angularsen,


6
Puoi più o meno tenere traccia dei metadati se li usi svn patch patch_name.patchinvece di patch -p0, perché sono nel file patch e la patch svn li comprende.
mat

Questo non include le modifiche agli esterni.
congusbongus,

182

È possibile archiviare le modifiche correnti svn diffin un file patch, quindi ripristinare la copia di lavoro:

svn diff > stash.patch
svn revert -R .

Dopo aver implementato la funzione preparatoria, è quindi possibile applicare la patch con l'utilità patch:

patch < stash.patch

Come altri hanno notato, questo non funzionerà con le svn:propertiesoperazioni ad albero (aggiungere, rimuovere, rinominare file e directory).

I file binari potrebbero anche dare problemi, non so come patch (o TortoiseSVN in questo caso li gestisce).


4
Questo probabilmente non funziona troppo bene con i file rimossi / rinominati, credo.
JesperE,

7
Vedi il riquadro "Why Not Use Patches?" a svnbook.red-bean.com/en/1.5/… per capire perché questa è una cattiva idea.
sbi,

4
@sbi: non credo sia una valida giustificazione per un downvote. Non è una "cattiva risposta". Non è la risposta perfetta, tutto qui. Non credo che questa persona meriti una punizione per il suo suggerimento. Preferiresti che non rispondesse? Se sì, allora sì, dovresti sottovalutare. Altrimenti questo sta punendo le buone intenzioni.
Sedat Kapanoglu,

5
nel caso in cui qualcun altro, come me, pensasse che questa fosse la soluzione più leggera e decidesse di provarla, dovevo usare la patch -p0 <stash.patch - altrimenti si lamentava di non riuscire a trovare i file da
correggere

4
Questo consiglio aiuta soprattutto se provieni da un background git e sei costretto a usare SVN per vari motivi. Un piccolo miglioramento nei consigli già forniti agli utenti per la prima volta della patch: $ patch --strip=0 < stash.patch Questo assicurerà che la patch non ti chieda il nome del file quando applichi la patch.
ksinkar,

43

Il modo più semplice sarebbe usare un ramo temporaneo, come questo:

$ 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

Questo potrebbe (e probabilmente dovrebbe) essere inserito in uno script se fatto su una base più regolare.


2
Perché questo viene annullato, mentre vengono votate le "soluzioni" che non funzionano nemmeno quando hai cancellato / aggiunto file o modificato proprietà? Sì, questa non è la cosa più semplice da fare quando lo fai per la prima volta, ma, oltre a far estrarre un'altra copia per lavorare in parallelo, questa è l'unica soluzione che funziona in tutti i casi.
sbi,

5
Bel uso della sintassi ^ per la radice repo (da svn 1.6). Buona soluzione quando il tuo repository ha trunk / tag / branch al livello superiore.
Bendin,

4
Non mi piace molto mettere tutti questi rami temporanei sul server. Penso che questo dovrebbe essere fatto localmente, invece di ingombrare il server (e generare e-mail di check-in di spuri, se si generano e-mail al check-in). Tuttavia, un'opzione che vale la pena ricordare.
sleske,

3
@sleske: sì, stai impegnando la tua scorta temporanea sul server, ma il ramo stesso viene eliminato. Comunque, penso che questo sia il modo più veloce e robusto per farlo.
Jesper,

5
@sleske: SVN non è un VCS distribuito, quindi tutto deve essere sul server. È così e basta.
sbi,

24

A partire dalla 1.10.0 (13-04-2018), hai un svn shelvecomando sperimentale . ( TortoiseSVN supporta il comando ) Non è altro che un aiuto per salvare una patch e applicarla di nuovo, quindi ha le stesse limitazioni di svn diff+ patch(cioè non può gestire file binari e rinominazioni). ( Modifica : sembra che il supporto binario arriverà alla prossima versione 1.11.0 )

Modifica ^ 2: con 1.11.0 (rilasciato il 30-10-2018), i file binari sono supportati . La scaffalatura dei file rinominati è rimasta non supportata. La scaffalatura in 1.11 non è compatibile con gli scaffali creati da 1.10.

Modifica ^ 3: con 1.12.0 (rilasciato il 24-04-2019), sono supportate la copia e la ridenominazione . La scaffalatura in 1.12 è incompatibile con gli scaffali creati da versioni precedenti.

Modifica ^ 4: non ci sono cambiamenti attorno allo scaffale con 1.13.0 e 1.14.0 . I comandi sono ancora contrassegnati come sperimentali ed è necessario definire SVN_EXPERIMENTAL_COMMANDS=shelf3per abilitare la funzione. Sembra che la funzionalità non sia attualmente modificata .

Le note di progettazione sono disponibili nel Wiki degli sviluppatori .

$ 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

9

Non conosco un modo semplice per farlo con solo svn. Onestamente, consiglierei di usare git-svnper fare un repository git che funge da copia funzionante in svn, e solo per usarlo git stash. Sostituisci semplicemente git pullcon git svn rebasee git pushcon git svn dcommite puoi effettivamente mantenere il 90% del tuo flusso di lavoro git e continuare a parlare con un server svn.


Ma il link stackoverflow.com/questions/1554278/… che cito nei commenti sopra propone una soluzione pratica per fare una scorta solo in svn.
VonC

Giusto; in effetti, google mi ha portato a quella soluzione su un blog proprio ora. Continuo a sostenere che, per questo interrogante, git-svn è una soluzione naturale.
Walter Mundt,

Dubito che la soluzione segua i nomi dei file, dal momento che git no.
NO_NAME

4

Esiste un piccolo script Python 2 chiamato svn-stashdisponibile in GPL 3: https://github.com/frankcortes/svn-stash .

Funziona come le svn diff/patchsoluzioni menzionate e offre il push e il pop-up delle modifiche come diff in alcune directory locali. Sfortunatamente, gli stash non possono essere nominati e solo l'ultimo può essere spuntato (beh, sì, è uno stack, ma non c'è un vero motivo per tale limitazione.) Ma poi, potresti sempre costruire le caratteristiche mancanti nel fonte.

È scritto per * ix, ma dopo aver sostituito ogni "/" os.sepfunziona bene anche con Windows.

Se usi svn 1.7 o versioni successive, devi cambiare is_a_current_stash(): rimuovi la linea if ".svn" in os.listdir(CURRENT_DIR):, dato che esiste solo un sottodir .svn di livello superiore in 1.7 WC.


Non per me sotto le finestre! :(
Antonio Petricca,

4

Puoi farlo facilmente usando Intellij IDEA - Shelve Changes


In questo modo può gestire metadata changese directory creates/deletes? Come esattamente cosa git stashfa?
Wonsuc,

3

un'altra opzione è quella di copiare il checkout corrente in una nuova directory e ripristinare tutte le modifiche. in questo modo risparmierai la seccatura di creare un ramo temporaneo sul tuo server — dopo tutto lo stash è un'operazione locale, che non tutti dovrebbero vedere e possono essere eseguiti abbastanza spesso.

dopo aver eseguito l'aggiornamento rapido è possibile aggiornare la copia di lavoro principale ed eliminare la "zona di stashing"


Nota: è essenzialmente lo stesso del check out di una seconda copia di lavoro - solo senza il checkout :-).
sleske,

6
@sleske: sì, senza l'enorme quantità di larghezza di banda necessaria per un nuovo checkout
knittl

Piaccia o no, questa è la risposta che rispecchia più da vicino il comportamento "git stash". La creazione di un ramo È interessante, ma è più correlata allo scaffalature TFS.
Charles Roberto Canato,

1

Ho anche desiderato questa funzione. Attualmente uso TortoiseSVN.

Non ho trovato una soluzione hardfast se non per esportare l'albero, tornare al repository apportare le mie modifiche e impegnarmi, quindi confrontare le modifiche dall'albero esportato di nuovo nella mia directory controllata dalla fonte usando uno strumento come Beyond Compare.

Oppure, un'altra soluzione potrebbe essere quella di diramare da HEAD in un'altra directory, apportare le modifiche e il commit. Quando sei pronto per unire quelli all'altra copia di lavoro, esegui un aggiornamento e unisci le modifiche.


1

Tengo sempre un secondo checkout, che chiamo "trunk_clean". Ogni volta che devo fare una modifica rapida e isolata in relazione a ciò che sto facendo, mi impegno semplicemente a fare quel checkout.


0

Le idee di ramificazione e patch sopra sono fantastiche, ma non funzionano bene per me. Uso uno strumento di visualizzazione visiva, quindi l'esecuzione git diffnon produce patch basate su testo. Il nostro sistema di compilazione crea un nuovo ambiente ogni volta che viene creato un ramo, quindi la creazione di rami temporanei "stash" diventerebbe confusa.

Invece, ho scritto un piccolo script di shell che copia un file in una directory "shelf", aggiunge un timestamp e ripristina la modifica. Non è robusto come le soluzioni sopra, ma evita anche alcune delle insidie ​​che ho incontrato.


0

Sulla base della risposta di Walter, ho creato i seguenti alias nel mio file 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'

Questi alias sono molto più facili da usare e da ricordare.

Uso:

svn.stash per stash modifiche e svn.stash.apply per applicare stash.


0

Nella mia pratica, utilizzo git initper creare un repository Git nella trunkdirectory del mio repository Subversion, e poi aggiungo *.gitalle Suctions ignorare i pattern.

Dopo aver modificato alcuni file, se voglio continuare il mio lavoro con la linea principale di Subversion, lo uso solo git stashper riporre il mio lavoro. Dopo aver eseguito il commit nel repository Subversion, utilizzo git stash popper ripristinare le mie modifiche.


2
Questa è in realtà una buona soluzione! Molte altre soluzioni utilizzano strumenti di terze parti per risolvere il problema; questo usa Git come strumento di terze parti. Questo ha diversi vantaggi: 1) Git è molto generale e potente. 2) Molte persone hanno già installato Git.
Lii,

Sono curioso di sapere come funziona se non esegui un commit git.
B2K

0

Uso:

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

Creerà un ramo dalla posizione corrente e dalla revisione corrente, quindi eseguirà il commit delle modifiche nella copia di lavoro a quel ramo senza passare ad esso.

utilizzo: copia SRC [@REV] ... DST

SRC e DST possono essere ciascuno un percorso di copia (WC) o URL di lavoro:

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

Si noti che le modifiche nella copia di lavoro non verranno ripristinate automaticamente ( cpè solo CoPying modifiche a un nuovo ramo) e è necessario ripristinarle manualmente.

Per ripristinare le modifiche, è possibile unire le modifiche dal ramo appena creato alla copia di lavoro.

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

--ignore-ancestry viene utilizzato per non aggiornare le informazioni di unione nella copia di lavoro.

Uso:

svn ls -v ^/trash-stash/

per vedere cosa hai sul percorso stash. Vengono inoltre stampate le revisioni commesse.

Se non hai più bisogno della scorta, esegui:

svn rm ^/trash-stash/my-stash

Questa soluzione è meglio dell'uso della patch in quanto se nuove modifiche nella copia di lavoro o nel ramo corrente sono in conflitto con le modifiche nello stash, è possibile risolvere i conflitti usando svn significa, mentre patchin alcuni casi falliranno o addirittura applicheranno la patch in modo errato.


-1

Dal momento che Subversion non supporta stashperfettamente le funzionalità,
faccio semplicemente un modo manuale come questo.

Posizionare Developmente Production(release)proiettare su un percorso separato.

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

Puoi lavorare con qualsiasi nuova funzionalità per il tuo progetto nel percorso di sviluppo
e commetteresti solo progressi significativi o qualcosa dovrebbe essere rilasciato per la scuderia.

Quando devi rilasciarlo per la produzione, apri il progetto di produzione, aggiorna svn e fai cose per rilasciare (compilare, esportare ... ecc.).

So che questo rende un po 'problematico, ma il rilascio di progressi non avviene spesso (non per me, ma so che alcuni progetti lo fanno) rispetto allo sviluppo dei progressi, in questo modo si adatta a me.

Sto usando svn per progetti specifici poiché i membri del team di progetto lo usano, quindi devo seguire.
La migliore soluzione è quella di utilizzare gitun sistema di controllo versione perfetto e migliore di svn.


Non è abbastanza chiaro cosa stai facendo (quale versione è stata verificata nelle directory che menzioni?), Ma sembra un duplicato della risposta più votata ("Guarda una nuova copia funzionante").
sleske,

@sleske Spiacente, non ho letto i dettagli del tuo caso. Nel mio caso, ho solo bisogno deve prod2 situazioni. Sviluppare funzionalità completamente nuove sarebbe complicato con svn. Non sono sicuro se esiste un metodo chiaro per risolvere il tuo caso nel mondo svn.
Wonsuc,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.