¿Por qué squash git se compromete para las solicitudes de extracción?

200

¿Por qué todos los repositorios serios de Github que realizo solicitan quieren que reduzca mis confirmaciones en una única confirmación?

Pensé que el registro de git estaba allí para que pudieras inspeccionar todo tu historial y ver exactamente qué cambios ocurrieron dónde, pero aplastarlo lo saca del historial y lo agrupa todo en una confirmación. ¿Cual es el punto?

Esto también parece ir en contra del mantra "comprometerse temprano y comprometerse con frecuencia".

hamstar
fuente
1
Para un gran repositorio, ahorra mucho tiempo y espacio cuando la gente quiere hacer algo con el repositorio.
raptortech97
8
" Esto también parece ir en contra del" compromiso temprano y el compromiso frecuente "mantra ". Y el revisor no quiere revisarlos, eso es seguro.
JensG
77
No es necesario poner una edición en la pregunta para resumir cuál es la respuesta percibida. Ese es el lugar de respuestas aceptadas y votos a favor. Las personas pueden sacar sus propias conclusiones al leer las respuestas.
55
Todavía no entiendo por qué existe el deseo de aplastar los commits. Creo que hay demasiados inconvenientes para aplastar casi sin profesionales. Es un problema de presentación que se hace pasar por un problema de datos.
mkobit
3
Cualquier análisis útil en el registro se pierde con las calabazas. Por lo tanto, parece que un desarrollador creó una función con una sola confirmación. También es difícil ver por qué existe un código extraño. El historial de una característica puede ser importante y puede explicar en gran medida por qué el código es como es. La visión concisa del registro es útil, pero ¿no se puede lograr de otra manera?
Tjaart

Respuestas:

158

Para que tenga un historial de git claro y conciso que documente clara y fácilmente los cambios realizados y los motivos.

Por ejemplo, un registro de git típico 'sin aplastar' para mí podría tener el siguiente aspecto:

7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
787g8fgf78... hotfix for #5849564648
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

¡Que desastre!

Mientras que un registro de git más cuidadosamente administrado y fusionado con un pequeño enfoque adicional en los mensajes para esto podría verse así:

7hgf8978g9... 8483948393 Added new slideshow feature
787g8fgf78... 5849564648 Hotfix for android display issue
9080gf6567... 6589685988 Implemented pop-up to select language

Creo que puede ver el punto de aplastar los compromisos en general y el mismo principio se aplica a las solicitudes de extracción: legibilidad de la historia. También puede estar agregando a un registro de confirmación que ya tiene cientos o incluso miles de confirmaciones y esto ayudará a mantener la historia cada vez más breve y concisa.

Desea comprometerse temprano y con frecuencia. Es una mejor práctica por muchas razones. Encuentro que esto me lleva a tener compromisos frecuentes que son "wip" (trabajo en progreso) o "parte A hecho" o "error tipográfico, corrección menor" donde estoy usando git para ayudarme a trabajar y darme puntos de trabajo que Puedo volver a si el siguiente código no funciona a medida que progreso para que las cosas funcionen. Sin embargo, no necesito ni quiero ese historial como parte del historial final de git, por lo que puedo aplastar mis commits, pero vea las notas a continuación sobre lo que esto significa en una rama de desarrollo frente a master.

Si hay hitos importantes que representan distintas etapas de trabajo, todavía está bien tener más de una confirmación por función / tarea / error. Sin embargo, esto a menudo puede resaltar el hecho de que el ticket en desarrollo es "demasiado grande" y debe dividirse en piezas más pequeñas que pueden ser independientes, por ejemplo:

8754390gf87... Implement feature switches

parece "1 pieza de trabajo". ¡O existen o no! No parece tener sentido separarlo. Sin embargo, la experiencia me ha demostrado que (dependiendo del tamaño y la complejidad de la organización) una ruta más granular podría ser:

fgfd7897899... Add field to database, add indexes and a trigger for the dw group
9458947548g... Add 'backend' code in controller for when id is passed in url.
6256ac24426... Add 'backend' code to make field available for views.
402c476edf6... Add feature to UI

Las piezas pequeñas significan revisiones de código más fáciles, pruebas de unidad más fáciles, mejor oportunidad para qa, mejor alineación con el Principio de responsabilidad única, etc.

Para los aspectos prácticos de cuándo hacer tales aplastamientos, existen básicamente dos etapas distintas que tienen su propio flujo de trabajo.

  • su desarrollo, por ejemplo, solicitudes de extracción
  • su trabajo agregado a la rama principal, por ejemplo, maestro

Durante su desarrollo, se compromete 'temprano y con frecuencia' y con mensajes rápidos 'desechables'. Es posible que desee aplastar aquí a veces, por ejemplo, aplastar en wip y confirmaciones de mensajes de todo. Está bien, dentro de la rama, retener múltiples confirmaciones que representan pasos distintos que realizó en el desarrollo. La mayoría de las calabazas que elija hacer deben estar dentro de estas ramas de características, mientras se desarrollan y antes de fusionarse para dominar.
Al agregar a la rama de línea principal, desea que las confirmaciones sean concisas y estén formateadas correctamente de acuerdo con el historial de línea principal existente. Esto podría incluir la identificación del sistema Ticket Tracker, por ejemplo, JIRA como se muestra en los ejemplos. La compresión realmente no se aplica aquí a menos que desee 'acumular' varias confirmaciones distintas en master. Normalmente no lo haces.

El uso --no-ffal fusionar con master usará una confirmación para la fusión y también preservará el historial (en la rama). Algunas organizaciones consideran que esta es una mejor práctica. Ver más en https://stackoverflow.com/q/9069061/631619 También verá el efecto práctico en git logdonde un --no-ffcommit será el último commit, en la parte superior de HEAD (cuando se acaba de hacer), mientras que sin --no-ffél puede ser más abajo en la historia, dependiendo de las fechas y otras confirmaciones.

Michael Durrant
fuente
30
Estoy completamente en desacuerdo con esta filosofía. Los pequeños compromisos regulares dividen una tarea en tareas más pequeñas y, a menudo, brindan información útil sobre exactamente en qué se estaba trabajando cuando se cambió una línea. Si tiene pequeñas confirmaciones "WIP", debe usar una nueva versión interactiva para limpiar eso antes de presionarlas. El aplastamiento de relaciones públicas parece derrotar por completo el punto de los pequeños compromisos regulares en mi humilde opinión. Es casi irrespetuoso con el autor que se ha esforzado por dar mensajes de registro con información de confirmación específica y simplemente lo descarta todo.
Jez
66
Interesante. Al final del día, baso mi 'filosofía' en lo que he visto funcionar. Para ser claros, dividir el trabajo en partes pequeñas y comprometer cada uno es excelente mientras se trabaja en el cambio. Mi experiencia ha sido que una vez que este trabajo se fusiona para dominar, lo principal que tiende a ser examinado es 'qué, en su totalidad, es este compromiso de fusión que (generalmente) causó problemas x ...
Michael Durrant
66
También soy anti squash. No debes escribir mensajes tontos de confirmación en primer lugar. Y en el ejemplo (85493g2458 ... Se corrigió el problema de visualización de la presentación de diapositivas, es decir) Eso sería mucho más útil si depurara el código asociado.
Thomas Davis
55
Es desafortunado que tal dogma descuidado y fuera de lugar haya surgido en la práctica de SCM. Las herramientas de filtrado deben utilizarse para gestionar la legibilidad del historial y no el comportamiento de compromiso.
ctpenrose
44
tendencias perturbadoras y dogmas. yow Prefiero un argumento más basado en hechos presentado de una manera más agradable. Puede publicar / votar una opinión diferente. Ese es el punto de votación de la comunidad
Michael Durrant
26

Debido a que a menudo la persona que saca un RP se preocupa por el efecto neto de la "característica X añadida", no por las "plantillas base, la función de corrección de errores X, la función de agregar Y, los errores tipográficos fijos en los comentarios, los parámetros de escala de datos ajustados, el hashmap funciona mejor que lista "... nivel de detalle

Si crees que tus 16 commits están mejor representados por 2 commits en lugar de 1 "Característica agregada X, re-factorizada Z para usar X", entonces probablemente sea bueno proponer un pr con 2 commits, pero entonces sería mejor proponer 2 pr's separadas en ese caso (si el repositorio todavía insiste en pr's de commit único)

Esto no va en contra del mantra "comprometerse temprano y comprometerse con frecuencia", como en su repositorio, mientras desarrolla aún tiene los detalles granulares, por lo que tiene una mínima posibilidad de perder el trabajo, y otras personas pueden revisar / extraer / proponer PR está en contra de su trabajo mientras se desarrolla el nuevo PR.

Andrew Hill
fuente
44
Pero cuando aplastas los compromisos, también pierdes ese historial en tu tenedor, ¿verdad? ¿Por qué querrías tirar esa historia?
hamstar
44
a) si desea preservar el historial en su rama, es bastante fácil tener una rama "para fusionar" y una rama "dev" (que podría ser lo que necesita de todos modos, ya que podría agregar nuevas confirmaciones a su desarrollador principal) bifurcar después de proponer el RP) b) aún conserva el efecto neto de esas confirmaciones, solo sería en los casos en que desea revertir, o elegir un subcomponente de la RP que las confirmaciones individuales serían necesarias . En ese caso, probablemente esté haciendo varias cosas (por ejemplo: corrección de errores X, agregar función Y) en un solo RP y podría necesitarse de nuevo múltiples RP.
Andrew Hill
@ AndrewHill ¡qué gran concepto! Quiero presentar este flujo de trabajo a mi equipo, ¿cómo te ha funcionado? Escribí una publicación de blog al respecto y encontré este comentario mientras lo investigaba.
Droogans
19

La razón principal de lo que puedo ver es la siguiente:

  • La interfaz de usuario de GitHub para fusionar solicitudes de extracción actualmente (octubre de 2015) no le permite editar la primera línea del mensaje de confirmación, lo que obliga a que sea Merge pull request #123 from joebloggs/fix-snafoo
  • La interfaz de usuario de GitHub para explorar el historial de confirmación actualmente no le permite ver el historial de la rama desde el --first-parentpunto de vista
  • La interfaz de usuario de GitHub para ver la culpa en un archivo actualmente no le permite ver la culpa del archivo con el --first-parentpunto de vista (tenga en cuenta que esto solo se solucionó en Git 2.6.2, por lo que podríamos perdonar a GitHub por no tener eso disponible)

Entonces, cuando combina las tres situaciones anteriores, obtiene una situación en la que las confirmaciones sin aplastar que se fusionan se ven feas desde la interfaz de usuario de GitHub.

Tu historial con commits aplastados se parecerá

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... Hotfix for android display issue
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... Implemented pop-up to select language

Mientras que sin compromisos aplastados, la historia se verá algo así como

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... hotfix for #5849564648
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Cuando tiene muchas confirmaciones en un seguimiento de relaciones públicas en el que se produjo un cambio, puede convertirse en una pesadilla si se limita a usar la interfaz de usuario de GitHub .

Por ejemplo, encuentra que un puntero nulo se desreferencia en algún lugar de un archivo ... por lo que dice "¿quién hizo esto y cuándo? ¿Qué versiones de lanzamiento se ven afectadas?". Luego paseas a la vista de culpa en la interfaz de usuario de GitHub y ves que la línea se cambió en789fdfffdf... "oh, pero espere un segundo, esa línea solo tenía su sangría cambiada para encajar con el resto del código", así que ahora necesita navegar al estado del árbol para ese archivo en el compromiso principal y volver a visitar la página de la culpa ... eventualmente encuentras el commit ... es un commit de hace 6 meses ... "oh **** esto podría estar afectando a los usuarios durante 6 meses" dices ... ah, pero espera, ese commit en realidad estaba en una solicitud de extracción y solo se fusionó ayer y nadie ha cortado un lanzamiento todavía ... "Maldita sea por fusionar confirmaciones sin aplastar el historial" es el grito que generalmente se puede escuchar después de aproximadamente 2 o 3 expediciones de arqueología de código a través de IU de GitHub

Ahora consideremos cómo funciona esto si usa la línea de comandos de Git (y la súper increíble 2.6.2 que tiene la solución git blame --first-parent)

  • Si estaba utilizando la línea de comando Git, podría controlar el mensaje de confirmación de fusión por completo y, por lo tanto, la confirmación de fusión podría tener una buena línea de resumen.

Entonces nuestra historia de compromiso se vería así

$ git log
1256556316... #423 Added new slideshow feature
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... #324 Hotfix for android display issue
787g8fgf78... hotfix for #5849564648
f56556316e... #28 Implemented pop-up to select language
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Pero también podemos hacer

$ git log --first-parent
1256556316... #423 Added new slideshow feature
56556316ad... #324 Hotfix for android display issue
f56556316e... #28 Implemented pop-up to select language

(En otras palabras: la CLI de Git le permite tener su pastel y comérselo también)

Ahora, cuando nos topamos con el problema del puntero nulo ... bueno, simplemente usamos git blame --first-parent -w dodgy-file.ce inmediatamente se nos da la confirmación exacta donde se introdujo la desreferencia del puntero nulo a la rama maestra, ignorando los simples cambios de espacios en blanco.

Por supuesto, si está haciendo fusiones usando la interfaz de usuario de GitHub, entonces git log --first-parentes realmente horrible gracias a que GitHub obliga a la primera línea del mensaje de confirmación de fusión:

1256556316... Merge pull request #423 from jrandom/add-slideshows
56556316ad... Merge pull request #324 from ahacker/fix-android-display
f56556316e... Merge pull request #28 from somwhere/select-lang-popup

Entonces, para resumir una larga historia:

La interfaz de usuario de GitHub (octubre de 2015) tiene una serie de deficiencias con respecto a cómo combina las solicitudes de extracción, cómo presenta el historial de confirmación y cómo atribuye la información de culpa. La mejor forma actual de piratear estos defectos en la interfaz de usuario de GitHub es solicitar a las personas que eliminen sus compromisos antes de fusionarse.

La CLI de Git no tiene estos problemas y puede elegir fácilmente qué vista desea ver para que ambos puedan descubrir la razón por la cual se realizó un cambio en particular de esa manera (mirando el historial de las confirmaciones no aplastadas) y ver los commits efectivamente aplastados.

Publicar guión

La razón final a menudo citada para las confirmaciones de aplastamiento es hacer que el backport sea más fácil ... si solo tiene una confirmación para el puerto de respaldo (es decir, la confirmación aplastada), entonces es fácil elegir ...

Bueno, si está mirando el historial de git git log --first-parent, puede elegir los commits de fusión. La mayoría de las personas se confunden al elegir las -m Nopciones de fusión de fusión porque tiene que especificar la opción, pero si obtuvo la confirmación, git log --first-parententonces sabe que es el primer padre que desea seguir, por lo que serágit cherry-pick -m 1 ...

Stephen Connolly
fuente
1
¡Buena respuesta! Pero elegir una gama de cerezas es bastante fácil, no estoy seguro de que lo compre como una razón.
Stiggler
1
@stiggler Personalmente no lo compro como una razón, pero otros lo han visto como una razón. En mi humilde opinión, las herramientas de git son lo que quieres y aplastar commits es malo ... pero yo diría que haber impulsado --first-parentmi fijación para ganar un argumento en contra de aplastar commits ;-)
Stephen Connolly
1
Esta debería ser la respuesta aceptada. Mucho menos dogma y muestra que el filtrado de la historia se puede utilizar para "tener su pastel y comérselo también". No tiene que aniquilar la historia para aportar claridad a las exploraciones de historia de git.
ctpenrose
No será tan fácil elegir si al aplastar tus confirmaciones terminas agrupando muchos cambios en diferentes archivos en una sola confirmación. Supongo que esto varía mucho según la base del código, qué cambios se hicieron, cuántas personas están colaborando, etc. No creo que podamos llegar a una sola regla general que sea válida en todos los casos.
Amy Pellegrini
2

Estoy de acuerdo con los sentimientos expresados ​​en otras respuestas en este hilo sobre mostrar un historial claro y conciso de las características agregadas y los errores corregidos. Sin embargo, quería abordar otro aspecto que su pregunta eludió pero que no mencionó explícitamente. Parte del problema que puede tener sobre algunos de los métodos de trabajo de git es que git le permite reescribir la historia, lo que parece extraño cuando se le presenta a git después de usar otras formas de control de fuente donde tales acciones son imposibles. Además, esto también va en contra del principio generalmente aceptado del control de origen de que una vez que algo se confirma / se registra en el control de origen, debe poder volver a ese estado sin importar los cambios que realice después de ese compromiso. Como implica en su pregunta, esta es una de esas situaciones. Creo que git es un gran sistema de control de versiones; sin embargo, para comprenderlo, debe comprender algunos de los detalles de implementación y las decisiones de diseño que lo respaldan, y como resultado tiene una curva de aprendizaje más pronunciada. Tenga en cuenta que git estaba destinado a ser un sistema de control de versiones distribuido y eso ayudará a explicar por qué los diseñadores permiten que el historial de git se reescriba con confirmaciones de squash como un ejemplo de esto.

Fred Thomsen
fuente
Estrictamente hablando, Git no te permite cambiar la historia. Los objetos de compromiso son inmutables. Solo los punteros de rama son mutables, y solo entonces con una "actualización forzada", que generalmente se considera algo que solo se hace con fines de desarrollo, no en la rama maestra. Siempre puede volver a un estado anterior utilizando el registro de registros, a menos que git gcse haya ejecutado para descartar objetos sin referencia.
Jon Purdy
Tienes razón, y tal vez debería haber mencionado esto más arriba. Sin embargo, argumentaría que algo así como un empuje forzado o cambios de compromiso que ya han sido empujados a un repositorio remoto, calificaría como reescribir el historial, ya que el orden y la disposición de los compromisos son tan importantes como los propios compromisos.
Fred Thomsen
Eso es cierto para un compromiso dado. En la imagen más grande, pienso en el rebase interactivo y la rama de filtro como una reescritura efectiva de la historia al cambiar su composición.
Michael Durrant
1
Para ser justos, SVN mantiene el enfoque de "cada confirmación es sagrada", pero cuando la fusión crea una única confirmación con esa fusión y oculta las revisiones que contribuyeron a eso, parece que todas las fusiones de SVN están "modificadas". No lo son, solo tiene que decirle al comando de historial que le muestre los componentes combinados. Me gusta este enfoque lo mejor.
gbjbaanb
1

Debido a la perspectiva ... La mejor práctica es tener una confirmación única por cada <<issue-management-system>>problema si es posible.

Puede tener tantas confirmaciones como desee en su propia rama / repositorio de funciones, pero es su historial relevante para lo que está haciendo ahora para SU perspectiva ... no es la HISTORIA para todo el EQUIPO / PROYECTO o la aplicación de su perspectiva que se mantendrá dentro de varios meses ...

Por lo tanto, cada vez que desee confirmar una corrección de errores o una función en un repositorio común (este ejemplo es con la rama de desarrollo), puede hacerlo de la siguiente manera:

cómo volver a desarrollar su rama de características para que se desarrolle rápidamente

    # set your current branch , make a backup of it , caveat minute precision
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # squash all your changes at once 
    git reset $(git merge-base develop $curr_branch)

    # check the modified files to add 
    git status

    # add the modified files dir by dir, or file by file or all as shown
    git add --all 

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # add the single message of your commit for the stuff you did 
    git commit -m "<<MY-ISSUE-ID>>: add my very important feature"

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # make a backup once again , use seconds precision if you were too fast ... 
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # compare the old backup with the new backup , should not have any differences 
    git diff <<old-backup>>..<<new-backup-branch>>


    # you would have to git push force to your feature branch 
    git push --force 
Yordan Georgiev
fuente
esto ni siquiera intenta responder a la pregunta formulada, ¿por qué squash git se compromete para las solicitudes de extracción? Vea cómo responder
mosquito
después del intento de explicación, debería hacerlo ...
Yordan Georgiev
De alguna manera, no veo cómo la explicación adicional ofrece algo sustancial sobre los puntos formulados y explicados en las respuestas más votadas que se publicaron hace varios años (aunque se aprecia el esfuerzo por mejorar la primera revisión)
mosquito
la palabra clave es la "perspectiva", el concepto de perspectiva hace que explicar este tipo de problemas en TI sea mucho más fácil, por ejemplo: su perspectiva para la respuesta de esta pregunta es la que ya se ha proporcionado, mi perspectiva está usando la palabra clave "perspectiva" y proporcionar un ejemplo práctico de cómo implementar la misma mejor práctica explicada a través de diferentes perspectivas ...
Yordan Georgiev
1

Vería si el código se hace público o no.

Cuando los proyectos se mantienen privados:

Yo recomendaría no aplastar y ver la fabricación de la salchicha. Si usa confirmaciones buenas y pequeñas, las herramientas como git bisect son muy útiles y la gente puede identificar rápidamente las confirmaciones de regresión y ver por qué lo hizo (debido al mensaje de confirmación).

Cuando los proyectos se hacen públicos:

Aplasta todo porque algunos commits pueden contener fugas de seguridad. Por ejemplo, una contraseña que se ha confirmado y eliminado nuevamente.

Vince V.
fuente