the strategy strategies practices good following combination branching best database version-control tfs merge branch

database - strategy - Bases de datos y “rama”



tfvc branching (10)

Acabo de encontrar un artículo más antiguo escrito en 2008 por Jeff Atwood; Esperemos que todavía sea relevante para su problema.

Obtenga su base de datos bajo el control de versiones

Menciona la serie de cinco partes escrita por K. Scott Allen:

Actualmente estamos desarrollando una aplicación que utiliza una base de datos.

Cada vez que actualizamos la estructura de la base de datos, tenemos que proporcionar un script para actualizar la base de datos de la versión anterior a la actual.

Así que la base de datos tiene actualmente un número que nos dio su versión actual y luego nuestro software realiza una actualización cuando queremos usar una base de datos "antigua".

El problema que estamos encontrando es cuando tenemos sucursales:

Cuando creamos una nueva gran característica, que no estará disponible para los usuarios (y no se incluirá en las versiones), creamos una rama.

La rama principal (troncal) se fusionará regularmente para garantizar que el brunch de creación tenga las últimas correcciones de errores.

Aquí hay una ilustración:

El problema es con nuestros scripts de actualización. Se actualizan desde la versión anterior a la actual, y luego actualizan el número de versión de la base de datos.

Imagina que tenemos la versión 17 de DB al crear la rama.

Luego hacemos la rama, y ​​hacemos cambios en el DB Trunk. El DB tiene ahora la versión 18.

Luego hacemos un cambio de db en la rama. Como sabemos que ya ha habido una nueva versión "18", creamos la versión 19 y el actualizador 18-> 19.

Entonces el tronco se fusiona en la rama.

En este mismo momento podemos tener algunos actualizadores que nunca se ejecutarán.

Si alguien actualizó su base de datos antes de la fusión, su base de datos se marcará teniendo la versión 19, la actualización 17-> 18 nunca se hará.

Queremos cambiar este comportamiento pero no podemos encontrar cómo:

Nuestras limitaciones son:

  • No podemos hacer todos los cambios en la misma rama.
  • A veces tenemos más de 2 sucursales, y solo podemos combinarlas desde la troncal a la rama de funciones hasta que la función esté terminada.

¿Qué podemos hacer para asegurar una continuidad entre nuestra rama de base de datos?


Creo que la forma en que plantea el problema es imposible de resolver, pero si cambia parte de su proceso hay una solución. Comencemos con la primera parte: por qué es imposible resolverlo utilizando solo deltas. En lo siguiente, asumo que tiene el tronco principal y dos ramas dev-a y dev-b ; ambas ramas provienen del mismo punto en el tiempo.

Porque no puede trabajar

Digamos que Alice agrega un script delta a dev-a:

ALTER TABLE t1 (ALTER COLUMN col5 char(4))

y Bob agregan otro script en dev-b

ALTER TABLE t1 (ALTER COLUMN col5 int)

Los dos scripts son claramente incompatibles y terminas en un código de ruptura en main cuando te fusionas con cualquiera de los dos. La herramienta de combinación no puede ser de ayuda si los archivos de script tienen nombres diferentes.

Solución posible

Mi sugerencia es describir su base de datos en términos tanto de línea de base como de deltas: los scripts delta siempre deben referirse a una línea de base específica, de modo que pueda calcular un nuevo esquema de línea de base resultante de la aplicación de deltas sucesivos a una línea de base específica.

Un ejemplo

dev-a *--B.A1--D.1@[email protected]*--B.A3-- / / main -- B.0 --*--------------------------*--B.1---*---------- / / dev-b *[email protected]*

tenga en cuenta que después de la bifurcación usted inmediatamente separa una nueva línea de base, lo mismo antes de cada combinación. De esta forma podrás comprobar que las líneas de base son compatibles.

Comentario final

Administrar deltas en el control de versiones es como reinventar la rueda, ya que cada script delta es funcionalmente equivalente a guardar diferentes versiones del script de línea de base. Dicho esto, estoy de acuerdo con usted en que en la práctica transmiten más valor y obligan a las personas a pensar qué sucede en la producción cuando cambia la base de datos.

Si opta por almacenar solo la línea de base, tiene muchas herramientas para admitir.

Otra opción es serializar el trabajo en la base de datos, como un todo o particionar el esquema en áreas separadas con propietarios únicos.


Creo que la forma más fácil es utilizar el enfoque Ruby-on-rails. Cada cambio de base de datos es un archivo de script independiente, no importa lo pequeño que sea. Cada archivo de secuencia de comandos está numerado, y cuando realiza una actualización, simplemente ejecuta cada secuencia de comandos desde el número de su base de datos actual hasta la última.

Lo que esto significa en la práctica es que su sistema de versión de base de datos deja de ser v18 a v19, y comienza a ser v18.0 a v18.01, luego v18.02, etc. Lo que comunique al cliente puede convertirse en un gran script de actualización v19 , pero a medida que se desarrolle, estará realizando muchas, muchas pequeñas actualizaciones.

Tendrá que modificar esto ligeramente para que funcione para su sistema, cada secuencia de comandos tendrá que ser renumerada a medida que se fusiona con la sucursal o tendrá que asegurarse de que las secuencias de comandos de actualización no se limitan a rastrear el número de la última actualización, sino a cada número de actualización, por lo que los huecos faltantes se completarán a medida que el script se fusione.

También tendrás que acumular estas pequeñas actualizaciones en el siguiente número importante a medida que crees la etiqueta de lanzamiento (en la troncal primero) para mantener la sensatez.

edición: fundamentalmente, primero debe deshacerse de la idea de utilizar un sdcript de actualización para pasar de una versión a otra. Por ejemplo, si comienza con una tabla y el troncal agrega la columna A y la rama agrega la columna B, entonces fusiona el tronco a la bifurcación; no puede realistamente "actualizar" a la versión con ambas, a menos que el número de versión de la bifurcación sea siempre mayor que el script de actualización del troncal, y eso no funciona si posteriormente fusiona el troncal con la rama. Por lo tanto, debe desechar la idea de una "versión" que se aplique a las ramas de desarrollo. La única forma de hacerlo es actualizar cada cambio de forma independiente y realizar un seguimiento de cada cambio de forma individual. Luego puede decir que necesita el "último lanzamiento principal más colA más colB" (es cierto que si combina el enlace troncal, puede tomar el lanzamiento principal actual del troncal, ya sea su v18 o v19, pero aún necesita aplicar cada actualización de rama individualmente) .

Así que empiezas con el tronco en DB v18. Rama y haz cambios. Luego fusionas el troncal más tarde, donde el DB está en v19. Sus cambios de rama anteriores aún deben aplicarse (o ya deberían aplicarse, pero es posible que deba escribir un script de actualización de rama con todos los cambios de rama en él, si vuelve a crear su base de datos). Tenga en cuenta que la rama no tiene un número de versión "v20" en absoluto, y los cambios de ramas no se realizan en un solo script de actualización como el que tiene en el troncal. Puede agregar estos cambios que realice en la rama como un solo script si lo desea (o 1 script de ''desde la última fusión del tronco'' cambia) o tantos scripts pequeños. Cuando se completa la rama, la última tarea consiste en tomar todos los cambios de base de datos realizados para la rama y agregarlos a un script que se pueda aplicar al mejorador principal, y cuando se fusiona en un tronco, ese script se fusiona en La secuencia de comandos de actualización actual y el número de versión de base de datos bumping.

Hay una alternativa que puede funcionar para usted, pero descubrí que es un poco inestable cuando intenta actualizar las bases de datos con datos, a veces simplemente no pudo hacer la actualización y la base de datos tuvo que borrarse y volver a crearse. (Lo que, para ser justos, es probablemente lo que hubiera tenido que pasar si usara scripts SQL en ese momento). Eso es para usar el proyecto de base de datos de Visual Studio. Esto almacena cada parte del esquema como un archivo, por lo que tendrá 1 script por tabla. El mismo Studio Studio le ocultará estos objetos, que le mostrarán diseñadores en lugar de scripts, pero se almacenarán como archivos en el control de versiones. VS puede implementar el proyecto e intentará actualizar su base de datos si ya existe. Tenga cuidado con las opciones, muchos valores predeterminados dicen "soltar y crear" en lugar de usar alterar para actualizar una tabla existente.

Estos proyectos pueden generar un script SQL (en gran parte legible por la máquina) para la implementación, solíamos generarlos y entregarlos a un equipo de DBA que no usó VS y solo aceptó SQL.

Y, por último, está Roundhouse que no es algo que haya usado, pero podría ayudarlo a convertirse en el nuevo "script" de actualización. Es un proyecto gratuito y he leído que es más poderoso y más fácil de usar que los proyectos de VS DB. Es una herramienta de gestión de cambios y versiones DB, se integra con VS y usa scripts SQL.


Creo que un enfoque que satisfaga la mayoría de sus requisitos es adoptar el concepto de "Refactorización de base de datos".

Hay un buen libro sobre este tema Refactoring Databases: Evolutionary Database Design

Una refactorización de base de datos es un pequeño cambio en el esquema de su base de datos que mejora su diseño sin cambiar su semántica (por ejemplo, no agrega nada ni rompe nada). El proceso de refactorización de la base de datos es la mejora evolutiva de su esquema de base de datos para mejorar su capacidad de satisfacer las nuevas necesidades de sus clientes, respaldar el desarrollo de software evolutivo y corregir los problemas existentes de diseño de la base de datos.

El libro describe la refactorización de bases de datos desde el punto de vista de:

  • Tecnología. Incluye código fuente completo sobre cómo implementar cada refactorización en el nivel de la base de datos y para la mayoría de las refactorizaciones mostramos cómo cambiaría la aplicación para reflejar el cambio en la base de datos. Nuestros ejemplos de código se encuentran en los metadatos de Oracle, Java e Hibernate (las refactorizaciones son fáciles de traducir a otros entornos y, a veces, analizamos características específicas del proveedor que simplifican algunas refactorizaciones).

  • Proceso. Describe en detalle el proceso de refactorización de la base de datos, tanto en la simple situación de una aplicación que accede a la base de datos como en la situación de la base de datos a la que acceden muchos programas, muchos de los cuales están fuera del alcance de su autoridad. Los ejemplos técnicos asumen esta última situación, por lo que si se encuentra en una situación simple, puede encontrar que algunas de nuestras soluciones son un poco más complicadas de lo que necesita (¡qué suerte!).

  • Cultura. Si bien es técnicamente sencillo implementar refactorizaciones individuales y es claramente posible (aunque un poco complicado) adaptar sus procesos internos para respaldar la refactorización de bases de datos, el hecho es que los desafíos culturales dentro de su organización probablemente serán el obstáculo más difícil de superar.


Desea migraciones de base de datos. Muchos frameworks tienen plugins para esto. Por ejemplo, CakePHP usa un complemento de CakeDC para administrar. Aquí hay algunas herramientas genéricas: http://en.wikipedia.org/wiki/Schema_migration#Available_Tools .

Si desea rodar su propia cuenta, quizás en lugar de mantener la versión actual de la base de datos en la base de datos, mantenga una lista de los parches que se han aplicado. Entonces, en lugar de la tabla de version con una fila con valor 19 , en cambio, tiene una tabla de patches con varias filas:

Patches 1 2 3 4 5 8

Mirando esto necesitas aplicar los parches 6 y 7.


El método de versión de base de datos que está utilizando es ciertamente incorrecto, en mi opinión. Si algo tiene que tener versiones, debe ser el código fuente. El código fuente tiene versiones. Su entorno en vivo es solo una instancia del código fuente.

La respuesta es aplicar cambios en la base de datos utilizando scripts de cambio redistribuibles .

  • Todos los cambios, independientemente de la rama en la que se encuentre (incluso en maestro / troncal) se deben realizar en una secuencia de comandos separada.
  • Haga una secuencia de sus scripts, para que los más nuevos no se ejecuten primero. Tener un prefijo con fecha en el formato AAAAMMDD para nombre de archivo nos ha funcionado.
  • Cuando esto sucede, el cambio se realiza en el código fuente, no en la base de datos. Puede tener tantas instancias / compilaciones para varias etiquetas / sucursales en el VCS que desee. Por ejemplo, compilaciones en vivo separadas para cada rama.
  • Entonces solo tienes que hacer la compilación para cada instancia (probablemente todos los días). La compilación debería recuperar los archivos de la rama relevante y realizar la compilación / implementación. Dado que los scripts son redistribuibles, los scripts antiguos no tienen efecto en la base de datos. Sólo los cambios recientes se implementan en la base de datos.

Pero, ¿cómo hacer scripts redistribuibles?

Esta es una pregunta que es difícil de responder, ya que no ha especificado qué base de datos está utilizando. Así que te daré un ejemplo sobre cómo lo hace mi organización.

Permítanme tomar un ejemplo simple: si necesitamos agregar una columna a una tabla en particular, no solo escribimos ALTER TABLE ... ADD COLUMN ... Escribimos código para agregar una columna, si y solo si esa columna no existe en la tabla dada.

Ahora, tenemos una API separada para manejar todo ese código repetitivo que verifica la existencia. Así que nuestros scripts son simplemente llamadas a esas APIs. Tendrás que escribir el tuyo. Estas API no son realmente tan difíciles (estamos usando Oracle RDBMS). Pero nos dan una gran ventaja en el control de versiones y la implementación.

Pero, ese es solo un escenario, hay millones de maneras en que una definición de esquema puede cambiar

Si de hecho El tipo de datos de una columna puede cambiar; Se puede agregar una nueva tabla; Una columna de atributo se puede combinar en una clave primaria (muy rara); Las secuencias pueden cambiar; Restricciones; Llaves extranjeras; Todos ellos pueden cambiar.

Pero resulta que todo esto puede manejarse mediante API con privilegios especiales para leer tablas de metadatos. No estoy diciendo que sea fácil, pero estoy diciendo que es un costo de una sola vez.

Pero, ¿cómo deshacer un cambio de base de datos?

Mi experiencia personal es que, si pones un esfuerzo real en el diseño antes de tocar el teclado para escribir ALTER TABLE , este escenario es extremadamente raro. Y si alguna vez hay una reversión, deberías manejarlo manualmente. (por ejemplo, eliminar manualmente la columna añadida).

Normalmente, los cambios en las vistas y los procedimientos almacenados son bastante comunes, y los cambios en las definiciones de tablas son raros.

Construyendo la base de datos

Como dije antes, la construcción de la base de datos se puede hacer ejecutando todos los scripts redistribuibles. Los scripts pre-desplegados no tienen efecto.

El script de implementación de la base de datos no debe comenzar con DROP DATABASE . Su base de datos tiene muchos datos que se usaron para pruebas unitarias. A menos que haga un sistema realmente simple, estos datos serán valiosos en el futuro para la prueba. Sus evaluadores no estarán muy contentos de agregar diez mil registros a varias tablas cada vez que se actualice una base de datos.

Deje a un lado los evaluadores, ¿cómo planea actualizar la base de datos de producción de su cliente / cliente sin aniquilar todos sus datos de producción? Es por esto que debe usar scripts de cambio redistribuibles.

Puedes probar esquemas de números de versión como 18.1-branchname etc ... Pero realmente van a fallar por completo. Porque puedes fusionar tu fuente, no sus instancias.


Esta idea puede o no funcionar, pero leer acerca de su trabajo hasta ahora y la respuesta anterior parece reinventar la rueda. La "rueda" es el control de código fuente, con sus características de bifurcación, fusión y seguimiento de versiones.

En este momento, para cada cambio de esquema de base de datos, tiene un archivo SQL que contiene los cambios del anterior. Ya mencionaste los problemas importantes que tienes con este enfoque.

Reemplace su método con este: ¡Mantenga UNO (y solo UNO!) Archivo SQL, que almacena todos los comandos DDL para crear tablas, índices, etc. desde cero. ¿Necesitas agregar un nuevo campo? Agregue una línea "ALTER TABLE" en su archivo SQL. De esta manera, su herramienta de control de fuente administrará su esquema de base de datos, y cada rama puede tener una diferente.

De repente, el código fuente está sincronizado con el esquema de la base de datos, los trabajos de fusión y fusión, y así sucesivamente.

Nota: Solo para aclarar el propósito del script mencionado aquí es recrear la base de datos desde cero hasta una versión específica, cada vez.

EDITAR: Pasé un tiempo buscando material para apoyar este enfoque. Aquí hay uno que se ve particularmente bien, con un historial comprobado:

Gestión de versiones del esquema de base de datos 101

¿Has visto esta situación antes?

  • Su equipo está escribiendo una aplicación empresarial alrededor de una base de datos
  • Dado que todos están construyendo alrededor de la misma base de datos, el esquema de la base de datos está en flujo
  • Todos tienen sus propias copias "locales" de la base de datos.
  • Cada vez que alguien cambia el esquema, todas estas copias necesitan el último esquema para trabajar con la última compilación del código.
  • Cada vez que implementa en una base de datos provisional o de producción, el esquema debe funcionar con la última compilación del código
  • Factores tales como dependencias de esquema, cambios de datos, cambios de configuración y desarrolladores remotos enturbian el agua

¿Cómo aborda actualmente este problema de mantener en funcionamiento las versiones de la base de datos? ¿Sospechas que esto está tomando más tiempo del necesario? Hay muchas formas de abordar este problema y la respuesta depende del flujo de trabajo en su entorno. El siguiente artículo describe una metodología simplificada y destilada que puede utilizar como punto de partida.

  • Dado que puede implementarse con ANSI SQL, es una base de datos independiente.
  • Dado que depende de las secuencias de comandos, requiere una administración de almacenamiento insignificante y puede encajar en su programa de administración de versión de código actual

Existen herramientas específicamente diseñadas para hacer frente a este tipo de problemas.

Uno es DBSourceTools

DBSourceTools es una utilidad GUI para ayudar a los desarrolladores a poner las bases de datos de SQL Server bajo el control de origen. Un potente script de base de datos, editor de código, generador de SQL y herramienta de control de versiones de base de datos. Compare esquemas, cree scripts de diferencias, edite T-SQL con facilidad. Mejor que Management Studio.

Otro: neXtep Designer

NeXtep designer es un entorno de desarrollo integrado para desarrolladores de bases de datos. El concepto principal detrás del producto es aprovechar el control de versiones para calcular los scripts de SQL incrementales que necesita para entregar sus desarrollos.

El objetivo de este proyecto es crear una plataforma de desarrollo que proporcione todas las herramientas que necesita un desarrollador de base de datos al tiempo que automatiza las tareas de generación de las entregas (= SQL resultante de un desarrollo).

Para obtener más información sobre la problemática de entregar actualizaciones de bases de datos, lo invitamos a leer el artículo sobre cómo entregar actualizaciones de bases de datos que le presentará nuestra visión de las mejores y peores prácticas.


Recientemente hemos empezado a usar las herramientas de datos del servidor Sql (SSDT), que reemplazaron el tipo de proyecto de base de datos de Visual Studio, para controlar la versión de nuestras bases de datos SQL. Crea un proyecto para cada base de datos, con elementos para vistas y procedimientos almacenados y la capacidad de crear aplicaciones de nivel de datos (DACPAC) que se pueden implementar en instancias de SQL Server. SSDT también es compatible con Unit Testing y Static Data , y ofrece a los desarrolladores la opción de realizar pruebas rápidas de sandbox usando una instancia de LocalDB . Hay una buena descripción general en video de TechEd de las herramientas SSDT y muchos más recursos en línea.

En su situación, usaría SSDT para administrar los objetos de su base de datos en el control de versiones junto con el código de su aplicación, utilizando el mismo proceso de fusión para impulsar las características entre las sucursales. Cuando llegue el momento de actualizar una instalación existente, debe crear los DACPAC y utilizar el proceso de actualización de la aplicación de nivel de datos para aplicar los cambios. Alternativamente, también puede usar herramientas de sincronización de base de datos como DBGhost o RedGate para aplicar actualizaciones al esquema existente.


Usamos el siguiente procedimiento durante aproximadamente 1.5 años. No sé si esta es la mejor solución, pero no tuvimos ningún problema con ella (excepto algunos errores humanos en un archivo delta, como olvidar una declaración USE ).

Tiene algunas simularidades con la respuesta que dio Krumia, pero difiere en el punto de que en este enfoque solo se ejecutan los nuevos scripts de cambio / archivos delta. Esto hace que sea mucho más fácil escribir esos archivos.

Archivos delta

Escriba todos los cambios en la base de datos que realice para una característica en un archivo delta. Puede tener varias declaraciones en un archivo delta o dividirlas en varias. Una vez confirmado el archivo, es mejor (y una vez fusionado es necesario) iniciar uno nuevo y dejar el anterior sin tocar.

Coloque todos los archivos delta en un directorio y YYYY-MM-DD-HH.mm.description.sql un patrón de nombre como YYYY-MM-DD-HH.mm.description.sql . Es esencial que pueda ordenarlos a tiempo (por lo tanto, la marca de tiempo) para que sepa qué archivo debe ejecutarse primero. Además, no desea tener un conflicto de fusión con esos archivos, por lo que debe ser único (en todas las sucursales).

Fusionar / tirar

Cree un script de combinación (para examinar un script bash) que realice las siguientes acciones:

  1. Tenga en cuenta el cometer-hash actual
  2. Hacer la fusión real (o tirar)
  3. Obtenga una lista de todos los archivos delta que se agregan con esta combinación ( git diff --stat $old_hash..HEAD -- path/to/delta-files )
  4. Ejecute esos archivos delta, en el orden especificado por la marca de tiempo

Al utilizar git para determinar qué archivos son nuevos (y, por lo tanto, qué acciones de la base de datos aún no se ejecutan en la rama actual), ya no estará vinculado a la numeración de versiones.

Alternando archivos delta

Puede suceder que dentro de una combinación de archivos delta de diferentes ramas pueda ser ''nuevo para ejecutar'' y que esos archivos se alternen de la siguiente manera:

  1. 2014-08-04-delta-from-feature_A.sql
  2. 2014-08-05-delta-from-feature_B.sql
  3. 2014-08-06-delta-from-feature_A.sql

A medida que la marca de tiempo determina el orden de ejecución, primero se agregará algo de la característica A, luego la característica B, luego volverá a la característica A. Cuando escriba los archivos delta apropiados, que son ejecutables por ellos mismos / independientes, eso no debería t ser un problema