1 == Gran Maestría en Git ==
3 Esta página con nombre pretencioso es el cajón donde dejar los trucos de Git no categorizados.
5 === Lanzamientos de Código ===
7 Para mis proyectos, Git controla únicamente los ficheros que me gustaría archivar y enviar a
8 los usuarios. Para crear un tarball del código fuente, ejecuto:
10 $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
12 === Commit De Lo Que Cambió ===
14 Decirle a Git cuándo agregaste, eliminaste o renombraste archivos es complicado
15 para ciertos proyectos. En cambio, puedes escribir:
20 Git va a mirar los archivos en el directorio actual y resolver los detalles
21 por si mismo. En lugar del segundo comando add, corre `git commit -a` si estás en condiciones
22 de hacer commit. Ver en *git help ignore* como especificar archivos
23 que deberían ser ignorados.
25 Puedes hacer lo de arriba en un único paso con:
27 $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
29 Las opciones *-z* y *-0* previenen efectos secundarios adversos de archivos que contienen
30 caracteres extraños. Como este comando agrega archivos ignorados, podrías querer usar la opción
33 === ¡Mi Commit Es Muy Grande! ===
35 ¿Postergaste hacer un commit por demasiado tiempo? ¿Estabas enfervorizado
36 escribiendo código y te olvidaste del control de fuentes hasta ahora?
37 ¿Hiciste una serie de cambios no relacionados, simplemente porque es tu estilo?
39 No te preocupes, ejecuta:
43 Por cada edición que hiciste, Git va a mostrar el pedazo de código que fue cambiado,
44 y preguntar si debería ser parte del próximo commit. Contesta con "y" o "n".
45 Hay otras opciones, como posponer la decisión; escribe "?" para saber más.
47 Una vez satisfecho, escribe
51 para hacer un commit que solo contiene los cambios seleccionados (los cambios 'staged'). Asegúrate
52 de omitir la opción *-a*, o Git va a poner todo lo editado en el commit.
54 ¿Que pasa si editaste varios archivos en varios lugares? Revisar cada cambio uno por uno
55 se vuelve frustrante y adormecedor. En este caso, usa *git add -i*, cuya interfaz
56 es menos clara pero más flexible. Con solo presionar un par de teclas, puedes poner
57 o sacar del 'stage' varios archivos a la vez, o revisar y seleccionar cambios solamente
58 en archivos particulares. Como alternativa se puede usar *git commit --interactive*, el
59 cual hace commit luego de que terminas.
61 ==== Cambios en el 'stage' ====
63 Hasta el momento hemos evitado el famoso 'indice' de git, pero ahora debemos enfrentarlo
64 para explicar lo de arriba. El indice es un área temporal de montaje. Git evita enviar
65 datos directamente entre tu proyecto y su historia. En su lugar, Git primero escribe datos
66 al índice, y luego copia los datos del índice a su destino final.
68 Por ejemplo, *commit -a* es en realidad un proceso de 2 pasos. El primer paso pone
69 una instantánea del estado actual de cada archivo administrado en el índice. El segundo paso graba de forma permanente esa instantánea que está en el índice. Un commit hecho sin *-a* solo efectúa el segundo paso, y solo tiene sentido luego de haber ejecutado comandos que de alguna forma alteran el índice, como *git add*.
71 Usualmente podemos ignorar el índice y pretender que estamos leyendo y escribiendo
72 directo en la historia. En esta ocasión, queremos un control más fino de lo que se
73 escribe en la historia, y nos vemos forzados a manipular el índice. Guardamos una instantánea de algunos, pero no todos, de nuestros cambios en el índice, y luego grabamos
74 de forma permanente esta instantánea cuidadosamente organizada.
76 === No Pierdas La Cabeza ===
78 El tag HEAD (Cabeza) es como un cursor que normalmente apunta al último commit, avanzando
79 con cada nuevo commit. Algunos comandos de Git te dejan moverlo. Por ejemplo:
83 mueve el HEAD tres commits hacia atrás. Por lo tanto todos los comandos de Git ahora
84 actúan como si no hubieras hecho esos últimos tres commits, mientras tus archivos
85 permanecen en el presente. Ver la página de ayuda para algunas aplicaciones.
87 ¿Como hago para volver al futuro? Los commits del pasado nada saben del futuro.
89 Teniendo el SHA1 del HEAD original, hacemos:
93 Pero supongamos que nunca lo anotaste. No te preocupes, para comandos como este, Git
94 guarda el HEAD original como un tag llamado ORIG_HEAD, y puedes volver sano y salvo con:
98 === Cazando Cabezas ===
100 Quizás ORIG_HEAD no es suficiente. Quizás acabas de descubrir que cometiste un error monumental
101 y que hay que volver a un commit antiguo en una rama olvidada hace largo tiempo.
103 Por defecto, Git guarda un commit por al menos 2 semanas, incluso si le ordenaste destruir
104 la rama que lo contenía. El problema es encontrar el hash apropiado. Podrías
105 mirar todos los hashes en `.git/objects` y usar prueba y error para encontrar el que buscas.
106 Pero hay una forma mucho más fácil.
108 Git guarda el hash de cada commit que hace en `.git/logs`. El subdirectorio `refs` contiene
109 la historia de la actividad en todas las ramas, mientras que el archivo `HEAD` tiene cada hash
110 que alguna vez ha tomado. Este último puede usarse para encontrar hashes de commits en branches
111 que se han borrado de manera accidental.
113 El comando reflog provee una interfaz amigable para estos logs. Prueba
117 En lugar de cortar y pegar hashes del reflog, intenta:
119 $ git checkout "@{10 minutes ago}"
121 O prueba un checkout del 5to commit que visitaste hacia atrás:
123 $ git checkout "@{5}"
125 Ver la sección ``Specifying Revisions'' de *git help rev-parse* por mas datos.
127 Podrías querer configurar un periodo de gracia mayor para los commits condenados. Por ejemplo:
129 $ git config gc.pruneexpire "30 days"
131 significa que un commmit eliminado se va a perder de forma permanente solo cuando
132 hayan pasado 30 días y se ejecute *git gc*.
134 También podrías querer deshabilitar invocaciones automáticas de *git gc*:
136 $ git config gc.auto 0
138 en cuyo caso los commits solo serán borrados cuando ejecutes *git gc* de forma manual.
140 === Construyendo sobre Git ===
142 Siguiendo la tradición UNIX, el diseño de Git permite ser fácilmente usado como un componente de bajo
143 nivel de otros programas, como GUI e interfaces web, interfaces de linea de comandos alternativas,
144 herramientas de manejo de patches, herramientas de importación y conversión, etc.
145 De hecho, algunos de los comandos de Git son ellos mismos scripts parados sobre los hombros de gigantes.
146 Con unos pocos ajustes, puedes personalizar Git para cubrir tus necesidades.
148 Un truco simple es usar los alias incluidos en git para acortar los comandos
149 usados de forma más frecuente:
151 $ git config --global alias.co checkout
152 $ git config --global --get-regexp alias # muestra los alias actuales
154 $ git co foo # igual a 'git checkout foo'
156 Otro es imprimir la rama actual en el prompt, o en el título de la ventana.
160 $ git symbolic-ref HEAD
162 muestra el nombre de la rama actual. En la práctica, es probable que quieras quitar el
163 "refs/heads/" e ignorar los errores:
165 $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
167 El subdirectorio +contrib+ es la cueva de los tesoros de las herramientas hechas con Git.
168 Con tiempo, algunas de ellas pueden ser promovidas a comandos oficiales. En Debian y Ubuntu,
169 este directorio está en +/usr/share/doc/git-core/contrib+.
171 Un residente popular es +workdir/git-new-workdir+. Usando symlinks inteligentes, este script
172 crea un nuevo directorio de trabajo cuya historia es compartida con el repositorio original:
173 $ git-new-workdir repositorio/existente nuevo/directorio
175 El nuevo directorio y sus archivos interiores pueden ser vistos como un clon, excepto que como la
176 historia es compartida, ambos árboles se mantienen sincronizados de forma automática.
177 No hay necesidad de merges, push ni pull.
179 === Acrobacias Peligrosas ===
181 En estos días, Git hace difícil que el usuario destruya datos de manera accidental.
182 Pero si sabes lo que estás haciendo, puedes hacer caso omiso de las trabas de seguridad para
183 los comandos comunes.
186 *Checkout*: Los cambios no commiteados hacen que checkout falle. Para destruir tus cambios, y hacer checkout de un commit dado, usa la opción de forzar:
188 $ git checkout -f HEAD^
190 Por otro lado, si especificas una ruta específica para hacer checkout, no hay chequeos de seguridad.
191 Las rutas suministradas son sobre-escritas de forma silenciosa.
192 Hay que tener cuidado al usar checkout de esta forma:
194 *Reset*: Reset también falla en presencia de cambios sin commmitear. Para hacerlo a la fuerza, ejecuta:
196 $ git reset --hard [COMMIT]
198 *Branch*: El borrado de una rama falla si esto causa que se pierdan cambios, para forzarlo escribe:
200 $ git branch -D rama_muerta # en lugar de -d
202 De forma similar, intentar sobreescribir una rama moviendo otra, falla si esto resultase en pérdida de datos. Para forzar el mover una rama, corre:
204 $ git branch -M [ORIGEN] DESTINO # en lugar de -m
206 A diferencia de checkout y reset, estos dos comandos evitan la destrucción de datos.
207 Los cambios están aún guardados en el subdirectorio .git, y pueden obtenerse
208 recuperando el hash apropiado de `.git/logs` (ver "Cazando Cabezas" arriba).
209 Por defecto, serán guardados por al menos dos semanas.
211 *Clean*: Algunos comandos de Git se rehúsan a proceder porque están preocupados
212 de destruir archivos no monitoreados. Si tienes la certeza de que todos los archivos
213 y directorios sin monitorear son prescindibles, se pueden borrar sin piedad con:
217 ¡La próxima vez, ese comando molesto va a funcionar!
219 === Mejora Tu Imagen Pública ===
221 Los errores estúpidos abundan en la historia de muchos proyectos. El más
222 preocupante son los archivos perdidos por el olvido de ejecutar *git add*.
223 Por suerte nunca perdí datos cruciales por omisión accidental, dado que muy rara
224 vez elimino directorios de trabajo originales. Lo normal es que note el error un par
225 de commits mas adelante, por lo que el único daño es un poco de historia perdida
226 y el tener que admitir la culpa.
228 También me preocupo por no tenes espacios en blanco al final de las líneas.
229 Aunque son inofensivos, procuro que nunca aparezcan en la historia pública.
231 Además, si bien nunca me sucedió, me preocupo por no dejar conflictos de merge
232 sin resolver. Usualmente los descubro al compilar el proyecto, pero hay algunos
233 casos en los que se puede no notar.
235 Es útil comprar un seguro contra la idiotez, usando un _hook_ para alertarme de
239 $ cp pre-commit.sample pre-commit # En versiones mas viejas de Git: chmod +x pre-commit
241 Ahora Git aborta un commit si se detectan espacios inútiles en blanco o conflictos
242 de merge sin resolver.
244 Para esta guía, eventualmente agregué lo siguiente al inicio del hook *pre-commit*,
245 pare prevenirme de la desatención.
247 if git ls-files -o | grep '\.txt$'; then
248 echo FALLA! Archivos .txt sin monitorear.
252 Varias operaciones de git soportan hooks; ver *git help hooks*. Se pueden escribir
253 hooks para quejarse de errores ortográficos en los mensajes de commit, agregar nuevos archivos,
254 indentar párrafos, agregar una entrada en una página, reproducir un sonido, etc.
255 Habíamos activado el hook *post-update* antes, cuando discutíamos como usar Git sobre HTTP.
256 Los hooks se ejecutan cada vez que la rama HEAD sufre cambios. Este hook en particular actualiza
257 algunos archivos que Git necesita para comunicación no nativa (como HTTP).