Guarde temporalmente los cambios no confirmados en Subversion (a la "git-stash")


312

Mientras programo el software almacenado en un repositorio de Subversion, a menudo modifico algunos archivos, luego me doy cuenta de que me gustaría hacer algún cambio preparatorio para mi trabajo principal. Por ejemplo, al implementar una nueva funcionalidad, noto algunas refactorizaciones que podrían ayudarme.

Para no mezclar dos cambios no relacionados, en estos casos me gustaría "guardar" mis cambios, es decir, volver a la versión del repositorio, hacer otros cambios, confirmarlos y luego "recuperar" mis cambios.

git-stash permite hacer exactamente eso. ¿Hay alguna manera de hacer esto con Subversion, ya sea directamente o con algún complemento o script? Los complementos de Eclipse también estarían bien.


66
solo curiosidad, pero ¿por qué no usar git-svn?
cmcginty

3
Algunas noticias relevantes: infoworld.com/d/application-development/… (citando: "También señala que la próxima versión de Subversion 1.8 debería acercarlo a las capacidades de Git, con características como Git stash, en las que un desarrollador puede hacer cambios localmente y luego póngalos a un lado y se compromete fuera de línea, que registra los cambios completados cuando un desarrollador está fuera de línea y lo mueve al repositorio principal cuando el desarrollador se vuelve a conectar. "
Sebastiaan van den Broek

1
Actualización (a partir del 26/04/2012): La estantería ahora está programada para 1.9, sin ninguna ETA. Así que puede llevar un tiempo ...
sleske

10
Actualización (a partir del 2012-11-17): Las estanterías ahora están programadas para la 1.10. ¿Tal vez siempre está programado para <próximo lanzamiento +1>? ;-)
sleske

3
Actualización (a partir del 23/03/2015, 2 años y medio después): una buena noticia es que Shelving todavía está programado para la 1.10. Las malas noticias son el lanzamiento de ETA: Q2 2015 (provisional) 1.9.0 / 2017? (especulativo en el mejor de los casos) Versión 1.10.0 ( subversion.apache.org/roadmap.html )
ribamar

Respuestas:


69

Cuando tengo cambios no confirmados de una tarea en mi copia de trabajo y necesito cambiar a otra tarea, hago una de dos cosas:

  1. Echa un vistazo a una nueva copia de trabajo para la segunda tarea.

    o

  2. Comience una rama:

    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
    

Tengo algunos scripts que ayudan a automatizar esto.


65
esto dará como resultado mucha basura en su servidor de subversión
knittl

3
@knittl: No, no lo hará. Y lo que es aún más importante: no provocará la pérdida de cambios, como lo hace su sugerencia. Esto, y tener otra copia extraída de la troncal / misma rama, son las dos únicas formas confiables de hacerlo que sé. Si te sientes incómodo con esto, solo mira otra copia y trabaja en paralelo.
sbi

2
@knittl: la rama se puede crear en una ruta discreta que está fuera de la ubicación predeterminada de las ramas o etiquetas del proyecto. Por ejemplo, un equipo puede designar project\temp\<creationdate-reason>o project\personal\<creationdate-reason>para este propósito.
rwong

12
Todavía es desafortunado que la rama tenga que crearse en el servidor. No es que tales ramas dupliquen muchos datos, sino que crean muchas referencias innecesarias de las que un sistema como git no tiene.
thepeer

8
Esto no es útil con un repositorio grande. Esta no es una opción en mi entorno laboral. Y aunque desearía que nuestro repositorio fuera más pequeño y mejor organizado, y francamente, un repositorio git en lugar de svn, estoy limitado a los límites de cómo está organizado nuestro código en nuestra organización.
AdrianVeidt

337

Esta publicación de blog aconseja usar diff y patch.

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

Tenga en cuenta que esto no oculta los cambios de metadatos o (creo) que el directorio crea / elimina. (Sí, svn rastrea esos por separado del contenido del directorio, a diferencia de git).


13
Este es un duplicado accidental de stackoverflow.com/questions/1554278/… - envíe votos a favor allí.
Walter Mundt

2
Tampoco parece incluir archivos binarios, lo cual es molesto. Al menos cuando se usa TortoiseSVN para generar el parche.
angularsen


66
Puede hacer un seguimiento más o menos de los metadatos si los usa en svn patch patch_name.patchlugar de hacerlo patch -p0, porque están en el archivo de parche y svn patch los comprende.
mat

Esto no incluye cambios a externos.
congusbongus

182

Puede almacenar sus cambios actuales svn diffen un archivo de parche y luego revertir su copia de trabajo:

svn diff > stash.patch
svn revert -R .

Después de implementar su función preparatoria, puede aplicar su parche con la utilidad de parche:

patch < stash.patch

Como otros han señalado, esto no funcionará con svn:propertieslas operaciones en árbol (agregar, eliminar, renombrar archivos y directorios).

Los archivos binarios también podrían dar problemas, no sé cómo parchear (o TortoiseSVN en este caso los maneja).


44
Probablemente esto no funcione demasiado bien con los archivos eliminados / renombrados, creo.
JesperE

77
Vea el cuadro titulado "¿Por qué no utilizar parches en su lugar?" en svnbook.red-bean.com/en/1.5/… para entender por qué es una mala idea.
sbi

44
@sbi: No creo que sea una justificación válida para un voto negativo. No es una "mala respuesta". Simplemente no es la respuesta perfecta, eso es todo. No creo que esta persona merezca castigo por su sugerencia. ¿Preferirías que no contestara? Si es así, entonces sí, debería votar a favor. De lo contrario, esto está castigando las buenas intenciones.
Sedat Kapanoglu el

55
en caso de que alguien más, como yo, pensara que esta parece ser la solución más ligera y decide probarlo, tuve que usar el parche -p0 <stash.patch; de lo contrario, se quejó de no poder encontrar los archivos para parchear
CupawnTae

44
Este consejo es útil especialmente si proviene de un entorno git y se ve obligado a usar SVN debido a varias razones. Una pequeña mejora en los consejos ya dados para los usuarios por primera vez del parche: $ patch --strip=0 < stash.patch esto asegurará que el parche no le pregunte el nombre del archivo cuando lo esté aplicando.
ksinkar

43

La forma más fácil sería usar una rama temporal, como esta:

$ 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

Esto podría (y probablemente debería) incluirse en un script si se realiza de forma más regular.


2
¿Por qué se rechaza esto, mientras que se votan "soluciones" que ni siquiera funcionan cuando ha eliminado / agregado archivos o ha cambiado alguna propiedad? Sí, esto no es lo más fácil de hacer cuando lo haces por primera vez, pero, además de tener otra copia extraída para trabajar en paralelo, esta es la única solución que funciona en todos los casos.
sbi

55
Buen uso de la sintaxis ^ para la raíz del repositorio (desde svn 1.6). Buena solución cuando su repositorio tiene tronco / etiquetas / ramas en el nivel superior.
bendin

44
Realmente no me gusta poner todas estas ramas temporales en el servidor. Siento que esto debe hacerse localmente, en lugar de abarrotar el servidor (y generar correos electrónicos de registro espurios, si genera correos en el registro). Aún así, una opción que vale la pena recordar.
sleske

3
@sleske: sí, está confirmando su reserva temporal al servidor, pero la rama en sí se elimina. De todos modos, creo que esta es la forma más rápida y robusta de hacerlo.
JesperE

55
@sleske: SVN no es un VCS distribuido, por lo que todo tiene que estar en el servidor. Esa es la forma como es.
sbi

24

A partir de 1.10.0 (13/04/2018), tiene un svn shelvecomando experimental . ( TortoiseSVN admite el comando ) No es más que un ayudante para guardar un parche y volver a aplicarlo, por lo que tiene las mismas limitaciones que svn diff+ patch(es decir, no puede manejar archivos binarios y renombrar). ( Editar : Parece que el soporte binario llegará en la próxima versión 1.11.0 )

Editar ^ 2: con 1.11.0 (lanzado el 10-10-2018), se admiten archivos binarios . La archivación de archivos renombrados permaneció sin soporte. Las estanterías en 1.11 son incompatibles con las estanterías creadas por 1.10.

Editar ^ 3: con 1.12.0 (lanzado el 24/04/2019), se admite la copia y el cambio de nombre . Las estanterías en 1.12 son incompatibles con las estanterías creadas por versiones anteriores.

Editar ^ 4: No hay cambios en las estanterías con 1.13.0 y 1.14.0 . Los comandos todavía están marcados como experimentales y debe definirlos SVN_EXPERIMENTAL_COMMANDS=shelf3para habilitar la función. Parece que la función no está actualmente actualizada .

Las notas de diseño se pueden encontrar en Wiki de desarrolladores .

$ 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

No conozco una manera fácil de hacerlo con solo svn. Honestamente, recomendaría usarlo git-svnpara hacer un repositorio git que actúe como una copia de trabajo svn, y solo usarlo git stashcon eso. Simplemente reemplace git pullcon git svn rebasey git pushcon git svn dcommity realmente puede mantener el 90% de su flujo de trabajo de git y seguir hablando con un servidor svn.


Pero el enlace stackoverflow.com/questions/1554278/… que menciono en los comentarios anteriores propone una solución práctica para hacer un alijo solo en svn.
VonC

Lo suficientemente justo; de hecho, google me llevó a esa solución en un blog hace un momento. Todavía mantengo que, para este interrogador, git-svn es una solución natural.
Walter Mundt

Dudo que la solución siga el cambio de nombre de los archivos, ya que git no.
NO_NAME

4

Hay un pequeño script de Python 2 llamado svn-stashdisponible bajo GPL 3: https://github.com/frankcortes/svn-stash .

Funciona como las svn diff/patchsoluciones mencionadas y ofrece empujar y hacer estallar los cambios como diferencias en algún directorio local. Desafortunadamente, los escondites no se pueden nombrar, y solo el último se puede reventar (bueno, sí, es una pila, pero no hay una razón real para tal limitación). Pero entonces, siempre puedes construir las características que faltan en el fuente.

Está escrito para * ix, pero después de reemplazar cada "/" os.sepfunciona muy bien en Windows también.

Si usa svn 1.7 o superior, debe cambiar is_a_current_stash(): elimine la línea if ".svn" in os.listdir(CURRENT_DIR):, ya que solo hay un subdirectorio .svn de nivel superior en 1.7 WC.


¡No es para mí debajo de las ventanas! :(
Antonio Petricca

4

Puede hacerlo fácilmente usando Intellij IDEA - Cambios de estantería


¿De esta manera puede manejar metadata changesy directory creates/deletes? ¿Cómo exactamente qué git stashhace?
wonsuc

3

Otra opción es copiar su pago actual a un nuevo directorio y revertir todos sus cambios. de esta manera, ahorrará la molestia de crear una rama temporal en su servidor; después de todo, el ocultamiento es una operación local, que no todos deberían ver y que se puede hacer con bastante frecuencia.

después de confirmar su revisión, puede actualizar su copia de trabajo principal y eliminar su "área oculta"


Nota: Eso es esencialmente lo mismo que sacar una segunda copia de trabajo, solo sin el pago :-).
sleske

66
@sleske: sí, sin la gran cantidad de ancho de banda necesario para una nueva compra
knittl

Nos guste o no, esta es la respuesta que refleja más de cerca el comportamiento de "git stash". Crear una sucursal ES genial, pero está más relacionado con la estantería TFS.
Charles Roberto Canato

1

También he querido esta característica. Actualmente uso TortoiseSVN.

No he encontrado una solución rápida, excepto para exportar el árbol, volver al repositorio, hacer mis cambios y confirmar, luego comparar los cambios del árbol exportado nuevamente en mi directorio controlado de origen usando una herramienta como Beyond Compare.

O, otra solución podría ser bifurcar desde HEAD a otro directorio, realizar sus cambios y confirmar. Una vez que esté listo para fusionarlos con su otra copia de trabajo, realice una actualización y combine sus cambios.


1

Siempre mantengo un segundo pago, al que llamo "trunk_clean". Cada vez que necesito hacer un cambio rápido y aislado relacionado con lo que estoy haciendo, solo me comprometo con ese pago.


0

Las ideas de ramificación y parcheo anteriores son geniales, pero no me funcionan bien. Utilizo una herramienta visual diff, por lo que ejecutar git diffno produce parches basados ​​en texto. Nuestro sistema de compilación crea un nuevo entorno cada vez que se crea una rama, por lo que crear ramas temporales "ocultas" se volvería complicado.

En cambio, escribí un pequeño script de shell que copia un archivo en un directorio "estante", agrega una marca de tiempo y revierte el cambio. No es tan robusto como las soluciones anteriores, pero también evita algunos de los escollos con los que me encontré.


0

Basado en la respuesta de Walter, he creado los siguientes alias en mi archivo 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'

Estos alias son mucho más fáciles de usar y recordar.

Uso:

svn.stash para guardar los cambios y svn.stash.apply para aplicar el almacenamiento.


0

En mi práctica, uso git initpara crear un repositorio de Git en el trunkdirectorio de mi repositorio de Subversion, y luego agrego *.gita los patrones de ignorar Succiones.

Después de modificar algunos archivos, si deseo continuar mi trabajo con la línea principal de Subversion, solo uso git stashpara esconder mi trabajo. Después de comprometerme con el repositorio de Subversion, uso git stash poppara restaurar mis modificaciones.


2
¡Esta es realmente una buena solución! Muchas otras soluciones utilizan herramientas de terceros para resolver el problema; este usa Git como una herramienta de terceros. Esto tiene varias ventajas: 1) Git es muy general y poderoso. 2) Muchas personas ya tienen instalado Git.
Lii

Tengo curiosidad por saber cómo funciona esto si no haces un git commit.
B2K

0

Utilizar:

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

Creará una rama desde la ubicación actual y la revisión actual, y luego confirmará los cambios en la copia de trabajo a esa rama sin cambiar a ella.

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

SRC y DST pueden ser una ruta de copia de trabajo (WC) o una URL:

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

Tenga en cuenta que los cambios en la copia de trabajo no se revertirán automáticamente ( cpes solo Copiar los cambios a una nueva rama) y debe revertirlos manualmente.

Para restaurar los cambios, puede fusionar los cambios de la rama recién creada a su copia de trabajo.

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

--ignore-ancestry se utiliza para no actualizar la información de fusión en la copia de trabajo.

Utilizar:

svn ls -v ^/trash-stash/

para ver lo que tienes en el camino oculto. Las revisiones comprometidas también se imprimen.

Si ya no necesita el alijo, simplemente ejecute:

svn rm ^/trash-stash/my-stash

Esta solución es mejor que usar el parche, ya que si los nuevos cambios en la copia de trabajo o en la rama actual entran en conflicto con los cambios en el alijo, puede resolver los conflictos utilizando medios svn, mientras que patchen algunos casos simplemente fallará o incluso aplicará el parche incorrectamente.


-1

Dado que Subversion no admite la stashfunción perfectamente,
solo lo hago de forma manual como esta.

Colocar Developmenty Production(release)proyectar en un camino separado.

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

Puede trabajar cualquier característica nueva para su proyecto en la ruta de desarrollo,
y solo comprometería un progreso significativo o algo debería lanzarse para el establo.

Cuando tenga que lanzarlo para producción, abra el proyecto de producción, actualice svn y haga cosas para lanzar (compilar, exportar ... etc.).

Sé que esto es un poco problemático, pero la liberación del progreso no ocurre a menudo (no es para mí, pero sé que algunos proyectos sí) se comparan para desarrollar el progreso, de esta manera me queda bien.

Estoy usando svn para proyectos específicos ya que los miembros del equipo del proyecto lo usan, así que tengo que seguir.
La mejor solución es usar gitun sistema de control de versiones perfecto y mejor que svn.


No está muy claro lo que está haciendo (¿qué versión está desprotegida en los directorios que menciona?), Pero parece un duplicado de la respuesta más votada ("Echa un vistazo a una nueva copia de trabajo").
sleske

@sleske Lo siento, no leí los detalles de tu caso. En mi caso, solo necesito devy prod2 situaciones. Desarrollar una funcionalidad completamente nueva sería complicado con svn. No estoy seguro de si hay un método claro para resolver su caso en svn world.
wonsuc
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.