¿Usar Git Stash como flujo de trabajo es un antipatrón?

25

Recientemente he estado viendo cómo mi equipo y yo usamos Git y cómo funcionan nuestros flujos de trabajo. Actualmente utilizamos un flujo de trabajo de rama de características que parece funcionar bien.

También he visto a algunas personas en nuestro equipo usar el flujo de trabajo basado en git stash . El flujo de trabajo es más o menos así:

  • Trabajar en una rama principal (como master)
  • Haz compromisos a medida que avanzas
  • Si necesita obtener cambios o cambiar ramas, empuje sus cambios no confirmados al alijo
  • Una vez que haya realizado la actualización, elimine los cambios del alijo.

Debo mencionar que este flujo de trabajo se usa en lugar de un flujo de trabajo de rama de características. En lugar de tomar una rama y trabajar en ella, los desarrolladores aquí solo trabajan en una sola rama y empujan / salen de la pila como mejor les parezca.

De hecho, no creo que este sea un gran flujo de trabajo, y la ramificación sería más apropiada que usar git stash de esta manera. Puedo ver el valor de git stash como una operación de emergencia, pero no para usarlo en un flujo de trabajo diario y regular.

¿Usar git stash regularmente se consideraría un antipatrón? Si es así, ¿cuáles son algunos problemas específicos que podrían surgir? Si no, ¿cuáles son los beneficios?

joshin4colours
fuente
2
Puede intentar preguntar a sus compañeros de trabajo si han tenido algún problema con el flujo de trabajo. Si no lo han hecho, no lo consideraría perjudicial.
AlexFoxGill
@AlexG Ese es un punto válido. Estoy haciendo la pregunta aquí para ver si hay alguna "trampa" que la gente haya encontrado.
joshin4colours
3
Si lo entiendo correctamente ... If you need to get changes or switch branches, push your uncommitted changes onto the stash- para eso es exactamente el alijo.
3
@MichaelT Mi sucursal local permite todo. Incondicionalmente.
maaartinus
2
Mi patrón es: -> no use git stash -> use ramas de características -> guarde el trabajo en progreso con un mensaje de confirmación de 'wip'. -> rebase interactivo en la rama para aplastar los wip en una confirmación significativa, pero solo para sus cambios locales y no deseados . -> push to remote -> fusionar en master (y push) según sea apropiado para su flujo de trabajo git.
Michael Durrant

Respuestas:

31

Del libro de Git SCM :

A menudo, cuando ha estado trabajando en parte de su proyecto, las cosas están en un estado desordenado y desea cambiar las ramas por un tiempo para trabajar en otra cosa. El problema es que no desea realizar una tarea de trabajo a medias para poder volver a este punto más adelante. La respuesta a este problema es el comando git stash.

Stashing toma el estado sucio de su directorio de trabajo, es decir, sus archivos rastreados modificados y los cambios por etapas, y lo guarda en una pila de cambios sin terminar que puede volver a aplicar en cualquier momento.

Dada esta descripción, diría que este es un Anti Patrón. Una explicación demasiado simplificada de Git Stash sería que es el "Cortar y Pegar" del control de código fuente. Usted toma un montón de archivos modificados, los "guarda" en un bolígrafo fuera del flujo de trabajo de ramificación normal de Git, y luego vuelve a aplicar esos cambios a una rama diferente en una fecha posterior.

Retrocediendo un poco más, comprometerse a dominar es el anti patrón aquí. Usa ramas. Para eso fueron diseñados.

Realmente se reduce a esto:

Puede clavar un tornillo en la pared y sostendrá una imagen, pero usar un destornillador es lo que debe hacer. No use un martillo cuando el destornillador esté sentado a su lado.

Acerca de cometer un código "roto"

Si bien lo siguiente es opinión, he llegado a esta opinión por experiencia.

Comprometerse temprano y comprometerse a menudo. Comete tanto código roto como quieras. Vea su historial de confirmación local como "puntos de guardado" mientras piratea algo. Una vez que hayas hecho un trabajo lógico, haz un compromiso. Claro que puede romper todo, pero eso no importa siempre y cuando no empujes esas confirmaciones. Antes de empujar, rebaja y aplasta tus commits.

  1. Crear nueva sucursal
  2. Piratear piratear piratear
  3. Cometer código roto
  4. Pulir el código y hacerlo funcionar
  5. Confirmar código de trabajo
  6. Rebase y Squash
  7. Prueba
  8. Empuje cuando las pruebas pasan

Para el OP, este hilo de mensajes kernel de Linux podría ser de interés, porque parece que algunos miembros del equipo del OP están usando Git de manera similar.

@RibaldEddie dijo en un comentario a continuación:

En primer lugar, un alijo no está fuera de un "flujo de trabajo de ramificación" ya que debajo del capó un alijo es solo otra rama.

(a riesgo de provocar la ira de muchas personas)

Linus dijo:

Con "git stash", también puedes tener varias cosas escondidas diferentes, pero no se ponen en fila entre sí, son solo parches independientes aleatorios que has guardado porque eran inconvenientes en algún momento.

Lo que creo que @RibaldEddie está tratando de decir es que puedes usarlo git stashen un flujo de trabajo de rama de características, y esto es cierto. No es el uso de git stasheso es el problema. Es la combinación de comprometerse a dominar y usar git stash. Este es un anti patrón.

Aclarando git rebase

Del comentario de @ RibaldEddie:

Rebasar es mucho más como copiar y pegar y, lo que es peor, modifica el historial comprometido.

(El énfasis es mío)

Modificar el historial de confirmación no es algo malo, siempre y cuando sea un historial de confirmación local . Si modifica las confirmaciones que ya ha presionado, esencialmente huérfano a cualquier otra persona que use su rama. Esto es malo.

Ahora, digamos que ha realizado varias confirmaciones durante el transcurso de un día. Algunas confirmaciones fueron buenas. Algunos ... no tan buenos. El git rebasecomando junto con el aplastamiento de sus confirmaciones es una buena manera de limpiar su historial de confirmación local. Es bueno fusionarse en una confirmación con sucursales públicas porque mantiene limpio el historial de confirmación de las sucursales compartidas de su equipo. Después de cambiar el nombre, querrá volver a probar, pero si pasan las pruebas, puede empujar una confirmación limpia en lugar de varias sucias.

Hay otro hilo interesante del kernel de Linux en el historial de confirmación limpio .

De nuevo, de Linus:

Quiero un historial limpio, pero eso realmente significa (a) limpio y (b) historial.

Las personas pueden (y probablemente deberían) volver a crear sus árboles privados (su propio trabajo). Eso es una limpieza . Pero nunca el código de otros pueblos. Eso es un "destruir historia"

Entonces la parte de la historia es bastante fácil. Solo hay una regla principal y una aclaración menor:

  • Nunca debes destruir la historia de otras personas. No debes rebajar los compromisos que hicieron otras personas. Básicamente, si no tiene su cierre de sesión, está fuera de los límites: no puede modificarlo, porque no es suyo.

    Tenga en cuenta que esto realmente se trata de la historia de otras personas , no del código de otras personas . Si te enviaron cosas como un parche enviado por correo electrónico y lo aplicaste con "git am -s", entonces es su código, pero es tu historial.

    Por lo tanto, puede volverse loco con el tema "git rebase", aunque no haya escrito el código, siempre que el commit en sí sea privado.

  • Aclaración menor a la regla: una vez que haya publicado su historial en un sitio público, otras personas pueden estar usándolo, y ahora claramente ya no es su historial privado .

    Entonces, la aclaración menor realmente es que no se trata solo de "su compromiso", también se trata de que sea privado para su árbol, y aún no lo ha rechazado y anunciado.

...

Ahora la parte "limpia" es un poco más sutil, aunque las primeras reglas son bastante obvias y fáciles:

  • Mantenga su propia historia legible

    Algunas personas hacen esto simplemente resolviendo las cosas primero y sin cometer errores. pero eso es muy raro, y para el resto de nosotros, usamos "git rebase", etc., mientras trabajamos en nuestros problemas.

    Entonces "git rebase" no está mal. Pero es correcto solo si es TU PROPIO árbol git privado.

  • No expongas tu basura.

    Esto significa: si todavía está en la fase de "git rebase", no lo expulsa. Si no está listo, envía parches o usa árboles git privados (como un "reemplazo de la serie de parches") que no le cuenta al público en general.

(énfasis mío)

Conclusión

Al final, el OP tiene algunos desarrolladores que hacen esto:

git checkout master
(edit files)
git commit -am "..."
(edit files)
git stash
git pull
git stash (pop|apply)

Hay dos problemas aquí:

  1. Los desarrolladores se comprometen a dominar. Bloquee esto de inmediato. Realmente, este es el mayor problema.
  2. Los desarrolladores usan constantemente git stashy git pullen master cuando deberían usar ramas de características.

No hay nada de malo en usarlo git stash, especialmente antes de un tirón, pero usarlo git stashde esta manera es un anti patrón cuando hay mejores flujos de trabajo en Git.

Su uso de git stashun arenque rojo. No es el problema Comprometerse a dominar es el problema.

Greg Burghardt
fuente
44
Wow, esto no está bien. En primer lugar, un alijo no está fuera de un "flujo de trabajo de ramificación" ya que debajo del capó un alijo es solo otra rama. La idea de que es un flujo de trabajo de copiar y pegar no tiene sentido. Rebasar es mucho más como copiar y pegar y, lo que es peor, modifica el historial comprometido. Finalmente, su flujo de trabajo es completamente inútil tan pronto como necesite compartir el trabajo en progreso con sus colegas, ya que se desmorona tan pronto como presiona los cambios. Cómo esto tiene seis votos es alucinante.
RibaldEddie
8
@RibaldEddie: No hay nada de malo en volver a escribir y reescribir el historial de confirmaciones siempre que sea el historial de confirmación local. Una vez que empuje esas confirmaciones, su comentario será correcto, pero vuelva a leer mi respuesta. Dice: "Antes de empujar, rebaja y aplasta tus commits". Ese es el historial de confirmación local , que está completamente bajo su control.
Greg Burghardt
1
@RibaldEddie: he aclarado mi respuesta. Necesitaba un poco de limpieza.
Greg Burghardt
Proporcionaré algo más de contexto en una respuesta propia.
RibaldEddie
Vea mi respuesta a continuación: si bien comprometerse a dominar es un antipatrón, no es el verdadero problema.
RibaldEddie
7

Personalmente, solo uso stashpara interrupciones cortas e inesperadas, como alguien que hace una pregunta que requiere cambiar a una rama diferente. Hago esto porque me he olvidado de los escondites antes, luego no se aplicarían limpiamente. Los commits regulares en las ramas de características son mucho más difíciles de olvidar y más fáciles de fusionar, por lo que ahora tiendo a hacer un commit roto y luego hacer una git reset HEAD~1o una nueva versión si no quiero mantenerlo más tarde.

Sin embargo, lo mejor del control de versiones distribuidas es que las personas pueden usar su flujo de trabajo preferido en sus propios repositorios, siempre que los repositorios compartidos cumplan con los estándares. Me aseguraría de que las personas no solo usen un stashflujo de trabajo porque no tienen suficiente capacitación o conocimiento de las alternativas, pero si aún eligen un flujo de trabajo que usted encuentra subóptimo, lo dejaría.

Karl Bielefeldt
fuente
1

Creo que la parte de su pregunta que es un antipatrón es el uso de una única rama maestra compartida . Sin embargo, si tuviera que incluir una rama de desarrollo además de la rama maestra y luego usar escondites para lidiar con sus propios cambios de contexto en la rama de desarrollo, eso no sería un antipatrón, y refleja muy bien parte del flujo de trabajo describir por organizaciones como Etsy y Facebook.

Dicho esto, la respuesta anterior de @Greg Burghardt es demasiado favorable para el llamado flujo de trabajo de git-flow o característica-rama. Solía ​​abogar por una estrategia similar, pero después de darme cuenta de que agrega complejidad innecesaria y crea una falsa sensación de seguridad, ya no lo hago. También es un remanente de los días de los sistemas de control de versiones no descentralizados como la subversión.

En primer lugar, dado que Git es un sistema de control de versiones descentralizado a diferencia de Subversion, el repositorio local de un desarrollador es esencialmente una rama gigante del código en sí mismo. Lo que un desarrollo individual hace localmente no tiene y no debería tener un impacto en los otros miembros del equipo a menos que el código roto o defectuoso se envíe a las ramas compartidas en un repositorio compartido.

Sin embargo, el comando rebase puede dañar el historial de la rama cuando hay un conflicto de fusión en una de las confirmaciones reproducidas. De http://ryantablada.com/post/the-dangers-of-rebasing-a-branch

El resto del rebase transcurre sin problemas, las pruebas parecen pasar todas. Se hace una PR.

Y luego se escribe más código que se basa en la propiedad commentsForAllPosts y todo está roto. ¿Pero a quién vamos y pedimos ayuda? git blame muestra que la línea de código solo ha sido escrita por el ingeniero del lado del servidor y levanta las manos.

Ahora su ingeniero front-end está de vacaciones, baja por enfermedad o quién sabe. ¡Nadie puede descubrir cómo debería ser ese código!

Rebase ha eliminado la capacidad del equipo de mirar el historial para encontrar lo que salió mal porque cualquier conflicto de fusión en la rama secundaria se elimina y el código original se pierde para siempre.

Si surgiera este mismo conflicto de fusión y se utilizara la fusión, la culpa mostraría que esa línea de código se había tocado en el proceso de fusión, la confirmación en la rama principal y la confirmación en la rama secundaria. Algunos juegan con las tres permutaciones y puede recuperar la intención original en la base del código y trabajar sin un montón de cabeza rascándose un dedo. Y todo lo que realmente tenías era otra confirmación

Además, un modelo de ramificación múltiple presupone que no hay dos ramas que puedan contener cambios de código interdependientes. Cuando eso ocurre inevitablemente, el desarrollador ahora tiene que hacer malabarismos con más ramas para trabajar de manera eficiente.

El antipatrón fundamental que veo no está relacionado con las ramas frente a los escondites, sino más bien sobre los tipos de problemas de los que algunas personas muy inteligentes han estado hablando durante un tiempo: ¿tiene confianza en su código mediante el uso de pruebas unitarias y una buena arquitectura? ¿Puede realizar cambios incrementales en su código de modo que sus desarrolladores puedan razonar sobre los cambios fácilmente y comprender qué hará un cambio? ¿Sus desarrolladores incluso corren a través del nuevo código una vez para ver si realmente funciona? (Sí, he visto esto antes).

Si la respuesta a esas preguntas es no, entonces realmente no importa cuántas ramas tenga; los desarrolladores dirán que el código está listo, funciona y es apto para la producción cuando realmente no lo está, y no habrá un número de ramas. ayudarlo cuando ese código pase a producción de todos modos.

RibaldEddie
fuente
2
Su comentario acerca de rebase teniendo problemas con la resolución de fusión no es cierto. Un rebase es el mismo tipo de fusión que una fusión real. Git solo está fusionando commits bajo el capó. Rebasar y resolver un conflicto de fusión no daña el historial de confirmación. Rebasar y resolver incorrectamente un conflicto de fusión daña las cosas, lo que es igual de probable con una fusión normal. Tengo que estar de acuerdo en que las ramas de características agregan complejidad, pero esto podría ser una complejidad necesaria si los desarrolladores deben hacer malabarismos con múltiples cambios no relacionados.
Greg Burghardt
¿Entonces estás diciendo que las fusiones pueden destruir la historia? ¡Suena como una justificación más para evitar tener demasiadas ramas!
RibaldEddie
1
Las fusiones no pueden destruir la historia. Creo que podría estar malinterpretando cómo funciona el rebase y la fusión. Cuando se desencadena un conflicto de fusión, corresponde al humano resolverlo. Si el humano lo resuelve incorrectamente, no puede culpar a Git (o SVN, o CVS, o {insertar el control de origen aquí}).
Greg Burghardt
Ahora lo que estás diciendo está en conflicto con lo que dijiste antes. ¿Leíste el artículo que cité? ¿Entiendes el contexto en el que un rebase perderá historia?
RibaldEddie
1
He leido el articulo. "En Keen tratamos de insertar nuestro código después de casi cada confirmación". Eso me parece una locura. O están haciendo confirmaciones excesivamente grandes o están enviando código que aún no está listo. Si haces públicos todos tus commits de inmediato, entonces sí, el rebase causará problemas. Es el principio "Doctor, doctor, me duele cuando hago esto".
Kyralessa
1

git stashes una herramienta En sí mismo no es un patrón ni un antipatrón. Es una herramienta, al igual que un martillo es una herramienta. Usar un martillo para clavar clavos es un patrón y usar un martillo para clavar tornillos es un antipatrón. Del mismo modo, hay flujos de trabajo y entornos donde git stashes la herramienta correcta para usar, y flujos de trabajo y entornos donde está mal.

El flujo de trabajo de 'todos se comprometen y empujan a la línea principal' es uno que funciona de manera bastante razonable donde no hay cambios de alto riesgo. A menudo se usa en entornos svn donde hay un servidor central autorizado que tiene el código.

Sin embargo, Git se une para eliminar el único servidor central. Tener a todos los desarrolladores haciendo commit, pull(o rebasesi te gusta eso), pushtodo el tiempo puede hacer un gran desastre.

Los mayores problemas surgen cuando tienes algo en progreso que está roto y necesitas trabajar en un error de prioridad. Esto significa que debe dejar de lado ese trabajo por un momento, tomar lo último, trabajar en eso sin que el trabajo anterior en progreso cause problemas con la compilación que está tratando de hacer.

Para esto, git stashsería la herramienta adecuada para usar.

Sin embargo, hay un problema mayor que acecha en el corazón de este flujo de trabajo. Es que todos los roles de las ramas de control de versiones están en una sola rama. Mainline, desarrollo, mantenimiento, acumulación y empaque están en Master. Esto es un problema (Consulte Estrategias avanzadas de ramificación SCM para obtener más información sobre este enfoque de sucursales)

Y sí, usted dijo que no es un gran flujo de trabajo y que hay problemas con él. Sin embargo, el problema no es con la herramienta git stash. El problema es la falta de ramificaciones distintas para roles o políticas incompatibles .

git stashsin embargo, es algo que utilicé cuando tuve una situación en la que construí un poco, me puse en un estado pegajoso que no estaba seguro de si era el correcto ... así que escondí mis cambios y luego exploró otro enfoque para resolver el problema. Si eso funcionó, genial, deseche las cosas en el alijo y continúe. Si la otra exploración se volvió más pegajosa, reinicie el cambio anterior y vuelva a aplicar el alijo para trabajar en eso. La alternativa sería confirmar, finalizar la compra, bifurcar y luego continuar en esta nueva bifurcación o volver y restablecerla. La pregunta es si realmente vale la pena poner eso en la historia cuando es algo que quiero explorar un poco.

git stashNo es un anti patrón. Usarlo git stashcomo una alternativa a la ramificación mientras todos se comprometen con el Maestro es un anti patrón, pero no por eso git stash.

Si aún no lo ha alcanzado, solo espere hasta que tenga problemas con las compilaciones , cuando alguien necesite realizar cambios arquitectónicos significativos en muchos archivos (y los conflictos de fusión) o algún código de trabajo en progreso no probado que se filtre a producción para esto. antipatrón para ponerte al día.

Comunidad
fuente