10 problemas comunes de Git y cómo solucionarlos

Originalmente escribí este artículo para Codementor en octubre de 2014. Debería tener algo para todos, desde usuarios de git bastante nuevos hasta desarrolladores experimentados.

1. Descartar modificaciones de archivos locales

A veces, la mejor manera de tener una idea de un problema es sumergirse y jugar con el código. Desafortunadamente, los cambios realizados en el proceso a veces resultan ser menos que óptimos, en cuyo caso revertir el archivo a su estado original puede ser la solución más rápida y fácil:

git checkout - Gemfile # reset ruta especificada
pago git - lib bin # también funciona con múltiples argumentos

En caso de que se pregunte, el guión doble (-) es una forma común para que las utilidades de la línea de comandos signifiquen el final de las opciones de comando.

2. Deshacer confirmaciones locales

Lamentablemente, a veces nos lleva un poco más de tiempo darnos cuenta de que estamos en el camino equivocado, y para ese momento uno o más cambios ya pueden haberse comprometido localmente. Esto es cuando git reset es útil:

git reset HEAD ~ 2 # deshacer las últimas dos confirmaciones, mantener los cambios
git reset --HAD HEAD ~ 2 # deshace las dos últimas confirmaciones, descarta los cambios

¡Tenga cuidado con la opción --hard! Restablece su árbol de trabajo y el índice, por lo que todas sus modificaciones se perderán para siempre.

3. Elimine un archivo de git sin eliminarlo de su sistema de archivos

Si no tiene cuidado durante una adición de git, puede terminar agregando archivos que no desea comprometer. Sin embargo, git rm lo eliminará tanto de su área de preparación como de su sistema de archivos, que puede no ser lo que desea. En ese caso, asegúrese de eliminar solo la versión por etapas y agregue el archivo a su .gitignore para evitar cometer el mismo error por segunda vez:

git reset filename # o git rm - nombre de archivo en caché
echo filename >> .gitignore # agréguelo a .gitignore para evitar volver a agregarlo

4. Edite un mensaje de confirmación

Los errores tipográficos suceden, pero afortunadamente en el caso de los mensajes de confirmación, es muy fácil solucionarlos:

git commit --amend # start $ EDITOR para editar el mensaje
git commit --amend -m "Nuevo mensaje" # establece el nuevo mensaje directamente

Pero eso no es todo lo que git -mend puede hacer por usted. ¿Olvidaste agregar un archivo? ¡Solo agrégalo y modifica la confirmación anterior!

git add forgotten_file git commit --amend

Tenga en cuenta que --amend en realidad creará un nuevo commit que reemplaza al anterior, por lo que no lo use para modificar los commits que ya se han enviado a un repositorio central. Se puede hacer una excepción a esta regla si está absolutamente seguro de que ningún otro desarrollador ya ha revisado la versión anterior y ha basado su propio trabajo en ella, en cuyo caso un empuje forzado (git push --force) aún puede estar bien. La opción --force es necesaria aquí ya que el historial del árbol se modificó localmente, lo que significa que el servidor remoto rechazará la inserción, ya que no es posible la combinación de avance rápido.

5. Limpie los compromisos locales antes de presionar

Si bien --amend es muy útil, no ayuda si la confirmación que desea reformular no es la última. En ese caso, un rebase interactivo es útil:

git rebase --interactive
# si no especificó ninguna información de seguimiento para esta rama
# tendrá que agregar información de sucursal ascendente y remota:
git rebase - rama de origen interactivo

Esto abrirá su editor configurado y le presentará el siguiente menú:

elija 8a20121 Actualice la versión de Ruby a 2.1.3
pick 22dcc45 Agrega una biblioteca elegante
# Rebase fcb7d7c..22dcc45 en fcb7d7c
# #
# Comandos: # p, pick = use commit
# r, reword = use commit, pero edite el mensaje de commit
# e, edit = use commit, pero deténgase para enmendar
# s, squash = usar commit, pero fusionarse con el commit anterior
# f, fixup = como "squash", pero descarta el mensaje de registro de este commit
# x, exec = ejecutar comando (el resto de la línea) usando shell
# #
# Estas líneas se pueden reordenar; Se ejecutan de arriba a abajo.
# #
# Si elimina una línea aquí, ESTE COMITÉ SE PERDERÁ.
# #
# Sin embargo, si elimina todo, se cancelará el rebase.
# #
# Tenga en cuenta que las confirmaciones vacías están comentadas

En la parte superior, verá una lista de confirmaciones locales, seguida de una explicación de los comandos disponibles. Simplemente seleccione los commit (s) que desea actualizar, cambie la selección para volver a redactar (o r para abreviar), y se lo dirigirá a una nueva vista donde puede editar el mensaje.

Sin embargo, como se puede ver en la lista anterior, los rebases interactivos ofrecen mucho más que la simple edición de mensajes de confirmación: puede eliminar completamente las confirmaciones eliminándolas de la lista, así como editarlas, reordenarlas y aplastarlas. Squashing le permite fusionar varios commits en uno, que es algo que me gusta hacer en las ramas de características antes de empujarlos al control remoto. ¡No más confirmaciones de "Agregar archivo olvidado" y "Reparar error tipográfico" grabadas por la eternidad!

6. Revertir commits empujados

A pesar de las correcciones demostradas en los consejos anteriores, los commits defectuosos ocasionalmente llegan al repositorio central. Aún así, esta no es razón para desesperarse, ya que git ofrece una manera fácil de revertir confirmaciones simples o múltiples:

git revert c761f5c # revierte el commit con el id especificado
git revert HEAD ^ # revierte el penúltimo commit
git revert desarrollar ~ 4..desarrollar ~ 2 # revierte una gama completa de confirmaciones

En caso de que no desee crear confirmaciones de reversión adicionales, pero solo aplique los cambios necesarios a su árbol de trabajo, puede usar la opción --no-commit / -n.

# deshacer la última confirmación, pero no cree una confirmación de reversión
git revert -n HEAD

La página del manual en man 1 git-revert enumera más opciones y proporciona algunos ejemplos adicionales.

7. Evitar conflictos de fusión repetidos

Como todo desarrollador sabe, arreglar conflictos de fusión puede ser tedioso, pero resolver el mismo conflicto repetidamente (por ejemplo, en ramas de funciones de larga ejecución) es totalmente molesto. Si ha sufrido esto en el pasado, le complacerá conocer la función de resolución grabada de reutilización infrautilizada. Agréguelo a su configuración global para habilitarlo para todos los proyectos:

git config --global rerere.enabled true

Alternativamente, puede habilitarlo por proyecto creando manualmente el directorio .git / rr-cache.

Esto seguramente no es una función para todos, pero para las personas que lo necesitan, puede ser un ahorro de tiempo real. Imagine que su equipo está trabajando en varias ramas de características al mismo tiempo. Ahora desea fusionarlos todos en una sola rama de prelanzamiento comprobable. Como se esperaba, hay varios conflictos de fusión, que usted resuelve. Desafortunadamente, resulta que una de las ramas aún no está allí, por lo que decide volver a fusionarla. Varios días (o semanas) más tarde, cuando la rama esté finalmente lista, la fusionará nuevamente, pero gracias a las resoluciones registradas, no tendrá que resolver los mismos conflictos de fusión nuevamente.

La página man (man git-rerere) tiene más información sobre casos de uso y comandos adicionales (estado de git rerere, git rerere diff, etc.).

8. Encuentra el commit que rompió algo después de una fusión

Rastrear el commit que introdujo un error después de una gran fusión puede llevar bastante tiempo. Afortunadamente, git ofrece una gran herramienta de búsqueda binaria en forma de git-bisect. Primero debes realizar la configuración inicial:

git bisect start # inicia la sesión de bisección
git bisect bad # marca la revisión actual como mala
git bisect buena revisión # marca la última buena revisión conocida

Después de esto, git realizará automáticamente una revisión a mitad de camino entre las versiones "buenas" y "malas" conocidas. Ahora puede volver a ejecutar sus especificaciones y marcar la confirmación como "buena" o "mala" en consecuencia.

git bisect bueno # o git bisec malo

Este proceso continúa hasta llegar a la confirmación que introdujo el error.

9. Evita errores comunes con ganchos git

Algunos errores ocurren repetidamente, pero sería fácil evitarlos ejecutando ciertas comprobaciones o tareas de limpieza en una etapa definida del flujo de trabajo de git. Este es exactamente el escenario para el que se diseñaron los ganchos. Para crear un nuevo enlace, agregue un archivo ejecutable a .git / hooks. El nombre del script debe corresponder a uno de los ganchos disponibles, cuya lista completa está disponible en la página del manual (man githooks). También puede definir enlaces globales para usar en todos sus proyectos creando un directorio de plantillas que git usará al inicializar un nuevo repositorio (consulte man git-init para obtener más información). Así es como se ve la entrada relevante en ~ / .gitconfig y un directorio de plantilla de ejemplo:

[en eso]
    templatedir = ~ / .git_template
  
→ árbol .git_template
  .git_template
  └── ganchos
      └── pre-commit

Cuando inicializa un nuevo repositorio, los archivos en el directorio de la plantilla se copiarán en la ubicación correspondiente en el directorio .git de su proyecto.

Lo que sigue es un ejemplo de enlace commit-msg ligeramente inventado, que garantizará que cada mensaje de confirmación haga referencia a un número de ticket como "# 123".

#! / usr / bin / env ruby
mensaje = File.read (ARGV [0])

a menos que mensaje = ~ / \ s * # \ d + /
  pone "[POLÍTICA] Su mensaje no hace referencia a un ticket".
  salida 1
fin

10. Cuando todo lo demás falla

Hasta ahora hemos cubierto bastante sobre cómo solucionar errores comunes al trabajar con git. La mayoría de ellos tienen soluciones bastante fáciles, sin embargo, hay momentos en que uno tiene que sacar las armas grandes y reescribir la historia de una rama entera. Un caso de uso común para esto es la eliminación de datos confidenciales (por ejemplo, credenciales de inicio de sesión para sistemas de producción) que se enviaron a un repositorio público:

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch secrets.txt' \
  --prune-empty --tag-name-filter cat - --todos

Esto eliminará el archivo secrets.txt de cada rama y etiqueta. También eliminará cualquier confirmación que esté vacía como resultado de la operación anterior. Tenga en cuenta que esto reescribirá el historial completo de su proyecto, lo que puede ser muy perjudicial en un flujo de trabajo distribuido. Además, aunque el archivo en cuestión ahora se ha eliminado, ¡las credenciales que contenía aún deben considerarse comprometidas!

GitHub tiene un muy buen tutorial sobre la eliminación de datos confidenciales y man git-filter-branch tiene todos los detalles sobre los diversos filtros disponibles y sus opciones.

Publicado originalmente en gist.github.com.