Guia GIT 2.0

Table of Contents

1. Libros

2. Comandos utiles

# mostrar todos las ramas (branches) del repositorio (local y remoto?)
git branch --all

# mostrar los logs en una sola linea, y lo muestra de una manera entendible
git log --oneline --decorate -graph

# apuntar a un cambio
git checkout hashCommit

3. Glosario

3.1. Head

  • Es un puntero que guarda la referencia del branch activo (el que se esté utilizando)
  • En el repositorio remoto origin el puntero HEAD apunta a la rama master
  • En nuestro repositorio local, se puede cambiar la referencia haciendo git checkout nombreRama

3.2. Track

3.2.1. Conceptos

  • Su traducción al español es rastrear ó podría ser también en seguimiento
  • Son todos los archivos (modificados/sin modificar) que aparecen en el staging area ó area de stage
  • Se hace un seguimiento de los cambios que sufran esos archivos
  • Los untracked files son los que aún NO están en el área de stage

Observaciones:

  1. El mensaje Cambios no rastreados para el commit es igual a Changes not staged for commit:
    • hace referencia a los cambios de archivos que están en el staging area
    • son tracked files archivos rastreados
  2. El mensaje Cambios a ser confirmados: es igual a Changes to be committed:
    • hace referencia a los archivos que están en el staging area
    • son cambios pendientes a confirmar (commit)
  3. El mensaje Archivos sin seguimiento:
    • hace referencia a los archivos que NO están en el staging area
  1. Clonamos nuestro repositorio

todos los archivos estarán por defecto rastreados (tracked) y sin modificar

  1. Modificamos los archivos del repositorio

tendrán cambios "no rastreados" (se da cuenta, porque compara con el último commit) son cambios pendientes a agregar al area de stage piden a gritos agregar los cambios con git add

  1. Creamos nuevos archivos, y aún no ejecutamos git add

los archivos NO estarán en el "area de stage" (unstaged files) (se da cuenta, porque compara con el último commit y ve que esos archivos NO estaban) por tanto los cambios de esos archivos no serán rastreados y serán (untracked files)

  1. Agregamos los archivos al "area de Stage" con git add los archivos serán rastreados, osea tendrán seguimiento de sus cambios

(comparando con el último commit, para ver si hubo cambios)

3.2.2. Posibles escenarios

# todos los archivos son "rastreados" y en "staging area"
git clone urlrepositorio

# si creamos archivos, pero no los agregamos con "git add"
# su estado es: untracked files (archivos sin seguimiento), y NO estarán en el "área de stage"
touch promociones.txt descuentos.txt

# cambiamos el estado a "tracked files", y pasando al "área de stage"
# cambios pendientes de confirmar (commit)
git add .

# si modificamos un archivo rastreado
# pasa a un estado "unstaged changes" osea "cambios no rastreados, pendientes a agregar al area de stage"
#
# Obs: no confundir con el archivo no rastreado osea "untracked file",
# este nuevo estado hace referencia a los cambios
echo "descuentos para adultos" >> descuentos.txt

# si borramos un archivo rastreado,
# cambia de estado a "untracked files", se borra del "staging area"
# (el archivo no se borra del directorio)
git rm --cached promociones.txt

3.2.4. Referencias Extraoficiales

3.3. Stage

  • También conocido como Index ó Staging Area
  • Su traducción al español es Area de preparación
  • Es el paso previo a la confirmación (commit)
  • Son los archivos que agregamos con el comando git add y sus cambios serán rastreados (se hará un seguimiento)
  • Los archivos del area de preparación pueden tener dos estados

    • Unstaged changes: Si los modificamos
    • Staged changes: Si no los modificamos
    • Unstaged changes y Staged changes (osea ambos estados): Si los modificamos, los agregamos con git add y los volvemos a modificar y NO hacemos git add
    History
    - commits
    Stage (Index)
    - staged files
    - tracked files
    - unstaged changes
    - staged changes
    Working Directory/Tree
    - unstaged files
    - untracked files

3.4. Tracking Branch

3.4.1. Conceptos

  • Cuando creamos una rama local a partir de una rama remota con git checkout la rama local se convierte en una rama de seguimiento porque guarda una referencia hacia la rama remota.
  • Si queremos hacer un seguimiento de una rama remota se utiliza git checkout --track remoto/nombreRama

3.5. MERGEHEAD

3.5.1. Conceptos

  • MERGEHEAD está presente siempre que hay una fusión (merge) en curso.
  • El comando git merge --abort es equivalente a git reset --merge cuando MERGEHEAD está presente.

3.5.2. Referencias Web Oficiales

3.6. Detached Head

3.6.1. Conceptos

  1. El detached head ó head desacoplado sucede cuando el head apunta a una confirmación (commit) que no es la última,

es decir alguna de las confirmaciones (commits) anteriores al último (al que debería apuntar HEAD)

  1. Si queremos ir a una confirmación anterior podes utilizar git checkout HEAD~n (siendo n cualquier número natural, y representa cuantas confirmaciones hacia atrás queremos ir respecto de HEAD)
  2. Podemos llegar a detached head desde cualquier rama (osea, no importa que hagamos git checkout fix/precios y no estemos en la rama master)

3.6.2. Posibles Escenarios

# hacemos que HEAD apunte al commit anterior del último commit
# - alternativa sería agregar el hash del commit que mencionamos
# Obs: master^ es equivalente a master~ y HEAD^ y también HEAD~
git checkout master^

# si revisamos el estado del directorio de trabajo
# dirá "HEAD está desacoplada en b30b31"
# (suponiendo que b30b31 es el hash del anteúltimo commit)
git status
# hacemos que HEAD apunte 3 commits atrás, respecto de donde apuntaba HEAD
git checkout HEAD~3

# si revisamos el estado del directorio de trabajo, dirá que HEAD está desacoplado
git status

# si revisamos el registro de commits, desaparecerán los últimos 3 cambios
git log --oneline --decorate --graph

# hacemos que HEAD apunte 2 commits atrás, respecto del nuevo commit al que apuntaba HEAD
git checkout HEAD~2
# hacemos que HEAD del master apunte 2 commits atrás,
# respecto del último al apuntaba HEAD en la rama master
git checkout master~2

# si revisamos el estado del directorio de trabajo, dirá que HEAD está desacoplado
git status

# si revisamos el registro de commits,
# desaparecerán los últimos 2 cambios que hicimos en la rama master
git log --oneline --decorate --graph

3.6.3. Posibles Soluciones

  • Si estamos en un head desacoplado se recomienda confirmar (commit) los cambios que hagamos, y crear una rama del cambio
git checkout HEAD~3

# generamos algunos cambios
touch producto-{1,2,3}.txt

# agregamos los cambios, y confirmamos
git add . && git commit -m "Agregamos cambios olvidados?"

# creamos la rama y le asignamos el hash del commit 192abc3
# (suponiendo que 192abc3 fue el hash que se generó con el commit)
git branch nombreNuevaRama 192abc3

3.6.4. Referencias Web Extraoficiales

3.7. Master

  • es el nombre por defecto del branch inicial, se asigna en el momento que inicializamos el repositorio con git init

3.8. Origin

3.8.1. Conceptos

  • Es el nombre por defecto al repositorio remoto, cuando se hace el git clone
  • Si quisieramos podriamos cambiar su nombre por otro con git clone -o otroNombre

3.8.2. Ejemplos

# clonamos el repositorio remoto
# que tendrá de nombre "origin"
# y tendremos la rama inicial llamada origin/master que apunta al repositorio remoto
git clone urlRepositorioRemoto

# clonamos el repositorio remoto
# que tendrá de nombre "pomposo" en vez de "origin"
# y tendremos la rama inicial llamada pomposo/master que apunta al repositorio remoto
# Obs: Podemos usar el atajo -o en vez de --origin
git clone --origin pomposo urlRepositorio

# chequeamos el nombre que le asignamos al repositorio remoto
# aparecerá en la primera columna
# Obs: Podemos usar el atajo -v en vez de --verbose
git remote --verbose

3.8.3. Fragmentos de Documentación

OPTIONS:

-v, –verbose Run verbosely. Does not affect the reporting of progress status to the standard error stream.

-o <name>, –origin <name> Instead of using the remote name origin to keep track of the upstream repository, use <name>. Overrides clone.defaultRemoteName from the config.

3.9. Repositorio remoto Vs Repositorio local

  • El repositorio remoto que se encuentra en la nube ó en una red (al que se suben los cambios)
  • El repositorio local, es el que tenemos en nuestro ordenador
origin/master es una rama que hace referencia (ó apunta) al repositorio remoto (donde todos suben sus cambios)
master es una rama que hace referencia (ó apunta) al repositorio local (cambios locales, de nuestro ordenador)

3.10. Branches

3.10.1. Conceptos

Se utilizan para representar nuevas características (features) a agregar, ó errores (bugs) a resolver.

Están separadas de la rama principal master (se ramifican de esta), para poder enfocarnos sólo en esos nuevos cambios. Si alguno del equipo hace cambios en la rama master y los sube al repositorio remoto, no se verá alterado nuestro branch.

Al terminar los cambios en el branch, se debe ir a la rama master (checkout master) y fusionar los cambios (merge master:nombreBranch)

3.10.3. Referencias Web Extraoficiales

3.11. Commits

  • Representan cambios/confirmaciones realizadas en una o varias ramas

3.12. Tags

3.13. Fetch

3.13.1. Conceptos

Actualiza las referencias (ó punteros) del repositorio local, que apuntan a las que apuntan ramas del repositorio remoto, que pueden apuntar al mismo repositorio remoto (que suele ser origin por defecto), ó a otro repositorio remoto que agreguemos.

Si hay cambios en el repositorio remoto, y queremos traer las referencias/punteros a esas ramas remotas pero sin modificar nuestro directorio de trabajo. Es decir si varios modificaron el branch master, otros branches (con nuevas características, funcionalidades, bugs resueltos, etc..) pero aún no queremos que esos cambios se apliquen a nuestras ramas locales, sólo queremos traernos las referencias y luego integralos/fusionarlos (merge) con lo que tenemos nosotros.

3.14. Reset Vs Checkout

3.14.1. Reset

  • Modifica el área de stage (Index) sin tocar el árbol de trabajo (the working tree)

3.14.2. Checkout

  • Modifica el árbol de trabajo (the working tree) sin modificar el área de stage (Index)

3.14.3. Ejemplos

# agregamos el archivo al "Stage" (ó Index)
git add productos.csv

# agregamos archivos al Stage (ó Index)
# -p: lo hace de manera interactiva
git add -p

# sacamos los archivos del "Stage" (Area de stage)
# -p: lo hace de manera interactiva
git reset -p

# sacamos los archivos del árbol de trabajo "Working Tree"
# -p: lo hace de manera interactiva
git checkout -p

3.14.4. Referencias Extraoficiales

3.15. Conflict Markers

4. Interesante

4.1. Git garbage collection process

  1. git garbage collection process

5. Workflows

5.1. Git flow

5.1.1. Conceptos

5.1.2. Referencias Oficiales

5.1.3. Referencias Extraoficiales

5.2. Github flow

5.2.1. Conceptos

5.2.3. Referencias Extraoficiales

5.3. Trunk-based Development

5.3.1. Conceptos

5.3.3. Referencias Extraoficiales

6. Problemas Comunes

6.1. Deshacer cambios del Area de Unstaged

6.1.1. Conceptos aplicados

  1. Stage files
    • Archivos guardados en el area de stage (area de preparado) paso previo a confirmación (commit)
    • Estado de los archivos luego de git add
  2. Unstage files
    • Archivos que se agregaron ó que se sacaron con git rm
  3. Tracked files
    • Archivos que están en el area de stage
    • Concepto de seguimiento ó rastreo de las modificaciones de los archivos que están en área de stage
  4. Untracked files
    • Archivos que NO están en el area de stage, no se hace un seguimiento de sus modificaciones
  5. Unstaged changes
    • Aplica sólo a los archivos que están en el área de stage
    • Concepto de que los archivos rastreados (que están en el area de stage) fueron modificados y se necesita que se agregen los cambios al area de stage con git add
    • Si hacemos una confirmación (commit) de los cambios, estos no serán confirmados, por tanto tampoco se publicarán (push) a menos que los agregemos con git add
  6. Staged changes
    • Aplica sólo a los archivos que están en el área de stage
    • Los archivos rastreados o en seguimiento del area de stage, no fueron modificados

6.1.2. Posible Escenario

  1. Estamos en una rama feature-horarios e hicimos varios cambios pero no hicimos git add
  2. Queremos deshacer los cambios actuales

6.1.3. Posible Solución

# Eliminamos del árbol de trabajo todos los archivos
# que no están bajo el control de versiones
# situandonos del directorio actual
#
# Observación:
# Se sugiere tener CUIDADO con el uso de este comando,
# porque también puede borrar archivos que están en el .gitignore (aquellos que no queremos subir al remoto)
#
# -d: Elimina los directorios  y archivos sin seguimiento (untracked files)
# -f: Forzar la acción de limpiar el directorio
git clean -df

6.2. Deshacer cambios del Area de Staged

6.2.1. Posible Escenario

  1. Hacemos modificaciones en varios archivos y los agregamos al área de staged con git add
  2. Queremos deshacer esos cambios (el estado cambiará a unstaged changes)

Observaciones: Los cambios que pasan al estado unstaged changes NO se agregarán al commit (confirmación de cambios)

6.2.2. Posible Solución

# creamos el archivo
# - estado del archivo: unstaged, por tanto también untracked (sin seguimiento de los cambios)
touch productos.csv

# agregamos el archivo al "area de staged"
# - estado del archivo: staged, por tanto también tracked (en seguimiento de los cambios)
# - estado de los cambios: unstaged (porque no se modificó)
git add productos.csv

# agregamos algunos cambios
# - estado del archivo: staged
# - estado de los cambios: unstaged (porque se modificó, pero ahora hay que agregarlos con git add)
echo "banana, $500" >> productos.csv

# agregamos ambos
# - estado del archivo: staged
# - estado de los cambios: staged
git add .

# deshacemos los cambios de los archivos que estén en el "area de staged"
# - estado del archivo: staged
# - estado de los cambios del archivo: unstaged
#
# Observación:
# Deshace los cambios que estén en "area de staged"
# pero aún se conservan en el árbol de trabajo (the working tree)
git reset --patch

# OJO..! WARNING "podemos perder los cambios, y no los recuperamos"
#
# deshacemos los cambios de los archivos del árbol de trabajo
# a diferencia del "git reset --patch", estos cambios se perderán
git checkout --patch
# Deshacemos los cambios de manera "interactiva"
# git nos pregunta que cambios queremos deshacer ó conservar
# y muestra las diferencias de las modificaciones
#
# Observación:
# - Cuando nos pregunte si queremos deshacer los cambios, elegimos yes
# - NO sirve si el estado del cambio es "unstaged changes" (cambios que no se agregaron con git add)
git reset --patch
# Sólo sirve para archivos del "area de staged" que fueron modificados
# pero que tienen estado "unstaged" (osea piden que hagas un git add)
#
# Git nos pregunta de cuales archivos queremos deshacer los cambios
# de manera interactiva (esto es gracias al parámetro --patch)
#
# ATENCIÓN..!
# Esto modifica el árbol de trabajo, los cambios que deshagamos no se pueden recuperar
git checkout --patch
# Deshacemos los cambios de TODOS los archivos del árbol de trabajo (the working tree)
# que fueron modificados
#
git checkout -- .

6.3. Mostrar diferencias previo confirmar cambios

6.3.1. Conceptos

  • Cuando confirmamos cambios nos referimos a hacer un commit
  • En el área de stage aparecen los archivos rastreados los que agregamos con git add

6.3.2. Posible escenario

  1. Realizamos modificaciones en los archivos en el área de stage
  2. Queremos ver QUE modificaciones hicimos previo a confirmar (commit) los cambios

6.3.3. Posible solución

# mostramos las diferencia de los archivos del area de staged
# contra la última confirmacion (commit)
git diff --staged

6.4. Pushear branches locales

6.4.1. Posible Escenario

Queremos hacer un push para publicar los cambios/confirmaciones de una rama local, al repositorio remoto (que suele tener el nombre de origin)

6.4.2. Posibles Soluciones

Usamos el push de la siguiente manera git push (remoto) (rama) suponiendo que la rama local y la rama remota tienen el mismo nombre.

Pero si queremos que el nombre de la rama remota, se suba con un nombre distinto al repositorio remoto ya sea porque en el remoto tiene otro nombre o porque así lo queremos, procedemos con lo siguiente git push (remoto) ramaLocal:ramaRemota

Si queremos saber a que rama local apunta HEAD (apuntará a la rama con que hicimos git checkout nombreRama) ó el historial de cambio de referencias de las ramas locales, ejecutamos git reflog

Si queremos conocer las referencias de las ramas locales, ejecutamos git branch -vv

# Creamos un nuevo branch,
# y hacemos que HEAD cambie su referencia (apunte) al nuevo branch
# - Al cambiar la referencia de HEAD, los nuevos commits (cambios) se aplicarán al branch que apunte HEAD
# - Al pasar el parámetro --branch, git crea primero el branch y luego el checkout a ese branch
git checkout -b nombreNuevoBranch

# luego de hacer los cambios
# pusheamos el branch al repositorio remoto
# git push origin -u origin new_feature_name # yo usé este, pero la -u que hace?
git push origin nombreBranch
# Si queremos que el branch local tenga otro nombre en el repositorio remoto
#
# 1. nos traemos los cambios del servidor, sin modificar nuestro directorio de trabajo
# para luego fusionarlo/integrarlo cuando queramos (merge)
git fetch origin
# 2. listamos todos los branches (locales y remotos) por si queremos subir los cambios a otro branch remoto
git branch --all
# 3. subimos los cambios
git push origin branchLocal:branchRemoto

# Cambiamos la referencia de HEAD, apuntando al branch principal (osea master)
# - Los commits (cambios) que hagamos, se aplicaran al branch al que apunta HEAD
# - Al cambiar de branch, se verán cambios en la ruta del repositorio local (nueva estructura, archivos modificados, ...)
git checkout master
# Integramos/fusionamos los cambios del branch, a la rama actual (a la que apunte HEAD)
# - en nuestro caso sería master, porque habiamos hecho el checkout master
git merge origin/nombreBranch
# subimos los cambios (el master fusionado con el branch)
git push

6.5. Recuperar referencias de ramas sin modificar repositorio local

6.5.1. Posible escenario

Cuando existen ramas remotas que queremos recuperar (fetch) a nuestro directorio de trabajo, podemos actualizar los punteros/referencias "no editables" que apuntan esas ramas remotas, y fusionarlas con las ramas locales cuando queramos.

6.5.2. Posibles soluciones

# Recuperamos (fetch) las referencias/punteros de las ramas de repositorio remoto, sin modificar nuestro directorio de trabajo
# - los cambios que se hayan realizado en las ramas remotas, no alteran las nuestras
# - solo nos traemos y actualizamos los punteros/referencias no editables, que podemos ver con "git branch --remotes"
git fetch origin

# integramos/fusionamos (merge) los confirmaciones/cambios de la rama remota "serverfix" a la rama actual
# - si querés cambiar el branch actual a otro, debemos hacer "git checkout nombreRama"
# - si queres saber en que rama estás ejecutamos "git branches --all"
# - decimos que "serverfix" es una rama remota porque "origin" es el nombre por defecto del repositorio remoto
git merge origin/serverfix

6.6. Actualizar referencias de branches locales

6.6.1. Tips

Para ver las ramas de seguimiento (track) asignadas, es decir la referencia que guardan nuestras ramas locales (si es que tienen referencias) podemos utilizar git branch -vv

Las referencias tienen el formato (remoto)/(rama) pudiendo cambiar el repositorio remoto origin por otro (si es que tenemos varios repositorios)

si tenemos "origin/serverFix"
el repositorio al que apunta es el por defecto "origin"
y a la rama "serverFix"

si tenemos "servidor2/serverFix"
estamos apuntando a otro repositorio remoto, 
que previamente habiamos agregado con: git remote add servidor2 urlRepositorioRemoto
si no recordamos el nombre, podemos listar los repositorios con: git remote --verbose

6.6.2. Posible Escenario

Si ya tenemos un branch local llamado "serverfix" pero no tiene una referencia/puntero apuntando a una rama remota se la podemos asignar con git branch -u nombreRemoto/nombreBranch

  • con esto estamos haciendo un seguimiento (track) de una rama remota
  • la referencia puede apuntar a una rama remota con el mismo nombre, ó a otra rama remota

6.6.3. Posibles Soluciones

# Cambiamos de rama local a "serverfix"
# - la referencia de "HEAD" apuntará a esa rama a la que cambiamos, osea "serverfix"
# - el directorio de trabajo cambiará o no, segun las confirmaciones/cambios aplicados que tenga esta rama
# - los cambios/confirmaciones que hagamos se harán en esta rama local
git checkout serverfix
# Le agregamos a la rama local la referencia/puntero hacia la rama remota "serverfix" del repositorio remoto "origin"
git branch -u origin/serverfix
# Si tenemos más repositorios remotos aparte del origin,
# podemos hacer seguimiento de una rama hacia ese otro repositorio remoto
# Es decir cambiamos la referencia/puntero del repositorio por defecto "origin", por otro distinto que es servidor2
# (suponiendo que ese servidor2 aparece en el listado de "git remote --verbose")
git branch -u servidor2/serverfix

6.7. Fusionar confirmaciones de branches remotos y branches locales

6.7.1. Posible Escenario

Cuando existen ramas remotas que queremos recuperar (fetch) a nuestro directorio de trabajo, podemos:

  1. Integrar/fusionar (merge) con nuestras ramas locales existentes
  2. Crear nuevas ramas locales, y que tengan los cambios de esas ramas remotas (para no modificar las nuestras)

6.7.2. Posibles Soluciones

Es importante respetar el siguiente formato para crear un branch local, y traer los cambios de una rama remota git checkout -b (ramaLocal) (nombreRemoto)/(rama)

# Fusionamos/Integramos (merge) nuestra rama local "serverfix" con la rama remota "serverfix"
#
# 1. Creamos un branch llamado "severfix"
# 2. Traemos los cambios del branch remoto "serverfix"
#    - Es una rama remota, porque "origin" apunta al repositorio remoto (servidor) por defecto
#    - Aunque diga checkout, al tener el parámetro -b se crea primero el branch local, y luego hace el checkout
#      a esa rama
git checkout -b serverfix origin/serverfix
# Si queremos que se agreguen los cambios en otra rama local,
# modificamos el primer argumento, en este caso le pusimos el nombre "issuefix01"
git checkout -b issuefix01 origin/serverfix

6.8. Confirmaciones pendientes por subir ó por recuperar

6.8.1. Posible Escenario

Realizamos varios cambios en un branch (local) que aún no hemos subido (push) al repositorio remoto, otros colaboradores han realizado cambios en el mismo branch (remoto).

Si hacemos git branch -vv nos devolverá las ramas de seguimiento y su estado (ahead ó behind).

  • Si dice (remoto)/nombreRama detrás 3 es porque hay 3 confirmaciones (commits) en la rama remota que no hemos recuperado (fetch) aún, es decir pendiente de fusionar/integrar (merge) con la rama local.
  • Si dice (remoto)/(nombreRama) delante 2 es porque hay 2 confirmaciones (commits) en nuestra rama local que aún no hemos subido (push).
  • Si NO dice delante, ni detrás, es porque está actualizada.

Importante: Los números del estado ahead ó behind de las ramas de seguimiento, se actualizan cuando recuperamos (fetch) los datos del servidor con git fetch

Observación: Cuando ejecutamos git pull por detrás está haciendo un git fetch y luego git merge que puede NO tener los resultados esperados, no se recomienda del todo usar git pull porque trae los cambios y ya nos modifica el directorio de trabajo.

[jelou@jelou-pc test-conflictos]$ git branch -vv
features/org-files 8beab19 Probando cambios en una ramita nueva
fix/estructura     1471f2f [origin/fix/estructura] cambiamos estructura
master             3db94eb [origin/master: delante 2, detrás 3] Nuevos cambios

6.8.2. Posibles Soluciones

# recuperamos los datos del servidor
# aunque no los aplica en el repositorio remoto, nosotros
# decidimos si subir los cambios (push) ó fusionar/integrar los cambios (merge)
git fetch origin

# si decidimos subir los cambios
# suponiendo que la rama de seguimiento tiene N confirmaciones delante
# (esas N confirmaciones delante, representan cambios locales que queremos subir al servidor)
git push nombreRemoto nombreRama

# si decidimos fusionar los cambios
# suponiendo que la rama de segumiento tiene N confirmaciones detrás
# (esas N confirmaciones detrás, representan cambios en el servidor, que debemos recuperar a nuestro entorno)
git merge nombreRemoto/nombreRama

6.9. Eliminar branches

6.9.1. Eliminar branches locales

# actualizamos las referencias
git fetch origin

# listamos todos los branches (locales y remotos)
git branch -a

# eliminamos una rama local
# Obs: podemos cambiar --delete por su forma abreviada -d
git branch --delete nombreRama

# eliminamos de manera forzosa
# -D sería similar a --force --delete
git branch -D nombreRama

6.9.2. Eliminar branches remotos

# actualizamos las referencias
git fetch origin

# listamos todos los branches (locales y remotos)
git branch -a

# borramos la rama remota
# git push (remoto) (rama)
git push origin --delete nombreRama

6.9.3. Referencias Web Extraoficiales

7. Resolver Conflictos Elementales

git diff –check

7.1. Conceptos Elementales

Si hay cambios NO confirmados (commits) cuando comenzó la fusión (merge) el comando git merge --abort puede que no pueda reconstruir los cambios.

Por tanto, se recomienda siempre confirmar (commit) ó guardar los cambios previo a ejecutar el git merge

7.2. Técnicas de Fusión

7.2.1. Fast Foward Merge

  1. Conceptos

    A fast-forward merge can occur when there is a linear path between branches that we want to merge. If a master has not diverged, instead of creating a new commit, it will just point the master to the latest commit of the hotfix branch. All commits from the hotfix branch are now available in the master branch.

    Ocurre cuando el registro (log) de confirmaciones (commits) entre las ramas que queremos fusionar no diverge (osea es lineal), se fusionarán las ramas y HEAD apuntará al último commit de la rama a fusionada.

  2. Posibles Escenarios
    1. Creamos una rama feature/horarios, generamos cambios en los archivos y hacemos una confirmacion (la llamaremos c3).
    2. Queremos fusionar los cambios de la confirmación (c3) en la rama principal master que apunta a (c2)
    3. Como no existen diferencias, ni conflictos, git hace el fast foward merge y HEAD apuntará a c3
        master feature-horarios
    c0 c1 c2 c3
          master
    c0 c1 c2 c3
    git checkout master
    touch README.md
    git add README.md && git commit -m "Primer commit"
    
    # creamos una nueva rama, y la activamos para trabajar en ella
    # las confirmaciones (commits) que hagamos, se harán en esta rama
    git checkout -b features/horarios
    
    # generamos algunos cambios
    echo "El nuevo horario es de 10 a 19hs" >> README.md
    # agregamos el archivo alterado
    git add README.md
    # hacemos la confirmación del cambio
    git commit -m "Agregamos el nuevo horario"
    
    # volvemos a nuestra rama principal
    git checkout master
    # y hacemos una fusión con la rama que modificamos
    git merge features/horarios
    

7.3. Marcadores de Conflicto

7.3.1. Conceptos

Cuando hacemos un git merge ó git rebase para fusionar cambios de branches, git nos avisa que hubo un conflicto porque se modificaron las mismas lineas en uno o varios archivos en ambas ramas.

Entonces los archivos que tengan conflictos serán modificados por git, y se agregarán los marcadores de conflictos que delimitan las lineas que debemos resolver.

Marcador Descripción
<<<<<<< Delimita el inicio del conflicto
   
===== Separa los cambios de las ramas en conflicto
   
>>>>>>> Delimita el fin del conflicto
  1. Lo que está entre <<<<<<< HEAD y =======, son los cambios actuales del último commit al que apunta HEAD
  2. Lo que está entre ======= y >>>>>>> fix/promociones, son los cambios de la rama que vamos a fusionar

Agregamos promociones <<<<<<< HEAD cambiamos descuentos ===== agregamos promociones >>>>>>> fix/promociones

7.3.2. Formato diff3

En este se agrega un nuevo marcador que es |||||| que nos trae también al ancestro en común de las ramas que estamos fusionando, antes que hubiese un conflicto.

# configuramos por defecto diff3
git config --global merge.conflictstyle diff3
git checkout master
# creamos y agregamos contenido al archivo
vim productos.csv
# agregamos el archivo modificado, y confirmamos el cambio
git add productos.csv && git commit -m "Agregamos los productos"

git checkout -b feature-precios
# modificamos el archivo, agregamos una columna con los precios
vim productos.csv
# agregamos el archivo modificado, y confirmamos el cambio
git add productos.csv && git commit -m "Agregamos los productos"

git checkout master

git checkout -b feature-descuentos
# modificamos el archivo, modificamos columna con los descuentos
# y borramos una de las filas
vim productos.csv
# agregamos el archivo modificado, y confirmamos el cambio
git add productos.csv && git commit -m "Agregamos los precios y borramos un producto"

git checkout master

# fusionamos
git merge feature-precios
# fusionamos una segunda vez (tendrá un conflicto)
git merge feature-descuentos

Explicación sobre los marcadores

  1. Lo que está entre <<<<<<< HEAD y |||||||
    • HEAD apunta a este commit
    • fueron los primeros cambios fusionados
    • se le suele decir OURS ó "nuestros cambios"
    • son los cambios de la rama feature-precios
  2. Lo que está entre ||||||| y =======
    • es el contenido al principio, previo a los cambios de las ramas
    • es el commit antes de fusionar las ramas feature-precios y feature-descuentos
    • es el commit antes que se bifurque el árbol de trabajo en las dos ramas que le agregan los precios/descuentos
  3. Lo que está entre ======= y >>>>>>> features-descuentos
    • Son los cambios de la rama que estamos tratando de fusionar (el segundo merge)
    • Se le suele decir THEIRS ó "cambios de ellos"

Explicación de los cambios realizados

  1. Estamos en la rama principal, y creamos un listado de formato csv de una columna, con los productos
  2. Creamos una rama precios en base a la rama principal, y agregamos una columna con los precios
  3. Creamos una rama descuentos en base a la rama principal, y agregamos una columna con los descuentos y además le sacamos el último producto "kiwi"

<<<<<<< HEAD Producto,Precio Banana,$3 Kiwi,$2

            7f9b53f

Producto Banana Kiwi ===== Producto,Descuento Banana,5% >>>>>>> features-descuentos

7.3.3. Referencias Web Oficiales

7.3.5. Referencias Issues

7.4. [TODO] Separar confirmaciones antes de una fusión

7.4.1. Conceptos

Si tenemos cambios que NO queremos confirmar (commit) antes de comenzar una fusión (merge) se recomienda realizar git stash de los cambios. Luego de terminar la fusión git merge ó cancelarla git merge --abort se sugiere ejecutar git stash pop

7.4.2. Posibles Soluciones

# separamos los últimos cambios no confirmados (que no hicimos commit)
# (se podria decir que se guarda en una pila de cambios)
git stash

# abortamos la fusión
git merge --abort

# podemos revisar el listado (opcional)
# (vemos la pila de cambios)
git stash list

# retomamos los cambios que habiamos separado
# (sacamos los últimos cambios separados de la pila de cambios)
git stash pop

7.4.3. Referencias Web Oficiales

7.5. Reorganizar registro de confirmaciones

7.5.1. Conceptos

  • Si utilizamos el comando git rebase es posible alterar el registro/historial de confirmaciones
  • El uso del git rebase se recomienda usar con CUIDADO tal como con git reset --hard
  • NO se recomienda usar git rebase en repositorios públicos, podemos arruinar el trabajo de los demás.

7.5.2. Posible Escenario

  1. Creamos una rama feature/precios y generamos varias confirmaciones c1, c2, c3 (commits)
  2. Creamos una rama feature/promociones y generamos varias confirmaciones c4, c5, c6 (commits)
  3. Queremos traernos las confirmaciones de ambas ramas, pero si hubiese algun conflicto no queremos que figure un merged en el registro de confirmacion git log
# activamos la rama master (por si acaso que estémos en otra rama)
git checkout master

# creamos una rama para agregar los precios
git checkout -b feature/precios
# generamos algunos cambios
echo "prod1 $500" > productos.txt
echo "prod1 $300" >> productos.txt
# agregamos el archivo modificado y confirmamos los cambios
git add productos.txt && git commit -m "agregamos los precios"

# volvemos a activar la rama master
git checkout master

# nos creamos una nueva rama basada en la rama master
git checkout -b feature/promociones
# modificamos los datos
vim productos.txt
# agregamos el archivo modificado y confirmamos los cambios
git add productos.txt && git commit -m "agregamos las promociones"

7.5.3. Posibles Soluciones

Quizás se podría hacer el rebase directo en el master, pero esto podría alterar el registro de confirmaciones y no habría vuelta atrás. Por eso lo hacemos en las ramas feature/descuentos y feature/promociones para reorganizarlas y luego traerlas a la rama master

# activamos la rama de los descuentos
git checkout feature/descuentos
# sobreescribimos el registro de confirmaciones
# para que la última confirmación sea la de feature/descuentos
git rebase master

# retomamos la rama master
# y nos traemos los cambios reorganizados de la rama feature/descuentos
git checkout master
git rebase feature/descuentos

# repetimos las operaciones anteriores
git checkout feature/promociones
git rebase master
# si aparece un conflicto, lo solucionamos
# y le avisamos a git que siga con el rebase
git rebase --continue # esto es solo si aparece un conflicto

git checkout master
git rebase feature/descuentos

7.6. Archivos con diferencias

Para remover del directorio de trabajo los archivos con diferencias

# -f hace referencia a --force
git clean -f

7.7. Deshacer fusión no publicada

7.7.1. Posible Escenario

  • Si resolvimos un conflicto pero queremos deshacer el merge, podemos usar el comando git reset
  • Con No publicada nos referimos al haber ejecutado el comando git push

7.7.2. Posible Solución

  • Cuando se trata de resolver conflictos, no podemos usar el git reset --soft que mantiene los cambios
# al usar el parámetro --hard
# deshacemos los cambios, y no mantenemos los cambios
git reset --hard HEAD~1

7.7.3. Referencias Web Extraoficiales

7.8. Abortar Fusión

7.8.1. Posible Escenario

  • Fusionamos branches ejecutando el comando git merge y nos salta un warning de conflicto

7.8.2. Posible solución

  • Podemos volver a ejecutar el comando git merge pero con el parámetro –abort
# deshacemos el merge anterior
# restauramos el directorio y los archivos que se modificaron con el merge anterior
git merge --abort

7.8.3. Referencias Web Oficiales

7.9. Cambiar Archivos

7.9.1. Posible Escenario

  1. Hacemos un merge y tenemos un conflicto
  2. Nos informan que nos quedemos con nuestro cambio, porque el cambio de nuestro colega estaba mal
#
git checkout merge-into-ours

#
git merge from-theirs

7.9.2. Posibles Soluciones

# nos evitamos buscar las diferencias con "git mergetool"
# nos quedamos solo con nuestro cambio
git checkout --ours README.md

# suponiendo que cambian de parecer
# y nos dicen que la revisión correcta, NO es la nuestra
git checkout --theirs README.md

# agregamos el archivo modificado
git add README.md

git commit -am "Conflicto solucionado"
# Mostramos las confirmaciones que tienen conflicto
# más en detalle de un archivo específico (suponiendo que ese archivo generó el conflicto)
git log --merge -p README.md
# Stage #1 is the common ancestor of the files
# Common base version of the file.
# archivo ancestro en común
git show :1:README.md

# Stage #2 is the target-branch version
# 'Ours' version of the file.
# archivo del repositorio local, el nuestro
git show :2:README.md

# Es el MERGE_HEAD
# Stage #3 is the version you are merging from
# 'Theirs' version of the file.
# archivo del repositorio remoto (aunque puede ser local y venir de otra rama local)
# archivo que queremos fusionar
git show :3:README.md

7.10. Registro de Confirmaciones en conflicto

Si queremos ver el registro de confirmaciones, pero sólo de aquellas confirmaciones en conflicto

git log --merge

7.11. Resolver Conflictos con MergeTools

7.11.1. Conceptos

Cuando tenemos conflictos en distintas partes de un archivo, revisar las diferencias desde la terminal sin herramientas puede ser confuso. Con mergetools de git podemos configurar la herramienta (vimdiff, emacs,…) el formato (ediff, diff3,..) y facilitar la corrección del conflicto.

LOCAL (ours) BASE REMOTE (theirs)
MERGED
LOCAL (A) BASE REMOTE (B)
MERGED (C)
(A) OLD VERSION ANCESTOR (B) NEW VERSION
MERGED (C)

Observaciones:

  • Las letras en paréntesis es como lo suelen denotar otros también, con menos detalle.

Con cualquiera de las herramientas que usemos como mergetool, tanto vim como emacs veremos las siguientes secciones

  • LOCAL: archivo de la rama actual, último commit antes del conflicto (al que apunta HEAD)
  • REMOTO: archivo que estamos fusionando con la rama actual
  • BASE: ancestro común, cómo se veía el archivo antes de ambos cambios
  • FUSIONADO: resultado de fusión, esto es lo que se guarda en el repositorio

7.11.2. Ejecutar Mergetools

git mergetool

7.11.3. Configurar Mergetools con Emacs

  1. Introducción
  2. Configuración
    # Configuramos emacs como difftool
    git config --global diff.tool emacs
    git config --global difftool.prompt false
    # the result of the following command is:
    # cmd = emacs -nw --eval \"(progn (setq vc-handled-backends ()) (ediff-files \\\"$LOCAL\\\" \\\"$REMOTE\\\"))\"
    git config --global difftool.emacs.cmd \
        'emacs -nw --eval "(progn (setq vc-handled-backends ()) (ediff-files \"$LOCAL\" \"$REMOTE\"))"'
    
    # Configuramos emacs como mergetool
    git config --global merge.tool emacs
    git config --global mergetool.prompt false
    # the result of the following command is:
    # cmd =  emacs -nw --eval \"(progn (setq vc-handled-backends ()) (ediff-merge-files-with-ancestor \\\"$LOCAL\\\" \\\"$REMOTE\\\" \\\"$BASE\\\" nil \\\"$MERGED\\\"))\"
    git config --global mergetool.emacs.cmd \
        'emacs -nw --eval "(progn (setq vc-handled-backends ()) (ediff-merge-files-with-ancestor \"$LOCAL\" \"$REMOTE\" \"$BASE\" nil \"$MERGED\"))"'
    
  3. Teclas atajo
    Atajo Descripción
    k,p siguiente diferencia (cambio)
    j,n anterior diferencia (cambio)
    q salir
    a Agrega los cambios de (A) en (C) osea en la fusión
    b Agrega los cambios de (B) a (C) osea en la fusión
    r Vuelve al cambio anterior (A ó B)
    + Agrega las diferencias de ambos, con los símbolos << y >>
  4. Referencias

7.11.4. Configurar Mergetools con Vim

  1. Configuración
    git config merge.tool vimdiff
    git config merge.conflictstyle diff3
    git config mergetool.prompt false
    
  2. Comandos y Teclas de atajo
    Comando Comando Corto Descripción
    :diffget N :diffg N Agrega los cambios de la ventana N en Merged
    :diffoff :diffo Desactiva las diferencias
    :difft :difft Activa las diferencias
    :diffupdate :diffu  
    :diffput    
    Comando Comando Corto Descripción
    :set relativenumber   Agrega los números de las lineas
    Atajo Descripción
    ]c siguiente cambio
    [c anterior cambio
    C w w siguiente ventana
  3. Referencias Youtube
  4. Referencias Web Extraoficiales
  5. Referencias Issues

8. Resolver Conflictos Basicos

8.1. Fusionar rama y agrupar confirmaciones

8.1.1. Posible Escenario

  1. Creamos una rama feature/anuncios con nuevas características
  2. Hacemos varias modificaciones, por tanto varias confirmaciones (commits)
  3. Queremos fusionar feature/anuncios con la rama master pero sin todas las confirmaciones de esa rama es decir queremos que la fusión tenga sólo una confirmación (commit)
# creamos la rama, y la activamos
git checkout -b feature/anuncios

# hacemos varios cambios
mkdir anuncios && cd anuncios && touch anuncio{1..5}.txt && cd ..
git commit -am "agregamos varios anuncios"

mkdir imagenes && touch imagenes/imagen{1,3,7}.png
git commit -am "agregamos imagenes de los anuncios"

# publicamos nuestra rama (opcional)
git push origin feature/anuncios

8.1.2. Posible Solución

Utilizamos git merge con el parámetro --squash

# activamos la rama master
git checkout master

# fusionamos los cambios de la rama feature/anuncios en la rama master
# - pero todos los commits de esa rama, aparecerán como un solo commit
# - si no le pasamos squash, el master tendrá todos los commits que hicimos en esa rama
git merge --squash feature/anuncios

git commit -m "Agregamos varios anuncios"

9. Resolver Conflictos Complejos

git pull --strategy=theirs remote_branch

git diff ..@{upstream}

git log ..@{upstream}

9.1. Merge Vs Rebase

9.3. Referencias Web Extraoficiales

10. [TODO] Otros

Author: jelou

Created: 2021-10-19 mar 01:40

Validate