ver recuperar modelo limpiar heads entre diferencias comparacion borrado archivo svn git merge branch git-svn

recuperar - ¿Es peligroso git-svn dcommit después de fusionarse en git?



recuperar commit borrado git (6)

¡La respuesta de Greg Hewgill en la parte superior no es segura! Si aparecían nuevas confirmaciones en el enlace entre las dos "git svn rebase", la combinación no avanzará rápidamente.

Se puede asegurar mediante el uso de la marca "- ff-only" en git-merge, pero normalmente no ejecuto "git svn rebase" en la rama, solo "git rebase master" en él (suponiendo que solo es un local rama). Luego, se garantiza que un "git merge thebranch" será rápido.

Mi motivación para probar git-svn es la fusión y ramificación sin esfuerzo. Entonces noté que el hombre git-svn (1) dice:

No se recomienda ejecutar git-merge o git-pull en una sucursal de la que planea comprometerse. Subversion no representa fusiones de ninguna manera razonable o útil; para que los usuarios que usan Subversion no puedan ver las fusiones que hayas realizado. Además, si se fusiona o tira de una rama git que es un espejo de una rama SVN, dcommit puede comprometerse con la rama incorrecta.

¿Significa esto que no puedo crear una rama local desde svn / trunk (o una rama), piratear, fusionar de nuevo a svn / trunk, y luego comprometer? Entiendo que los usuarios de svn verán que el mismo desorden que se fusiona en svn pre 1.5.x siempre ha existido, pero ¿hay algún otro inconveniente? Esa última frase me preocupa, también. ¿La gente rutinariamente hace este tipo de cosas?


Crear ramas locales definitivamente es posible con git-svn. Siempre y cuando solo estés usando ramas locales para ti, y no trates de usar git para unir ramas svn ascendentes, estarás bien.

Tengo una rama "maestra" que utilizo para rastrear el servidor svn. Esta es la única rama de la que me comprometo. Si estoy trabajando, creo una rama temática y trabajo en ella. Cuando quiero comprometerlo, hago lo siguiente:

  1. Comprométalo todo con la rama temática
  2. git svn rebase (resuelve cualquier conflicto entre tu trabajo y svn)
  3. GIT Checkout Master
  4. git svn rebase (esto hace que el siguiente paso sea una fusión de avance rápido, ver los comentarios de Aaron a continuación)
  5. git merge topic_branch
  6. resolver cualquier conflicto de fusión (no debería haber ninguno en este punto)
  7. git svn dcommit

También tengo otra situación en la que necesito mantener algunos cambios locales (para la depuración) que nunca deberían ser enviados a svn. Para eso, tengo la rama principal anterior pero también una rama llamada "trabajo" donde normalmente trabajo. Las ramas temáticas están ramificadas fuera del trabajo. Cuando quiero comprometerme a trabajar allí, busco master y uso cherry-pick para elegir los commits de la rama de trabajo que quiero comprometer con svn. Esto se debe a que quiero evitar cometer los tres commit de cambios locales. Luego, me comprometo desde la rama principal y rebase todo.

Vale la pena ejecutar git svn dcommit -n primero para asegurarte de que estás a punto de cometer exactamente lo que pretendes cometer. ¡A diferencia de git, reescribir el historial en svn es difícil!

Creo que debe haber una mejor manera de fusionar el cambio en una rama temática sin saltearse las confirmaciones de cambios locales que con el uso de la función de selección selectiva, por lo que si alguien tiene alguna idea, sería bienvenida.


En realidad, encontré una forma aún mejor con la --no-ff en git merge. Toda esta técnica de squash que utilicé antes ya no es necesaria.

Mi nuevo flujo de trabajo ahora es el siguiente:

  • Tengo una rama "maestra" que es la única rama de la que me comprometo y que clona el repositorio SVN ( -s suponiendo que tiene un diseño de SVN estándar en el repositorio trunk/ , branches/ y tags/ ):

    git svn clone [-s] <svn-url>

  • Trabajo en una rama local "trabajo" ( -b crea la rama "trabajo")

    git checkout -b work

  • cometer localmente en la rama de "trabajo" ( -s para cerrar la sesión de su mensaje de confirmación). En la continuación, supongo que hiciste 3 confirmaciones locales

    ... (work)$> git commit -s -m "msg 1" ... (work)$> git commit -s -m "msg 2" ... (work)$> git commit -s -m "msg 3"

Ahora quiere comprometerse con el servidor SVN

  • [Eventualmente] esconde las modificaciones que no desea que se vean comprometidas en el servidor SVN (a menudo comenta un código en el archivo principal solo porque quiere acelerar la compilación y enfocarse en una característica dada)

    (work)$> git stash

  • rebase la rama principal con el repositorio SVN (para actualizar desde el servidor SVN)

    (work)$> git checkout master (master)$> git svn rebase

  • volver a la rama de trabajo y rebase con maestro

    (master)$> git checkout work (work)$> git rebase master

  • Asegúrese de que todo esté bien usando, por ejemplo:

    (work)$> git log --graph --oneline --decorate

  • Ahora es el momento de fusionar los tres commits de la rama "work" en "master" usando esta maravillosa --no-ff

    (work)$> git checkout master (master)$> git merge --no-ff work

  • Puedes notar el estado de los registros:

    (master)$> git log --graph --oneline --decorate * 56a779b (work, master) Merge branch ''work'' |/ | * af6f7ae msg 3 | * 8750643 msg 2 | * 08464ae msg 1 |/ * 21e20fa (git-svn) last svn commit

  • Ahora es probable que desee editar ( amend ) la última confirmación para sus tipos SVN (de lo contrario, solo verán una única confirmación con el mensaje "Merge branch ''work''"

    (master)$> git commit --amend

  • Finalmente confirmar en el servidor SVN

    (master)$> git svn dcommit

  • Regrese al trabajo y eventualmente recupere sus archivos escondidos:

    (master)$> git checkout work (work)$> git stash pop


Una forma segura de fusionar ramas svn en git es usar git merge --squash. Esto creará una única confirmación y se detendrá para que pueda agregar un mensaje.

Digamos que tiene una rama svn de tema, llamada svn-branch.

git svn fetch git checkout remotes/trunk -b big-merge git merge --squash svn-branch

en este punto, tiene todos los cambios de svn-branch aplastados en una confirmación esperando en el índice

git commit


Vuelva a establecer la base de la rama local de git en la rama master git y luego comprométalo y de esa manera parece que hizo todos los commits en secuencia para que svn las personas puedan verlo linealmente como están acostumbrados. Asumiendo que tienes una rama local llamada tema, podrías hacer

git rebase master topic

que luego reproducirá tus compromisos sobre la rama principal listos para que comprometas


Solución simple: eliminar la rama ''trabajo'' después de la fusión

Respuesta corta: puede usar git de la manera que desee (consulte a continuación un flujo de trabajo simple), incluida la fusión. Solo asegúrese de seguir cada '' git merge work '' con '' git branch -d work '' para eliminar la rama de trabajo temporal.

Explicación de fondo: El problema merge / dcommit es que siempre que ''git svn dcommit'' una rama, el historial de fusión de esa rama es ''aplanado'': git se olvida de todas las operaciones de fusión que entraron en esta rama: solo se conserva el contenido del archivo, pero se pierde el hecho de que este contenido (parcialmente) proviene de una rama específica. Ver: ¿Por qué git svn dcommit pierde el historial de fusiones de fusiones para sucursales locales?

(Nota: no hay mucho que git-svn pueda hacer al respecto: svn simplemente no entiende las fusiones git mucho más poderosas. Por lo tanto, dentro del repositorio svn esta información de fusión no se puede representar de ninguna manera).

Pero este es todo el problema. Si elimina la rama ''work'' después de que se haya fusionado en la ''rama principal'', entonces su repositorio git está 100% limpio y se ve exactamente como su repositorio svn.

Mi flujo de trabajo: por supuesto, primero cloné el repositorio de svn remoto en un repositorio git local (esto puede llevar algo de tiempo):

$> git svn clone <svn-repository-url> <local-directory>

Todo el trabajo sucede dentro del "directorio local". Cada vez que necesito obtener actualizaciones del servidor (como ''svn update''), hago lo siguiente:

$> git checkout master $> git svn rebase

Hago todo mi trabajo de desarrollo en un ''trabajo'' de rama separado que se crea así:

$> git checkout -b work

Por supuesto, puede crear tantas ramas para su trabajo como quiera y combinarlas y ajustarlas de nuevo según lo desee (simplemente elimínelas cuando haya terminado con ellas, como se explica a continuación). En mi trabajo normal, me comprometo muy frecuentemente:

$> git commit -am ''-- finished a little piece of work''

El siguiente paso (git rebase -i) es opcional --- es solo limpiar el historial antes de archivarlo en svn: una vez que alcanzo una piedra estable que quiero compartir con otros, reescribo la historia de este ''trabajo'' bifurcar y limpiar los mensajes de compromiso (otros desarrolladores no necesitan ver todos los pequeños pasos y errores que hice en el camino --- solo el resultado). Por esto, lo hago

$> git log

y copia el hash sha-1 del último commit que está activo en el repositorio svn (como lo indica un git-svn-id). Entonces llamo

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Simplemente pegue el hash sha-1 de nuestro último commit de svn en lugar del mío. Es posible que desee leer la documentación con ''git help rebase'' para más detalles. En resumen: este comando abre primero un editor que presenta tus compromisos ---- simplemente cambia ''pick'' por ''squash'' para todos los commits que quieras aplastar con commits previos. Por supuesto, la primera línea debe permanecer como una ''selección''. De esta forma, puedes condensar tus muchos pequeños commits en una o más unidades significativas. Guarde y salga del editor. Obtendrá otro editor que le pedirá que vuelva a escribir los mensajes de registro de compromiso.

En resumen: después de terminar el "pirateo de código", masajeo mi rama de "trabajo" hasta que vea cómo quiero presentarlo a los otros programadores (o cómo quiero ver el trabajo dentro de unas semanas cuando examino el historial) .

Para enviar los cambios al repositorio svn, hago:

$> git checkout master $> git svn rebase

Ahora estamos de vuelta en la antigua rama ''master'' actualizada con todos los cambios que ocurrieron mientras tanto en el repositorio svn (sus nuevos cambios están ocultos en la rama ''work'').

Si hay cambios que pueden entrar en conflicto con sus nuevos cambios de "trabajo", debe resolverlos localmente antes de que pueda enviar su nuevo trabajo (consulte los detalles más adelante). Entonces, podemos impulsar nuestros cambios a svn:

$> git checkout master $> git merge work # (1) merge your ''work'' into ''master'' $> git branch -d work # (2) remove the work branch immediately after merging $> git svn dcommit # (3) push your changes to the svn repository

Nota 1: El comando ''git branch -d work'' es bastante seguro: solo le permite eliminar ramas que ya no necesita (porque ya están fusionadas en su rama actual). Si ejecuta este comando por error antes de fusionar su trabajo con la rama ''master'', obtendrá un mensaje de error.

Nota 2: asegúrese de eliminar su rama con ''git branch -d work'' entre merging y dcommit: Si intenta eliminar la rama después de realizar commit, aparece un mensaje de error: cuando lo hace ''git svn dcommit'', git olvida que su rama se ha fusionado con ''maestro''. Tienes que eliminarlo con ''git branch -D work'' que no hace la comprobación de seguridad.

Ahora, inmediatamente creo una nueva rama de ''trabajo'' para evitar hackear accidentalmente la rama ''principal'':

$> git checkout -b work $> git branch # show my branches: master * work

Integrando su ''trabajo'' con cambios en svn: Esto es lo que hago cuando ''git svn rebase'' revela que otros cambiaron el repositorio svn mientras yo trabajaba en mi rama ''work'':

$> git checkout master $> git svn rebase # ''svn pull'' changes $> git checkout work # go to my work $> git checkout -b integration # make a copy of the branch $> git merge master # integrate my changes with theirs $> ... check/fix/debug ... $> ... rewrite history with rebase -i if needed $> git checkout master # try again to push my changes $> git svn rebase # hopefully no further changes to merge $> git merge integration # (1) merge your work with theirs $> git branch -d work # (2) remove branches that are merged $> git branch -d integration # (2) remove branches that are merged $> git svn dcommit # (3) push your changes to the svn repository

Existen soluciones más potentes: el flujo de trabajo presentado es simplista: utiliza los poderes de git solo dentro de cada ronda de ''update / hack / dcommit'', pero deja el historial de proyectos a largo plazo tan lineal como el repositorio svn. Esto está bien si solo quieres comenzar a usar git merge en pequeños pasos en un proyecto svn heredado.

Cuando te familiarices con la fusión de git, puedes explorar otros flujos de trabajo: si sabes lo que estás haciendo, puedes mezclar git merges con combinaciones de svn (¿ Usar git-svn (o similar) solo para ayudar con svn merge? )