27 Enmendar repetidamente
Uno de los principales placeres del control de versiones es la libertad de experimentar sin miedo. Si arruinas las cosas, siempre puedes volver a una versión más feliz de tu proyecto. Describimos varios métodos de este tipo de viajes en el tiempo en Capítulo 30. ¡Pero debes tener un buen commit al que recurrir!
27.1 Analogía de la escalada en roca
Usar una confirmación de Git es como usar anclajes y otras protecciones al escalar. Si estás cruzando una pared rocosa peligrosa, debes asegurarte de haber usado protección para atraparte si te caes. Las confirmaciones desempeñan un papel similar: si comete un error, no podrá superar la confirmación anterior. Codificar sin confirmaciones es como escalar libremente: puedes viajar mucho más rápido en el corto plazo, pero en el largo plazo las posibilidades de fallas catastróficas son altas. Al igual que la protección para la escalada en roca, debes ser prudente en el uso de confirmaciones. Realizar commits con demasiada frecuencia ralentizará su progreso; utilice más confirmaciones cuando se encuentre en territorio incierto o peligroso. Los cmmits también son útiles para los demás porque muestran su viaje, no sólo el destino.
Paquetes R, Hadley Wickham (Wickham (2015))
Hablemos de esto:
usa más confirmaciones cuando estés en territorio incierto o peligroso
Cuando hago algo complicado, a menudo avanzo hacia mi objetivo en pequeños incrementos, comprobando que todo sigue funcionando en el camino. ¿Si, funciona? Haz un commit. Este es mi nuevo peor escenario. Sigue adelante.
¿Qué no se podría amar?
Esto puede generar una gran cantidad de pequeñas confirmaciones. Esto está absolutamente bien y no hay nada de qué avergonzarse. Pero algún día puede que empieces a preocuparte por la utilidad y la estética de tu historial de Git.
La modificación repetida es un patrón en el que, en lugar de saturar su historial con muchas confirmaciones pequeñas, construye una confirmación “buena” gradualmente, mediante modificaciones.
Sí, hay otras formas de hacer esto, p. mediante squashingo y rebase interactivo, pero creo que modificar es la mejor manera de comenzar.
27.2 Bosquejo del flujo de trabajo
27.2.1 Condición inicial
Comience con su proyecto en un estado funcional:
- ¿Paquete R? Ejecute sus pruebas o
R CMD check
. - ¿Análisis de los datos? Vuelva a ejecutar su secuencia de comandos o vuelva a representar su
.Rmd
con el nuevo fragmento. - ¿Sitio web o libro? Asegúrese de que el proyecto aún se compile.
- Ya entiendes la idea.
Asegúrese de que su “árbol de trabajo esté limpio” y esté sincronizado con su control remoto de GitHub. git status
debería mostrar algo como:
~/tmp/myrepo % git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
27.2.2 Ponte a trabajar
Imaginemos que comenzamos en la confirmación C, la confirmación anterior es B y, antes de eso, A:
... -- A -- B -- C
Da un pequeño paso hacia tu objetivo. Vuelva a comprobar que su proyecto “funciona”.
Realice esos cambios y realice una confirmación con el mensaje “WIP”, que significa “trabajo en progreso”. Haz esto en RStudio o en el shell. (Apéndice A):
git add ruta/al/archivo/cambiado
git commit -m "WIP"
El mensaje puede ser cualquier cosa, pero “WIP” es una convención común. Si lo usa, cada vez que regrese a un proyecto donde el mensaje de confirmación más reciente es “WIP”, sabrá que probablemente estaba en medio de algo. Si presiona un commit “WIP”, a propósito o por error, le indica a otras personas que es posible que se produzcan más commits.
Tu historial ahora se ve así:
A -- B -- C -- WIP*
¡No envies! El *
anterior significa una confirmación que existe solo en tu repositorio local, no (todavía) en GitHub. Si llamaste a git status
, verías algo como “Your branch is ahead of ‘origin/main’ by 1 commit.”, que también se muestra en el panel Git de RStudio.
Trabaja un poco más. Vuelva a verificar que su proyecto aún esté en un estado funcional. Organiza y confirma nuevamente, pero esta vez modifica tu confirmación anterior. RStudio ofrece una casilla de verificación para “Amend previous commit” o en el shell:
git commit --amend --no-edit
La parte --no-edit
conserva el mensaje de confirmación actual de “WIP”.
¡No envies! Tu historial ahora se ve así:
A -- B -- C -- WIP*
pero los cambios asociados con la confirmación WIP*
ahora representan sus dos últimas confirmaciones, es decir, todos los cambios acumulados desde el estado C.
Sigue así.
Digamos que finalmente has logrado tu objetivo. Una última vez, verifique que su proyecto sea funcional y se encuentre en un estado que esté dispuesto a compartir con otros.
Confirme, enmendando nuevamente, pero esta vez con un mensaje de confirmación real. Piense en esto como una confirmación D. Envie. Haga esto en RStudio o el shell:
git commit --amend -m "Implementa una característica increíble; cierra #43"
git push
Tu historial, y el de GitHub, se ve así:
A -- B -- C -- D
Hasta donde el mundo sabe, implementaste la función de una sola vez. Pero tenías que trabajar en la tarea de forma gradual, con la tranquilidad de que nunca podrías romper las cosas.
27.3 ¿Qué pasa si necesito retroceder?
Imagine que está en medio de un flujo de trabajo de Enmendar repetidamente:
A -- B -- C -- WIP*
y realiza algunos cambios que rompen su proyecto, p. las pruebas empiezan a fallar. Estos malos cambios aún no se han confirmado, pero están guardados. Quiere volver al último buen estado, representado por WIP*
.
En la jerga de Git, desea realizar un restablecimiento completo al estado WIP*
. Sus archivos locales se restablecerán a la fuerza a su estado a partir de la confirmación WIP*
. Con la línea de comando:
git reset --hard
que es implícitamente lo mismo que
git reset --hard HEAD
que dice: “restablecer mis archivos a su estado en la confirmación más reciente”.
Esto también es posible en RStudio. De hecho, la forma RStudio hace que sea más fácil restablecer selectivamente sólo archivos específicos o sólo ciertos cambios. Haga clic en “Diff” o “Commit”. Seleccione un archivo con cambios que no desee. Utilice “Discard All” para descartar todos los cambios en ese archivo. Utilice “Discard chunk” para descartar cambios específicos en un archivo. Repita este procedimiento para cada archivo afectado hasta que vuelva a un estado aceptable. Continuar.
Si confrimo una versión en mal estado, vaya a Capítulo 38 para ver más escenarios de reinicio.
27.4 ¿Por qué no enviamos el progreso intermedio?
Modificar una confirmación es un ejemplo de lo que se llama “reescribir el historial de Git”.
Reescribir el historial que ya ha sido enviado a GitHub (y, por lo tanto, potencialmente retirado por otra persona) es una práctica controvertida. Como la mayoría de las prácticas controvertidas, mucha gente todavía se entrega a ellas, al igual que yo.
Pero existe la posibilidad muy real de que crees dolores de cabeza a ti mismo y a los demás, por lo que en debemos recomendarte que te abstengas. Una vez que hayas enviado algo, considéralo escrito en piedra y sigue adelante.
27.5 Um, ¿y si lo envie?
¡Te dije que no lo hicieras!
Pero bueno, aquí estamos.
Imaginemos que enviaste este estado a GitHub por error:
A -- B -- C -- WIP (85bf30a)
y procedió a git commit --amend
nuevamente localmente, lo que llevó a este estado:
A -- B -- C -- WIP* (6e884e6)
Estoy mostrando deliberadamente dos historiales que parecen iguales, en términos de mensajes de confirmación. Pero el último SHA revela que en realidad son diferentes.
Estás en un aprieto ahora, ya que no puedes hacer un simple enviar o importar. Una inserción será rechazada e importar probablemente dará lugar a una fusión que no desea.
Tienes dos opciones:
Si tiene colaboradores que pueden haber retirado el repositorio en la confirmación
WIP (85bf30a)
, debe considerar esa historia en particular como escrita en piedra ahora. Si hay algún trabajo muy valioso que solo existe localmente, como un archivo específico, guarde una copia de ese en una nueva ruta de archivo, temporalmente. Restablezca por completo su repositorio local aC
(git reset --hard HEAD^
) e importe de GitHub. GitHub y la historia local ahora muestran esto:A -- B -- C -- WIP (85bf30a)
Si guardó algún trabajo valioso en una ruta de archivo temporal, importelo nuevamente al repositorio ahora; guardar, preparar, confirmar y enviar. GitHub y la historia local ahora muestran esto:
A -- B -- C -- WIP (85bf30a) -- E
Si no tiene colaboradores o tiene motivos para creer que no se han importado, puede reescribir el historial, incluso en GitHub. También puedes asegurarte de que tu confirmación local tenga un mensaje real que no sea “WIP” en este momento. Fuerza el envío de tu historial a GitHub (
git push --force
). GitHub y la historia local ahora muestran esto:A -- B -- C -- D
En ambos casos, has realizado los cambios que deseas y tu repositorio local y el control remoto de GitHub están sincronizados nuevamente. La historia es mejor en el segundo caso, pero eso es una cuestión secundaria.
Hay muchas maneras diferentes de reescribir la historia y rescatar algunas de estas situaciones, pero consideramos que los enfoques descritos anteriormente son muy accesibles.