summaryrefslogtreecommitdiff
path: root/es/2.md
diff options
context:
space:
mode:
Diffstat (limited to 'es/2.md')
-rw-r--r--es/2.md420
1 files changed, 420 insertions, 0 deletions
diff --git a/es/2.md b/es/2.md
index ae3f093..2afc726 100644
--- a/es/2.md
+++ b/es/2.md
@@ -558,3 +558,423 @@ Las estrategias no son lo mismo que las opciones. Las estrategias *seleccionan*
qué algoritmo de merge utilizar. Las opciones *configuran* el algoritmo.
## Subtree
+
+Los subtrees son un mecanismo para introducir otro proyecto dentro del actual.
+Este sub-proyecto funciona como una rama independiente: se añade como un remoto
+independiente en una rama suelta, que no tiene relación con las otras.
+
+- `git read-tree` para leer el subproyecto e inicializarlo en una rama
+ independiente
+- `git merge -Xsubtree` para combinar (*merge*) la rama del subproyecto en el
+ proyecto actual sin problemas
+- `git diff-tree` para analizar cambios en el subproyecto, un `diff` normal no
+ funcionaría
+
+*No se utiliza mucho* porque hoy en día se usan los *submodules* en su lugar.
+Pero es interesante para ver lo flexible de los métodos de merge.
+
+## Rerere: Reuse Recorded Resolution — I
+
+El rerere es un mecanismo para guardar soluciones a conflictos, y enseñar a Git
+a resolverlos cuando vuelvan a darse. Por ejemplo:
+
+1. Al mergear una rama de test aparecen conflictos
+2. Se arreglan los conflictos
+3. Los tests van mal
+4. Se deshace el merge
+5. Se arreglan los tests
+6. Se vuelve a hacer el merge => los mismos conflictos reaparecen
+
+Con el **rerere** se podía haber guardado la resolución en el paso 1, de esta
+manera al aplicar el paso 6 Git podía haber recordado la solución del paso 1 y
+volver a aplicarla sin necesitar interacción humana.
+
+## Rerere: Reuse Recorded Resolution — II
+
+- `git config --global rerere.enabled true`
+- `git rerere status`
+- `git rerere diff` para ver los cambios que aplicaría rerere
+- `git add` + `git commit` guarda la resolución del conflicto:\
+ `"Recorded resolution"`
+- `git reset --hard HEAD^` volver atrás el en el tiempo, y volver a hacer el
+ merge resuelve el conflicto automáticamente:\
+ `"Resolved with previous resolution"`
+- `git checkout --conflict=merge <file>` recupera el conflicto, desactivando el
+ rerere
+- `git rerere` se puede aplicar después el rerere automáticamente
+
+## Debugging con Git
+
+- `git blame|annotate` muestra qué commit introdujo las líneas del archivo
+ indicado. Las líneas que empiezan con `^` fueron introducidas con la creación
+ del archivo. Usar `-C` intenta buscar los bloques que han sido movidos[^move].
+
+[^move]: Git no guarda record de los movimientos pero aplica algoritmos de
+ búsqueda que pueden calcular este tipo de cambios.
+
+- `git bisect` aplica el [método de la bisección][bisect] para buscar commits
+ que introdujeron errores en el repositorio. Método:
+ - Tomar dos commits: uno con error y otro sin él
+ - Toma un commit en el centro de los dos: si tiene error, el commit que
+ introdujo el error en el repositorio estará entre éste commit y el que no
+ tenía error del paso anterior. Si no tiene error, será al revés.
+ - Con el nuevo rango, repite el algoritmo tomando nuevos commits cada vez,
+ hasta encontrar el commit que introdujo el fallo.
+
+[bisect]: https://en.wikipedia.org/wiki/Bisection_method
+
+## Debugging con Git: `git bisect`o
+
+Pasos a aplicar:
+
+1. `git bisect start` empezar
+2. `git bisect bad` indicar commit actual como erróneo
+3. `git bisect good <commit-id>` indicar último commit que se tiene constancia
+ de que era correcto
+4. `git bisect good/bad` Git elegirá commits y saltará a ellos para que los
+ marquemos como `good` o `bad`:\
+ `"Bisecting, N revisions left to test"`
+5. `git bisect reset` volver al inicio
+
+Se puede automatizar con `git bisect run <command>`. El comando debe retornar
+`0` en casos de acierto y cualquier otro valor cuando falle.
+
+## Submodule
+
+Son un modo interesante para gestionar subproyectos en el repositorio.
+
+- `git submodule add <URL>` crea un nuevo subproyecto e inicializa el archivo
+ `.gitmodules`
+- `.gitmodules` es un archivo que guarda los la información de los
+ subproyectos: la URL, el nombre y el path.
+- `git submodule add ...` seguido de `git diff --cached` muestra que el
+ contenido de un submodulo no se gestiona como un archivo cualquiera:\
+ `"Subproject commit ---"`
+- `git diff --submodule --cached` muestra mejor el contenido
+- `git push` añade el submódulo pero como una referencia a otro repositorio, no
+ como archivo
+
+## Clonar submódulos
+
+- `git clone` **no** clona los submódulos automáticamente
+- `git submodule init` inicializa el control de submódulos
+- `git submodule update` para actualizar los archivos de los submódulos
+- `git clone --recurse-submodule <URL>` descarga automáticamente todos los
+ submódulos (recursivamente) al hacer `clone`. Si no se hace, debe lanzarse
+ `git submodule update --init` o `git submodule update --init --recursive` más
+ tarde.
+
+## Trabajar con submodulos
+
+- `git config --global diff.submodule log` para no tener que añadir cada vez
+ `--submodule` que se hace `git diff`
+- `git submodule update --remote <submodule>` para actualizar los contenidos
+ del submódulo. También se puede hacer `fetch` + `merge` dentro del submódulo.
+- `git config -f .gitmodules submodule.<submodule>.branch <adarra>` para
+ cambiar el submódulo de rama. Equivalente a editar el archivo `.gitmodules`
+- `git config status.submodulesummary 1` para que `git status` muestre el
+ estado de los submódulos.
+- `git log --submodule` para mostrar el listado de cambios de los submódulos
+
+## Publicar cambios de los submódulos
+
+- `git push` dentro del submódulo publica sus cambios (son un repositorio
+ normal)
+- `git push --recurse-submodules=check|on-demand` hay dos estrategias. `check`
+ comprueba si deben pushearse cambios, mientras que `on-demand` pushea
+ automáticamente los submódulos si hay cambios en ellos.
+ - `git config push.recurseSubmodules check|on-demand` para configurarlo por
+ defecto
+
+## Trucos con submódulos
+
+- `git submodule foreach <command>` lanza un comando en cada submódulo.
+ Ejemplo: `git submodule foreach 'git stash'`
+
+
+## Bundle
+
+Son archivos que guardan conjuntos de commits o repositorios completos de modo
+que son fáciles de compartir.
+
+- `git bundle create <file> [<commit selector>]` guarda los commits
+ seleccionados en el archivo `<file>`. Normalmente es interesante introducir
+ `HEAD` para que luego el bundle sepa dónde estaba el repositorio.
+
+- `git clone <bundle-file>` para clonar desde un bundle. Necesita que el bundle
+ contenga todos los commits del repositorio.
+
+- `git bundle verify <bundle-file>` para comprobar si un bundle parcial se
+ puede aplicar en el el repositorio actual
+
+- `git bundle list-heads <bundle-file>` para ver las ramas en el archivo
+
+- `git fetch/pull <bundle-file>` permite aplicar commits desde el archivo
+ bundle
+
+## Replace
+
+La base de datos de Git no puede alterarse, pero con `git replace` puede
+hacerse que parezca que ha cambiado.
+
+- `git replace <object> <replacement>` hace que cuando se use `<object>`
+ funcione como si se hubiese usado `<replacement>` en su lugar.
+
+*No es común, investigar en el libro posibles casos de uso*
+
+## Sistemas de credenciales
+
+Al usar credenciales sobre HTTP Git necesita que todas las operaciones con el
+servidor usen usuario y contraseña. Este comportamiento puede configurarse:
+
+- `git config --global credential.helper <modo>`
+ 1. Pedir siempre (lo hace por defecto))
+ 2. `cache` mantener las credenciales en memoria 15 minutos
+ 3. `store` guardar las credenciales
+
+Hay más opciones (`--timeout`, sistemas de credenciales a medida, etc.)
+
+
+# Configuración avanzada
+
+## Configuración
+
+Ver documentación sobre la configuración `git help git-config`
+
+- Editor y paginador: `core.editor` y `core.pager`
+- Plantillas para commits: `commit.template`
+- Herramientas de merge y diff: `merge.tool` y `diff.tool`
+- Control de espacios en blanco: `core.autocrlf` y `core.whitespace`
+
+## Git attributes — I
+
+Dentro del proyecto, a nivel de archivo, se puede cambiar el comportamiento de
+Git mediante los atributos (*attribute*).
+
+- `.gitattributes` o `.git/info/attributes`, el primero se puede compartir en
+ el repositorio, el segundo no.
+
+El archivo `.gitattributes` es similar a `.gitignore`, además de las plantillas
+que seleccionan los archivos se definen unos atributos para ellos. Muchas veces
+se requiere de configuración adicional para los atributos mediante *drivers*.
+Ver `git help attributes`
+
+## Git attributes — II
+
+Hay muchos atributos diferentes, los más interesantes:
+
+- `binary` para tratar un archivo como binario
+- `diff=<filtro>` pasa el archivo por el filtro indicado antes de hacer diff.
+ Necesita configuración adicional:
+ - `git config diff.<filtro>.textconv <programa>`
+ - Por ejemplo: `*.png diff=exif`
+ - `git config diff.exif.textconv exiftool`\
+ para comparar los resultados de ejecutar `exiftool` sobre el archivo, en
+ lugar de sus contenidos
+- `ident` añade identificación. Sustituye el texto en `$Id$` por el SHA-1 del
+ commit actual.
+
+## Git attributes — III
+
+- `filter` pasa los archivos por un filtro
+ - `smudge` aplica el filtro cuando el archivo pasa del *index* al *working
+ directory*
+ - `clean` en el camino opuesto
+ - Ejemplo: `*.c filter=ident`
+ - `git config filter.ident.clean indent`\
+ `git config filter.ident.smudge cat`\
+ indenta los archivos automáticamente al hacer `git add` (a la vuelta
+ no aplica nada)
+- `export-ignore` ignora archivos al hacer un archivado del repositorio (`git
+ archive`)
+- `export-subst` aplica las expansiones que `git log` aplicaría y sustituye
+ patrones en los archivos
+- `merge` selecciona estrategias de merge por archivo
+
+## Git hooks
+
+Los hooks son programas que se ejecutan en momentos importantes del uso del
+repositorio. Se usan muy a menudo.
+
+- `.git/hooks` es el directorio donde se guardan, deben usar un nombre en
+ particular y tener permisos de ejecución
+
+Por defecto, se instalan varios hooks al hacer `git init` pero están
+desactivados con `.sample` al final de su nombre. Se instalan para servir de
+muestra.
+
+## Hooks del lado del cliente
+
+Para uso privado. **No se copian junto al repositorio**[^compartir-hooks]. Son
+interesantes como alertas para quien use el repositorio: que los tests no
+pasan, que hay un problema en los formatos, etc. Algunos interesantes:
+
+- `pre-commit` se ejecuta antes de escribir el mensaje del commit. Se suele
+ utilizar para comprobar si se ha olvidado algo.
+- `prepare-commit-msg` procesa el mensaje de commit por defecto antes de
+ mostrárselo al usuario. Sirve para añadir información adicional en el
+ mensaje, cambiar la plantilla, etc.
+- `commit-msg` procesa el contenido el mensaje del commit. Si el mensaje del
+ commit no es correcto puede rechazarse. Se utiliza para asegurar que el
+ mensaje de commit usa la forma adecuada.
+
+[^compartir-hooks]: Hay herramientas que facilitan el intercambio de hooks,
+ pero no están incluidas en Git.
+
+## Hooks del lado del servidor
+
+Son interesantes para imponer políticas de desarrollo, ya que pueden rechazar
+`push`es si estas políticas no se cumplen. Algunos interesantes:
+
+- `pre-receive` se lanza al recibir un `push`, recibe todas las referencias y
+ si da error no se actualizarán los datos del servidor
+- `update` similar al anterior, pero se ejecuta una vez por cada rama
+- `post-receive` se ejecuta después de haber recibido todo bien, puede usarse
+ como notificación
+
+
+# Git por dentro
+
+## Git por dentro: introducción
+
+Git es una herramienta de bajo nivel y tiene comandos para trabajar a este
+nivel conocidos como *plumbing*. Los que se han utilizado hasta ahora se
+conocen como *porcelain*[^baño].
+
+Git es una base de datos *content-addressed*. Sus elementos se guardan por
+clave y valor (*key-value*) donde la clave se obtiene del valor. Hasta ahora se
+ha visto: se usan los hashes SHA-1 de los contenidos del commit para
+identificarlos.
+
+El contenido de la base de datos se almacena en el directorio `.git`. Donde se
+almacenan elementos de diferentes tipos.
+
+[^baño]: En un baño, la porcelana está por fuera, las tuberías por dentro
+
+## Tipos de datos
+
+Hay dos tipos de dato principales en la base de datos de Git:
+
+- Objetos: que tienen contenidos: commits, blobs, árboles, etc.
+- Referencias: son elementos que apuntan a otros: ramas, etiquetas ligeras,
+ etc.
+
+## Objetos
+
+Se almacenan en `.git/objects` usando sus hashes como identificador. Los
+primeros dos caracteres como nombre de directorio.\
+Por ejemplo: `.git/objects/cf/6cbb8a400c7cad0f7f93610366c3672f598cdd`
+
+- `git hash-object -w` para escribir nuevos contenidos, devuelve el hash del
+ objeto escrito
+- `git cat-file` para leerlos, `-p` para mostrarlo bonito.
+
+## Tipos de objetos
+
+`git cat-file -t` muestra el tipo
+
+- `blob` para guardar los contenidos de los archivos u otro tipo de datos
+- `tree` para guardar directorios o los nombres de los archivos\
+ Contienen entradas que apuntan a otros `tree`s o `blob`s identificándolos por
+ su hash.
+- `commit` para guardar los datos de los commits. Contiene: un `tree` con el
+ snapshot, el mensaje, la autoría, etc.
+
+## Tipos de objetos: tree
+
+![](img/data-model-1.png)
+
+## Tipos de objetos: commit
+
+![](img/data-model-3.png)
+
+## Referencias
+
+Sólo contienen un identificador
+
+- `git update-ref` puede actualizar su contenido. También se puede realizar
+ manualmente
+
+Hay diferentes tipos de referencias:
+
+- Tag: Se guardan en `.git/refs/tag`. Cualquier objeto puede etiquetarse
+ (normalmente sólo se hace con los commits). Hay dos tipos:
+ - Lightweight tag: sólo son una referencia
+ - Annotated tag: son un **objeto** y una referencia a éste
+- Ramas: en `.git/refs/heads`. Hacen referencia a un commit
+- Remotos: en `.git/refs/remotes` similares a las ramas pero no se pueden
+ cambiar.
+- HEAD: `.git/HEAD`. Apunta a una rama, pero puede apuntar a un commit
+ (*detached HEAD*).
+
+## Referencias: ramas
+
+![](img/data-model-4.png)
+
+## Ejercicio: notas
+
+- `git notes` gestiona las notas
+
+*Ver qué son, y cómo funcionan: `git help git-notes`*
+
+## Packfiles
+
+Para que el almacenamiento de archivos sea más eficiente Git utiliza sistemas
+de compresión de archivos, pero no es suficiente.
+
+Git se basa en snapshots, pero no guarda los archivos una y otra vez en cada
+commit, sino que guarda sólo aquellos que han cambiado, y por partes.
+
+Además, agrupa los archivos en *packfile*s para agilizar el proceso.
+
+- `.git/objects/pack`
+- `git gc` (*garbage colector*) agrupa los archivos y los comprime (git lo
+ ejecuta automáticamente)
+- `git verify-pack` comprueba el contenido de un packfile
+
+## Mantenimiento y recuperación de datos
+
+- `git gc --auto` reorganiza los packfiles
+- `git reflog` almacena los cambios realizados en HEAD haciendo que los commits
+ perdidos puedan recuperarse desde éste.
+- `git fsck --full` muestra los elementos perdidos, permitiendo también la
+ recuperación.
+
+*EJERCICIO: si se añade un archivo grande por accidente, `git rm` no lo
+elimina, por lo que se convierte en un problema. ¿Cómo se borra? ¿Por qué?*
+
+## El refspec
+
+Para gestionar el mapeo entre las ramas remotas y locales. Éste es el contenido
+de `.git/config` tras hacer
+`git remote add origin https://github.com/schacon/simplegit-progit`:
+
+```
+[remote "origin"]
+ url = https://github.com/schacon/simplegit-progit
+ fetch = +refs/heads/*:refs/remotes/origin/*
+```
+
+El bloque `fetch` indica mediante el refspec cómo se deben gestionar las ramas.
+Puede haber muchas entradas, con diferentes reglas. Formato:
+
+- Un `+` indica (opcional) si deben actualizarse incluso cuando no es
+ *fast-forward*
+- `<fuente>:<destino>` indica cómo mapear las ramas. Ambos son patrones. Puede
+ usarse `*` para indicar «todo»
+
+## El refspec manualmente
+
+Pueden usarse patrones al hacer `fetch` o `push` para indicar este aquí y este
+allá.
+
+Git aplica expansiones basándose en el refspec:
+
+- `git log origin/master`\
+ => `git log remotes/origin/master`\
+ => `git log refs/remotes/origin/master`
+
+Antes, para borrar las ramas había que usar el refspec:
+
+- `git push :<rama>` al añadir la fuente vacía, en el remoto se borra la rama
+- Ahora puede usarse `git push --delete <rama>`