Compartir a través de



Junio de 2017

Volumen 32, número 6

DevOps: aspectos internos de GIT para desarrolladores de Visual Studio

Por Jonathan Waldman | Junio de 2017

En mi remisión al artículo sobre DevOps de GIT (msdn.com/magazine/mt767697), expliqué cómo el sistema de control de versiones (VCS) de GIT difiere de los sistemas VCS centralizados que posiblemente ya conoce. Luego, demostré como realizar determinadas tareas de GIT mediante las herramientas de GIT en Visual Studio. En este artículo, resumiré los cambios importantes del funcionamiento de GIT en el IDE de Visual Studio 2017 recién lanzado, explicaré cómo se implementa el repositorio GIT en el sistema de archivos y examinaré la topología de su almacén de datos, así como la estructura y el contenido de sus distintos objetos de almacenamiento. Terminaré con una explicación básica de las ramas de GIT, en la que proporcionaré una perspectiva que, espero, sirva para prepararle para operaciones de GIT más avanzadas que presentaré en futuros artículos.

Nota: No uso servidores ni equipos remotos en este artículo. Exploro un escenario puramente local que puede seguir con cualquier máquina Windows que tenga Visual Studio 2017 y GIT para Windows (G4W) instalado (con o sin conexión a Internet o de red). Este artículo es una introducción a los aspectos internos de GIT, en el que se supone que conoce las herramientas de GIT de Visual Studio, así como las operaciones y los conceptos básicos de GIT.

Visual Studio, GIT y usted

GIT no solo hace referencia al repositorio que contiene el almacén de datos de control de versiones, sino también al motor que procesa los comandos para administrarlo: Los comandos de fontanería llevan a cabo operaciones básicas; los comandos de porcelana incluyen los comandos de fontanería de manera similar a una macro, lo que permite invocarlos más fácilmente cuanto menos pormenorizados. A medida que domine GIT, descubrirá que algunas tareas requieren el uso de estos comandos (algunos de los cuales usaré en este artículo) y que se requiere una interfaz de línea de comandos (CLI) para invocarlos. Lamentablemente, Visual Studio 2017 ya no instala una CLI de GIT porque usa un nuevo motor de GIT denominado MinGit que no incluye ninguna. MinGit ("minimal Git"), que se introdujo con G4W 2.10, es una API portátil con un conjunto de características reducido diseñada para las aplicaciones de Windows que necesitan interactuar con repositorios GIT. G4W y, por extensión, MinGit, son bifurcaciones del proyecto de código abierto de GIT. Esto significa que ambos heredan las correcciones y actualizaciones oficiales de GIT en cuanto están disponibles, y garantiza que Visual Studio pueda hacer lo mismo.

Para acceder a la CLI de GIT (y seguir conmigo), recomiendo instalar el paquete de G4W completo. Aunque existen otras opciones de herramientas de CLI/GUI de GIT, G4W (como elemento primario oficial de MinGit) es una sabia elección, especialmente porque comparte sus archivos de configuración con MinGit. Para obtener la configuración de G4W más reciente, visite la sección Descargas en el origen oficial del sitio: git-scm.com. Ejecute el programa de instalación y seleccione la casilla Git Bash Here (crea una ventana de símbolo del sistema GIT) y la casilla Git GUI Here (crea una ventana de GUI de GIT), lo que permite hacer clic con el botón derecho fácilmente en una carpeta en el Explorador de Windows. A continuación, seleccione una de las dos opciones para la carpeta actual ("Bash" en Git Bash hace referencia a Bourne Again Shell, que presenta una CLI de GIT en un shell de Unix para G4W). A continuación, seleccione Use Git (Usar GIT) en el símbolo del sistema de Windows, que configura su entorno para que pueda ejecutar cómodamente los comandos GIT desde una consola del Administrador de paquetes de Visual Studio (Windows PowerShell) o una ventana del símbolo del sistema.

Si G4W se instala mediante las opciones que recomiendo aquí (consulte la Figura 1), las rutas de comunicación vigentes al comunicarse con un repositorio GIT: Visual Studio 2017 usa MinGit API, mientras que las sesiones de PowerShell y del símbolo del sistema usan la CLI de G4W, una ruta de comunicación muy diferente de camino al repositorio GIT. Aunque actúan como puntos de conexión de comunicación diferentes, MinGit y G4W derivan del código fuente de GIT oficial, además de compartir sus archivos de configuración. Observe que al emitir comandos de porcelana, estos se traducen en comandos de fontanería antes de que la CLI los procese. El objetivo aquí es comprender que los expertos en GIT suelen recurrir a la emisión de comandos de fontanería de GIT sin sistema operativo a una CLI (y algunos prosperan al hacerlo), ya que esta es la manera más básica y directa para administrar, consultar y actualizar un repositorio GIT. Al contrario que los comandos de fontanería de bajo nivel, los comandos de porcelana de nivel superior y las operaciones de GIT que expone el IDE de Visual Studio también pueden actualizar el repositorio GIT, aunque no siempre queda claro cómo, especialmente porque los comandos de porcelana suelen aceptar opciones que cambian la acción que llevan a cabo cuando se invocan. Llegué a la conclusión de que este conocimiento de los comandos de fontanería de GIT es esencial para usar el potencial de GIT, que es por lo que recomiendo encarecidamente instalar G4W con Visual Studio 2017. (Lea los detalles sobre los comandos de fontanería y porcelana de Git en git-scm.com/docs).

Rutas de acceso de comunicación entre MinGit API y el software GIT de la interfaz de la línea de comandos de Windows
Figura 1 Rutas de acceso de comunicación entre MinGit API y el software GIT de la interfaz de la línea de comandos de Windows

GIT de bajo nivel

Es natural para un desarrollador de Visual Studio intentar aprovechar los conocimientos existentes de un sistema VCS, como Team Foundation Server (TFS), al realizar la transición a GIT. Además, existe una superposición de los términos y los conceptos usados para describir las operaciones en ambos sistemas, como las de extracción de código del repositorio o inserción de código en el repositorio, combinación, ramificación, etc. No obstante, la suposición de que un vocabulario similar implica operaciones subyacentes similares es sumamente errónea y peligrosa. Ello se debe a que el sistema VCS de GIT descentralizado es fundamentalmente diferente en la manera de almacenar archivos y realizar su seguimiento, así como en el modo de implementar características de control de versiones conocidas. En resumen, al realizar la transición a GIT, quizás sea mejor olvidar todo lo aprendido sobre los sistemas VCS centralizados y empezar de nuevo.

Al trabajar en un proyecto de Visual Studio bajo el control del origen de GIT, el flujo de trabajo típico de edición, organización y confirmación es similar al siguiente: Puede agregar, editar y eliminar (en adelante y colectivamente, "cambiar") archivos del proyecto según sea necesario. Cuando esté listo, puede organizar todos o parte de esos cambios antes de confirmarlos en el repositorio. Una vez confirmados, estos cambios pasan a formar parte del historial completo y transparente del repositorio. Ahora, veamos cómo GIT administra todo esto internamente.

Gráfico acíclico dirigido De manera interna, cada confirmación termina como un vértice (nodo) en un gráfico acíclico dirigido administrado por GIT ("DAG" en el lenguaje de la teoría de gráficos). El DAG representa el repositorio GIT y cada vértice representa un elemento de datos conocido como un objeto de confirmación (consulte la** Figura 2**). Los vértices de un DAG están conectados con una línea denominada "arista"; es habitual que las aristas del DAG se dibujen como flechas para que puedan expresar una relación primario-secundario (la punta señala al vértice primario y la cola, al vértice secundario). El vértice de origen representa la primera confirmación del repositorio; un vértice terminal no tiene ningún elemento secundario. Las aristas del DAG expresan la relación primario-secundario exacta entre cada uno de sus vértices. Dado que los objetos de confirmación de GIT ("confirmaciones") son vértices, GIT puede aprovechar la estructura del DAG para modelar la relación primario-secundario entre todas las confirmaciones, lo que proporciona a GIT su capacidad de generar un historial de los cambios desde cualquier confirmación hasta la primera confirmación del repositorio. Además, a diferencia de los gráficos lineales, un DAG admite ramas (un elemento primario con más de uno secundario), además de combinaciones (un elemento secundario con más de uno primario). Se genera una rama de GIT siempre que un objeto de confirmación produce un nuevo elemento secundario y se produce una confirmación cuando los objetos de confirmación se combinan para formar un elemento secundario.

Gráfico acíclico dirigido que muestra el vértice, la arista, la punta, la cola, el vértice de origen y los vértices finales
Figura 2 Gráfico acíclico dirigido que muestra el vértice, la arista, la punta, la cola, el vértice de origen y los vértices finales; tres ramas (A, B y C); dos eventos de rama (en A4); y un evento de combinación (B3 y A5 combinados en A6)

Exploré el DAG y la terminología asociada minuciosamente porque tal conocimiento es un requisito previo para comprender las operaciones de GIT avanzadas, que tienden a realizarse manipulando vértices en el DAG de GIT. Además, los DAG ayudan a visualizar el repositorio GIT y se usan ampliamente en los materiales de enseñanza, durante las presentaciones y en las herramientas de la GUI de Git.

Introducción a los objetos GIT Hasta ahora, solo he mencionado el objeto de confirmación de GIT. No obstante, GIT almacena en realidad cuatro tipos de objetos diferentes en su repositorio: confirmación, árbol, blob y etiqueta. Para investigar cada uno de estos tipos, inicie Visual Studio (yo uso Visual Studio 2017, pero las versiones anteriores que incluyan compatibilidad con GIT funcionarán de manera parecida) y cree una nueva aplicación de consola mediante Archivo | Nuevo proyecto. Asigne un nombre al proyecto, marque la casilla Crear nuevo repositorio GIT y haga clic en Aceptar. (Si no configuró GIT en Visual Studio anteriormente, se mostrará un cuadro de diálogo Información del usuario de GIT. Si lo hizo, especifique su nombre y su dirección de correo electrónico, que están escritos en el repositorio GIT de cada una de sus confirmaciones, y marque la casilla "Establecer en .gitconfig global" si quiere usar esta información para todos los repositorios GIT del equipo).

Cuando termine, abra una ventana Explorador de soluciones (consulte la** Figura 3, Marcador 1**). Verá iconos de candado azul claro junto a los archivos insertados en el repositorio, aunque aún no haya emitido ninguna confirmación. (Este ejemplo muestra que, en ocasiones, Visual Studio lleva a cabo acciones en su repositorio que quizás no tenga previstas). Para ver qué hizo Visual Studio exactamente, observe el historial de cambios de la rama actual.

Nuevo proyecto de Visual Studio con su informe del historial del repositorio GIT
Figura 3 Nuevo proyecto de Visual Studio con su informe del historial del repositorio GIT

GIT denomina a la rama predeterminada rama maestra y la convierte en la actual. Visual Studio muestra el nombre de rama actual en el borde derecho de la barra de estado (Marcador 2). La rama actual identifica el objeto de confirmación en el DAG que será el elemento primario de la siguiente confirmación (encontrará más información sobre las ramas más adelante). Para ver el historial de confirmaciones de la rama actual, haga clic en la etiqueta maestra (Marcador 2) y, luego, en la opción Ver historial (Marcador 3) del menú.

La ventana Historial: maestro muestra varias columnas de información. En el lado izquierdo (Marcador 4) se muestran dos vértices sobre el DAG; cada uno de ellos representa gráficamente una confirmación del DAG de GIT. Las columnas Id., Autor, Fecha y Mensaje (Marcador 5) muestran los detalles de la confirmación. El elemento HEAD de la rama master se indica con un puntero rojo oscuro (Marcador 6). Explicaré con todo detalle su significado al final de este artículo. Este elemento HEAD marca la ubicación de la punta de la flecha de la arista siguiente después de que una confirmación agregue un nuevo vértice al DAG.

El informe muestra que Visual Studio emitió dos confirmaciones, cada una con su propio id. de confirmación (Marcador 7). La primera (anterior) se identifica de manera exclusiva con el id. a759f283; la segunda, con bfeb0957. Estos valores se truncan a partir del Algoritmo hash seguro 1 (SHA-1) hexadecimal completo de 40 caracteres. El algoritmo SHA-1 es una función de hash criptográfica diseñada para detectar daños, para lo cual toma un mensaje, como los datos de confirmación, y crea una síntesis del mensaje, el valor del hash SHA-1 completo, como el id. de confirmación. En resumen, el hash SHA-1 no solo actúa como una suma de comprobación, sino también como un GUID, ya que proporciona aproximadamente 1,46 x 1048 combinaciones únicas. Como muchas otras herramientas de GIT, Visual Studio solo expresa los primeros ocho caracteres del valor completo, ya que estos proporcionan 4300 millones de valores únicos, suficientes para evitar colisiones en el trabajo diario. Si quiere ver el valor SHA-1 completo, mantenga el puntero del mouse sobre una línea en el informe del historial (Marcador 8).

A pesar de que la columna de mensajes del informe Ver historial indica el objetivo declarado de cada confirmación (proporcionado por el autor durante una confirmación), solo es un comentario. Para examinar los cambios reales de una confirmación, haga clic con el botón derecho en una fila de la lista y seleccione Ver detalles de la confirmación (consulte la Figura 4).

Detalles de las dos primeras confirmaciones del repositorio
Figura 4 Detalles de las dos primeras confirmaciones del repositorio

La primera confirmación (Marcador 1) muestra dos cambios: .gitignore y .gitattributes (expliqué estos archivos en mi artículo anterior).  [add] junto a cada uno de ellos indica los archivos agregados al repositorio. La segunda confirmación (Marcador 2) muestra cinco archivos agregados, así como el id. del objeto de confirmación primaria como un vínculo en el que se puede hacer clic. Para copiar el valor SHA-1 completo en el Portapapeles, solo tiene que hacer clic en el menú Acciones y seleccionar Copiar identificador de confirmación.

Implementación del sistema de archivos en un repositorio GIT Para ver cómo GIT almacena estos archivos en el repositorio, haga clic con el botón derecho en la solución (no en el proyecto) en el Explorador de soluciones y seleccione Abrir carpeta en el Explorador de archivos. En la raíz de la solución, verá una carpeta oculta denominada .git (si no la ve, haga clic en Elementos ocultos en el menú Vista del Explorador de archivos). La carpeta .git es el repositorio GIT de su proyecto. Su carpeta objects define el DAG: Todos los vértices del DAG y todas las relaciones primario-secundario entre los vértices se codifican mediante los archivos que representan cada confirmación en el repositorio, empezando por el vértice original (consulte  ). El archivo HEAD de la carpeta .git y la carpeta refs definen las ramas. Vamos a echar un vistazo más detallado a estos elementos .git.

Exploración de objetos GIT

La carpeta .git\objects almacena todos los tipos de objetos GIT: confirmación (para confirmaciones), árbol (para carpetas), blob (para archivos binarios) y etiqueta (alias descriptivo para objetos de confirmación).

Objeto de confirmación Llegó el momento de iniciar una CLI de GIT. Puede usar la herramienta que prefiera (Git Bash, PowerShell o la ventana Comandos). Yo usaré PowerShell. Para empezar, navegue hasta la carpeta .git\objects de la raíz de la solución y, luego, enumere su contenido (Figura 5, Marcador 1). Observará que contiene varias carpetas denominadas con valores hexadecimales de dos caracteres. Para evitar superar el número de archivos que el sistema operativo permite en una carpeta, GIT quita los dos primeros caracteres de cada valor SHA-1 de 40 bytes para crear un nombre de carpeta y, después, usa los 38 caracteres restantes como el nombre de archivo del objeto que se va a almacenar. A modo de ilustración, la primera confirmación de mi proyecto tiene el id. a759f283, de modo que ese objeto aparecerá en una carpeta denominada a7 (los dos primeros caracteres del identificador). Según lo previsto, cuando abro esa carpeta, veo un archivo denominado 59f283. Recuerde que todos los archivos almacenados en estas carpetas de nombre hexadecimal son objetos GIT. Para ahorrar espacio, GIT comprime por zlib los archivos del almacén de objetos. Dado que este tipo de compresión produce archivos binarios, no podrá ver estos archivos con un editor de texto. En su lugar, deberá invocar comandos GIT que puedan extraer correctamente datos de objetos GIT y presentarlos con un formato que pueda resumir.

Exploración de objetos GIT mediante una interfaz de línea de comandos de GIT
Figura 5 Exploración de objetos GIT mediante una interfaz de línea de comandos de GIT

Sé que el archivo 59f283 contiene un objeto de confirmación porque es un id. de confirmación. No obstante, en ocasiones verá un archivo en la carpeta de objetos sin saber qué es. GIT proporciona el comando de fontanería cat-file para notificar el tipo de un objeto, así como su contenido (Marcador 3). Para obtener el tipo, especifique la opción -t (tipo) al invocar el comando, junto con los pocos caracteres únicos del nombre de archivo del objeto GIT:

git cat-file -t a759f2

En mi sistema, esto indica el valor "commit", que indica que el archivo que comienza por a759f2 contiene un objeto de confirmación. Especificar solo los primeros cinco caracteres del valor hash SHA-1 suele ser suficiente, pero puede especificar tantos como quiera (no olvide agregar los dos caracteres del nombre de la carpeta). Al emitir el mismo comando con la opción -p (impresión con sangría), GIT extrae información del objeto de confirmación y la presenta en formato de lenguaje natural (Marcador 4).

Un objeto de confirmación se compone de las siguientes propiedades: Parent Commit ID, Tree ID, Author Name, Author Email Address, Author Commit Timestamp, Committer Name, Committer Email Address, Committer Commit Timestamp y Commit Message (el id. de confirmación primaria no se muestra para la primera confirmación del repositorio). El valor SHA-1 de cada objeto de confirmación se calcula a partir de todos los valores incluidos en estas propiedades de objeto de confirmación, lo que prácticamente garantiza que cada objeto de confirmación tendrá un id. de confirmación único.

Objetos de árbol y blob Observe que, a pesar de que el objeto de confirmación contiene información sobre la confirmación, no contiene archivos ni carpetas. En su lugar, tiene una propiedad Tree ID (también un valor SHA-1) que apunta a un objeto de árbol GIT. Los objetos de árbol se almacenan en la carpeta .git\objects, junto con todos los demás objetos GIT.

La Figura 6 representa el objeto raíz del árbol que forma parte de cada objeto de confirmación. El objeto raíz del árbol se asigna, a su vez, a objetos de blob (que explicaré a continuación) y a otros objetos de árbol, según sea necesario.

Visualización de objetos GIT que expresan una confirmación
Figura 6 Visualización de objetos GIT que expresan una confirmación

Dado que la segunda confirmación de mi proyecto (id. de confirmación bfeb09) incluye archivos, además de carpetas (consulte el punto** 4** anterior), la usaré para ilustrar cómo funciona el objeto de árbol. La** Figura 7, Marcador 1** muestra la salida bfeb09 del archivo .cat ‑p. Esta vez, observe que incluye una propiedad primaria, que hace referencia correctamente al valor SHA-1 del primer objeto de confirmación. (Recuerde que es una referencia primaria del objeto de confirmación, que permite a GIT construir y mantener su DAG de confirmaciones).

Uso de la CLI de GIT para explorar los detalles del objeto de árbol
Figura 7 Uso de la CLI de GIT para explorar los detalles del objeto de árbol

El objeto raíz del árbol se asigna, a su vez, a objetos de blob (archivos comprimidos por zlib) y otros objetos de árbol, según sea necesario.

La confirmación bfeb09 contiene una propiedad de árbol con el id. ca853d. En la** Figura 7, Marcador 2** se muestra la salida ca853d del archivo .cat ‑p. Cada objeto de árbol contiene una propiedad de permisos que se corresponde con la máscara de permisos POSIX del objeto (040000 = directorio, 100644 = archivo no ejecutable normal, 100664 = archivo grabable por grupo no ejecutable normal, 100755 = archivo ejecutable normal, 120000 = vínculo simbólico y 160000 = Gitlink); tipo (árbol o blob); SHA-1 (para el árbol o el blob); y nombre. El nombre es el nombre de carpeta (para los objetos de árbol) o el nombre de archivo (para los objetos de blob). Observe que este objeto de árbol se compone de tres objetos de blob y otro objeto de árbol. Puede observar que los tres blobs hacen referencia a los archivos .gitattributes, .gitignore y DemoConsole.sln, y que el árbol hace referencia a la carpeta DemoConsoleApp (Figura 7, Marcador 3). Aunque el objeto de árbol ca853d está asociado con la segunda confirmación del proyecto, sus dos primeros blobs representan los archivos .gitattributes y .gitignore, que se agregaron durante la primera confirmación (consulte la** Figura 4, Marcador 1**). El motivo por el que estos archivos aparecen en el árbol de la segunda confirmación es que cada confirmación representa el objeto de confirmación anterior junto con los cambios capturados por el objeto de confirmación actual. Para profundizar un nivel más en el árbol, en la Figura 7, Marcador 3 se muestra la salida cat-file -p a763da, que incluye tres blobs más (App.config, DemoConsoleApp.csproj y Program.cs) y otro árbol (carpeta Propiedades).

Una vez más, los objetos de blob son como archivos comprimidos por zlib. Si el archivo descomprimido contiene texto, puede extraer todo el contenido de un blob con el mismo comando cat-file y el id. del blob (Figura 7, Marcador 5). Dado que los objetos de blob representan archivos, GIT usa el id. de blob SHA-1 para determinar si un archivo cambió desde la confirmación anterior; también usa valores SHA-1 para diferenciar dos confirmaciones cualesquiera del repositorio.

Objeto de etiqueta La naturaleza alfanumérica críptica de los valores SHA-1 puede resultar un poco incómoda de comunicar. El objeto de etiqueta permite asignar un nombre descriptivo a cualquier objeto de confirmación, árbol o blob, aunque lo más habitual es etiquetar solo objetos de confirmación. Existen dos tipos de objeto de etiqueta: ligero y anotado. Ambos tipos aparecen como archivos en la carpeta .git\refs\tags, donde el nombre del archivo es el nombre de la etiqueta. El contenido de un archivo de etiqueta ligero es el valor SHA-1 para un objeto de confirmación, árbol o blob existente. El contenido de un archivo de etiqueta de anotación es el valor SHA-1 para un objeto de etiqueta, que se almacena en la carpeta .git\objects junto con todos los demás objetos GIT. Para ver el contenido de un objeto de etiqueta, use el mismo comando cat-file -p. Verá el valor SHA-1 del objeto etiquetado, junto con el tipo de objeto, el autor, la fecha y hora, y el mensaje de la etiqueta. Existen varias maneras de etiquetar confirmaciones en Visual Studio. Una de ellas es hacer clic en el vínculo Crear etiqueta de la ventana Detalles de la confirmación ( ). Los nombres de etiqueta aparecen en la ventana Detalles de la confirmación (, Marcador 3) y en los informes Ver historial (consulte , Marcador 9 más arriba).

GIT rellena la información y empaqueta las carpetas en la carpeta .git\objects al aplicar optimizaciones de almacenamiento a objetos en el repositorio. Explicaré estas carpetas y las optimizaciones de almacenamiento de archivos de GIT con más detalles en un artículo futuro.

Ahora que conoce los cuatro tipos de objetos GIT, observe que GIT se conoce como un sistema de archivos direccionable por contenido, porque cualquier tipo de contenido en cualquier número de archivos y carpetas se puede reducir a un solo valor SHA-1. Ese valor SHA-1 se puede usar posteriormente para volver a crear el mismo contenido de manera precisa y fiable. Dicho de otro modo, el valor SHA-1 es la clave y el contenido es el valor en una implementación elevada de la generalmente prosaica tabla de búsqueda controlada por el índice de la clave. Además, GIT puede economizar si el contenido del archivo no cambia entre las confirmaciones, ya que un archivo sin cambios produce el mismo valor SHA-1. Esto significa que el objeto de confirmación puede hacer referencia al mismo valor de id. de blob o árbol SHA-1 usado en una confirmación anterior sin tener que crear nuevos objetos, de modo que no se crean copias de los archivos.

Bifurcaciones

Para comprender realmente qué es una rama de GIT, debe controlar cómo GIT define internamente una rama. Básicamente, esto se reduce a captar el objetivo de dos términos clave: head y HEAD.

El primero, head (todo en minúsculas), es una referencia que GIT mantiene para cada nuevo objeto de confirmación. Para ilustrar cómo funciona, la Figura 8 muestra varias confirmaciones y operaciones de rama. Para la confirmación 01, GIT crea la primera referencia head del repositorio y la denomina master de forma predeterminada (master es un nombre arbitrario sin ningún significado especial, aparte de ser el predeterminado. Los equipos de GIT suelen cambiar el nombre de esta referencia). Cuando GIT crea una nueva referencia head, crea un archivo de texto en su carpeta ref\heads y coloca el valor SHA‑1 completo del nuevo objeto de confirmación en dicho archivo. Para la confirmación 01, esto significa que GIT crea un archivo denominado master, en el que coloca el valor SHA-1 del objeto de confirmación A1. Para la confirmación 02, GIT actualiza el archivo head master en la carpeta heads, para lo cual quita el valor SHA-1 antiguo y lo reemplaza por id. de confirmación del valor SHA-1 completo de A2. GIT realiza el mismo procedimiento para la confirmación 03: Actualiza el archivo head denominado master en la carpeta heads para que contenga el id. de confirmación completo de A3.

Dos archivos head son mejor que uno: GIT mantiene varios archivos en su carpeta heads junto con un único archivo HEAD
Figura 8 Dos archivos head son mejor que uno: GIT mantiene varios archivos en su carpeta heads junto con un único archivo HEAD

Es posible que haya adivinado correctamente que el archivo denominado master de la carpeta heads es el nombre de rama del objeto de confirmación al que apunta. Curiosamente, un nombre de rama apunta inicialmente a un solo objeto de confirmación en lugar de una secuencia de confirmaciones (explicaré este concepto dentro de un momento).

Observe la sección sobre la creación de ramas y la desprotección de archivos en la** Figura 8**. Aquí, el usuario creó una nueva rama para una característica de vista previa de impresión de Visual Studio. El usuario asignó el nombre feat_print_preview a la rama, la basó en master y activó la casilla Desproteger rama en el panel Nueva rama local de Team Explorer. La activación de la casilla indica a GIT que quiere que la nueva rama se convierta en la actual (lo explicaré dentro de un momento). Entre bastidores, GIT crea un nuevo archivo head en la carpeta heads denominado feat_print_preview, en el que coloca el valor SHA-1 del objeto de confirmación A3. Esto significa que ahora existen dos archivos en la carpeta heads: master y feat_print_preview, que apuntan al objeto A3.

En la confirmación 04, GIT se enfrenta a una decisión: Por lo general, actualizaría el valor SHA-1 para la referencia de archivo de la carpeta heads, pero ahora dicha carpeta contiene dos referencias de archivo. ¿Cuál de ellas debe actualizar? Eso es lo que hace HEAD. HEAD (todo en mayúsculas) es un único archivo en la raíz de la carpeta .git que apunta a un archivo "head" (todo en minúsculas) de la carpeta heads. (Tenga en cuenta que "HEAD" es en realidad un archivo que siempre se denomina HEAD, mientras que los archivos "head" no tienen un nombre concreto). El archivo HEAD de head contiene el id. de confirmación que se asignará como id. primario para el siguiente objeto de confirmación. Desde un punto de vista práctico, HEAD marca la ubicación actual de GIT en el DAG. Puede haber muchos head, pero siempre hay un solo HEAD.

Volviendo a ** 8**, la confirmación 01 muestra que HEAD apunta al archivo head denominado master, que, a su vez, apunta al objeto A1 (es decir, el archivo head master contiene el valor SHA-1 del objeto de confirmación A1). En la confirmación 02, GIT no tiene que hacer nada con el archivo HEAD, porque este ya apunta al archivo master. Sucede lo mismo con la confirmación 03. No obstante, en el paso de creación y desprotección de una nueva rama, el usuario creó una rama y marcó la casilla Desproteger rama para desproteger los archivos de la rama. En respuesta, GIT actualiza HEAD para que apunte al archivo head denominado feat_print_preview en lugar de apuntar a master. (Si el usuario no hubiese activado la casilla Desproteger rama, HEAD seguiría apuntando a master).

Ahora que conoce HEAD, puede ver que la confirmación 04 ya no necesita a GIT para tomar ninguna decisión: GIT simplemente inspecciona el valor de HEAD y observa que apunta al archivo head denominado feat_print_preview. Luego, sabe que debe actualizar el valor SHA-1 en el archivo head feat_print_preview para que contenga el id. de confirmación de B1.

En el paso de desprotección de la rama, el usuario accedió al panel de ramas de Team Explorer, hizo clic con el botón derecho en la rama master y seleccionó Desproteger. En respuesta, GIT desprotege los archivos de la confirmación A3 y actualiza el archivo HEAD para que apunte al archivo head denominado master.

En este punto, debería estar claro por qué las operaciones de rama de GIT son tan rápidas y eficientes: La creación de una nueva rama se reduce a crear un archivo de texto (head) y actualizar otro (HEAD). El cambio de ramas implica la actualización de un solo archivo de texto (HEAD) y, luego, una pequeña disminución del rendimiento mientras los archivos del directorio de trabajo se actualizan desde el repositorio.

Observe que los objetos de confirmación no contienen información de ramas. De hecho, las ramas solo se mantienen en el archivo HEAD y en los distintos archivos de la carpeta heads que sirven como referencias. Sin embargo, cuando los desarrolladores que usan GIT comentan que están en una rama o hacen referencia a una rama, suelen referirse coloquialmente a la secuencia de objetos de confirmación que se origina con master o a partir de una nueva rama formada. En la Figura 2 anterior se muestra lo que muchos desarrolladores identificarían como tres ramas: A, B y C. La rama A sigue la secuencia A1 a A6. La actividad de la rama en A4 produce dos nuevas ramas: B1 y C1. Así, se puede hacer referencia a la secuencia de confirmaciones que empieza con B1 y continúa hasta B3 como "rama B", mientras que la secuencia C1 a C2 se puede denominar "rama C".

Lo importante aquí es no olvidar la definición formal de una rama de GIT: Simplemente, es un puntero a un objeto de confirmación. Además, GIT mantiene punteros de rama para todas las ramas (denominadas heads) y un solo puntero de rama para la rama actual (denominada HEAD).

En mi próximo artículo exploraré los detalles sobre la extracción e inserción de archivos en el repositorio, así como el rol del índice y cómo construye objetos de árbol durante las confirmaciones. También exploraré cómo GIT optimiza el almacenamiento y cómo fusiona y diferencia el trabajo.


Jonathan Waldman es un Microsoft Certified Professional que trabaja con las tecnologías de Microsoft desde su origen. Su especialidad es la ergonomía de software. Waldman es miembro del equipo técnico de Pluralsight y, actualmente, dirige proyectos de desarrollo de software del sector privado e institucional. Puede ponerse en contacto con él a través de la dirección jonathan.waldman@live.com.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Kraig Brockschmidt y Ralph Squillace