2009-08-17 9 views
14

Hay un lugar especial en el infierno para las personas que codifican rutas absolutas y credenciales de bases de datos en múltiples lugares aleatorios en aplicaciones web. Lamentablemente, antes de ir al infierno están causando estragos en la Tierra. Y tenemos que lidiar con su código.git: ¿cómo me fusiono entre ramas mientras mantengo algunos conjuntos de cambios exclusivos de una rama?

Tengo que realizar algunos pequeños cambios en una de esas aplicaciones web. Creo una nueva rama features, y realizo una búsqueda global & replace para actualizar las rutas y credenciales a mi entorno local. Lo cometo. También etiqueto esto como local.

alegremente me salto a la penitencia piratería peligrosa, y después de un centenar de parches desconcertantes, quiero combinar mis features cambios en la rama master, pero no quiero el local comprometen a fusionarse.

en adelante, voy a ser la fusión de ida y vuelta entre master y features, y me gustaría local a quedarse en features, y nunca aparecen en master.

Idealmente, me gustaría que todo esto suceda mágicamente, con tan pocos parámetros divertidos y todo lo demás posible.

¿Hay una manera simple y obvia de hacerlo que me falta?

Puedo pensar en un par, pero todos me requieren recuerde que no quiero que se cometan. Y definitivamente no es mi fuerte. Especialmente con programas tan mal pirateados.

En su defecto, estoy interesado en formas más complicadas y manuales para manejar la situación.

+0

Sí, sí, sí. Idealmente, debería refactorizar la estúpida aplicación. Tan pronto como me paguen por hacerlo. (Y puedo pensar en algunos otros casos donde tal patrón tendría sentido, incluso en aplicaciones bien estructuradas) – kch

+0

Agregué una respuesta a su comentario en mi respuesta. – VonC

+0

La otra opción: use una de las extensiones de edredones (culpa, git apilada, etc.) y cambie su nombre a un parche en una cola. Creo que estas extensiones no te permitirán fusionar un parche que no se ha integrado de nuevo en las confirmaciones regulares, por lo que no puedes fusionarlo accidentalmente. Todavía no es ideal porque debes recordar abrir y volver a aplicar el parche. parche cada vez que tienes nuevas confirmaciones normales, pero resuelves el problema de olvidar que no quieres esa confirmación. – quark

Respuesta

1

Una solución técnica (Git) estaría utilizando git attributes, utilizando la combinación de atributos.

merge 

El atributo merge afecta la forma en tres versiones de un archivo se fusiona cuando es necesaria una fusión a nivel de archivo durante git merge.

Usted encontrará en la cuestión SO "How do I tell git to always select my local version for conflicted merges on a specific file?" un ejemplo del uso de tal atributo, a la fuerza de mantenimiento de la versión local de ciertos archivos cuando la fusión a una rama determinada.

El problema con la configuración de combinación de atributos es que los archivos que contienen los caminos pueden contener otro código modificado, el cual quiero fusionó

No olvide que puede asociar cualquier tipo de secuencia de comandos para manejar esas fusiones a través de git attributes. Eso incluye una secuencia de comandos capaz de mantener los cambios que desea locales, mientras combina el resto. Es más complicado escribir un "administrador de combinación", pero es un camino hacia una solución automatizada ad-hoc.


Una solución menos técnico sería separar los valores de la configuración de los archivos de configuración:

  • el archivo de configuración contiene sólo los nombres que ser reemplazados
  • los valores de configuración son varios archivos (uno por entorno) con los valores reales para cada nombre.

Una secuencia de comandos se utiliza para reemplazar el nombre en el archivo de configuración real por los valores de uno de los archivos de valores de configuración necesarios para un entorno determinado.

+0

El problema con la configuración de atributos de fusión es que los archivos que contienen las rutas pueden contener otro código modificado, que deseo fusionar. Entonces se trata más de ese conjunto de cambios en lugar de todo el archivo. – kch

+0

Interesante respuesta, pero sí, parece un montón de problemas. – kch

3

Puede usar git cherry pick para fusionar solo los parches que seleccione. Simplemente escoge cada compromiso, excepto el local en la rama principal.

+1

Me gusta esta respuesta, excepto: ¿cómo se hace esto si tiene un * lote * de cambios? ¿Hay un patrón de una línea para excluir solo un parche de un conjunto? – quark

+0

Por analogía, la extensión de trasplante Mercurial tiene una bandera para esto: 'hg transplant -p local'. (Hay muchos otros argumentos que necesitaría para que funcione, pero esto es lo que saltaría el conjunto de cambios dado). – quark

+0

@quark: la rebase interactiva puede hacer justamente eso. 'git checkout -b tmp features && git rebase -i --onto dev dev tmp' te colocaría en un editor donde puedes elegir qué commits entre' dev' y 'features' colocar en una nueva rama' tmp' comenzando desde 'dev'. Puedes aplastar múltiples commits en uno también, e incluso reordenar commits si te sientes aventurero. –

0

Haría un rebase interactivo contra el maestro y movería su path-name-fixup-commit hasta el final. Entonces, puedes unirte al punto. Solo sigue moviendo tu compromiso especial hasta el final.

También puede encontrar el alijo útil. En lugar de comprometer las correcciones de nombre de ruta, puede guardarlas. Si prueba este enfoque, puede verificar la pregunta en How to reverse apply a stash.

+0

Sí, pero eso no me ayuda a fusionarme. Intenté usar el alijo por un tiempo, pero se vuelve aburrido realmente rápido. Es mejor volver a establecer la base -i el compromiso. – kch

0

Bueno, porque hay una respuesta proporcionada hasta ahora una solución sencilla, voy a asumir que lo que yo quiero hacer es imposible, y añadir a la pila de soluciones ocasionalmente útiles:

Si siempre está en desarrollo en el features rama, a continuación, puede combinar features a master, y luego, en master, git revert local. (¿Dónde local es la etiqueta referencia al cometer donde ha personalizado los caminos, etc para su entorno local.)

Ahora que nunca se debe fusionar master en features, porque eso sería fusionar la inversa local comprometen también.

En este caso, master se convierte en una especie de rama de implementación, que solo recibe fusiones de otras ramas. (Idealmente, solo de la rama features)

Esto va cuesta abajo muy fácilmente, simplemente agregue otro desarrollador al flujo de trabajo y las cosas se vuelven realmente complicadas. Todavía se puede solucionar mediante el uso de estrategias de fusión explícitas, pero en general es un dolor.

+0

Parece que la verdadera respuesta es arreglar el código. Extraiga las rutas codificadas en un archivo de configuración que puede anularse con un archivo de configuración local. Rastree un archivo de configuración predeterminado pero no el archivo de configuración local o algo similar. –

+0

Sí, eso es generalmente un buen consejo, pero vea mi propio comentario bajo mi pregunta. – kch

0

No sé si esto iba a funcionar, pero:

  1. Crear una confirmación de que, dado el "maestro" versión de los archivos de configuración, los convierte en la versión que necesita a nivel local. Tenga en cuenta el SHA-1. Lo llamaremos MAKE_LOCAL
  2. Cree un compromiso que, dada su versión local de los archivos de configuración, los convierta en la versión apropiada para el maestro. Tenga en cuenta el SHA-1.Lo llamaremos MAKE_REMOTE
  3. El uso de ganchos GIT, cuando confirma:
    1. git cherry-pick MAKE_REMOTE (o utilizamos git diff y patch)
    2. Permitir la confirmación para comenzar
    3. git cherry-pick MAKE_LOCAL (o utilizar git diff y patch)

Creo que hay un wa aún mejor y de la transformación de archivos de esta manera, pero no recuerdo (si puedes encontrar la presentación de git de shacon desde RubyConf, y puedes navegar a través de 800 diapositivas, está ahí con algunos buenos ejemplos).

1

ok. esto no se garantiza que funcione cada vez mas algo como esto puede trabajar (y en los casos que no usted tendrá unos cambios conflictivos de todos modos que tiene que ser resuelto):

  • hacer su sucursal local
  • hacer locales -sólo cambiar
  • continuar con el desarrollo

cuando se hace fusionar al maestro:

  • rebase -i master desde su sucursal y mueva el cambio solo local al END de la cadena de parches.
  • resuelve cualquier conflicto en el proceso. Si el cambio solo local está en los archivos de configuración y no los está tocando en el desarrollo normal, entonces no tendrá problemas. Si, de lo contrario, tiene un conflicto, entonces este es un caso en el que realmente cambia en la misma área y necesita su atención para resolverlo de todos modos.
  • la salida
  • maestro
  • fusionar su local de la sucursal -1:

    git fusionar locales^

Esto le dejará con maestro que tiene todos los cambios en el local, a excepción de la última uno.

Si tiene varios cambios locales = únicos, le sugiero squash juntos durante la rebase.

6

Mi solución para este problema utiliza rebase en lugar de fusionar

A partir de un árbol de comprometerse así:

a-b-c <-- master 
\ 
    d <-- local 
    \ 
    e-f-g <-- dev 

$ git rebase master --onto dev locales

 master 
     V 
    a-b-c-e'-f'-g' <-- dev 
    \ 
     d <-- local 

$ git checkout master

$ git merge dev

   master 
       V 
    a-b-c-e'-f'-g' <-- dev 
    \ 
     d <-- local 

$ git rebase --onto maestro maestro local

   master 
       V 
    a-b-c-e'-f'-g' <-- dev 
       \ 
       d' <-- local 

$ git rama local de f dev

   master 
       V 
    a-b-c-e'-f'-g' 
       \ 
       d' <-- local 
       ^
       dev 
+2

OK.Ahora, ¿qué tal un buen enlace a un lugar que explique toda esta magia? :) He escaneado git-scm.com/book anteriormente, pero no tengo ninguna base para entender lo que acaba de hacer. – oligofren

1

Personalmente, si tuviera que hacer algo como esto y para lo que estaba Para evitar que se reproduzcan las credenciales a medida que avanzo, agregaría dos ramas más, terminando con una disposición similar a la siguiente:

master: el código original que heredó

localcred: bifurque desde el maestro y agregue solo el parche que cambia todas las credenciales a las que necesita localmente. Trate esta rama como de solo lectura en lo sucesivo (y posiblemente agregue un enlace para evitar confirmaciones accidentales).

feature: rama de maestro, y todas las correcciones van aquí (y, posiblemente, añadir un gancho para evitar la fusión con el parche en localcred)

local: una rama (no una etiqueta!) Que va a comenzar como un rama de localcred, y luego fusionar cada vez que necesite ejecutar sus pruebas unitarias. Todas las pruebas se realizan desde aquí, pero aquí no ocurre ningún desarrollo. Además, esta rama es desechable, porque es posible que desee rebase dentro de feature, y la manera más rápida de lidiar con el resultado será eliminar la rama local, ramificarla nuevamente desde localcred y fusionar feature antes de ejecutar sus pruebas. Es probable que esto sea una operación bastante común en mi flujo de trabajo que construiría un alias para hacerlo repetidamente en solo unas pocas teclas, pero trabajo muchísimo con la eliminación de las ramas de Git, lo que asusta a algunas personas que mírame, entonces YMMV.

cuando se piensa en sus arreglos están listos para su publicación, lo hace su rebase final del feature para limpiar la historia, volcar y volver a crear local para su prueba final, fusionar feature en master, y una vez que es aceptada aguas arriba, fusionar master en localcred y rebase su parche de credencial en la parte superior, luego descargue y vuelva a crear local y feature y vuelva a jugar el juego.

Si desea probar rápidamente un gran conjunto de pequeñas variaciones de código sin tener que comprometerse y combinar cada vez, la caja local, realice los cambios hasta que estés satisfecho, comprometerse, e inmediatamente cherry-pick de local en feature , luego suelta y recrea local.

¿Satisface sus necesidades?

0

La pregunta es antigua, pero todavía no he encontrado una buena respuesta. Actualmente estoy enfrentando el mismo problema y debajo está mi solución para manejarlo:

Hay dos sucursales en mi repositorio local: master y local_settings. Después de cortar la rama local_settings desde master, cometí todas las rutas locales, sin etiquetar ni tratar de recordarlas. Durante el desarrollo local, cambié a la rama local_settings, por lo que puedo ejecutar una aplicación utilizando rutas de acceso locales. Pero cuando es hora de comprometerlo escondo un estado actual y cambio a la rama master. Luego hago estallar el conjunto de cambios escondido y lo envío al master. Y el último paso es volver a local_settings, fusionar desde master y continuar con el desarrollo. Para recapitular: me comprometo en la rama local_settings solo cambios que se mantendrán localmente y nunca entrarán en el maestro; y no se fusiona desde local_settings hasta master.

Ahora digamos que necesito agregar una "buena" modificación a un archivo con una ruta de acceso local agregada anteriormente, pero la modificación "buena" es deseada en la rama master. Hago mis cambios cuando la copia de trabajo es una cabecera para local_settings, la oculta y revisa master. El alijo mantiene un conjunto de cambios, que es relativo a local_settings, aunque ya estoy en master. git stash pop aplica el conjunto de cambios oculto a la copia de trabajo y termina teniendo una diferencia relativa al maestro, pero solo con la modificación reciente excluyendo la ruta local que se había agregado anteriormente y que no formaba parte del conjunto de cambios oculto recientemente. Por lo tanto, puede comprometerse sin desordenar rutas en la rama master. Luego, una vez más, se fusiona desde master hasta local_settings.

Cuestiones relacionadas