Tymczasowo odrzuć niezaangażowane zmiany w Subversion (a la „git-stash”)

312

Podczas programowania oprogramowania przechowywanego w repozytorium Subversion często modyfikuję niektóre pliki, a następnie zauważam, że chciałbym dokonać pewnych zmian przygotowawczych do mojej głównej pracy. Np. Podczas wdrażania nowej funkcjonalności zauważam pewne refaktoryzacje, które mogą mi pomóc.

Aby nie mieszać dwóch niepowiązanych zmian, w tych przypadkach chciałbym „ukryć” moje zmiany, tj. Przywrócić wersję repozytorium, wprowadzić inne zmiany, zatwierdzić je, a następnie „przywrócić” moje zmiany.

git-stash pozwala to zrobić. Czy można to zrobić za pomocą Subversion, bezpośrednio lub za pomocą wtyczki lub skryptu. Wtyczki Eclipse również byłyby w porządku.

Śleske
źródło
6
po prostu ciekawy, ale dlaczego nie użyć git-svn?
cmcginty,
3
Kilka istotnych wiadomości: infoworld.com/d/application-development/… (cytując: „Zauważa również, że nadchodzące wydanie Subversion 1.8 powinno przybliżyć go do możliwości Gita, dzięki funkcjom takim jak skrytka Git, w których deweloper może wprowadzać zmiany lokalnie a następnie odłóż je na bok i zatwierdza tryb offline, który rejestruje ukończone zmiany, gdy programista jest offline i przenosi je do głównego repozytorium, gdy programista ponownie się połączy. ”
Sebastiaan van den Broek
1
Aktualizacja (stan na 26.04.2012): Regały są teraz zaplanowane na 1.9, bez ETA. Może to chwilę potrwać ...
śleske,
10
Aktualizacja (stan na 17.11.2012): Regały są teraz zaplanowane na 1.10. Może zawsze jest zaplanowane na <następne wydanie +1>? ;-)
śleske,
3
Aktualizacja (stan na 23.03.2015, 2 lata i pół później): Dobrą wiadomością jest to, że półki nadal są zaplanowane na 1.10. Złe wiadomości to ETA: Q2 2015 (wstępnie) Release 1.9.0 / 2017? (co najwyżej spekulacyjne) Wydanie 1.10.0 ( subversion.apache.org/roadmap.html )
ribamar

Odpowiedzi:

69

Kiedy mam nieprzypisane zmiany z jednego zadania w mojej kopii roboczej i muszę przejść do innego zadania, robię jedną z dwóch rzeczy:

  1. Sprawdź nową kopię roboczą dla drugiego zadania.

    lub

  2. Załóż oddział:

    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
    

Mam kilka skryptów, które pomagają to zautomatyzować.

zginać
źródło
65
spowoduje to wiele śmieci na twoim serwerze subversion
knittl
3
@knittl: Nie, nie będzie. Co jeszcze ważniejsze: nie spowoduje to utraty zmian, tak jak sugeruje to Twoja sugestia. To, i posiadanie kolejnej sprawdzonej kopii pnia / tej samej gałęzi, to jedyne dwa niezawodne sposoby, aby to zrobić, jakie znam. Jeśli czujesz się z tym niekomfortowo, po prostu sprawdź inną kopię i pracuj nad nią równolegle.
sbi
2
@knittl: gałąź może zostać utworzona w niepozornej ścieżce, która znajduje się poza domyślną lokalizacją gałęzi lub tagów projektu. Na przykład zespół może wyznaczyć project\temp\<creationdate-reason>lub project\personal\<creationdate-reason>w tym celu.
rwong
12
Nadal niefortunnie jest, że gałąź musi w ogóle zostać utworzona na serwerze. Nie chodzi o to, że takie gałęzie duplikują dużo danych, ale tworzą wiele niepotrzebnych odniesień, bez których system taki jak git nie ma.
thepeer,
8
nie jest to przydatne w przypadku dużego repozytorium. To absolutnie nie jest opcja w moim środowisku pracy. I chociaż chciałbym, aby nasze repozytorium było mniejsze i lepiej zorganizowane, i szczerze mówiąc, repozytorium git zamiast svn, ograniczam się do granic organizacji naszego kodu w naszej organizacji.
AdrianVeidt
337

Ten post na blogu zaleca używanie diff i łatki.

  • git stash w przybliżeniu staje się svn diff > patch_name.patch; svn revert -R .
  • git stash apply staje się patch -p0 < patch_name.patch

Zauważ, że to nie ukrywa zmian metadanych lub (chyba) katalog tworzy / usuwa. (Tak, svn śledzi je osobno od zawartości katalogu, w przeciwieństwie do git.)

Walter Mundt
źródło
13
To jest przypadkowy duplikat stackoverflow.com/questions/1554278/… - wyślij tam opinie.
Walter Mundt,
2
Wydaje się również, że nie zawiera plików binarnych, co jest denerwujące. Przynajmniej przy użyciu TortoiseSVN do wygenerowania poprawki.
angularsen
1
stackoverflow.com/questions/159853/… może w tym pomóc.
Walter Mundt
6
Jeśli używasz svn patch patch_name.patchzamiast patch -p0, możesz mniej więcej śledzić metadane , ponieważ znajdują się one w pliku łaty, a łatka svn je rozumie.
mat
Nie obejmuje to zmian zewnętrznych.
congusbongus
182

Możesz zapisać bieżące zmiany svn diffw pliku łatki, a następnie przywrócić kopię roboczą:

svn diff > stash.patch
svn revert -R .

Po zaimplementowaniu funkcji przygotowawczej możesz następnie zastosować łatkę za pomocą narzędzia do łatania:

patch < stash.patch

Jak zauważyli inni, nie będzie to działać svn:properties operacjami drzewa i (dodawanie, usuwanie, zmiana nazw plików i katalogów).

Pliki binarne mogą również powodować problemy, nie wiem jak łata (lub TortoiseSVN w tym przypadku je obsługuje).

knittl
źródło
4
Myślę, że to prawdopodobnie nie działa zbyt dobrze w przypadku plików usuniętych / o zmienionych nazwach.
JesperE
7
Zobacz ramkę zatytułowaną „Dlaczego zamiast tego nie używać poprawek?” na svnbook.red-bean.com/en/1.5/…, aby zrozumieć, dlaczego jest to zły pomysł.
sbi
4
@sbi: Nie wydaje mi się, żeby to było uzasadnione uzasadnienie opinii. To nie jest „zła odpowiedź”. To po prostu nie jest idealna odpowiedź. Nie sądzę, aby ta osoba zasługiwała na karę za swoją sugestię. Wolisz, żeby zamiast tego nie odpowiadał? Jeśli tak, to tak, należy zagłosować. W przeciwnym razie jest to karanie dobrych intencji.
Sedat Kapanoglu
5
na wypadek gdyby ktoś inny, jak ja, pomyślał, że to wygląda na najlżejsze rozwiązanie i zdecyduje się go wypróbować, musiałem użyć łatki -p0 <stash.patch - w przeciwnym razie narzekał na brak możliwości znalezienia plików do łatania
CupawnTae
4
Ta rada jest przydatna, zwłaszcza jeśli pochodzisz z git i jesteś zmuszony do korzystania z SVN z różnych powodów. Niewielkie ulepszenie w poradach już udzielonych pierwszym użytkownikom łatki: $ patch --strip=0 < stash.patch sprawi to, że łatka nie będzie pytać o nazwę pliku podczas nakładania łatki.
ksinkar
43

Najprostszym sposobem byłoby użycie gałęzi tymczasowej, takiej jak ta:

$ 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

Można to (i prawdopodobnie należy) umieścić w skrypcie, jeśli jest wykonywane częściej.

JesperE
źródło
2
Dlaczego jest to odrzucane, podczas gdy głosowane są „rozwiązania”, które nie działają nawet po usunięciu / dodaniu plików lub zmianie jakichkolwiek właściwości? Tak, nie jest to najłatwiejsze, gdy robisz to po raz pierwszy, ale oprócz tego, że kolejna kopia została sprawdzona do pracy równoległej, jest to jedyne rozwiązanie, które działa we wszystkich przypadkach.
sbi
5
Ładne użycie składni ^ do rootowania repo (od svn 1.6). Dobre rozwiązanie, gdy twoje repozytorium ma trunk / tagi / oddziały na najwyższym poziomie.
zginać
4
Naprawdę nie lubię umieszczać wszystkich tymczasowych gałęzi na serwerze. Uważam, że należy to zrobić lokalnie, zamiast zaśmiecać serwer (i generować wiadomości e-mail rejestrujące spurios, jeśli generujesz wiadomości e-mail podczas rejestrowania). Mimo to warto pamiętać.
sleske
3
@sleske: tak, przekazujesz tymczasową skrytkę do serwera, ale sama gałąź została usunięta. W każdym razie uważam, że jest to najszybszy i najbardziej niezawodny sposób na zrobienie tego.
JesperE
5
@sleske: SVN nie jest rozproszonym VCS, więc wszystko musi być na serwerze. Tak już po prostu jest.
sbi
24

Od 1.10.0 (2018-04-13) masz svn shelvepolecenie eksperymentalne . ( TortoiseSVN obsługuje polecenie ) Zapisywanie łatki i stosowanie jej jest niczym innym jak pomocnikiem, więc ma takie same ograniczenia jak svn diff+ patch(tzn. Nie obsługuje plików binarnych i nazw). ( Edycja : Wygląda na to, że obsługa binarna będzie dostępna w następnej wersji 1.11.0 )

Edytuj ^ 2: W wersji 1.11.0 (wydanej 30.10.2018) obsługiwane są pliki binarne . Pliki o zmienionej nazwie półki pozostały nieobsługiwane. Półki w wersji 1.11 są niezgodne z półkami utworzonymi w wersji 1.10.

Edycja ^ 3: W wersji 1.12.0 (wydanej 24.04.2019), obsługiwane jest kopiowanie i zmiana nazwy . Półki w wersji 1.12 są niezgodne z półkami utworzonymi we wcześniejszych wersjach.

Edycja ^ 4: Nie ma zmian wokół półek z 1.13.0 i 1.14.0 . Polecenia są nadal oznaczone jako eksperymentalne i należy je zdefiniować, SVN_EXPERIMENTAL_COMMANDS=shelf3aby włączyć tę funkcję. Wygląda na to, że ta funkcja jest obecnie nienazwana .

Uwagi do projektu można znaleźć na Wiki dla programistów .

$ 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
źródło
9

Nie wiem, jak to zrobić za pomocą svn. Szczerze mówiąc, radzę użyć git-svnrepozytorium git, które działa jak kopia robocza svn, i po prostu git stashz tym korzystać. Wystarczy wymienić git pullz git svn rebasei git pushz git svn dcommiti rzeczywiście można zachować 90% swojej git workflow i nadal rozmawiać z serwerem SVN.

Walter Mundt
źródło
Ale link stackoverflow.com/questions/1554278/ ... Wspomniałem w powyższych komentarzach, proponuje praktyczne rozwiązanie, aby zrobić skrytkę tylko w svn.
VonC
Słusznie; w rzeczywistości, Google prowadzi mnie teraz do tego rozwiązania na blogu. Nadal twierdzę, że dla tego pytającego git-svn jest naturalnym rozwiązaniem.
Walter Mundt,
Wątpię, aby rozwiązanie to zmieniało nazwy plików, ponieważ git nie.
NO_NAME
4

Istnieje niewielki skrypt Python 2 o nazwie svn-stashdostępny na licencji GPL 3: https://github.com/frankcortes/svn-stash .

Działa jak svn diff/patchwspomniane rozwiązania i oferuje wypychanie i wyskakiwanie zmian jako różnic do jakiegoś lokalnego katalogu. Niestety skrytek nie można nazwać, a tylko ostatnią można wysunąć (cóż, tak, to jest stos, ale nie ma prawdziwego powodu takiego ograniczenia). Ale wtedy zawsze możesz wbudować brakujące funkcje w źródło.

Jest napisany dla * ix, ale po zamianie każdego „/” os.sepdziała również pod Windows.

Jeśli używasz svn w wersji 1.7 lub nowszej, musisz zmienić is_a_current_stash(): usuń linię if ".svn" in os.listdir(CURRENT_DIR):, ponieważ jest tylko jeden podkatalog .svn najwyższego poziomu w 1.7 toaletach.

cxxl
źródło
Nie dotyczy mnie to pod oknami! :(
Antonio Petricca
4

Możesz to łatwo zrobić za pomocą Intellij IDEA - Shelve Changes

Lili
źródło
Czy w ten sposób można sobie poradzić metadata changesi directory creates/deletes? Jak dokładnie to, co git stashrobi?
wonsuc
3

Inną opcją jest skopiowanie bieżącej kasy do nowego katalogu i przywrócenie wszystkich zmian. w ten sposób zaoszczędzisz kłopotów związanych z tworzeniem tymczasowej gałęzi na serwerze - w końcu ukrywanie jest operacją lokalną, której nie wszyscy powinni widzieć i którą można wykonywać dość często.

po zatwierdzeniu poprawki możesz zaktualizować główną kopię roboczą i usunąć „obszar przechowywania”

knittl
źródło
Uwaga: Zasadniczo jest to to samo, co pobranie drugiej kopii roboczej - tylko bez pobrania :-).
sleske
6
@sleske: tak, bez ogromnej przepustowości niezbędnej do nowej kasy
knittl
Czy ci się to podoba, czy nie, jest to odpowiedź, która bardziej odzwierciedla zachowanie „git stash”. Utworzenie gałęzi JEST fajne, ale bardziej związane jest z półkami TFS.
Charles Roberto Canato,
1

Chciałem też tę funkcję. Obecnie używam TortoiseSVN.

Nie znalazłem trwałego rozwiązania poza eksportowaniem drzewa, przywróceniem do repozytorium, dokonaniem zmian i zatwierdzeniem, a następnie porównaniem zmian z wyeksportowanego drzewa z powrotem do katalogu kontrolowanego przez źródło za pomocą narzędzia takiego jak Beyond Compare.

Lub innym rozwiązaniem może być przejście z HEAD do innego katalogu, dokonanie zmian i zatwierdzenie. Gdy będziesz gotowy połączyć je z powrotem w drugą kopię roboczą, wykonaj aktualizację i scal zmiany.

Anthony Shaw
źródło
1

Zawsze mam drugą kasę, którą nazywam „trunk_clean”. Ilekroć muszę dokonać szybkiej, odizolowanej zmiany związanej z tym, co robię, po prostu zatwierdzam tę kasę.

angularsen
źródło
0

Powyższe pomysły dotyczące rozgałęziania i łatania są świetne, ale dla mnie nie działają dobrze. Używam wizualnego narzędzia do porównywania, więc uruchamianie git diffnie powoduje tworzenia poprawek tekstowych. Nasz system kompilacji uruchamia nowe środowisko za każdym razem, gdy tworzona jest gałąź, więc tworzenie tymczasowych gałęzi „ukrytych” byłoby nieporządne.

Zamiast tego napisałem mały skrypt powłoki, który kopiuje plik do katalogu „shelf”, dodaje znacznik czasu i cofa zmianę. Nie jest tak solidny jak powyższe rozwiązania, ale pozwala także uniknąć niektórych pułapek, na które wpadłem.

Ryan DeBeasi
źródło
0

Na podstawie odpowiedzi Waltera stworzyłem następujące aliasy w moim pliku 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'

Te aliasy są znacznie łatwiejsze w użyciu i zapamiętywaniu.

Stosowanie:

svn.stash, aby ukryć zmiany i svn.stash.apply, aby zastosować ukryty .

Raheel
źródło
0

W swojej praktyce używam git initdo tworzenia repozytorium Git w trunkkatalogu mojego repozytorium Subversion, a następnie dodaję*.git do wzorców ignorowania Suctions.

Po zmodyfikowaniu niektórych plików, jeśli chcę kontynuować pracę z linią główną Subversion, po prostu używam git stashdo ukrycia mojej pracy. Po zatwierdzeniu do repozytorium Subversion używam git stash popdo przywracania moich modyfikacji.

yhluo
źródło
2
To jest naprawdę dobre rozwiązanie! Wiele innych rozwiązań wykorzystuje narzędzia innych firm do rozwiązania problemu; ten używa Git jako narzędzia zewnętrznego. Ma to kilka zalet: 1) Git jest bardzo ogólny i potężny. 2) Wiele osób ma już zainstalowanego Gita.
Lii
Jestem ciekawy, jak to działa, jeśli nie wykonujesz również git commit.
B2K,
0

Posługiwać się:

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

Utworzy gałąź z bieżącej lokalizacji i bieżącej wersji, a następnie zatwierdzi zmiany w kopii roboczej do tej gałęzi bez przełączania się na nią.

użycie: skopiuj SRC [@REV] ... DST

SRC i DST mogą być ścieżką kopii roboczej (WC) lub adresem URL:

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

Pamiętaj, że zmiany w kopii roboczej nie zostaną automatycznie przywrócone ( cpto tylko zmiany CoPying w nowym oddziale) i musisz je ręcznie przywrócić.

Aby przywrócić zmiany, możesz po prostu scalić zmiany z nowo utworzonego oddziału do kopii roboczej.

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

--ignore-ancestry jest używany, aby nie aktualizować informacji o scaleniu w kopii roboczej.

Posługiwać się:

svn ls -v ^/trash-stash/

aby zobaczyć, co masz na skrytce. Zatwierdzone wersje są również drukowane.

Jeśli nie potrzebujesz już skrytki, po prostu uruchom:

svn rm ^/trash-stash/my-stash

To rozwiązanie jest lepsze niż używanie łaty, ponieważ jeśli nowe zmiany w kopii roboczej lub w bieżącym oddziale powodują konflikt ze zmianami w skrytce, możesz rozwiązać konflikty za pomocą svn, podczas gdy patchw niektórych przypadkach po prostu się nie powiedzie, a nawet zastosuje nieprawidłowo.

anton_rh
źródło
-1

Ponieważ Subversion nie obsługuje stashidealnie funkcji,
po prostu robię to ręcznie.

Umieść Developmenti Production(release)rzutuj na oddzielną ścieżkę.

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

Możesz pracować nad wszystkimi nowymi funkcjami dla swojego projektu na ścieżce programistycznej,
popełniłbyś jedynie znaczący postęp lub coś powinno zostać wydane dla stabilnego.

Kiedy musisz wydać go do produkcji, otwórz projekt produkcyjny, zaktualizuj svn i zrób rzeczy do wydania (kompilacja, eksport ... itd.).

Wiem, że jest to trochę kłopotliwe, ale zwalnianie postępu nie zdarza się często (nie dotyczy mnie, ale wiem, że niektóre projekty tak robią) w porównaniu z rozwojem postępu, w ten sposób pasuje mi.

Używam svn do konkretnych projektów, ponieważ członkowie zespołu projektowego go używają, więc muszę podążać.
Najlepszym rozwiązaniem jest użycie, gitktóry ma doskonały system kontroli wersji i lepszy niż svn.

wonsuc
źródło
Nie jest do końca jasne, co robisz (jaka wersja jest sprawdzana we wspomnianych katalogach?), Ale wygląda jak duplikat najczęściej głosowanej odpowiedzi („Sprawdź nową kopię roboczą”).
śleske,
@sleske Przepraszamy, nie przeczytałem szczegółów twojej sprawy. W moim przypadku potrzebuję tylko devi prod, 2 sytuacji. Opracowanie całkowicie nowej funkcjonalności byłoby skomplikowane przy pomocy svn. Nie jestem pewien, czy istnieje jasna metoda rozwiązania twojej sprawy w świecie svn.
wonsuc