summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--es/2.md561
1 files changed, 561 insertions, 0 deletions
diff --git a/es/2.md b/es/2.md
index e69de29..4fc1c71 100644
--- a/es/2.md
+++ b/es/2.md
@@ -0,0 +1,561 @@
+---
+title: GIT
+subtitle: Uso avanzado
+license: CC-BY-NC-SA 4.0
+author: Ekaitz Zarraga - ElenQ Technology
+links-as-notes: true
+lang: spanish
+polyglossia-lang:
+ name: spanish
+how-to: pandoc -f markdown+smart -t beamer % -o pdf/2.pdf --pdf-engine=xelatex --template=./template.tex
+...
+
+## Licencia
+
+- CC-BY-SA 4.0
+- El contenido del documento ha sido adaptado de [**Pro Git (Scott Chacon, Ben
+ Straub)**](https://git-scm.com/book/en/v2).
+
+# Git en el servidor
+
+## Git en el servidor: introducción
+
+Para compartir el trabajo con el resto del equipo de desarrollo suele
+utilizarse un servidor: siempre está disponible, facilita la gestión, etc.
+
+En los servidores suelen utilizarse lo que se conoce como *bare repository*.
+Éstos no tienen *working directory*: sólo tienen directorio `.git`.
+
+- `git init --bare` construye un *bare repository*
+
+> Los *bare repository* suelen nombrarse `loquesea.git` porque sólo contienen
+> la propia carpeta `.git`, pero siguen siendo directorios, a pesar de su
+> extensión.
+
+## Protocolos
+
+- **Local**: un repositorio que está en otro lugar del sistema de archivos de
+ la máquina: `git clone /path/to/file`
+- **HTTP**: tiene modos *smart* o *dumb*, el primero siendo capaz de gestionar
+ permisos.
+- **SSH**: es uno de los modos más utilizados. Necesita unas credenciales:\
+ `git clone ssh://[<user>@]<host>/<project>.git`\
+ También se puede usar el modo corto de SCP: `[<user>@]<host>:<project>.git`
+- **Git**: Similar a SSH pero sin autenticación
+
+## Protocolos — Un ejemplo
+
+Mi [servidor](https://git.elenq.tech/):
+
+- Está accesible desde la Web mediante `cgit`. Hay otras interfaces web como
+ `gitweb`, que viene con el propio Git.
+- Los proyectos públicos se pueden clonar sin problema con el protocolo Git.
+- Para escribir, aporto acceso mediante SSH para tener control de permisos.
+
+*En el libro se cuenta cómo montarlos*
+
+## Workflows con Git
+
+Git es un sistema distribuido, lo que permite muchos workflows distintos. Sin
+embargo, hoy en día se usa sobre todo el workflow *centralizado*, debido al
+éxito de las plataformas web que integran muchas partes del proceso de
+desarrollo de software (incidencias, releases, etc) junto al control de
+versiones: GitHub, GitLab, Gitea...
+
+![Los clientes se sincronizan con el servidor](img/centralized_workflow.png){ height=140px }
+
+## Mediante email
+
+Muchos proyectos de software libre se basan en el email:
+
+- `git format-patch <selector-de-commits>` crea parches a partir de los commits
+ seleccionados
+- `git send-email <parches>` envía los parches por email (requiere de
+ configuración adicional)
+- `git am <parches>` aplica los parches en el proyectos mediante commits
+- `git apply <parches>` aplica los cambios de los parches sin commitear
+
+
+# Herramientas avanzadas de Git
+
+## Selectores de commits
+
+Se pueden aplicar de forma generalizada en muchísimos comandos de Git.
+
+- `git show <selector de commits>` para mostrar los objetos seleccionados.
+
+> Ver la ayuda de `git show`
+
+> Ver la ayuda de `git rev-parse` para leer la sintaxis de los selectores
+
+## Selectores de commits — I
+
+- **Hash SHA-1** el propio identificador de un commit es un selector válido.
+ Como son muy largos pueden abreviarse (ver `git log --abbrev-commit`)
+
+- **Cabezas de Ramas** (también llamadas *head*), son el nombre de la rama y se
+ resuelven al commit al que la rama apunta. Ejemplo `git show <rama>`. Para
+ ver cuál es el commit subyacente `git rev-parse <rama>`. *Cuidado, a veces
+ funcionan como cabeza y otras como la rama completa*
+
+- **El Reflog** es un registro histórico de los cambios aplicados en Git
+ (similar al undo/redo de cualquier programa). Las entradas del Reflog pueden
+ usarse como selectores. Es interesante porque pueden usar el tiempo:
+ `HEAD@{yesterday}`
+
+ - `git reflog` para listar las entradas del *reflog*.
+
+
+## Selectores de commits — II
+
+- **Ancestros** de los commits:
+
+ - El acento circunflejo (`^`) indica el padre del selector utilizado. Por
+ ejemplo: `git show HEAD^`. Los *merge commits* tienen muchos padres, y se
+ puede elegir cuál de ellos con un número después del símbolo `^`.
+
+ - La vírgula (`~`) sirve para obtener los ancestros del selector. Es similar
+ al anterior, pero al añadir un número se elige la generación, no cuál de
+ sus padres. `git show HEAD~2` elige el padre del padre mientras que
+ `git show HEAD^2` elige el segundo padre.
+
+> Usados por separado, son equivalentes
+
+## Selectores de commits — III
+
+- **Rango de commits** indicados mediante `..` seleccionan una lista de commits
+ entre los dos selectores indicados en los extremos.
+ ![](img/double-dot.png){width=350px}\
+ - `git log master..experiment` => D C\
+ - `git log experiment..master` => F E
+
+## Selectores de commits — IV
+
+- Los **puntos triples** actúan como un rango pero seleccionan los commits a
+ los que se puede llegar desde las dos cabezas indicadas pero que las dos
+ ramas no tienen en común.
+ ![](img/double-dot.png){width=350px}\
+ - `git log master...experiment` => F E D C\
+ - Para ver de dónde vienen, `--left-right`:
+ ```
+ git log master...experiment --left-right
+ < F
+ < E
+ > D
+ > C
+ ```
+
+## Selectores de commits — V
+
+- **Puntos múltiples** pueden seleccionarse mediante `--not` o `^` (por
+ delante) para retirar commits de un selector:
+ - Los siguientes comandos son equivalentes:
+ ```
+ git log refA..refB
+ git log ^refA refB
+ git log refB --not refA
+ ```
+ - Los siguientes comandos obtienen los commits de `refA` y `refB` pero
+ descartan los de `refC`:
+ ```
+ git log refA refB ^refC
+ git log refA refB --not refC
+ ```
+
+## Staging interactivo
+
+- `git add --interactive|-i` para gestionar el *stash* de forma interactiva
+- `git add --patch|-p` para seleccionar los cambios manualmente
+
+> Se pueden aplicar en muchos comandos diferentes. Son muy útiles.
+
+
+## El stash
+
+El *stash* es un saco donde guardar cambios. Los cambios no guardados en el
+repositorio pueden guardarse ahí para poder realizar cambios en el repositorio
+y luego recuperarlos, para que no se pierdan en el proceso.
+
+- `git stash [push]` introduce los cambios en el stash y limpia el directorio
+ de trabajo
+- `git stash list` muestra las entradas del stash
+- `git stash apply [<entrada del stash>]` aplica los cambios de la entrada del
+ stash seleccionada (por defecto la última) en el *working directory*.
+- `git stash drop [<entrada del stash>]` borra la entrada del stash
+ seleccionada
+- `git stash pop` = `git stash apply` + `git stash drop` (sólo aplica el `drop`
+ si el `apply` ha ido bien)
+
+Se usan mensajes *push* y *pop* porque las entradas del stash funcionan como
+una pila (*stack*)
+
+## El stash — opciones interesantes
+
+- `git stash apply --index` introduce los cambios en la *staging area* (también
+ se llama *index*). Por defecto no lo hace.
+- `git stash --keep-index` los archivos indexados (en el *staging area*) no se
+ mandan al stash, se mantienen en el *index*
+- `git stash --include-untracked|-u` los archivos no-trackeados también se
+ mandan al *stash*.
+- `git stash --all|-a` los archivos ignorados (`.gitignore`) también se envían
+ al *stash*
+- `git stash --patch|-p` para seleccionar los cambios a enviar al *stash* de
+ forma interactiva
+- `git stash branch <rama> [<entrada del stash>]` crea una rama nueva y aplica
+ el *stash* en ella. Si todo va bien borra la entrada del *stash* más tarde.
+
+## Para limpiar el directorio
+
+- `git stash --all` limpia el directorio enviando todo al *stash*. Luego se
+ puede eliminar haciendo `drop`
+
+- `git clean` hace esto mismo, sin pasar por el *stash*.
+ - `-f` (*force*) es necesario porque si no sólo aplica un *dry-run* (una
+ muestra de lo que pasaría sin llegar a aplicarlo)
+
+> CUIDADO: aplicar siempre el `--dry-run|-n` en `git clean` primero por si
+> acaso.
+
+## Firmas
+
+Para evitar commits falsos, se pueden firmar por GPG.
+
+> GPG (Gnu Privacy Guard), es una implementación de PGP (Pretty Good Privacy)
+
+Hay que configurar `user.signkey`
+
+- `git commit -S` para firmar el commit
+- `git tag -s` para firmar etiquetas
+- `git merge --verify-signatures` comprueba las firmas en el merge
+- `git tag -v` comprueba las firmas en una etiqueta
+
+Si se utilizan firmas, todo el equipo de desarrollo debe utilizarlas.
+
+## Búsquedas
+
+- `git grep` para hacer búsquedas mediante expresiones regulares. Similar al
+ comando `grep`, pero es más rápido y busca en el index y el histórico
+- `git log` es muy potente para buscar en el histórico
+ - `-S` funciones *pickaxe*
+ - `-L` para ver la evolución de una línea de código o una función
+
+## Reescribir la historia
+
+- `git commit --amend` es como un mini-rebase
+- `git rebase -i|--interactive <selector>` cambia los commits necesarios para
+ llegar al selector.\
+ Abre un editor de texto con los commits seleccionados y permite manipularlos
+ con diversos comandos (los explica en el propio editor) que sirven para
+ fusionar commits, cambiar los mensajes, editar los contenidos... En algunos
+ casos se pausa para editar el contexto, permite continuar mediante
+ `git rebase --continue` o cancelar con `--abort`. El propio proceso es
+ bastante explicativo.
+
+- `git filter-branch` es mucho más poderoso pero menos utilizado. Permite
+ cambiar todos los commits de una rama
+
+## Reset y checkout en profundidad — I
+
+Para entenderlos en detalle es necesario comprender los estados de los archivos
+en profundidad. Hay tres secciones disponibles:
+
+1. HEAD: el snapshot del último commit
+2. Index: el *staging area*, la propuesta del próximo commit
+3. Working directory: el lugar donde realizamos los cambios
+
+![Egoren arteko trantsizioak](img/reset-workflow.png){height=150px}
+
+## Reset y checkout en profundidad — II
+
+- `git reset` tiene tres modos:
+ - `--soft`: Mover la rama apuntada por HEAD
+ - `--mixed`: `--soft` + pasar los cambios al index
+ - `--hard`: `--mixed` + aplica los cambios también en tu *working directory*
+
+## Reset y checkout en profundidad — III
+
+Empezando con un repositorio vacío:
+
+![&nbsp;](img/reset-ex1.png)
+
+## Reset y checkout en profundidad — IV
+
+Pasar un archivo al *index*:
+
+![&nbsp;](img/reset-ex2.png)
+
+## Reset y checkout en profundidad — V
+
+Escribirlo en el repositorio:
+
+![&nbsp;](img/reset-ex3.png)
+
+## Reset y checkout en profundidad — VI
+
+Cambiar un archivo:
+
+![&nbsp;](img/reset-ex4.png)
+
+## Reset y checkout en profundidad — VII
+
+Mandar los cambios al *index*
+
+![&nbsp;](img/reset-ex5.png)
+
+## Reset y checkout en profundidad — VIII
+
+Añadir un commit nuevo:
+
+![&nbsp;](img/reset-ex6.png)
+
+## Reset y checkout en profundidad — IX
+
+Añadir otro commit
+
+![&nbsp;](img/reset-start.png)
+
+## Reset y checkout en profundidad — X
+
+`--soft`:
+
+![&nbsp;](img/reset-soft.png)
+
+## Reset y checkout en profundidad — XI
+
+`--mixed` (es el que se aplica por defecto):
+
+![&nbsp;](img/reset-mixed.png)
+
+## Reset y checkout en profundidad — XII
+
+`--hard`:
+
+![&nbsp;](img/reset-hard.png)
+
+## Reset y checkout en profundidad — XIII
+
+Además de las tres opciones, se le pueden entregar archivos a `git reset`.
+
+En este caso, el primer paso (mover el HEAD) no puede aplicarse del todo[^head]
+pero el resto pueden aplicarse sin problemas. Esto habilita funcionamientos
+interesantes:
+
+- `git reset <archivo>` realmente hace\
+ `git reset --mixed HEAD <archivo>`\
+ 1. ~~Mueve el HEAD~~
+ 2. Coloca el estado del archivo en el index
+\
+ Esto es: **quita el archivo del staging area**
+
+[^head]: El HEAD no se puede mover a medias, o se mueve el repositorio completo
+ o no se mueve.
+
+## Reset y checkout en profundidad — XIV
+
+A nivel gráfico:
+
+![&nbsp;](img/reset-path1.png)
+
+## Reset y checkout en profundidad — XV
+
+En un ejemplo más complejo: `git reset <commit> -- <file>`
+
+1. El HEAD no se puede mover
+2. Se mueve el estado que `<file>` tenía en `<commit>` al index (`--mixed`).
+
+![&nbsp;](img/reset-path3.png){height=220px}
+
+## Reset y checkout en profundidad — XVI
+
+Puede usarse este concepto para hacer un *squash* (combinar muchos commits en
+uno) fácilmente [^rebase-interactive].
+
+- `git reset --soft HEAD~<N>` lleva HEAD hacia atrás, manteniendo los cambios
+ en el *index*. Después, se puede hacer `git commit` para aplicar los cambios
+ que quedaron en el *index*, todos de una vez. En **un solo commit**.
+
+[^rebase-interactive]: También se puede hacer con `git rebase --interactive|-i`
+
+## Reset y checkout en profundidad — XVII
+
+Gráficamente:
+
+![&nbsp;](img/reset-squash-r1.png)
+
+## Reset y checkout en profundidad — XVIII
+
+Mover el HEAD manteniendo los cambios en el index:
+
+![&nbsp;](img/reset-squash-r2.png)
+
+## Reset y checkout en profundidad — XIX
+
+Añadir el commit con los cambios que quedaron en el index
+
+![&nbsp;](img/reset-squash-r3.png)
+
+## Reset y checkout en profundidad — XX
+
+`checkout` y `reset` son similares pero no son iguales:
+
+- `git checkout` no manipula el HEAD, sino que lo reasigna a la referencia que
+ se le indique, sin transformar la rama subyacente.
+
+- `git checkout <branch>` y `git reset --hard <branch>` son casi iguales, pero
+ el checkout no pisa el directorio de trabajo directamente.
+
+- `git checkout <archivo>` **pisa el estado del archivo** en el *working
+ directory*, parecido a `git reset --hard`. CUIDADO
+
+A ambos se les puede enviar `--patch` para realizarlos por partes.
+
+## Reset y checkout en profundidad — XXI
+
+Efecto en el HEAD:
+
+![&nbsp;](img/reset-checkout.png)
+
+
+## Merges avanzados, gestión de conflictos — I
+
+Antes de hacer un *merge* es interesante limpiar el *working directory* (`git
+stash`), de este modo, si algo falla es fácil volver atrás.
+
+- `git merge --abort` deshace el merge en caso de conflicto.
+
+ > CUIDADO: Si hay cambios en el directorio de trabajo, no sabrá abortar.
+
+## Merges avanzados, gestión de conflictos — II
+
+En los conflictos, Git aporta 3 archivos:
+
+1. Stage 1: el *common ancestor* (*base*), el commit al que se puede acceder
+ subiendo por ambas ramas. Para verlo puede usarse `git merge-base`
+2. Stage 2: Nuestra (*ours*) versión, lo que está en nuestra rama
+3. Stage 3: Su (*theirs*) versión, lo que está en la rama que estamos
+ mergeando.
+
+- `git show :<stageN>:<file>` para ver los archivos
+ - `:<stageN>:<file>` obtiene el hash del blob señalado
+
+- `git diff` muestra los cambios:
+ - `git diff -1|--base`
+ - `git diff -2|--ours`
+ - `git diff -3|--theirs`
+
+## Merges avanzados, gestión de conflictos — III
+
+- `git show :<stageN>:<file>` se puede utilizar para obtener el contenido de
+ los diferentes archivos y aplicarlos directamente.
+
+- `git merge-file` puede utilizarse para aplicar el merge a mano. Aplica
+ algoritmos de merging.
+
+## Merges avanzados, gestión de conflictos — IV
+
+Git tiene herramientas mejores:
+
+- `git checkout --conflict` te devuelve al estado del conflicto, con los
+ marcadores, para deshacer los cambios que se hayan podido realizar por error
+ al tratar de corregir el conflicto
+
+- `git checkout --ours/--theirs` para ver uno o otro estado
+
+- `git log --oneline --left-right --merge` facilita mostrar el contexto
+
+- `git diff` muestra un *combined diff* cuando se aplica en un conflicto
+
+- `git show -p|--patch` junto con `--cc` muestra el *combined diff*
+
+## Merges avanzados, gestión de conflictos — V
+
+Este es el aspecto de un *combined diff*:
+
+```
+diff --cc hello.rb
+index 0399cd5,59727f0..0000000
+--- a/hello.rb
++++ b/hello.rb
+@@@ -1,7 -1,7 +1,7 @@@
+ #! /usr/bin/env ruby
+
+ def hello
+- puts 'hola world'
+ - puts 'hello mundo'
+++ puts 'hola mundo'
+ end
+```
+
+Muestra dos columnas con símbolos `+` y `-`, mostrando qué ha ocurrido en cada
+lado del merge.
+
+## Merges avanzados, gestión de conflictos — VI
+
+Puede ser interesante usar `diff3` para los conflictos. Por defecto se usa
+`merge`. `diff3` muestra una parte adicional en el conflicto, que muestra la
+*base*.
+
+```
+<<<<<<< ours
+ puts 'hola world'
+||||||| base
+ puts 'hello world'
+=======
+ puts 'hello mundo'
+>>>>>>> theirs
+```
+
+- `git checkout --conflict=diff3` para mostrar el conflicto en 3 partes
+
+- `git config --global merge.conflictstyle diff3` recomiendo configurarlo para
+ que funcione así siempre
+
+
+## Deshaciendo merges — I
+
+Dos modos:
+
+1. `git rest --hard HEAD~` (sobreescribe el histórico)
+2. `git revert -m 1 HEAD` añade un commit que deshace los cambios. Esto da
+ problemas a la hora de volver a combinar la rama.
+
+
+## Deshaciendo merges — II
+
+`git revert -m 1 HEAD` añade un commit que deshace los cambios. `^M` y `C6`
+tienen los mismos contenidos, pero los cambios en `topic` pueden accederse
+desde `master`, para Git, ambas están mergeadas. CUIDADO
+
+![](img/undomerge-revert.png)
+
+## Deshaciendo merges — III
+
+Lo que es peor, si se siguen añadiendo cambios en `topic` y se intenta hacer un
+merge nuevo, Git no añadirá los cambios previos, pensando que ya fueron
+combinados (y lo fueron) pero sin saber que se deshicieron. CUIDADO
+
+![](img/undomerge-revert2.png)
+
+## Deshaciendo merges — IV
+
+Para evitar el problema, hay que deshacer de nuevo el commit que se deshizo.
+Esto puede hacerse con un nuevo `revert`.
+
+![](img/undomerge-revert3.png)
+
+Ahora todos los cambios de `topic` se verán en `master`.
+
+## Preferencias en los merges
+
+`git merge` tiene muchas opciones y estrategias:
+
+- `-X` añade opciones. Por ejemplo `-Xours` resuelve los cambios a nuestro
+ favor.
+
+- `-s` selecciona estrategias. Por ejemplo `-s ours` en lugar de hacer un merge
+ sólo se queda con nuestros cambios. Es interesante para engañar a Git.
+
+Las estrategias no son lo mismo que las opciones. Las estrategias *seleccionan*
+qué algoritmo de merge utilizar. Las opciones *configuran* el algoritmo.
+
+## Subtree