Flexible vs ramificación estática(Git vs Clearcase/Accurev)
version-control (9)
Mi pregunta es sobre la forma en que Git maneja las ramas: cada vez que se ramifica desde una confirmación, esta rama no recibirá cambios de la rama principal a menos que la fuerce con una combinación.
Pero en otros sistemas como Clearcase o Accurev, puedes especificar cómo se completan las ramas con algún tipo de mecanismo de herencia : quiero decir, con Clearcase, usando config_spec, puedes decir "obtener todos los archivos modificados en branch / main / issue001 y luego continúe con los que están en / main o con esta línea base específica ".
En Accurev también tiene un mecanismo similar que permite a las transmisiones recibir cambios desde las ramas superiores (transmite como los llaman) sin fusionar o crear una nueva confirmación en la rama.
¿No te pierdes esto mientras usas Git? ¿Puedes enumerar escenarios donde esta herencia es imprescindible?
Gracias
Actualización Lea la respuesta de VonC a continuación para enfocar realmente mi pregunta. Una vez que aceptamos el "almacenamiento lineal" y los SCM basados en DAG tienen capacidades diferentes, mi pregunta es: ¿ cuáles son los escenarios de la vida real (especialmente para las empresas más que OSS) donde el lineal puede hacer cosas que no son posibles para DAG? ¿Valen la pena?
¿Has notado que también puedes descargar versiones específicas de archivos con GIT?
Solo usa esto:
git checkout [< tree-ish >] [--] < paths >
Al igual que las especificaciones de configuración, cualquier versión existente de un archivo (rutas) puede cargarse en el árbol de trabajo. Cita de los documentos de git-checkout:
La siguiente secuencia verifica la rama principal, revierte el Makefile a dos revisiones, elimina hello.c
por error y lo recupera del índice:
$ git checkout master
$ git checkout master~2 Makefile
$ rm -f hello.c
$ git checkout hello.c
ClearCase, sin MultiSite, es un repositorio único, pero se distribuye Git. ClearCase se compromete a nivel de archivo, pero Git se compromete a nivel de repositorio. (Esta última diferencia significa que la pregunta original se basa en un malentendido, como se señala en las otras publicaciones aquí).
Si estas son las diferencias de las que estamos hablando, entonces creo que "lineal" versus "DAG" es una manera confusa de distinguir estos sistemas SCM. En ClearCase, todas las versiones de un archivo se conocen como la versión del archivo "árbol", pero en realidad es un gráfico acíclico dirigido. La verdadera diferencia para Git es que los DAG de ClearCase existen por archivo. Por lo tanto, creo que es engañoso referirse a ClearCase como no DAG y a Git como DAG.
(BTW ClearCase versiona sus directorios de forma similar a sus archivos, pero esa es otra historia).
Dejando a un lado la teoría, he aquí una especie de práctica práctica sobre esto, desde mi punto de vista usando AccuRev en un entorno de producción comercial durante varios años: el modelo de herencia funciona muy bien siempre que los flujos secundarios no se hayan separado demasiado de los antepasados todavía en desarrollo. Se descompone cuando las transmisiones heredadas son demasiado diferentes.
La herencia (versiones posteriores como hijos de anteriores) permite que los cambios en las corrientes ancestrales estén activos en secuencias secundarias sin que nadie haga nada (a menos que se requiera una fusión, en cuyo caso se muestra una superposición profunda, lo que es bueno para poder ver )
Eso suena genial, y en la práctica lo es, cuando todas las secuencias involucradas son relativamente similares. Usamos ese modelo para hotfix y flujos de nivel de paquete de servicio por debajo de una versión de producción determinada. (En realidad, es un poco más complicado que eso para nosotros, pero esa es la idea general).
Las versiones de producción están en paralelo, sin herencia, con esos hijos de hotfix y service pack debajo de cada uno de ellos. Comenzar una nueva versión significa crear una nueva transmisión a nivel de versión y empujar manualmente todo desde la secuencia de mantenimiento más reciente para la versión anterior. Después de eso, los cambios en las versiones anteriores que se aplican a los posteriores deben insertarse manualmente en cada uno de ellos, lo que requiere más trabajo, pero permite un control mucho mayor.
Originalmente usamos el modelo de herencia en todos los lanzamientos, donde los posteriores eran hijos de anteriores. Eso funcionó bien por un tiempo, pero se volvió inmanejable con el tiempo. Las principales diferencias arquitectónicas entre lanzamientos heredadas inevitablemente cambian una mala idea. Sí, puede colocar una instantánea en el medio para bloquear la herencia, pero luego todos los cambios deben realizarse manualmente, y la única diferencia real entre parent-snapshot-child y las transmisiones paralelas no heredables es que la vista de secuencia gráfica completa continuamente empuja hacia abajo y a la derecha, que es un PITA.
Una cosa realmente buena de AccuRev es que tienes esta opción, todo el tiempo. No es una restricción inherente de la arquitectura de su programa SCM.
En cuanto al esquema de herencia utilizado por accurev: los usuarios de GIT probablemente "obtendrán" todo cuando analicen git-flow (ver también: http://github.com/nvie/gitflow y http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/ )
Este modelo de ramificación GIT lo hace más o menos (manualmente / con la ayuda de la herramienta git-flow) lo que accurev hace de manera automática y con gran soporte de GUI.
Entonces parece que GIT puede hacer lo que Accurev hace. Como nunca usé git / git-flow día a día, realmente no puedo decir cómo funciona pero parece prometedor. (Menos soporte de GUI apropiado :-)
Intentaré responderte la pregunta. (Debo decir aquí que no he usado GIT, solo leí al respecto, así que si algo que menciono a continuación es incorrecto, por favor corrígeme)
"¿Puedes enumerar escenarios donde esta herencia es imprescindible?"
No diré que es obligatorio, ya que puede resolver un problema con la herramienta que tiene, y podría ser una solución válida para su entorno. Supongo que es más una cuestión de procesos que la herramienta en sí misma. Asegurarse de que su proceso sea coherente y también le permita retroceder en el tiempo para reproducir cualquier paso / estado intermedio es el objetivo, y lo mejor de todo es que la herramienta le permite ejecutar su proceso y SCMP de la forma más sencilla posible.
El único escenario que puedo ver es útil para tener este comportamiento de ''herencia'' y usar el poder de la especificación de configuración, es cuando quieres que tu conjunto de cambios esté " aislado " asignado a una tarea (devtask, CR, SR o lo que sea el propósito / alcance de su conjunto de cambios)
Usar esta composición le permite tener su rama de desarrollo limpia y aún usar una combinación diferente (usando composición) del resto del código, y todavía tener solo lo que es relevante para la tarea aislada en una rama durante todo el ciclo de vida de la tarea, solo hasta la fase de integración.
Siendo purista teniendo que comprometer / fusionar / rebase solo para tener un "punto de partida definido", supongo que " contaminaría " tu sucursal y terminarás con tus cambios y otros cambios en tu conjunto de sucursal / cambio.
¿Cuándo / dónde este aislamiento es útil? Los puntos siguientes solo pueden tener sentido en el contexto de las empresas que persiguen CMM y algunas certificaciones ISO, y pueden no ser de interés para otro tipo de empresas o OSS.
Al ser muy quisquilloso, es posible que desee contar con precisión las líneas de código (agregado / modificado / eliminado) del conjunto de cambios correspondiente a un único desarrollador, más tarde utilizado como una entrada para el código y las estimaciones de esfuerzo.
Puede ser más fácil revisar el código en diferentes etapas, teniendo solo su código en una sola rama (no pegado con otros cambios)
En grandes proyectos con varios equipos y más de 500 desarrolladores trabajando simultáneamente en el mismo código base (donde los árboles gráficos de versiones de elementos individuales se ven como una maraña enredada con varias líneas de carga, una para cada cliente grande o una para cada tecnología) grandes configuraciones las especificaciones que usan una composición de varios grados de profundidad hacen que esta cantidad de personas trabaje a la perfección para adaptar el mismo producto / sistema (código base) a diferentes propósitos. Al usar esta especificación de configuración, dá dinámicamente a cada equipo o subgrupo, una visión diferente de lo que necesitan y de dónde necesitan ramificarse, (en cascada en varios casos) sin la necesidad de crear ramas intermedias de integración, o fusionar y reescalonar constantemente todo los bits con los que necesitas comenzar El código de la misma tarea / propósito era ramificarse de diferentes etiquetas, pero tenía sentido. (Puede argumentar aquí la ''línea de referencia conocida'' como un principio de la SCM, pero las etiquetas simples contempladas en un plan SCM escrito hicieron el trabajo). Debe ser posible resolver esto con GIT (supongo que de una manera no dinámica) pero encuentro realmente difícil de imaginar sin este comportamiento de ''herencia''. Supongo que el punto mencionado por VonC "si está ramificado, todos sus archivos se ramificarán desde el mismo punto de partida único" se rompió aquí, pero además estaba bien documentado en el SCMP, recuerdo que había una fuerte razón comercial para hacerlo de esa manera.
Sí, la creación de estas especificaciones de configuración que mencioné anteriormente no era gratuita, al principio había 4-5 personas bien pagadas detrás del SCM pero luego se redujeron mediante scripts automatizados que le preguntaron qué deseaba en términos de etiquetas / ramas / características y escribe la CS por ti.
La reproducibilidad aquí se logró simplemente guardando la especificación de configuración junto con la tarea en el sistema devTask, por lo que cada tarea se correlacionó con los requisitos y se mapeó con una especificación de configuración y un conjunto de cambios (archivos de código, documentos de diseño, documentos de prueba etc)
Así que hasta aquí una conclusión aquí podría ser, solo si su proyecto es lo suficientemente grande / complicado (y puede pagar SC Managers a lo largo de la vida del proyecto :)) entonces solo comenzará a pensar si necesita el comportamiento de ''herencia'' o herramienta realmente versátil; de lo contrario, irá directamente a una herramienta que es gratuita y que ya se encargará de la coherencia de su SCM ... pero podría haber otros factores en la herramienta SCM que podrían hacer que se apegue a uno u otro. .read en ..
Algunas notas al margen, que podrían estar fuera del tema, pero supongo que en algunos casos como el mío deben tenerse en cuenta.
Debo agregar aquí que usamos el "CC bueno" y no el UCM. Totalmente de acuerdo con VonC sobre una buena metodología que permite "guiar" la flexibilidad hacia una configuración más coherente . Lo bueno es que CC es bastante flexible y puedes encontrar (no sin cierto esfuerzo) una buena manera de tener algo coherente mientras que en otro SCM puedes tenerlo gratis. Pero por ejemplo aquí (y otros lugares en los que he trabajado con CC) para proyectos C / C ++ no podemos permitirnos el precio de no tener la función winkin (reutilizando los objetos Derive), que reduce varias veces X el tiempo de compilación. Se puede argumentar que tener un mejor diseño, un código más desacoplado y optimizar los Makefiles puede reducir la necesidad de compilar todo, pero hay casos en los que necesita compilar toda la bestia muchas veces al día, y compartir el DO ahorra Montones de tiempo / dinero. Donde estoy ahora tratamos de usar tanta herramienta gratuita como podamos, y creo que nos desharemos de CC si podemos encontrar una herramienta más barata o gratuita que implemente la función winkin .
Terminaré con algo que mencionó Paul, las diferentes herramientas son mejores que otras para diferentes propósitos, pero agregaré que se puede alejar de alguna limitación de la herramienta al tener un proceso coherente y sin escarificar la reproducibilidad, puntos clave del SCM. Al final, supongo que la respuesta vale? depende de su "problema", el SDLC que está ejecutando, sus procesos de SCM y si hay alguna característica adicional (como winkin) que pueda ser útil en su entorno.
mis 2 centavos
No estoy seguro de si está preguntando algo, pero está demostrando que las transmisiones de Accurev son herramientas diferentes a las de Git (o SVN). (No sé Clearcase.)
Por ejemplo, con Accurev se le obliga , como usted dice, a usar ciertos flujos de trabajo, lo que le da un historial auditable de cambios que no son compatibles con Git. La herencia de Accurev hace que ciertos flujos de trabajo sean más eficientes y otros imposibles.
Con Git puede tener una codificación exploratoria segregada en repos locales o en ramas de características, que Accurev no admitiría muy bien.
Diferentes herramientas son buenas para diferentes propósitos; es útil preguntar para qué sirve cada uno.
No estoy totalmente seguro de lo que estás pidiendo, pero parece que la semántica de seguimiento de git es lo que quieres. Cuando se ramifica desde un origen, puede hacer algo como:
git -t -b my_branch origen / master
Y luego el futuro "git pull" s fusionará automáticamente origen / master en su rama de trabajo. A continuación, puede usar "git cherry -v origin / master" para ver cuál es la diferencia. Puede usar "git rebase" antes de publicar los cambios para limpiar el historial, pero no debe usar rebase una vez que su historial sea público (es decir, que otras personas sigan esa rama).
Para entender por qué Git no ofrece ningún tipo de lo que se refiere como un "mecanismo de herencia" (que no involucra un compromiso), primero debe comprender uno de los conceptos básicos de esos SCM (por ejemplo, Git vs. ClearCase)
ClearCase utiliza un almacenamiento de versión lineal : cada versión de un elemento (archivo o directorio) está vinculada en una relación lineal directa con la versión anterior del mismo elemento.
Git utiliza un gráfico acíclico dirigido por DAG : cada "versión" de un archivo es en realidad parte de un conjunto global de cambios en un árbol que es en sí mismo parte de una confirmación. La versión anterior de eso se debe encontrar en una confirmación previa, accesible a través de una única ruta de gráfico acíclico dirigida.
En un sistema lineal, una especificación de configuración puede especificar varias reglas para lograr la "herencia" que ve (para un archivo dado, primero seleccione una determinada versión, y si no está presente, luego seleccione otra versión, y si no está presente, seleccione una tercero, y así sucesivamente).
La rama es una fork en una historia lineal , una versión dada para una determinada regla de selección (todas las otras reglas de selección anteriores a esa todavía se aplican, de ahí el efecto de "herencia")
En un DAG, un compromiso representa toda la "herencia" que obtendrá alguna vez; no hay una selección "acumulativa" de versiones. Solo hay una ruta en este gráfico para seleccionar todos los archivos que verá en este punto exacto (commit).
Una rama es solo una nueva ruta en este gráfico.
Para aplicar, en Git, algunas otras versiones, debe hacer lo siguiente:
- fusionar en su rama algún otro compromiso (como en el git pull "mencionado por la respuesta de stsquad ) o
- rebase su rama (como Greg menciona )
Pero dado que Git es un SCM basado en DAG, siempre dará como resultado un nuevo compromiso.
Lo que estás "perdiendo" con Git es algún tipo de "composición" (cuando estás seleccionando diferentes versiones con diferentes reglas de selección sucesivas), pero eso no sería práctico en un D VCS (como en "Distribuido"): cuando estás hacer una sucursal con Git, necesita hacerlo con un punto de partida y un contenido claramente definido y fácilmente replicado en otros repositorios.
En un VCS puramente central, puede definir su espacio de trabajo (en ClearCase, su "vista", ya sea instantánea o dinámica) con las reglas que desee.
unknown-google agrega en el comentario (y en su pregunta anterior):
Entonces, una vez que vemos que los dos modelos pueden lograr cosas diferentes (lineal frente a DAG), mi pregunta es: ¿cuáles son los escenarios de la vida real (especialmente para las empresas más que OSS) donde lineal puede hacer cosas que no son posibles para DAG? ¿Lo valen?
Cuando se trata de un "escenario de la vida real" en términos de reglas de selección, lo que puede hacer en un modelo lineal es tener varias reglas de selección para el mismo conjunto de archivos .
Considere esta "especificación de configuración" (es decir, "especificación de configuración" para las reglas de selección con ClearCase):
element /aPath/... aLabel3 -mkbranch myNewBranch
element /aPath/... aLabel2 -mkbranch myNewBranch
Selecciona todos los archivos etiquetados como '' aLabel2
'' (y bifurcación desde allí), excepto aquellos etiquetados como '' aLabel3
'' - y bifurcación desde allí - (porque esa regla precede a la que menciona '' aLabel2
'').
¿Vale la pena?
No.
En realidad, el sabor UCM de ClearCase (la metodología de " Administración de configuración unificada " incluida con el producto ClearCase y que representa todas las "mejores prácticas" deducidas del uso básico de ClearCase) no lo permite, por razones de simplificación . Un conjunto de archivos se denomina "componente", y si desea realizar una bifurcación para una determinada etiqueta (conocida como "línea de base"), eso se traduciría de la siguiente manera:
element /aPath/... .../myNewBranch
element /aPath/... aLabel3 -mkbranch myNewBranch
element /aPath/... /main/0 -mkbranch myNewBranch
Debes elegir un punto de inicio (aquí, '' aLabel3
'') e ir desde allí. Si también desea los archivos de '' aLabel2
'', realizará una combinación de todos los archivos '' aLabel2
'' con los de ''myNewBranch''.
Esa es una "simplificación" que no tiene que hacer con un DAG, donde cada nodo del gráfico representa un "punto de partida" definido exclusivamente para una sucursal, cualquiera que sea el conjunto de archivos involucrados.
Merge y rebase son suficientes para combinar ese punto de partida con otras versiones de un conjunto determinado de archivos, para lograr la "composición" deseada, mientras se mantiene esa historia particular aislada en una rama.
El objetivo general es razonar en "operaciones de control de versiones coherentes aplicadas a un componente coherente ". Un conjunto "coherente" de archivos es uno en un estado coherente bien definido:
- si está etiquetado, todos sus archivos están etiquetados
- si está ramificado, todos sus archivos se ramificarán desde el mismo punto de partida único
Eso se hace fácilmente en un sistema DAG; puede ser más difícil en un sistema lineal (especialmente con "Base ClearCase" donde la "especificación de configuración" puede ser complicada), pero se aplica con la metodología UCM de esa misma herramienta de base lineal.
En lugar de lograr esa "composición" a través de un "truco de regla de selección privada" (con ClearCase, alguna orden de regla seleccionada), lo logras solo con operaciones de VCS (rebase o merge), que dejan un rastro claro para todos (en oposición a una configuración espec privada para un desarrollador, o compartida entre algunos, pero no todos los desarrolladores). Nuevamente, impone un sentido de coherencia , en oposición a una "flexibilidad dinámica", que puede tener dificultades para reproducirse más adelante .
Eso le permite abandonar el dominio de VCS (Sistema de control de versiones) e ingresar al ámbito de SCM (Software Configuration Management) , que se ocupa principalmente de la " reproducibilidad ". Y eso (características de SCM) se puede lograr con un VCS basado en lineal o en un DAG.
Parece que lo que estás buscando podría ser git rebase
. Al volver a basar una rama, se separa conceptualmente de su punto de ramificación original y se vuelve a conectar en otro punto. (En realidad, la rebase se implementa aplicando cada parche de la bifurcación en secuencia al nuevo punto de bifurcación, creando un nuevo conjunto de parches.) En su ejemplo, puede volver a establecer una bifurcación de una bifurcación en la punta actual de una bifurcación superior, que esencialmente "heredará" todos los cambios realizados en la otra rama.