elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Tutorial básico de Quickjs


  Mostrar Mensajes
Páginas: 1 ... 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 [49] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 ... 448
481  Programación / Desarrollo Web / Re: problema con mis rutas al subir proyecto a mi servidor vps en: 21 Diciembre 2020, 16:55 pm
jajajaja muy buena #!drvy xD

proteus8 recuerda que Ajax y javascript los interpreta el navegador, entonces las rutas deben ser URL'S no una ruta path (del directorio, sistema de ficheros).

/var/www/cga/public/js no es una URL válida  :P

Y la URL tampoco se tiene que ver reflejada en el sistema de archivos aunque con los servidores que usan PHP esto es el comportamiento por defecto por lo general.

Pero es posible que si tienes:

/var/www/cga/public/resources/views/system/post1.php

No pueda ser alcanzado desde:

www.tudominio.com/cga/public/resources/views/system/post1.php

Y use cualquier otra ruta. Dependiendo de la configuración del servidor.
482  Programación / Desarrollo Web / Re: problema con mis rutas al subir proyecto a mi servidor vps en: 20 Diciembre 2020, 23:17 pm
La ruta no es relativa al archivo javascript sino a la url que carga ese archivo archivo javascript. Necesitamos la ruta del cual se ejecuta ese javascript.
483  Foros Generales / Foro Libre / Re: ¿Os pondreis la vacuna del Coronavirus cuando este disponible?. en: 15 Diciembre 2020, 23:57 pm
La gente no se da cuenta que la mayoría de los avances en medicina están burocratizados a morir. Por lo tanto los avances se perciben de forma lenta. Pero ante una amenaza global es más que obvio que van a poder agilizar el proceso debido a la situación. ¿Es más riesgoso? Sí, pero aún así, la diferencia debe ser mínima. En especial si tenemos varias organizaciones de diferentes países aprobando las vacunas.

La vacuna es importante seas joven o no. Tengas una predisposición o no.  Esto no es acerca de tu salud solamente. Esto es acerca de la salud de TODOS. Piensa que no todos van a poder tener acceso a la vacuna. En mi opinión es tu obligación moral inmunizarte contra este virus.

Pero ya me dirá alguien que la salud de los otros no es su problema (y por eso estamos como estamos).
484  Foros Generales / Sugerencias y dudas sobre el Foro / Re: ¿Postear este link está permitido? en: 15 Diciembre 2020, 23:37 pm
Esto no suena a algo que sea gratis exactamente... Por lo que dices, suena a un servicio al estilo Netbux (y ese es básicamente una estafa).
485  Programación / Programación General / Trabajando con las ramas de git (tercera parte) en: 14 Diciembre 2020, 18:30 pm
Indice

Prefacio

En este tema se explora como trabajar con las ramas de git. Es una continuación de este tema.

Como recordatorio, esta es una guía informal a git.

Las ramas en git

Como habíamos dicho en un principio, las ramas para git son simplemente apuntadores los cuales contienen identificadores de commit. Esto nos permite poder obtener fácilmente una procedencia de commits a la cual normalmente llamamos un historial. Es decir, cada commit que puede ser alcanzado por una determinada rama introduce un determinado número de cambios en la que culminan con el último commit de la rama, la última entrada de nuestro historial.

De esta forma podríamos decir que multiples ramas nos podrían permitir trabajar con diferentes conjunto de cambios sobre un mismo repositorio. Por lo general, no tratamos con conjuntos de cambios exclusivos entre cada rama lo que quiere decir que las ramas por lo general tienen un tronco en común aunque también es posible tener ramas completamente diferentes entre ellas.

Cuando creamos un repositorio en git y empezamos a crear commits, lo hacemos sobre una rama por defecto cuyo nombre es master (lo cual está sujeto a cambiar a main). Sin una rama en la cual apunte a un determinado commit no podríamos tener un historial adecuado. Sería tener un montón de commits en los cuales no tendríamos idea cual es nuestro estado actual. Si bien es cierto que cada commit contiene en sí puede reproducir un historial ya que tiene cada uno tiene una línea de procedencia, no podríamos saber con exactitud si este commit es la punta actual de una rama. Sin mencionar que git se encarga de eliminar commits que no pueden ser alcanzados por una rama. Ultimadamente, son nuestras ramas las que dan forma a nuestro historial.

Resulta entonces indispensable tener por lo menos una sola rama. ¿Pero porque necesitaríamos trabajar con más de una rama? A lo cual yo respondería que no es exactamente imposible trabajar con una sola rama, pero en mi opinión estaríamos obviando una de las ventajas más importantes de git. Es extremadamente común que un proyecto opte por avanzar de diferentes maneras y mantenga diferentes estados por diferentes razones. Cada rama extra en sí representa una nueva posibilidad para avanzar el desarrollo del repositorio. Como desarrollador, escritor, diseñador, animador, arquitecto... ¿Cuantas formas hay de realizar un determinado trabajo? Muchas formas. Estaríamos limitándonos si no entretuviéramos la idea de que quizás podríamos hacer las cosas de manera distinta. Las ramas entonces nos resultan convenientes para poder explorar las alternativas.

Pero estoy adelantándome un poco. La función de está parte de la guía es en sí como manipular las ramas en git, no exactamente como usarlas. Podría enumerar multiples escenarios en las cuales usar multiples ramas es útil, pero creo que sería mejor sí enseño y ejemplifico los usos que yo les he dado.

Listando las ramas

Antes de empezar, revisaremos el estado de nuestro repositorio ejemplo:



Y podemos ver aquí, que tengo un archivo sin rastrear pendiente por agregar al indice: password.txt. En nuestro directorio de trabajo también encontramos los archivos introducidos por 4 commits, un archivo de prueba, un archivo importante y un README el cual contiene el nombre de mi proyecto. Podemos ver también que estamos en la rama master. Por ahora eliminare el archivo password.txt ya que no me interesa.

Código
  1. rm -f password.txt

Ahora lo primero que tenemos que saber acerca de las ramas es... como listarlas. Para esto utilizaremos el comando:

Código
  1. git branch

El cual lista las ramas locales (más adelante veremos los otros tipos de ramas).



git branch colorea la rama en la que estamos de color verde y la marca con un asterisco. Si requerimos más información podemos usar el argumento -v, el cual nos índica el último commit al que apunta (entre otras cosas que veremos adelante).



Creando una nueva rama

Supongamos que quiero agregar nuevos cambios a mi repositorio. Tengo una nueva idea que necesito implementar en mi repositorio y no estoy seguro que vaya a funcionar. Podría trabajar sobre la rama master y si no me gusta lo que he hecho podría simplemente usar git reset para regresar mi rama a su lugar. Tendría que anotar en algún lado el commit al cual quiero regresar la rama o mirar en el log el commit al cual quiero regresar. O podría crear una rama extra para trabajar.

Para crear una rama simplemente usare:

Código
  1. git branch ramanueva



Aquí he agregado una nueva rama llamada: cambios-importantes. git branch me dice que la rama existe y está apuntando al mismo commit que master (5a76bed en mi caso). Podemos ver que la rama actual en la que estoy es master como lo índica git branch.

Cambiando a la nueva rama

Necesito usar la rama cambios-importantes,¿como hago el cambio de ramas? Recordemos que la rama actual en la que estamos esta dada por HEAD y necesitamos mover HEAD a que use está nueva rama. ¿Que comando podemos usar?

Código
  1. git checkout rama/commit

¿Recuerdan este comando? Lo utilizamos para inspeccionar commits pero está vez lo utilizaremos para hacer el cambio a la nueva rama.



Y ahora hemos cambiado de rama y estamos en cambios-importantes. Si revisamos con git status y con git branch:



Podemos ver que ahora ambos reflejan el cambio. git log --oneline también ha cambiado el texto un poco:



HEAD ahora apunta a cambios-importantes aunque también podemos ver que dice master a un lado. ¿Que significa esto? Que tanto cambios-importantes como master están apuntando a este commit. Solo que en está ocasión HEAD -> ya no usa master. git log nos está ayudando a encontrar la posición de nuestras ramas, como lo hace git branch -v.

Trabajando sobre la nueva rama

Bien, ahora lo que hare es crear una serie de commits sobre esta nueva rama. Para ejemplos de esta demonstración introduce cambios arbitrarios sobre archivos foo.txt, bar.txt y baz.txt y cada uno tendrá su propio commit.



Creo que los comandos que he usado se deben de poder entender fácilmente, ya que son los mismos comandos que he usado, solo que los he "juntado" con && para ahorrar espacio. Es un poco difícil ver los cambios como están mostrados por git log. Así que usare una gráfica.

Este es el estado que teníamos antes de hacer los commits. En el momento en que he agregado la nueva rama:


Cuando hicimos git checkout sobre la nueva rama:


Y cuando agregamos los tres commits:


Y aquí tenemos dos puntos marcados por las ramas. En un punto tenemos los nuevos archivos agregados, foo.txt, bar.txt, baz.txt y en el otro no. Podemos olvidarnos completamente de estos cambios haciendo:

Código
  1. git checkout master

Y si queremos volver a nuestros cambios:

Código
  1. git checkout cambios-importantes

Usando multiples ramas para mantener multiples versiones

Supongamos que no estoy contento con el trabajo que he hecho en la otra rama y quiero hacer cambios diferentes. Volveré a la rama master donde están nuestros archivos antes de estos cambios:



Y ahora creare otra rama a la cual llamare, cambios-importantes-2. En está ocasión, utilizare otro comando para crear la rama. La rama anterior ha sido creada llamando a git branch directamente. Posteriormente cambie de la rama master a la nueva rama usando git checkout. He usado dos comandos para crear y cambiar a la rama. git nos permite hacer las dos cosas al mismo tiempo porque es un caso de uso muy común y el comando que se usa es... git checkout con el argumento -b

Código
  1. git checkout -b ramanueva



Me he creado la rama y ha hecho el cambio. Ahora, verificamos el estado de todas nuestras ramas:



Nuestra nueva rama está apuntando al mismo commit que master y HEAD está apuntando a la nueva rama. Bien, digamos ahora que mi solución es crear solo dos archivos en lugar de tres: foobar.txt y baz.txt



Y aquí podemos ver los nuevos commits creados. Nuestro git log nos muestra todos los commits de los cuales podemos acceder desde cualquier rama en nuestro repositorio pero realmente el listado no nos dice mucho. git log nos está mostrando nuestros commits en orden cronológico inverso (últimos primero) pero esto realmente no nos ayuda a mentalizar nuestra línea de procedencia. Para esto, git log tiene un argumento que nos puede ayudar a visualizar nuestro historial mejor:

Código
  1. git log --graph

Lo útilizaremos en conjunto con --oneline (para abreviar) y --all para mostrar todos los commits.



git log ahora nos da una mejor representación de las ramas que tenemos, podemos ver que de master las dos ramas están divergiendo. Tenemos la punta de cambios importantes en 0a4e950 y la punta de cambios-importantes-2 en 77c64b1. Quizás no es del todo claro el formato que está dando git log así que también creare una gráfica:


Digamos ahora que no estoy convencido con ninguna de estas dos ramas y quiero crear una tercera rama. En las dos últimas ocasiones, hemos estados situados sobre la rama master antes de crear la rama ya que tanto git checkout como git branch utilizán HEAD para indicar donde es que la nueva rama debe apuntar. Sin embargo, las dos herramientas nos permiten especificar el commit o rama al cual queremos que nuestra nueva rama apunte. En esta ocasión, quiero crear una nueva rama cambios-importantes-3 que empiece en master (así fue con las otras dos ramas) y quiero cambiar inmediatamente a está nueva rama también. Usare:

Código
  1. git checkout -b nuevarama puntodepartida



Aquí creamos nuestra nueva rama cambios-importantes-3, empieza desde master y hemos hecho el cambio a esta rama también. Aquí verificamos otra vez el estado de nuestro repositorio:



Y en está rama nos interesa tener dos archivos: foobaz.txt y bar.txt. Así que creare los commits respectivos:



git log por desgracia es un poco difícil de leer porque las bifurcaciones no las imprime en paralelo y no hay una sola columna para cada rama. Si tienen problema visualizando, las trazare encima para que las puedan ver mejor:



Pero también utilizaré una gráfica nuevamente para mostrar el estado de nuestro repositorio:


Obteniendo las diferencias de cada rama con respecto a una rama en común

Ahora, revisaremos el estado de cada uno de las ramas con el comando git diff:

Código
  1. git diff rama/commit rama/commit

Donde la primera rama/commit es el punto inicial y la segunda rama/commit es el punto final. El comando como lo indica, nos regresa las diferencias entre dos commits. El orden es importante. Supongamos que A y B son dos commits que marcan dos estados diferentes de nuestro código. Dentro de A no existe C.txt pero si existe dentro de B. Si usamos git diff B A, nos dirá que la diferencia es que se ha borrado C.txt. En cambio, si usamos git diff A B nos dirá que se ha agregado C.txt.

Lo probaremos sobre la rama cambios-importantes:



Y aquí nos muestra que la diferencia entre nuestra rama master y cambios-importantes es que cambios-importantes ha creado 3 archivos, bar.txt, baz.txt, foo.txt.

No es necesario que los commits sean parientes para poder hacer git diff. También podría usar git diff entre las diferentes ramas que hemos creado:



Por ejemplo, en este caso, cambios-importantes-2 no tiene foo.txt ni bar.txt pero si tiene foobar.txt.

Las ramas que hemos creado no son excepcionalmente diferentes, cada uno agrega archivos diferentes. Usare el argumento --name-status para solo imprimir los archivos que fueron agregados.



Conozco muy bien los cambios en cada archivo así que no es necesario hacer una inspección completa.

Eliminando una rama

Digamos ahora que no me ha gustado para nada los cambios en cambios-importantes-3. ¿Como puedo deshacerme de esa rama? Para eso usamos el comando:

Código
  1. git branch -d rama-a-borrar



Para nuestra sorpresa, git no nos ha dejado eliminar la rama porque estamos usándola. Tendremos que cambiar de rama primero y en está ocasión simplemente ire a master:



Y nuevamente, git no nos ha dejado borrar la rama! En esta ocasión git nos advierte que la rama no está integrada desde la rama en la que estamos trabajando. Desde el punto de vista de git los commits que hemos creado en esa rama serían inalcanzables desde nuestra rama actual. En pocas palabras, existe la posibilidad de perder una forma de acceder a los commits si borramos esta rama. Eso es exactamente lo que queremos así que vamos a forzar el borrado de la rama (con la opción -D):



Recuerden que los commits que no son alcanzables por una rama son eventualmente borrados pero hasta entonces, todavía siguen existiendo. Podríamos recrear esta rama nuevamente con el comando:

Código
  1. git branch rama commit

Y no perderíamos absolutamente nada.

Integrando cambios de una rama a otra

Ahora he reducido mis opciones a dos posibles ramas, digamos que en este caso me interesan los cambios en cambios-importantes. Podría continuar trabajando sobre esta rama pero el propósito de la rama fue la de introducir un número de cambios y no usar esta rama como la principal. Quiero conservar mi rama master como la rama principal (veremos más adelante el uso de una rama principal). Así que necesito integrar los cambios hechos sobre la rama cambios-importantes e incluirlos en mi rama principal master.

Para simplificar el proceso, primero observemos como se ve nuestra rama master.


Y nosotros queremos incluir los cambios que están en cambios-importantes. Es decir, queremos obtener los cambios introducidos por los commits en cambios-importantes:


Esto es un trabajo para git merge.

Código
  1. git merge rama

Veamos que es lo que hace:



Fast Foward Merge

Lo primero que vemos es que dice que está actualizando 5a76bed contra 0a4e950, ambos son los commits para sus ramas correspondiente. En este caso, hicimos git merge en la rama master (es la rama en la cual estamos, HEAD) y está tratando de actualizar los cambios provenientes de cambios-importantes. La segunda línea nos dice que ha hecho un Fast-Forward y enseguida nos muestra un resumen de los cambios. La segunda línea resulta muy importante porque nos ha dicho que ha hecho un "Fast-Forward Merge". "Fast-Forward" en español se traduce literalmente a "Avance Rápido". Sus reproductores multimedia tienen una función similar de la cual git se ha inspirado para nombrar, el botón que realiza esta función es ⏩.

En este caso no estamos trabajando con un archivo multimedia, sino con una rama. Estamos "avanzando" o "adelantando" la rama. git simplemente ha movido la rama hacia "adelante". Si revisamos nuestro historial con git log podremos ver que nuestra rama master ahora está en la misma posición que cambios-importantes.



Y si queremos visualizar como git ha avanzado nuestra rama, podemos usar una gráfica:


El resultado es entonces que nuestra rama master ahora incluye los commits de cambios-importantes. Está es quizás la manera más sencilla de incluir los cambios de otra rama puesto que solo hemos movido la rama (algunos inclusive dirían que no es un merge verdadero). Podría volver a trabajar sobre la rama cambios-importantes y volver hacer git merge sobre esta, el resultado volvería a ser el mismo. Esto es porque master seguiría siendo un ancestro de cambios-importantes. Dicho de otra manera, cambios-importantes es un descendiente de master. ¿Pero que pasaría si la rama que quiero incluir no es un descendiente de nuestra rama?

3-Way Merge

Tenemos una rama que es así de momento, cambios-importantes-2. La punta de está rama no es descendiente de ninguna de las otras ramas pero ambas si tienen un ancestro en común:



Y usando la gráfica:


Como podemos ver tanto en el git log como en la gráfica, no hay forma de llegar de master o cambios-importantes a cambios-importantes-2. Digamos que quiero incluir estos cambios en master. ¿Que es lo que ocurriría?

En esta ocasión usamos el nombre de la otra rama para git merge:



Y ahora nos salta nuestro editor de texto pidiéndonos un mensaje para un commit. Trágicamente, no sabemos que demonios está pasando aquí.



Por lo pronto, dejamos el mensaje por defecto del commit, guardamos y cerramos. Y ahora nuestra terminal nos dice más:



Y la primera línea nos dice que ha utilizado la estrategia recursiva y que ha agregado un archivo foobar.txt. ¿Pero porque nos ha pedido un mensaje para un commit? Revisemos el historial nuevamente con git log:



En la gráfica:


Y esto es interesante porque aquí podemos ver un nuevo commit que no aparecía antes: 577fe28 con el mensaje que hemos puesto antes en nuestro editor. Lo que es más, master ahora apunta a este commit. ¿Que es lo que realmente ha pasado?

Esto es lo que se conoce como un '3-Way Merge' que es simplemente una manera de decir que se han usado tres puntos de referencia para crear un nuevo conjunto de cambios  que incluye los cambios entre dos puntos. Un nombre en español apropiado quizás sería "Unión basado en tres puntos" pero a lo largo de esta guía seguire usando el termino "3 Way merge" ¿Porque usa un tercer punto de referencia si nosotros solo le hemos pedido que incluya una rama dentro de otra (2 puntos)?

La manera más sencilla de entender porque usa un tercer punto de referencia, es tratar de usar solo estos dos puntos de referencia. Haremos un git diff entre cambios-importantes y cambios-importantes-2 para ver cuales son las diferencias entre las dos ramas:



Podemos ver que foo.txt ni bar.txt no aparecen en cambios-importantes-2 y foobar.txt no aparece en cambios-importantes:


Ahora, observando SOLO estás diferencias quisiera preguntarte: ¿cambios-importantes-2 agrega un archivo foobar.txt o cambios-importantes borra un archivo foobar.txt? La respuesta es... que no lo sabrías si solo tuvieras estos puntos de comparación. ¿Pero si tuvieras un tercero?


Ahora sí puedo deducir quien ha hecho que cambios. El tercer commit es un punto de referencia que se usa como base. Es comúnmente el ancestro común más cercano (la antigua posición de master). La estrategia que usamos tiene algo que decir cuando seleccionamos nuestro ancestro común. En este determinado caso, el ancestro común es bastante claro pero en otras situaciones quizás no tanto. La estrategia que git usa por defecto es la recursiva, la cual hace un "3-Way Merge" con los dos ancestros para usar esta como punto de referencia base.

Como podemos ver, git a producido un conjunto de cambios que no aparecen en ningún commit, así que git no puede simplemente mover la rama, tiene que crear un nuevo commit. Este commit contendrá todos los cambios y el mensaje que nos ha pedido git es para este nuevo commit. Este nuevo commit será algo diferente de nuestros otros commits, tendrá dos padres en lugar de uno. A este tipo de commits generalmente nos referimos por "merge commits" y para muchos, su peor pesadilla.

Estábamos en la rama master que era idéntica a cambios-importantes y nosotros buscábamos incluir los cambios de cambios-importantes-2 sobre master así que los cambios introducidos son básicamente la diferencia sobre el resultado y cambios-importantes (foobar.txt es agregado). Está es la explicación de porque obtuvimos esos cambios específicos en la terminal.

Deshaciendo un merge

Sigamos explorando git merge. Para esto, voy a regresar a master a donde estaba antes de hacer el último merge. Para esto simplemente podemos revisar git reflog sobre master. Yo se que master@{0} es el commit en el que estamos ahora mismo, master@{1} antes de hacer el último merge.

Volvamos entonces a este punto con git reset --hard:



Y básicamente he deshecho el merge. Todo merge puede ser deshecho con git reset puesto que al apuntar la rama a donde estaba podemos deshacer los cambios. Como punto adicional pude haber deshecho los dos merges si hubiese usado hecho git reset a master@{2}. La razón por la cual hago uso de --hard es porque el commit que git crea hace cambios sobre nuestro directorio de trabajo para hacer el commit, recuerden que agrego el archivo foobar.txt. Si hubiese usado --soft o --mixed hubiese conservado los cambios en el directorio de trabajo que hizo git para preparar el merge commit.

Resolviendo conflictos

Ahora, quiero hacer un cambio sobre cambios-importantes-2, primero hago un checkout a la rama:



Quiero introducir otros cambios sobre 'baz', así que hare git reset sobre el último commit (cambios-importantes-2~ es otra forma de referirnos al commit padre de cambios-importantes-2).



Recreare el archivo desde 0 y volveré a la rama master



Ahora podemos hacer nuestro merge nuevamente:



Y ahora nos dice que git merge ha fallado. ¿Que es lo que ha ocurrido? Revisemos el diff entre cambios-importantes y cambios-importantes-2:



Para visualizar mejor las diferencias usare la tabla anterior:


No ha cambiado mucho, seguimos usando la misma base de la cual hacemos comparaciones. Pero ahora baz.txt tiene dos cambios diferentes. En cambios-importantest (y por ende master) tenemos "baz" dentro de baz.txt pero en cambios-importantes-2 tenemos "foo" en baz.txt. Nuestras dos ramas contienen cambios diferentes y git no sabe si conservar los cambios de una rama o de la otra. Esto es lo que se conoce como un merge conflict. Revisemos git log y git status.



Nuestro git log nos muestra que no hemos movido master pero git status nos dice que tenemos "unmerged paths". Tenemos un nuevo archivo agregado al indice (que también está en el directorio de trabajo) pero tenemos una nueva sección que dice "unmerged paths" y dice que "ambos" han agregado baz.txt. ¿Como es esto posible? Si yo agrego un archivo al índice este debería remplazar el anterior. Revisemos a detalle el estado del índice:



Y aquí aparecen dos copias de baz.txt, cada uno con su objeto diferente y un número 2 y 3. La primera pregunta de la mayoría probablemente sea y ¿Porque no aparece 1? La razón de esto es que durante un conflicto git mantiene 3 copias del archivo el cual contiene un conflicto. Mantiene la copia de la base bajo el número 1 pero como no existe el archivo baz sobre nuestro ancestro común (son archivos agregados en ambas ramas) no existe está copia. También mantiene la copia de la rama en la que estamos trabajando y otra copia de la rama que estamos incluyendo.

¿Pero como lo solucionamos? Tenemos que agregar el archivo en conflicto nuevamente al índice. Pero primero exploremos el contenido de nuestro archivo baz.txt:



Nos ha remplazado el contenido de nuestro archivo por lo que se conoce como un marcador de conflicto. git ha explorado ambas versiones y ha puesto los cambios de ambas ramas en el mismo archivo (usando separadores textuales). De forma que nosotros podemos elegir cual de los dos cambios conservar. Es decir, git necesita que nosotros le indiquemos cual de los cambios conservar. No hace falta que sea una de las dos opciones, git simplemente nos informa los cambios de ambas ramas pero nosotros podemos remplazar el marcado de conflicto por cualquier otra cosa. En este caso, voy a optar por conservar los cambios de mi rama. Así que simplemente pondré "baz":



No lo he agregado al índice todavía pero observen como es que git status no nos menciona nada acerca de los nuevos cambios en nuestro directorio de trabajo. La razón de esto es muy sencillo, a pesar de que hice cambios sobre el archivo en el directorio de trabajo. Este archivo es exactamente el mismo que tenemos en nuestro último commit por eso no está detectando ningún cambio. Agreguemos el archivo al índice y revisemos nuevamente.



Ahora solo aparece un solo baz.txt, git status nos dice que hemos resuelto los conflictos pero que todavía estamos en medio del merge. Nos dice que usemos git commit para finalizar el merge.



Hacemos git commit para finalizar el merge. Nos debe abrir el editor de texto para introducir el mensaje del commit. Y listo! Hemos incluido la rama cambios-importantes-2 dentro de master.

Opciones para git merge

Antes de movernos al siguiente tema, mencionare unas opciones importantes con git merge.

Primero, tenemos el argumento --ff-only.

Código
  1. git merge rama --ff-only

El cual NUNCA intenta hacer un "3-Way Merge" y solo nos permite hacer un 'Fast-Forward Merge", fallará si no puede.

Código
  1. git merge rama --no-ff

Lo contrario a --ff-only, esto SIEMPRE crea un "merge commit" usando un "3-Way Merge".

Código
  1. git merge rama --no-edit

Evitamos tener que escribir un mensaje y usamos el mensaje por defecto.

Código
  1. git merge rama -m "Mensaje de commit"

Esto es bastante obvio, nos permite establecer el mensaje del commit sin usar nuestro editor.

Lo caótico que pueden ser los merge commits

Es muy probable que en algún momento tengan que integrar código de otras ramas muchas veces y no siempre será posible hacer un "Fast-Forward Merge". Este es un problema muy común colaborando con otros individuos dentro de un proyecto pero veremos más acerca de esto en la siguiente parte de la guía. Por ahora, volvamos a nuestro ejemplo. Visualicemos una vez más el estado de nuestro repositorio:


Los caminos no han cambiado realmente, lo único diferente es que tenemos dos commits diferentes puesto que los he cambiado para demostrar el último ejemplo.  Supongamos que ahora sigo trabajando sobre master y hago un commit:



Y ahora vuelvo a la rama cambios-importantes porque digamos que quiero agregar unos nuevos archivos y no los quiero en master aún porque no estoy seguro que los quiero incluir en master. Estoy usando cambios-importantest solo para ejemplificar la situación pero normalmente esto no es lo común. Es cierto que pude haber empezado una nueva rama desde el último commit en master (y esto es lo recomendable).



Nuestro historial es ahora un poco más caótico:




Ahora, digamos que quiero incluir los cambios que hay en master para probar si no perjudica en algó lo que tengo en cambios-importantest. Desde cambios-importantest hare un merge:



Revisemos nuevamente el estado de nuestro historial:



Algo difícil de seguir con git log pero ahí está el merge sobre master. La gráfica se está volviendo también más complicada.


Ahora, puedo  volver a master hacer merge sobre la rama cambios-importantes y simplemente movería la cabeza de master a cambios-importantes, en pocas palabras un "Fast-Forward merge". Creo que este historial no es exactamente limpio y tiene potencial para complicarse más y más. ¿Que podemos hacer entonces? Volvamos un paso atrás.




Alterando nuestras ramas para tener un historial más simple

Ahora en lugar de usar git merge para incluir los cambios, vamos a utilizar otra herramienta: git rebase. El uso de git rebase se explica fácilmente, cambiar la base de nuestro commit por otro nuevo. El comando es:

Código
  1. git rebase rama/commit

Básicamente cambiaremos la base de la rama en la cual estamos ejecutando el comando. Entonces, podemos hacer:



El comando nos dice que ha hecho el rebase sin ningún problema y también ha actualizado la rama. Nuestro historial ahora se ve más limpio. La gráfica por si alguien se lo pregunta:


Por favor noten que la rama master sigue apuntando al mismo commit lo que significa que los cambios aún no son parte de master, la rama cambios-importantest es la que ha cambiado. Si hiciera git merge de la rama cambios-importantest desde master este sería un "Fast-Forward Merge" y no habría necesidad de hacer un merge commit.

Habrá alguien que habrá notado que cambios-importantes ahora apunta a otro commit diferente. De e46a69a a db982e0. La razón de esto es que git rebase no ha cambiado el padre del commit (como su nombre sugiere), sino que ha creado un commit exactamente igual reproduciendo los cambios exactamente igual sobre la nueva base (la rama que especificamos, en este caso master).



Lo que git rebase hace realmente es encontrar una basa, un ancestro en común, entre la rama que queremos mover y la rama a la que queremos movernos. Lo siguiente que hace es encontrar las diferencias entre la base y la rama que queremos cambiar. En este caso, la diferencia es un solo commit y ahora querrá aplicar los cambios sobre la rama que le hemos especificado.

git rebase intentará aplicar los commits tal y cual sean la diferencia entre la base y la rama a mover. Sin embargo, git rebase es algo inteligente y no aplicará commits cuyos cambios ya se encuentran dentro de la rama. También detectará si algún commit sobre la rama introduce un conflicto como lo haría git merge en un "3-Way Merge". En ese punto, git rebase se detendrá, te dejará resolver el conflicto y tendrás que decirle a git rebase que continue con:

Código
  1. git rebase --continue

Deshaciendo un rebase

git rebase no elimina commits y podemos deshacer lo que hemos hecho fácilmente con git reset --hard sobre la posición en la que estaba anteriormente.



Incluyendo cambios sin hacer un merge propio

Ahora, en un principio yo quería incluir master dentro de mi rama cambios-importantes y lo que he hecho en mi explicación pasada es incluir cambios-importantes dentro de master que es el objetivo final. Pero en el ejemplo pasado no tuvimos la posibilidad de probar si los cambios en master funcionarían en cambios-importantes.

Podría hacer un git rebase sobre master para obtener este commit y agregarlo mi rama cambios-importantes pero tengo una mejor idea vamos a replicar  este commit en master en cambios-importantest. Para esto utilizare la herramienta git cherry-pick:

Código
  1. git cherry-pick rama/commit

git cherry-pick es una herramienta muy parecida a git rebase porque ambos aplican cambios de un número de commits sobre una rama. La diferencia está en que uno busca "deshacer" commits sobre la rama en la que está para aplicarlos sobre otra rama recipiente sin modificar esta otra. En cambio git cherry-pick busca obtener cambios de otra rama para aplicarlos sobre la rama recipiente y si actualiza está rama mientras que la otra rama permanece igual. Aquí en acción:




He duplicado el commit ce894c3 sobre cambios-importantes y ahora puedo verificar que los cambios no afectan lo que he hecho sobre la rama. Ahora, puedo simplemente revertir la rama antes de este nuevo commit pero mi intención es volver a hacer git rebase:



Ambas ramas incluían el commit "Agrega archivo foobaz", sin embargo al hacer git rebase este no incluyo este commit. La razón es como había explicado, git rebase aplica cambios de manera inteligente y en este caso se ha fijado que los cambios ya estaban en la rama. Así que no los incluye. Una gráfica para ser más específico:


Caso más elaborado para git rebase

git rebase realmente puede hacer mucho más. Es una herramienta muy flexible. Puedo tomar un rango de commits y aplicarlos sobre otro commit. Por ejemplo, digamos que quiero quitar ese merge commit que salió de master y cambios-importantes-2. Tendría que tomar todos estos commits:


Y aplicarlos sobre 0a4e950.

Esto es bastante sencillo de hacer. Primero haré el merge de cambios-importantest ya que ahora master está un commit detrás (el rebase anterior no ha movido master sino cambios-importantes).



Ahora sí, el estado concuerda con nuestra gráfica anterior. Ahora hare el rebase:



Resolviendo un conflicto de git rebase

En un momento explicare el comando, pero quiero mostrarles que en efecto ahora mismo estoy haciendo el rebase pero me ha dado un conflicto. ¿Recuerdan que en nuestro último ejemplo de git merge teníamos un conflicto con baz.txt al hacer git merge? Tuvimos que solucionar el conflicto manualmente y hacer git commit. Ahora git rebase nos está pidiendo exactamente lo mismo. Nosotros no optamos por conservar los cambios de baz.txt que introducía 32eb804 y este es el único cambio que introducía ese commit. Podemos revisar que otros cambios contiene git status:



Aquí es cuando la gente entra en pánico porque git status advierte que estás en medio de un rebase... interactivo?? Si, como he dicho antes git rebase es una herramienta sumamente flexible. Un rebase interactivo no es nada más que un rebase en el cual podemos interactuar entre cada uno de los pasos que realiza. De hecho podemos inclusive modificar o añadir nuevos pasos. Pero nuestra intención no fue la de empezar un rebase interactivo, simplemente nos ha tocado un conflicto y ahora tenemos que arreglarlo así que nos ha puesto en el modo interactivo para poder arreglarlo. Lo importante aquí es que no hay ningún otro cambio fuera de baz.txt. Si hubiese otros cambios tendríamos modificaciones por agregar al índice y no hay ninguna otra mas que el conflicto en baz.txt. El modo interactivo es una historia para otra guía :).

Como este commit no haría nada (porque mi intención es dejar a baz.txt con baz, cambio ya agregado desde la base) voy a saltarme este commit con el comando que me advierte git status:

Código
  1. git rebase --skip

En el caso de que necesitamos resolver un conflicto de manera que produce cambios diferentes a un commit existente tendremos que realizar el proceso común, agregar los cambios al índice y hacer commit o git rebase --continue. Las opciones son muy similares a las que nos ofrece git merge. --continue, --abort, etc. Creo yo que son bastantes intuitivas y no necesitan explicación.



Fuera de ese conflicto... la operación ha sido todo un éxito. Veamos como está nuestro git log ahora:



Nuestra historia ahora es completamente "lineal". No hay bifurcaciones ni nada por el estilo. Si comparamos las dos salidas nos damos cuenta que son los mismos commits con la excepción de dos commits. El merge commit ya no existe porque por defecto git rebase no recrea los merge commits. Teníamos también dos instancias de commits: "Agrega archivo baz" pero yo le he dicho a git rebase que simplemente salte este commit y no lo aplique. Fuera de estos dos commits, los otros commits que existen tienen los mismos cambios aunque sean nuevos commits (solo 3 son nuevos commits).

Como funciona git rebase realmente

Exploremos más a fondo que hace este comando. El primer argumento a git rebase es la rama/commit del cual obtener una base obtener el conjunto de commits a aplicar sobre nuestro objetivo.

Código
  1. git rebase A

Si A es un ancestro directo de HEAD (la rama que estamos revisando) esto significa que la base será A. El conjunto de commits seleccionados incluye todos desde A hasta HEAD pero sin incluir A. En otras palabras si tenemos:

Código:
A <- B <- C <- D <- E

Donde E es HEAD, el conjunto de commits que se selecciona es B, C, D y E. La cosa se complica más cuando se usa un commit que no es un ancestro directo como base. git tendrá que encontrar un ancestro en común entre los dos commits y ese será la nueva base. Los commits a aplicar siguen excluyendo está base pero incluyen todos los demás commits que no son ancestros del otro commit. En mi opinión es mucho más sencillo siempre especificar la base manualmente pero también se lo pueden dejar a git.

El segundo argumento que podemos establecer es la rama a la cual queremos hacer el rebase. Esto implica cambiar a la rama especificada, por ende cambiar HEAD. En mis ejemplos no he puesto la rama master porque HEAD ya es master. Especificar la rama no haría nada más que ser más explícitos con el comando. Recuerden que el conjunto de commits a aplicar se mide desde la base a HEAD. Está rama también será desplazada una vez que los nuevos commits hayan sido agregados sobre el objetivo.

Por defecto, el objetivo al cual aplicar los commits es también el primer argumento. Esto quiere decir que los commits obtenidos que no se encuentran en la línea de procedencia del primer argumento a la base en común de los dos commits/ramas son agregados sobre este primer argumento. En el caso en que la base coincida que también es el primer argumento (porque el primer argumento es ancestro de HEAD) el resultado sería recrear todos los commits en esa línea de procedencia. Si el primer argumento está un commit adelante de la base, en la línea de procedencia de la rama que está siendo movida (HEAD) tendrá la diferencia entre su posición original y la base más ese único commit.

Por fortuna, nosotros podemos especificar un objetivo diferente que la rama/commit que se usa para establecer la base. Para esto se usa la opción: --onto. Esto significa que puedo especificar la base de la cual crear el conjunto de commits a replicar y especificar en donde quiero poner estos commits.

El comando que yo utilice para este último rebase toma como argumento --onto 0a4e950. Esto quiere decir que los commits que quiero replicar los va agregar desde este punto. También le he especificado el argumento 5a76bed. Lo que significa que tomará todos los commits entre 5a76bed y HEAD (master) y los aplicará sobre 0a4e950. Al finalizar, moverá master para que apunte sobre el último commit a replicar.

Cuando hice git rebase las tres primeras líneas empezaban por Dropping.... La razón de esto es sencilla, la base que he seleccionado incluye todos los commits que he señalado anteriormente. Uno de esos commits es un merge commit y ese merge tiene otra línea de procedencia que también es incluida hasta llegar a la base. En pocas palabras, git rebase también ha seleccionado estos otros commits:


Pero el punto de inserción que he marcado (0a4e950) ya tenia incluido esos commits (porque son ancestros directos) así que no los ha recreado. El comando continua recreando los otros commits (que son los que yo necesito recrear) pero se detiene en el commit que introduce el conflicto sobre baz.txt. Al cual yo le he dicho que simplemente no lo recree y continua agregando los otros commits. Por defecto, git rebase no trata de recrear merge commits pero no descarta ningún commit del cual se pueda acceder desde ese commit. Así que lo que ha hecho es juntar los dos "linajes" sobre una misma línea de procedencia.

Finalmente, termina de recrear todos los commits y apunta la rama al último commit recreado. Es importante notar que ninguna de las otras ramas ha sido desplazada a los nuevos commits creados. git rebase no cambiará las ramas a sus contrapartes recreadas, así que necesitarán actualizar estas ramas o simplemente borrarlas y recrearlas.

Para resumir... por si no ha quedado claro, hay tres partes en cualquier rebase. La rama que se está moviendo (apuntada por HEAD), la base del cual se calcula el conjunto de commits diferentes entre HEAD -> base y objetivo -> base. La rama/commit objetivo sobre la cual el conjunto de commits será aplicado para calcular la rama a mover. Por defecto el primer argumento es el objetivo y también se usa para calcular la base. Se puede especificar un objetivo diferente con --onto pero la base siempre se calculara con el primer argumento. El segundo argumento simplemente es una manera corta para no tener que hacer git checkout sobre la rama y/o para ser más explícitos con el comando.

Epílogo

Al terminar está guía deberían poder crear y borrar una rama en cualquier parte de su repositorio. Deben tener una noción básica de como incluir los cambios entre diferentes ramas y como es que git trabaja para incluir esos cambios. También deberían tener una noción básica de como recrear su historial usando el comando git rebase.

Por último, mencionare que hay una tendencia por parte de los usuarios de git a favorecer git rebase sobre git merge y viceversa. Son dos herramientas diferentes que por lo general son utilizadas en conjunto y no se excluyen mutuamente. "git rebase es mejor" o "git merge es mejor" son opiniones erroneas, cada una tiene su uso y es necesario entender cuando sería mejor usar una y cuando usar las otra.
486  Programación / Desarrollo Web / Re: ¿como modificar un array declarada en un archvio js desde un onclick? en: 13 Diciembre 2020, 16:17 pm
players tendría que ser una variable global. ¿Ese es el contenido tal cual de tu file.js?
487  Programación / Desarrollo Web / Re: javascript, HTML, HTTP, cURL, entre otros... en: 3 Diciembre 2020, 18:31 pm
Yo creo que te tocaría usar un headless browser. Sería hacer algo con el protocolo WebDriver o Chrome DevTools para poder interactuar con los navegadores. Aunque me imagino que también podrías hacer eso de webkit pero creo que sería más trabajo.
488  Programación / Programación General / Re: Usando Git para manipular el directorio de trabajo, el índice y commits (segunda parte) en: 1 Diciembre 2020, 03:21 am
Si creen que hay algún error o no entienden alguna parte del texto por favor pregunten :D

Le he dado varias lecturas rápidas ahora y he corregido unos cuantos errores (una imagen no salía bien por ejemplo).
489  Programación / Programación General / Usando Git para manipular el directorio de trabajo, el índice y commits (segunda parte) en: 30 Noviembre 2020, 20:43 pm
Indice

Prefacio

En este tema se explora un poco más acerca de como interactuar con el directorio de trabajo, el índice y los commits. Es una continuación de este tema.

Como recordatorío, esta es una guía informal a git.

El flujo de trabajo de git

Creo que para ahora debe quedar claro un poco como debería uno trabajar con git. En resumen, uno trabaja sobre los archivos en su directorio de trabajo. Los cambios que se realicen sobre el directorio de trabajo deben de ser agregados al índice, si son cambios que queremos guardar en nuestro repositorio y finalmente uno debe hacer commit para tomar los cambios agregados al índice y hacerlos permanentes al repositorio.

A grandes rasgos, podríamos decir que son 3 etapas. En este diagrama podemos ver que en nuestro ejemplo que hemos trabajado tenemos 2 archivos en nuestro directorio de trabajo (recordad que .git es especial), nuestro índice con los últimos cambios agregados y finalmente el commit que contiene todos los archivos agregados a través del índice más la información del autor, el ancestro directo del commit y el mensaje que le hemos dado.



El formato del índice no es importante por ahora, son detalles específicos de como opera git y quizás podamos ver más acerca de los objetos de git en un futuro. Por lo pronto es nuestro interés poder interactuar correctamente con estas tres etapas.

El directorio de trabajo

Como ya hemos dicho algunas 3 o 4 veces, el directorio de trabajo alberga nuestros archivos en los que estamos trabajando. Es a través de estos archivos que podemos abrirlos desde un editor de texto, ejecutarlos, etc. Hasta ahora git simplemente ha usado el directorio de trabajo para rastrear cambios sobre archivos que están marcados para rastrear. ¿Que más puede hacer git con nuestro directorio de trabajo?

¡Nuestro directorio de trabajo puede ser nuestra ventana al pasado y al futuro! Por ejemplo, podemos decirle a git que nos proporcione nuestro código fuente en un determinado commit (a través de una rama o simplemente usando el identificador del commit).

Ejemplo:

Para este ejemplo, vamos a jugar un poco con nuestro repositorio de prueba y vamos a revisar commits anteriores. Por lo pronto, verificamos el contenido actual de nuestro repositorio.



Como podemos ver, estamos en la rama master, la cual está apuntando al commit 0ba7347 (como dice git log), tenemos 2 archivos con su contenido como tal y 3 commits.


Importante:

Es importante recordar que los identificadores que ven aquí no serán los mismos que ustedes tendrán, tienen que revisar sus identificadores con git log como lo he descrito en la imagen.


Ahora, volvamos un poco al tiempo a nuestro primer commit (en mi caso es de00cee). Para esto usare una herramienta de git:

Código
  1. git checkout



Y como podemos observar, git ha hecho el cambio y ahora nos está avisando que entramos en el modo detached HEAD. La advertencia nos dice que los cambios que realicemos en este modo no se conservarán normalmente pero si nos dice como podríamos conservar esos cambios. Por ahora trataremos nuestro directorio de trabajo como si solo tuviera permisos de lectura y no de escritura. Es decir, no deberíamos hacer ningún cambio en este estado por ahora.

Revisamos lo que ha hecho git checkout:



Y como podemos ver, nos ha modificado nuestro directorio de trabajo. Ya no tenemos nuestro archivo prueba.txt y nuestro archivo README.md es diferente, puesto que es la primera versión de nuestro archivo. También podemos ver que al revisar el estado del índice, nos dice que estamos en el modo especial mencionado anteriormente. git log nos dice que solo tenemos un commit y podemos ver que HEAD no está apuntando a master. ¿Nos hemos cargado los commits?

No, los commits siguen ahí y podemos revisarlos usando --all:



git simplemente ha sustituido nuestro directorio de trabajo por como se veía hace 2 commits. También ha tocado el índice para reflejar el estado de nuestro directorio de trabajo.

Revisaremos nuestro segundo commit:



Y ahora no nos ha dado la advertencia (porque seguimos en el mismo estado especial). Vamos a revisar nuevamente el commit:



Y no ha cambiado mucho en este commit, realmente solo hicimos cambios a un solo archivo.

Por ahora regresemos a colocar la cabeza en master:

Código
  1. git checkout master

Es importante notar que seguiríamos en detached HEAD si usaramos el identificador del commit al que apunta master. Tenemos que usar el nombre de la rama para colgar la cabeza correctamente.

Como pudimos observar, git checkout nos ha reproducido exactamente las copias de los archivos que hemos guardado a través de los commits y nos los ha mostrado usando el directorio de trabajo. ¿Que pasaría si yo tuviera cambios pendientes y le pidiera que me mostrase algún commit o rama? Probemos.

Hare un cambio sobre prueba.txt. En este caso, no importa el cambio. Solo que ha ocurrido un cambio. Ustedes lo pueden simular editando el archivo de prueba.txt y haciendo cualquier cambio. Yo usare este comando para editar el archivo:

Código
  1. echo 'a' >> prueba.txt



Como pueden ver, estoy de vuelta en la rama master como lo dice git status y me muestra que hay un cambio pendiente a prueba.txt que necesito agregar al indice. Supongamos que quiero revisar los contenidos del primer commit. ¿Que pasaría con los cambios que he hecho sobre el archivo prueba.txt? En ese punto del tiempo, no existía prueba.txt.



git nos avisa que no podemos hacer ese cambio porque el archivo sería sobrescrito. Nos dice que agreguemos los cambios al repositorio o que los guardemos en algún otro lado temporalmente (git tiene sus mecanismos para esto, pero lo veremos más tarde).

Esto significa que git checkout sobre un commit o rama es bastante seguro y nos advierte de cualquier peligro.

Ahora, git checkout también tiene otras utilidades, por ejemplo podemos revisar archivos en específico de otros commits. La notación no es muy diferente:

Código
  1. git checkout rama/commit /path/a/archivo

Aunque es preferible usar un -- en medio para evitar confusiones en los argumentos:

Código
  1. git checkout rama/commit -- /path/a/archivo

Con este comando podríamos obtener el archivo README.md hace 2 commits:



Ahora git status detecta que hubo un cambio sobre el archivo README.md porque me he traído la primera versión. Y no solo eso, me la ha agregado al índice automáticamente.

Digamos que quiero regresar a la última versión del archivo, tengo tres opciones para hacer esto:

Código
  1. git checkout HEAD -- README.md
  2. git checkout master -- README.md
  3. git checkout 0ba7 -- README.md

Recordemos que HEAD en este momento apunta a master que este apunta a 0ba7, el último commit de la rama, la punta. El primer comando no funcionaría si estuviese revisando el contenido de otro commit o rama, puesto que HEAD no estaría apuntando a master.



¿Pero que pasaría si tuviera cambios nuevos e hiciera git checkout rama/commit -- /path/a/archivo?



git no me ha avisado de cambios que pudiera perder. No, simplemente ha sobrescrito mi archivo. Es muy importante tener cuidado cuando utilicen git checkout de está forma, ya que podrían perder cambios importantes. Sin embargo, revisar las ramas o commits directamente es seguro puesto que git no nos deja hacer el cambio.

Otro punto importante es que git checkout restablece archivos directamente del índice si no se le especifica una rama o commit en esta forma. Lo que significa que si tenemos cambios sin agregar al índice podemos regresar al estado anterior (al del último commit). En nuestro caso ahora mismo tenemos agregado el archivo README.md al índice, por eso si hacemos:

Código
  1. git checkout README.md

No obtendremos ningún cambio sobre nuestro directorio de trabajo, puesto que git checkout está obteniendo la última copia del índice, la cual fue agregada al índice por la invocación anterior puesto que git checkout rama/commit -- /path/a/archivo no solo obtiene el archivo de la rama/commit sino que también la agrega al índice.



Sin embargo, si hiciéramos un cambio nuevo sobre este archivo:



Aquí podemos ver que he agregado cambios sobre el archivo README.md al índice pero tengo cambios pendientes todavía no agregados al índice. Si usará el comando:

Código
  1. git checkout README.md

Conservaría los cambios agregados al índice pero me desharía de los cambios que todavía no son parte del índice.



Como pueden ver en la sección inferior del git status ya no aparece que se haya modificado README.md puesto que ha vuelto a la versión que está registrada en el índice.

Por lo pronto quiero deshacerme de estos cambios en mi directorio de trabajo (que están agregados al índice por cierto) y volver a la última versión que tengo, simplemente usaré la otra versión del comando que use anteriormente (para demostrar que es lo mismo).

Código
  1. git checkout master -- README.md



Y así, nuestro archivo README.md ha vuelto a su última versión tanto en el directorio de trabajo como en el índice.

git checkout tiene más utilidades que veremos más adelante. Por lo pronto, expandiremos en un comando el cual se utiliza mucho en conjunto con este, git stash.

Como recordarán, git checkout nos advierte que tenemos cambios sin haber sido registrados en un commit aún y no nos dejará cambiar de rama/commit si sigue detectando que existen estos cambios. Nos ha dado dos alternativas, realizar un commit con los cambios o guardar los cambios en algún lugar. Como es muy posible que tengas que dejar de trabajar sobre la rama en la que estás trabajando, git tiene un comando que nos permite guardar estos cambios y aplicarloss de vuelta cuando sea necesario. El comando es:

Código
  1. git stash

Yo llamaría git stash otra herramienta indispensable para trabajar con el directorio de trabajo. Por defecto, git stash tomará los cambios de archivos modificados y los substituirá por las versiones del commit al cual apunta HEAD. En pocas palabras, nuestros archivos son los mismos que aparecen en nuestro commit.

En nuestro ejemplo anterior, podemos ver como tenemos archivos pendientes con git status sobre prueba.txt. Por lo pronto vamos a guardar esos cambios:



Nuestros cambios ahora han desaparecido y podemos hacer el cambio a cualquier otra rama/commit con git checkout ya que no tenemos modificaciones pendientes a agregar.

¿Pero que ha pasado con nuestros cambios? Nuestros cambios ahora son un stash. Podemos ver los stash que tenemos usando:

Código
  1. git stash list



Podemos ver nuestro stash como stash@{0} con su mensaje por defecto. De hecho, inclusive podríamos hacer git checkout sobre este identificador. Pero no es el uso normal. Cuando uno necesita poder acceder a los cambios tenemos dos opciones:

1) Aplicar los cambios y borrar el stash (yo diría que el uso más común)
2) Aplicar los cambios y conservar el stash.

Para aplicar los cambios y borrar el stash podemos usar:

Código
  1. git stash pop

Para aplicar los cambios y conservar el stash simplemente usamos:

Código
  1. git stash apply

Para borrar el stash:

Código
  1. git stash drop

Por defecto, estos comandos operan sobre el stash más reciente, pero puedes especificar el identificador del stash si tienes otros stash con los cuales quieres trabajar.

Por ahora, aplicare git stash pop sobre mi directorio de trabajo y regresare al estado original (con las modificaciones pendientes sobre prueba.txt):



Y volvemos al estado original, podemos ver que git nos dice que también ha borrado el stash usando git stash list (al final del comando lo confirma también).

Detalles importantes a recordar acerca de git stash:

1) Por defecto, no conserva los archivos que no han sido rastreados, e.g. archivos nuevos que nunca han sido agregados. Se necesita especificar el argumento -u:

Código
  1. git stash -u

2) Por defecto al aplicar los cambios no agrega inmediatamente los cambios sobre el índice. Para eso tienes que usar:

Código
  1. git stash pop --index

O simplemente volver a agregar los cambios al índice una vez que hayas hecho git stash pop o git stash apply:

Código
  1. git add .

Existen otros comandos que también modifican el directorio de trabajo pero en mi opinión estos dos comandos son unos de los más importantes y de los más usados. Estos comandos también interactúan con el índice pero su uso principal es sobre el directorio de trabajo.

El índice

Manipular el índice es otra de las tareas importantes para el usuario de git. Es a través del índice que uno establece que cambios son considerados para ser archivados en un commit. Exploremos un poco más acerca de como funciona el índice de git. Como habíamos dicho anteriormente, el índice de git nos sirve para rastrear posibles cambios y agregarlos posteriormente a un commit. Cuando agregamos un archivo al índice, git convierte dicho archivo a un objeto interno (el cual también es un archivo pero con un formato especial) y mantiene el registro del cambio. Hasta ahora, quizás tengamos una idea que el índice es una larga lista de cambios a ser agregados pero en realidad el índice mantiene una lista de objetos (donde cada objeto es de hecho el archivo en su totalidad pero comprimido). Cuando nosotros creamos un commit, git usa estos objetos en el índice para crear el nuevo commit.

El índice, al igual que el directorio de trabajo, está en constante cambio. No tenemos un índice nuevo entre cada commit. Cuando creamos un commit, el índice sigue teniendo los mismos objetos. Muchos otros comandos van a cambiar el índice, por ejemplo, el comando git checkout rama/commit hará cambios sobre el directorio de trabajo y el índice reflejara los contenidos de este commit. De esa manera, el índice puede verificar cuando existen cambios o no. En un principio, puede sonar extraño mover archivos entre el índice y el directorio de trabajo, pero es importante recordar que el índice mantiene información COMPLETA acerca de nuestros archivos puesto que no está muy lejos de poder convertirse en un commit (y son estos mismos objetos los que acaban en el commit). Recordemos que dado un solo commit, git es capaz de extraer tu código exactamente como lo has dejado a la hora de hacer el commit.

Ejemplo:

Los siguientes ejemplos son para ejemplificar el funcionamiento del índice de git. En nuestro ejemplo anterior habíamos dejado pendientes modificaciones sobre prueba.txt:



Lo que podemos inferir de esto es que el índice en este momento está manteniendo un objeto para el archivo prueba.txt correspondiente al mismo objeto que usa nuestro último commit el cual podemos identificar fácilmente con HEAD (que a estás alturas, deberíamos ya saber que apunta a master y este apunta al último commit). ¿Como es que sabemos esto? No hay cambios agregados al índice todavía, solo cambios pendientes por agregar al índice.

Podemos verificarlos con una serie de comandos internos de git:



El primer comando nos muestra el contenido del índice, en el cual tenemos un objeto para prueba.txt bajo el identificador SHA-1 6de3. Podemos leer el objeto y obtenemos el mismo contenido que existe tal cual en el último commit. Sin embargo, ¿Realmente estamos usando el mismo objeto? Para satisfacer la curiosidad de algunos cuantos, el último comando imprime el contenido tal cual está registrado en nuestro último commit (nuevamente, HEAD). Como podemos observar, no solo prueba.txt está usando el mismo objeto, sino que también README.md. Lo cual tiene sentido porque no se ha agregado NADA al índice todavía, tenemos un cambio pendiente sobre prueba.txt solamente. Así podemos comprobar que el índice, en este momento, contiene exactamente lo mismo que nuestro último commit.

Ahora, vamos a agregar los cambios al índice:



¡Y ahora nuestro índice cambio! Lo que ha hecho git es crear un nuevo objeto con el contenido de nuestro archivo y ahora el índice usa este nuevo objeto. Una vez que hagamos git commit, git usara estos nuevos objetos para construir dicho commit. En ese momento, el nuevo commit y el índice volverán a usar los mismos objetos, por lo cual no hay nuevos cambios (hasta que agreguemos nuevas cosas).

Ahora, quiero continuar explicando un poco más acerca de el índice de git. Por lo que usare este siguiente comando para volver antes de la explicación (cuando teniamos cambios pendientes en prueba.txt).

Usare el siguiente comando:

Código
  1. git reset prueba.txt



Un nuevo comando del cual todavía no sabemos nada, el cual explicaremos ahora.

Interactuando con el índice

En primer lugar, vamos a recapitular el estado de nuestro índice y nuestro directorio de trabajo. Lo primero que tenemos que preguntarnos es: ¿Nuestro directorio de trabajo ha cambiado en estos últimos comandos? Fuera de los comandos internos que he usado para ejemplificar el índice (no hacen cambios sobre el directorio de trabajo), los únicos comandos que hemos usado hasta ahora son git add y git reset. Y no, estos dos comandos no han alterado nuestro archivo prueba.txt en nuestro directorio de trabajo. Sin embargo, git add y git reset si han cambiado nuestro índice. git add agrego un nuevo objeto al índice para nuestro archivo prueba.txt, mientras que git reset, como se lo pueden imaginar, ha hecho exactamente lo contrario.

El comando en su "forma completa" usa:

Código
  1. git reset rama/commit -- /path/a/archivo/

Lo cual les puede resultar muy familiar al comando:

Código
  1. git checkout rama/commit -- /path/a/archivo/

¿Cual es la diferencia? Recordamos que git checkout rama/archivo -- /path/a/archivo/ cambiaba el archivo en nuestro directorio de trabajo y lo agregaba al índice. Si no especificamos la rama/commit, git checkout usará el índice para cambiar el directorio de trabajo. Esto quiere decir que:

Código
  1. git checkout prueba.txt

Hubiese tomado una copia del índice y después la agregaría al directorio de trabajo. La transición es del índice al directorio de trabajo. Sin embargo:

Código
  1. git reset prueba.txt

Ha hecho casi lo contrario: Ha tomado una copia del archivo de HEAD y la ha movido al índice. La transición es de HEAD al índice. Por otro lado:

Código
  1. git add prueba.txt

Haría algo también muy similar a git checkout y git reset. En este caso, git add movería el archivo del directorio de trabajo al índice. La transición es de directorio de trabajo al índice.

¿Que significa esto?

git checkout estaría alterando nuestro directorio de trabajo (el archivo sobre el cual estamos editando). En está función (sin especificar un commit/rama), está principalmente ELIMINANDO los últimos cambios que no han sido agregados al índice. Lo que se haya agregado al índice no será eliminado. Por otro lado, si se especifica una rama/commit, los contenidos guardados en el índice si se perderán. git checkout HEAD -- prueba.txt, eliminaría ambos cambios tanto del índice como del directorio de trabajo.

git reset estaría alternado el índice y no tocara el directorio de trabajo en lo absoluto. Si no especificamos la rama/commit, tomará el archivo de HEAD y remplazará cualquier cambio que exista sobre el índice. El índice vuelve al estado que tenía en HEAD. Especificar la rama, para establecer el índice no es un caso típico. Pero sería muy similar a lo que hace git checkout con la excepción que no modificará el directorio de trabajo. Realmente, es de lo más raro especificar una rama/commit porque en muchas formas es el equivalente de git add pero para agregar archivos de otros commits (es más como un git index-set, comando que no existe pero eso es lo que hace...).

Finalmente, como git reset no ha alterado el directorio de trabajo, podemos simplemente agregar el archivo nuevamente al índice. git reset y git add son considerados opuestos (cuando no se especifica la rama/commit en git reset).

Podríamos decir que git checkout y git reset son los comandos más confusos que existen en git. Porque ambos hacen cosas similares y al mismo tiempo pueden hacer cosas totalmente diferentes. Por ejemplo, ya hemos visto que git checkout también puede hacer cambios sobre HEAD y obtener los archivos de un commit/rama para desplegarlos en el directorio de trabajo. git reset también tiene otra función que veremos más adelante.

Ejemplo:

Vamos a probar cada uno de los casos de uso de estos tres comandos y utilizare varios ejemplos. Primero, establecer el punto del cual parte estos comandos:



No hemos hecho ningún commit desde el tema anterior, lo cual significa que si se han perdido pueden volver al tema anterior y seguir los pasos nuevamente. Lo único que faltaría sería editar el archivo prueba.txt como ustedes deseen. Por otro lado, si han seguido todos los ejemplos (y mis instrucciones) deberían tener los mismos resultados que yo.

Ahora, lo primero que haremos será agregar nuevamente los cambios sobre prueba.txt al índice. Usaremos:

Código
  1. git add .



Esta vez no hemos usado el nombre del archivo, sino que le hemos dicho a git que agregue todos los cambios detectados sobre el directorio actual (. representa nuestro directorio actual, notación común en sistemas linux). Ahora probaremos:

Código
  1. git checkout prueba.txt



¿Que ha ocurrido? Nada. Recordemos que git checkout toma los cambios del índice y los usa para el archivo en el directorio de trabajo. Nuestro archivo en el directorio de trabajo es exactamente igual al del índice, lo que significa que no hay cambios. ¿Pero que pasaría si agregara un cambio?



Ha eliminado los últimos cambios sobre el directorio de trabajo, ya que el índice tenía una versión anterior a los cambios que acabamos de hacer sobre el directorio de trabajo. ¿Y que pasaría si usara HEAD como argumento para la rama/commit?



Adios cambios. git checkout ha tomado el archivo de HEAD, lo ha puesto en nuestro directorio de trabajo (eliminando los cambios que tenía) y también los ha puesto en el índice. ¿Resultado? Adios cambios.

Volveremos a agregar cambios con:

Código
  1. echo 'a' >> prueba.txt

Y agregaremos una vez más el archivo con git add



Ahora, probaremos git reset:



Nuestro archivo prueba.txt sigue teniendo el mismo contenido antes y después del comando. Pero el índice muestra que los cambios agregados anteriormente han sido removidos.

El estado de los archivos con respecto al índice

Cada vez que agregamos un archivo o modificamos un archivo, el comando git status nos advierte de el estado del archivo con respecto al índice. Si creamos un archivo que no existe en el índice, ese archivo se dice que está untracked. En español significa que el archivo está sin rastrear. git no está rastreando cambios al archivo. Cuando agregamos el archivo al índice, el archivo ahora si es rastreado. ¿Pero porque es importante esto?

Es importante porque algunos de los comandos trabajan sobre el índice y el estado de los archivos en el índice va a determinar que es lo que hace cada comando. Para el índice un archivo puede ser:

A) Nuevo (sin rastrear)
B) Modificado (rastreado)
C) Borrado (rastreado)

Por ejemplo, git checkout rama/commit no tocará archivos que no han sido rastreados. Recordarán que git checkout arroja un error al intentar revisar un commit o rama si hay modificaciones pendientes. Sin embargo, git checkout rama/commit no arrojara ningún error sobre nuevos archivos que no hayan aparecido antes. Esta operación no solo nos entregará los archivos registrados en el commit sino que estos nuevos archivos que no han sido rastreados también aparecerán en conjunto en el directorio de trabajo.

De la misma forma hay algunos comandos que tendrán argumentos que trabajarán dependiendo de su estado en el índice. Por ejemplo, git add -u actualizará solo archivos rastreados pero no agregará archivos sin rastrear.

Por último, un archivo sin rastrear puede ser también ignorado por el índice. Esto significa que git status no se molestará en decirte nada acerca de estos archivos, pero siguen siendo prácticamente archivos sin rastrear.

Ejemplo:

Volveremos a usar el mismo estado que hemos usado por los últimos ejemplos:



Como podemos observar, prueba.txt es un archivo rastreado. Vamos a crear un segundo archivo, al cual simplemente llamaremos nuevo.txt:

Código
  1. echo 'soy nuevo' > nuevo.txt

Y verificamos nuevamente el estado del índice:



Podemos ver que tenemos un archivo sin rastrear (nuevo) y uno de nuestros archivo rastreados ha sido modificado. Ahora mismo voy a borrar un archivo que es rastreado:



Ahora probaremos git add -u:



Y nos ha agregado solo los archivos que son rastreados pero no los archivos sin rastrear. Sin embargo, si usamos git add -A o git add . :



Ha agregado el archivo sin rastrear.

El argumento -A de git add es para agregar todos los cambios en todo el directorio de trabajo. Mientras que git add . agrega todos los cambios (de archivos rastreados y no rastreados) sobre la carpeta en la que estamos. Si la carpeta es la raíz, entonces los dos hacen lo mismo. Había más diferencias en versiones anteriores de git en la cual git add -A era el único que añadía todos los cambios. La mayoría prefiere usar este argumento (en el caso que necesiten agregar todos los cambios) debido a esto.

Digamos que quiero regresar al estado inicial, en el cual solo tenía cambios pendientes sobre prueba.txt ¿Que comandos debería usar? Ahora mismo, hemos hecho 3 cambios sobre nuestro directorio de trabajo. Hemos borrado README.md, tenemos un nuevo archivo nuevo.txt y hemos cambiado prueba.txt. De igual forma, tenemos 3 cambios sobre el índice también. Exactamente los mismos cambios que existen en nuestro directorio de trabajo. Vamos a borrar los cambios sobre README.md:

Código
  1. git checkout HEAD -- README.md



Ha obtenido la copia de HEAD, la ha traido a nuestro directorio de trabajo y la ha puesto en el índice también.

Ahora quiero eliminar los cambios de prueba.txt en el índice pero quiero conservar los cambios en mi directorio de trabajo. Un trabajo para git reset:

Código
  1. git reset HEAD -- prueba.txt



Simplemente ha puesto en el índice la versión de HEAD, dejando los cambios en el directorio de trabajo intactos. Ahora git status nos dice que tenemos cambios pendientes sobre prueba.txt

Finalmente, ¿Que podemos hacer sobre nuestro archivo nuevo.txt? No podemos usar git checkout para eliminar estos cambios sobre nuestro directorio de trabajo:



Curiosamente, git reset si podría borrar el archivo del índice a pesar que no existe el archivo en HEAD, sin embargo tendríamos todavía que eliminar el archivo del directorio de trabajo. También podríamos simplemente removerlo del directorio de trabajo y añadir el cambio nuevamente al índice. Básicamente, dos comandos para realizar está operación.

Sin embargo, tenemos otra opción que puede hacer las dos cosas al mismo tiempo:

Código
  1. git rm

Este comando, hará las dos cosas, eliminará el archivo del índice y del directorio de trabajo.



Pero para nuestra desgracia, el comando ha fallado aquí. ¿La razón? git rm es bastante inteligente acerca de borrar cosas. En este caso, git rm ha identificado que este archivo es nuevo y que tenemos cambios a perder si borramos el archivo del directorio de trabajo. Por eso nos sugiere --cached, en caso de que queramos conservar el archivo en el directorio de trabajo pero quitarlo del índice o si estás seguro que quieres borrar ambos archivos puedes usar -f. Como estamos seguros que queremos quitar el archivo usaremos este argumento.



Ahora, nos ha eliminado el archivo por completo. Tanto del índice como del directorio de trabajo. Y hemos vuelto al estado original.

¿Que pasaría si usara git rm para eliminar algún archivo que tengo rastreado? Nuestro archivo no rastreado que estaba en el índice y directorio de trabajo, acabo con 0 cambios posibles sobre el índice. Podemos ver que no hay cambios a agregar al índice sobre un archivo nuevo.txt ni cambios agregados al índice sobre nuevo.txt.

Intentaremos borrar README.md, ¿Que pasaría con el índice?



Noten como ahora no hubo necesidad de usar -f. Ya que no existen cambios a perderse, estos están en HEAD. Si hubiese modificado README.md, git rm me hubiese adveritdo de lo mismo, que hay cambios posibles por perderse. En ambas ocasiones, tanto para nuevo.txt como para README.md ambos archivos fueron eliminados tanto del índice como del directorio de trabajao. Más sin embargo, el resultado de git status es diferente. ¿Porque he agregado un cambio al índice con README.md y no queda rastro alguno sobre nuevo.txt?

La razón es simple. HEAD, nuestro último commit, tiene una copia de README.md por lo que eliminar el archivo del índice produciría un cambio. Sin embargo, nuevo.txt no existe en HEAD es un archivo nuevo, si agrego el archivo al índice produciré un cambio ya que no existe anteriormente. Pero si no existe en el índice no hay ningún cambio pendiente porque no existe en HEAD en primer lugar.

Vamos a restablecer nuestro archivo README.md como lo hemos hecho anteriormente.



A seguir trabajaremos el caso de uso para mover archivos. ¿Suena sencillo no? Primero agregaremos prueba.txt al índice nuevamente. Creo que ahora deben saber al menos unas 4 diferentes formas en las que pueden agregar el archivo:

Código
  1. git add -A
  2. git add -u
  3. git add .
  4. git add prueba.txt



Digamos ahora que quiero cambiar el nombre de prueba.txt a muestra.txt. Lo intentaremos hacer sobre el directorio de trabajo con:

Código
  1. mv prueba.txt muestra.txt



¿Que ha pasado? git piensa que he borrado un archivo del directorio de trabajo y he creado uno nuevo. Esto es técnicamente cierto, prueba.txt no existe y ahora existe muestra.txt. Primero eliminare el archivo del índice. Tengo un par de opciones aquí. Podría agregar el archivo eliminado con git add. Si, así es, git add también hace eso.

Código
  1. git add prueba.txt

También podría eliminar el archivo del índice con git rm --cached como nos sugirió en su momento git rm. Lo cual le da mucho más sentido a su nombre ya que no estamos agregando un archivo, estamos borrandolo.

Código
  1. git rm --cached prueba.txt



Y también agregaremos el archivo muestra.txt al índice con git add.



Parece ser que git no es tan tonto como pensábamos. Ahora si ha deducido que el archivo prueba.txt ha sido renombrado a muestra.txt.  No solo eso, el archivo es ligeramente diferente de HEAD. Lo que significa que git ha hecho una comparación inteligente sobre nuestro archivo para deducir que muestra.txt era de hecho prueba.txt.

¿Tiene que existir una manera más sencilla de hacer esto, no? Pues la hay. Tenemos el comando git mv. Volveremos a poner muestra.txt como prueba.txt:



¿Mucho más sencillo no? Para seguir con los ejemplos, quitare los últimos cambios en el índice de prueba:

Código
  1. git reset HEAD -- prueba.txt

Ignorando archivos sin rastrear

Llega un momento en el que una persona necesita crear archivos sobre el directorio de trabajo y no está interesado en agregarlos al repositorio. Por ejemplo, quizás no quieras tener binarios compilados de C en tu repositorio. Quizás necesites tener una contraseña en algún archivo dentro del directorio de trabajo y no quieres que esa contraseña acabe en el repositorio. ¿Algún archivo temporal en especifico que genere tu programa? Uno siempre puede ser especifico con los comandos a utilizar para manipular el índice (recuerden que si no está en el índice, nunca será parte de un commit, ni del repositorio) sin embargo es muy fácil errar y muy probablemente en algún punto del desarrollo tu o alguién se equivoquen, agreguen estos archivos al índice, dentro de un commit o un repositorio en linea.

Para evitar llegar a tener esos problemas, podemos simplemente establecer que archivos deben ser ignorados para que NUNCA acaben en el índice y mucho menos en un commit. ¿Como hacemos esto? Existen varias formas de hacer esto, pero por lo general se usa un archivo .gitignore en la raíz. Dentro de este archivo uno puede escribir patrones sobre los nombres de los archivos que queremos ignorar. Es recomendable que este archivo sea añadido al repositorio (a través de un commit).

Ejemplo:

Agregaremos un nuevo archivo, password.txt en el cual estará nuestracontraseña. Este archivo es importante para poder establecer una conexión con un servidor o algo similar.



Ahora git status nos advierte que tenemos un archivo nuevo a agregar. Digamos que agrego los cambios a prueba.txt con git add -A porque eso es lo que uso siempre antes de hacer git commit.



Tenemos un problema. Ahora nuestra contraseña está en el índice, lo cual significa que puede acabar en un commit. En esta ocasión me he dado cuenta, así que simplemente lo borrare del indice.

Código
  1. git rm --cached password.txt
  2. git reset HEAD -- password.txt



Ahora agregare un archivo .gitignore con el contenido password.txt:



Como pueden ver, password.txt está en el directorio de trabajo pero git status lo está ignorando. ¿Que pasará si intentamos agregar el archivo?



Absolutamente nada. Y si agregaríamos todos los cambios con git add -A tampoco funcionaría gracias a nuestro archivo .gitignore. En está ocasión he puesto el nombre completo del archivo, pero realmente podría usar patrones más generalizados. Por ejemplo podría ignorar archivos con una extensión o archivos dentro de una carpeta.

Para continuar con los ejemplos, me desharé de .gitignore y password.txt. Así como también quitare los cambios de prueba.txt sobre el índice:

Código
  1. rm -f .gitignore password.txt
  2. git reset HEAD -- prueba.txt

Trabajando con los commits

Si has llegado a esta parte del tutorial, no hay mucho más que decir acerca de los commits. La idea detrás de ellos es bastante simple. Lo único que queda agregar sobre los commits es como trabajar con ellos, los diferentes casos de usos y explicar como trabajan las herramientas.

git commit

No queda mucho más que decir de este comando. git commit tomará el índice y básicamente guardara todos los objetos que tenga el índice dentro de un commit. Tendremos que colocar un mensaje en nuestro editor configurado y se creara el commit. Tenemos un par de opciones útiles con el comando.

La primera es el argumento: -a

Código
  1. git commit -a

Git tomará todos los cambios de archivos rastreados y los añadirá al índice antes de hacer el commit. Sin embargo, los nuevos archivos no serán agregados al índice con este argumento por lo que todavía tienen que agregar estos nuevos archivos por separado.

Otro argumento básico es:

Código
  1. git commit -m "mensaje para git"

Bastante sencillo, el argumento -m nos permite especificar el mensaje del commit sin usar un editor de texto en la terminal. Hare una nota aquí acerca del formato de los mensajes en los commits.

Los mensajes de commits se usan para indicar que es lo que el commit está haciendo. Hay un número de reglas que se usan generalmente para producir un mensaje adecuado. Estás reglas no están reforzadas por lo que pueden usar cualquier mensaje pero es buena idea tomarlas en cuenta. No tiene mucho tiempo en el que estaba escribiendo un mensaje para un commit dentro de mi editor de texto (nvim) y mi editor empezo a darle un formato extraño al texto de mi mensaje con colores un poco raros. Al principio pense que mi editor de texto estaba fallando y no entendía que era lo que estaba haciendo.



No entendía que era lo que estaba pasando hasta que un día me dí cuenta que las herramientas que trabajan con git usan un formato en especifico para presentar los mensajes. Para ser más específicos, la primera linea del commit se le considera el título del commit. Las herramientas que imprimen mensajes breves acerca del commit, usarán la primera linea del mensaje. Se recomienda que la segunda linea del mensaje este vacía. Es un separador entre el titulo y el cuerpo del mensaje.

Finalmente, está el cuerpo del mensaje el cual es más libre. El único detalle realmente es que el cuerpo del mensaje no debería extenderse fuera de los 72 caracteres. Es decir, cualquier linea en el cuerpo no puede tener más de 72 caracteres.

Otras reglas implicitas sobre los mensajes pueden ser:

1) No usar punto para el título
2) El título debe empezar con letra mayúscula
3) El título debe describir lo que hace y poderse leer de tal forma que: "Este commit TITULO DEL MENSAJE AQUI" tenga sentido.
4) El cuerpo del mensaje debe explicar que es lo que hace y porque, no como lo hace.

De esta forrma, nuestros mensajes dejarán de verse así:





Y se verán así, mucho mejor organizados.





Si necesitan ser explícitos con sus mensajes, recomiendo usar su editor de texto preferido para escribir el mensaje. Podrían configurar git para usar el editor adecuado o pueden simplemente escribir el mensaje en un archivo y usar los contenidos de ese archivo como el mensaje.

Código
  1. git commit -F archivoconmensaje.txt

Modificando commits

Llegará el día en que cometamos un error al hacer un commit. Quizás hemos escrito mal algo en el mensaje del commit. Quizás hemos agregado algo que no debíamos al commit. Quizás simplemente no queremos ninguno de estos commits. La realidad es que no podemos hacer modificaciones sobre un commit, no exactamente. Lo que podemos hacer es remplazar un commit por uno nuevo que contenga los cambios requeridos. Podrías preguntarte ¿Que importa si el commit no es el mismo si en un final tenemos el commit con el contenido que necesitamos? Y por ahora preferíría no ofrecer una respuesta hasta que empecemos a hablar acerca de colaborar con otros.

Ejemplo:

Crearemos un nuevo commit con los cambios que hemos venido conservando entre ejemplos:



He cometido un error intencional sobre mi mensaje de commit. ¿Como puedo cambiar el mensaje del commit?

Usaremos el comando:

Código
  1. git commit --amend



Y ahora nuestro mensaje ha sido corregido. Sin embargo, podemos observar también que los identificadores de estos dos commits son diferentes. Uno dice 349f02b y el otro dice 811439d. Ambos contienen los mismos cambios pero realmente son dos diferentes commits.

¿Que ha pasado con nuestro commit 349f02b?



Nuestro commit existe y podemos revisarlo con git checkout y si revisamos git log nos muestra un historial muy similar. Lo que ha ocurrido es que git commit ---amend ha desplazado la rama master. Ha creado un nuevo commit cuyo ancestro es el mismo ancestro al que HEAD apuntaba y ha dicho que este nuevo commit es el nuevo master.

La transición la podemos representar de está manera.



El commit es inaccesible desde master pero sigue ahí. git eventualmente eliminará el commit (porque no hay forma de llegar al commit de ninguna rama) pero por lo pronto sigue ahí y podemos rescatar lo que queramos. Siempre y cuando sepamos el identificador del commit (y git no lo haya eliminado). ¿Pero y si necesito modificar más del commit que solo el mensaje?

Haciendo cambios sobre el historial de git

Tendremos que usar una herramienta que nos ofrece muchas posibilidades para recrear el commit. Y está herramienta ya la conocemos: git reset. Hasta ahora solo hemos usado git reset para restablecer el índice pero git reset es de hecho una herramienta mucho más versátil en cuanto a commits se trata.

Primero, tendremos que repasar un poco acerca del flujo de trabajo. Imaginemos que estamos trabajando sobre un repositorio por lo menos con un solo commit. Ahora, no hemos trabajado en lo absoluto sobre el directorio de trabajo y no hemos tocado el índice. En pocas palabras, nuestro directorio de trabajao refleja los mismos archivos que nuestro último commit y nuestro indice.

Si nosotros editáramos un archivo en nuestro directorio de trabajo, este ahora sería diferente a la copia que tenemos en nuestro último commit y el índice. Si agregaramos este archivo al índice, el indice ahora tendría la misma copia que el directorio de trabajo y el archivo en el commit sería diferente a la copia que tenemos en el directorio de trabajo y el indice. Finalmente, hacemos un commit y ahora este último commit tendrá la misma versión que nuestro directorio de trabajo y nuestro indice. Este es el flujo de trabajo de git y necesitas entenderlo para entender como funciona git reset.

Hasta ahora, hemos usado git reset en su forma git reset rama/commit -- /path/a/archivo, la cual en la mayoría de los casos se puede escribir git reset /path/a/archivo si queremos trabajar con HEAD por defecto. En esta forma, hacemos cambios exclusivos sobre el índice. Ahora, existe otra forma de usar git reset:

Código
  1. git reset rama/commit

Y la forma es muy similar a la anterior. De hecho, este comando sobre HEAD haría lo que uno esperaría, que es quitar todos los cambios del índice. Piensa que si usas un archivo estarías quitando los cambios en el indice sobre un archivo, está forma está quitando todos los cambios de todos los archivos del índice. Pero ahora nuestro interes es ver que es lo que ocurre cuando especificamos otra rama/commit que no sea HEAD.

Esto es muy sencillo, git reset moverá la rama que estamos usando en HEAD al commit que le hemos dicho. De esta manera, no solo estamos moviendo la rama en cuestión sino también estamos moviendo HEAD indirectamente ya que HEAD apunta a una rama que apunta a un commit. Esto es lo primero que hará git reset rama/commit. Lo siguiente que hará git reset dependerá del modo que se haya elegido a trabajar git reset. Hay 6 modos descritos en el manual pero por ahora solo veremos 3.

Primero tenemos el modo --soft. En este modo, lo único que hace git reset es desplazar la rama al commit que le hayas dicho. Tu índice permanece igual, tu directorio de trabajo permanece igual, lo único que ha cambiado realmente es que la rama a la que apuntaba HEAD ahora está apuntando a otro commit.

Nuestro siguiente modo es --mixed. En este modo, git reset desplazará la rama y reiniciará el índice con los archivos del commit al que nos estamos desplazando. Tu directorio de trabajo permanece igual. Si no se especifica un modo, este será el modo que se utilice. De modo que git reset rama/commit lleva un --mixed implicito.

Finalmente, tenemos el modo --hard. En este modo, git reset desplazará la rama y dejará tanto el índice como el directorio de trabajo en el estado del commit al cual la rama ha sido desplazada.

Para visualizar estos cambios mejor usaremos gráficas:


En esta gráfica podemos ver el flujo de trabajo con git, que es lo que ocurre con con cada el índice y el directorio de trabajo en cada etapa de la edición de un archivo. Donde v1 es la primera versión del archivo y v2 es el archivo con los cambios aplicados. A la derecha de la gráfica, está el indicador de estos tres modos descritos. Estos describen el estado del índice y del directorio de trabajo al finalizar la operación. La operación también asume que hemos usado git reset primercommit como base desde un HEAD que apunta una rama que apunta al segundo commit.

También podemos ver que el resultado nos dejaría en ese determinado paso en nuestro flujo de trabajo. Es decir, --soft por ejemplo, nos dejaría con los mismos cambios en el índice y el directorio de trabajo. Prácticamente un punto antes de crear el commit. --mixed nos hubiera reiniciado el índice lo que significa que nos dejaría con nuestros archivos en el directorio de trabajo sin haberlos agregado al índice. Y finalmente --hard hubiera deshecho todo y estaríamos de vuelta en el primer paso (antes de agregar cambios al directorio de trabajo).

Ejemplo:

Para este ejemplo vamos a asumir el siguiente historial del git:



Si han seguido los ejemplos, deberían tener el mismo historial que yo (nuevamente, con la excepción de los identificadores).

Nuestro primer ejemplo será imitar lo que ha hecho git commit --amend. Es decir queremos cambiar el mensaje de nuestro último commit. Para esto vamos a usar el modo --soft de git reset:



Y como podemos ver, nos ha quitado nuestro commit 811439d y ha movido master a 0ba7347. No solo eso, pero en nuestro directorio de trabajo y en el índice tenemos la copía de la última versión, por lo que ahora simplemente necesitamos hacer git commit. Como el objetivo de este ejercicio es cambiar el nombre del commit hare git commit con un mensaje diferente:



Y así, hemos emitado el comportamiento de git commit --amend.

Digamos ahora, que no quiero conservar nada de este último commit. Esta vez usare el modo --hard:



Ahora ni el índice, ni el directorio de trabajo tiene rastro de los cambios que hice y master nuevamente ha cambiado de posición.

Ahora contemplemos el caso en el que no hayamos agregado un .gitignore, hemos agregado un archivo sensible al índice junto con otros archivos importantes y está vez hemos hecho commit. ¿Como podemos arreglarlo? Es decir, ¿Como podemos conservar los archivos importantes o significativos y deshacernos solo del archivo problema? Para esto utilizare el modo --mixed.

Primero, creare el archivo importante.txt con el contenido 'muy importante'. También creare un archivo password.txt con el contenido micontraseña. Agregare ambos archivos al índice y hare commit de ellos.



Por fortuna, me he dado cuenta que el archivo existe en mi último commit así que puedo substituir el último commit con git reset. Como --mixed es el modo por defecto, no necesito especificarlo.



Y ahora solo necesito agregar el archivo que necesito:



El archivo password.txt sigue en mi directorio de trabajo al crear el nuevo commit y git status seguirá molestando hasta que agregue un .gitignore. Realmente no importa mucho equivocarse entre --soft y --mixed. Corregir el estado del índice es sencillo la mayoría de las veces. Pude también haber usado --soft por ejemplo y simplemente remover el password.txt del índice con git rm --cached password.txt.

Ahora, digamos que no estoy contento con ningún commit y quiero volver al commit inicial. Hare git reset al primer commit:



Aquí es donde la mayoría de la gente se equivoca con el estado resultante del directorio de trabajo (con git reset sobre commits que no son continguos). Muchos esperarían que el directorio de trabajo sea exactamente los contenidos del segundo commit. Es decir, esperán que al agregar estos archivos al índice podremos, hacer commit y obtener exactamente el segundo commit. Sin embargo, la copia del directorio del trabajo (y el índice si se ha usado --soft) corresponde al estado del índice/directorio de trabajo cuando se hizo git reset. En este caso, el estado fue exactamente una copia exacta del último commit (y no del segundo commit).

Estamos en una posición en la que podemos agregar todos estos archivos al índice y obtener el estado que teníamos en el último commit antes de hacer git reset. Para ser más especifico, si hiciera git commit tendría básicamente los mismos cambios entre los 3 commits que ya no forman parte de la rama master (sin los cambios intermedios) en un solo commit. A esto generalmente se le conoce como un squash. En español esto se traduce a aplastar. Es decir, estamos aplastando una serie de commits en uno solo.



Pero, ¿Que si me he equivocado y quiero regresar a mi estado original antes del aquel git reset que "elimino" mis commits? Y es importante mencionar aquí que al igual que git commit --amend, git reset no elimina commits, solo desplaza las ramas de manera que son accesibles normalmente. Y puedo usar este mismo comando para regresar la rama a su lugar en el que estaba. Para esto necesito saber el identificador del commit al que quiero regresar mi rama. ¿Pero que si no puedo encontrar este identificador?

Por suerte para nosotros, git mantiene un registro de donde ha estado cada rama. Incluso hay registros para ver donde ha estado HEAD. El comando para ver estos registros es:

Código
  1. git reflog
  2. #Lease git-ref-log y no git-re-flog



Y aquí tenemos una lista de commits en los que master ha estado. Notamos que la primera linea nos dice la posición en la que está ahora mismo y la operación que provoco que llegaramos a este commit. La segunda linea dice que hemos hecho un git reset y acabamos en de00cee (aquí la descripción ha duplicado el identificador, pero los identificadores usualmente salen al principio de la linea). La tercera linea dice que hemos creado un commit. Esta es la posición que buscamos (antes del reset) y corresponde a 5a76bed.

Así que podremos hacer:

Código
  1. git reset 5a76bed



Y no hemos perdido nada. Hemos usado --mixed lo que significa que solo hemos conservado nuestro directorio de trabajo. El otro commit no ha sido eliminado, así que podemos revisarlo con git checkout sobre el commit o volver a desplazar la rama ahí.

Notación especial para especificar commits

Hasta ahora, hemos usado el identificador SHA-1 de cada uno de los commits para referirnos a esos commits. En algunas ocasiones hemos usado master y HEAD, los cuales son mucho más sencillos de usar que el identificador. Existen otras formas de poder referirnos a estos commits. Por ejemplo HEAD^ se refiere ala primer padre de HEAD. HEAD incluso puede ser escrito como @ para simplificar aún más. En nuestro reflog, podemos ver que tenemos una notación: master{n}, la cual nos entrega la posición de master n cambios atrás.

Hay un número de notaciones especiales para poder especificar el commit que necesitamos. No he utilizado estás notaciones en los ejemplos porque quizás puedan tener problemas con su shell (en mi shell, necesito escapar ^ por ejemplo), así que he usado los identificadores SHA-1.

Epilogo

Al finalizar esta parte de la guía. Deben poder hacer la mayoría de las cosas necesarías en git con su repositorio local.
490  Programación / Programación General / Introducción a Git (Primera Parte) en: 20 Noviembre 2020, 19:42 pm
Indice

Prefacio

Este es una breve lectura introductoria a git. El propósito es tener una explicación personal de git para miembros del foro. No es su propósito ser extremadamente específico y explicar git en su totalidad, ni de describir como funciona cada una de las herramientas que lo compone.

He puesto ejemplos que pueden seguir asumiendo que tengan acceso a una terminal y al propio programa de git. Los ejemplos están trabajados sobre un sistema en Linux pero es posible seguir los ejemplos usando Windows. Esta no es una guía para instalar git ya que hay diversas formas en las que uno puede descargar e instalar git. Sin embargo, para aquellos que estén en Windows yo recomiendo que utilicen git tal cual es disponible desde este sitio, ya que el instalador también provee un entorno similar al que uno tiene en un sistema Linux (Git Bash).

Se requiere saber un mínimo acerca de la terminal para seguir los ejercicios. En concreto, uno debería poder navegar el sistema de archivos a través de la terminal.

¿Que es git?

git es un sistema de control de versiones distribuido (DCVS, Distributed Version Control System). Su principal uso es el de mantener el historial del código fuente en un proyecto y poder manejar cada uno de los cambios sobre el código fuente a través del tiempo. Hoy en día, un número de herramientas se han desarrollado a la par de git por lo cual se podría decir que git se utiliza para muchas otras cosas.

Por ejemplo, las plataformas para distribuir código libre usan principalmente git. En un pasado, el código fuente era distribuido por otros sistemas de control de versiones, por ejemplo SVN. En el peor de los casos el código fuente se distribuía en comprimibles para cada versión. Hoy en día, git se ha vuelto el VCS por defecto. De forma que si quieres obtener el código fuente de un proyecto lo más probable es que necesites usar git. De igual forma, si quieres contribuir a un proyecto lo más probable es que necesites usar git también.

git está en todos lados. Administración de proyectos, distribución de paquetes, integración/entrega continua y muchos otros procesos en el desarrollo de software. Es muy probable que si estás involucrado en el desarrollo de algún software tengas que usar git en alguna ocasión. De hecho, como consumidor de software es probable que también tengas que usar git en alguna ocasión. De manera que aprender git nunca está de más.

¿Que significa que git sea distribuido?

Tradicionalmente, el control de versiones se hacia sobre una instancia central (un servidor) en la cual personas (clientes) introducían o pedían código fuente. Todo se procesaba en esta instancia central. Lo que significa que no podías agregar cambios o solicitar código fuente si la instancia no estaba disponible. En un sistema central todos dependen de está instancia.

En un sistema distribuido, uno no depende de una sola instancia. En sí cada cliente es dueño de una instancia que se puede valer por si misma. Así que cada instancia puede procesar cambios y solicitar información independientemente de cualquier otra instancia. Cada instancia puede recibir y compartir cambios entre todas las instancias que existen.

Cada instancia se le conoce como un "repositorio".

¿Que es un repositorio?

Del latín repositorium: Lugar donde se guarda algo.

Un repositorio de git es básicamente un lugar donde se guarda (o almacena) el código fuente. Un repositorio está marcado por la carpeta .git/ en la cual se encuentra toda la información acerca del repositorio. La carpeta que contiene a la carpeta .git/ es conocida como la carpeta raíz del repositorio. La carpeta raíz es la carpeta que contiene una versión del código fuente y también es conocida como directorio de trabajo para git.

¿Como crear un repositorio de git?

Para crear un repositorio en blanco de git se utiliza el comando:

Código
  1. git init

Dentro de esta carpeta se creará la carpeta .git/, la cual contendrá toda la información del repositorio.

Es importante mencionar que el repositorio está vació a estas alturas. La raíz del proyecto (la carpeta donde se hace git init) puede contener información a la hora de crear el repositorio pero está información todavía no forma parte del repositorio. Necesitan ser agregados manualmente.

Ejemplo:

Nota: Para efectos de esta explicación voy a utilizar un sistema en Linux con git.

Supongamos que tenemos nuestro directorio proyecto/:



Hasta ahora, es un directorio normal. El siguiente paso sera crear un archivo README.md que contenga el nombre del proyecto.



Ahora lo convertiremos a un repositorio git.



Y vamos a notar que el mismo comando nos está diciendo que el repositorio está vacío.

¿Como agregar información al repositorio?

Antes de empezar a agregar información al repositorio, es muy importante conocer como es que git mantiene nuestra información.

git mantiene conjuntos de archivos en lo que se denomina un commit. Es uno de las términos de los cuales no quisiera traducir literalmente al español. El commit es básicamente una replica del estado de tu código fuente en un punto en el tiempo. Es decir, el commit contiene el código fuente exactamente igual a la vez que se creo dicho commit. Dentro de un repositorio, pueden existir miles de commits. Cada uno simbolizando el progreso del código a través del tiempo.

¿Como crear un commit? ¿Que es el índice de git?

Para crear un commit, es importante también conocer un poco acerca del índice de git. El índice de git mantiene los cambios a agregar a un commit. Los cambios pueden ser varios entre cambiar el nombre a un archivo, remover o agregar un archivo o hacer alguna modificación sobre el archivo.

Es a través del índice de git en el cual podemos construir nuestros commits. Son los cambios agregados al índice los que forman parte del commit. Puedes pensar del índice como el paso intermedio a la construcción del commit. Imagina que el índice es un contenedor abierto (una caja de cartón por ejemplo) a la cual vas agregando cosas. Una vez que terminas de empaquetar todo, cierras este contenedor y ahora una vez cerrado este contenedor deja de ser el índice y ahora es un commit.

Ejemplo:

Vamos a empaquetar nuestro archivo README.md, es decir vamos a agregar el archivo al índice. Primero vamos a visualizar el estado del índice. Para revisarlo, podemos utilizar:

Código
  1. git status



Como puedes observar en la última línea git me está diciendo que no hay nada para hacer el commit pero hay archivos que no están siendo rastreados. Nuestro índice está vacio.

Usamos el comando que nos menciona git para agregar el archivo al índice:

Código
  1. git add README.md

y volvemos a revisar el estado del índice:



Ahora git dice que hay un cambio en el índice para el cual podemos crear un commit. Por lo pronto vamos a hacer otro cambio a nuestro archivo README.md y simplemente vamos a agregar una descripción.

Lo único que voy a hacer es agregar la siguiente línea a mi archivo, "Este es mi proyecto que estoy haciendo con git". Ustedes pueden usar cualquier editor para agregar la linea si no quieren usar su terminal.



Y como pueden ver, el índice ahora está rastreando los cambios en el archivo README.md. Y el índice me alerta que hay cambios pendientes que no han sido agregados al índice todavía.

Si yo creara un commit en este mismo momento, lo único que añadiría al repositorio son los cambios que han sido agregados al índice hasta ahora. Es decir, el nuevo archivo con el texto original (antes de la modificación).

Para que este nuevo cambio sea parte del commit, necesito agregarlo al índice. Nuevamente tenemos que usar:

Código
  1. git add README.md

Y al revisar el estado nuevamente con git status obtendremos los mismos resultados antes de hacer la modificación.

Finalmente, podemos crear nuestro commit. Para ello necesitamos usar el comando:

Código
  1. git commit


Importante:
Si está es la primera vez que utilizan git va a ser necesario configurar los datos relevantes al autor que realiza el commit. Configurar estas opciones son muy sencillas:

Código
  1. git config --global user.email "tucorreo@dominio.com"
  2. git config --global user.name "Tu nombre"


Un editor deberá aparecer (dependiendo de la configuración de git) y les pedirá que introduzcan un mensaje asociado al commit. Este mensaje en general actúa como un resumen de los cambios que se han realizado en ese dado commit. No es necesario que sea así, el mensaje puede ser cualquier cosa excepto un mensaje vació. Si el mensaje es vació (y no se ha configurado git para aceptar mensajes vacios), git aborta el comando y no se crea ningún commit.

Opcionalmente podemos utilizar:

Código
  1. git commit -m "mensaje corto"

Y esto nos permite crear un mensaje rápido para hacer el commit. Vamos a usar esta opción para crear el commit en caso de que no tengan un editor configurado correctamente.



Más acerca de los mensajes en los commits más adelante. Por ahora este es un ejercicio de prueba y la intención es que puedas agregar tus cambios al repositorio. Una vez que el commit se haya agregado puedes considerar que tu información es parte del repositorio. Si volvemos a checar el estado del índice veremos que no tendremos ningún cambió pendiente a agregar:



Y si queremos revisar que nuestro commit está ahí podemos usar:

Código
  1. git log



En el cual podemos observar varias cosas:

1) El nombre del Autor es mi usuario: "MinusFour"
2) Entre las flechas < > aparece el correo del autor (en mi caso lo he tapado).
3) La fecha en la que se realizo el commit.
4) El mensaje del commit.
5) Un identificador del commit (SHA-1 por defecto): de00cee7484d435d471b964920e6122a1511b788

Y por último, dos palabras entre paréntesis que tienen un significado especial: HEAD y master.  Ambas están relacionadas al siguiente tema importante de Git, las ramas.


Importante:
El identificador SHA1 no será el mismo para ustedes. Cada commit que se realize es único, incluso si son los mismos cambios. git no necesita el identificador entero para funcionar puedes usar solo una parte siempre y cuando no haya otro commit que también comparta esa parte.

¿Que es una rama (branch)?

Hasta ahora sabemos lo que es un repositorio y sus commits. Las ramas son el eslabón faltante entre estos dos. Cada repositorio está compuesto de una o varias ramas que estos a su vez están formados por commits. Las ramas en sí no son nada en especial para git y al mismo tiempo resultan extremadamente útil. Primero tenemos que explorar un poco más a fondo los commits.

Verás, cada commit después del primer commit tendrá un ancestro del cual proviene. Es decir, el primer commit será el ancestro del segundo commit, el segundo será el ancestro del tercero y así sucesivamente. Esta línea de sucesión entre commits es lo que se podría considerar una rama. Y git no necesita mucho para averiguar todos los commits que componen una rama. Usando un solo commit, git puede rastrear hasta el último ancestro (porque cada commit tiene un ancestro). Es por eso que las ramas son esencialmente punteros los cuales contienen el identificador del último commit, también llamado la punta de la rama.

Cuando creamos un repositorio, git crea una rama por defecto bajo el nombre de master (esto nombre posiblemente este por cambiar a main en un futuro). Cuando nosotros creamos un commit sobre la rama master, git remplaza el identificador del commit anterior por el identificador del nuevo commit (ya que este es el último commit, la nueva punta de la rama). ¿Y Como sabe git en que rama estás haciendo el commit? Pues para eso está el puntero especial HEAD cuya función es la de apuntar directamente al commit o rama en la que estamos trabajando. Cuando HEAD no apunta a una rama y apunta a un commit directamente se dice que la cabeza está desconectada (detached head). En esta situación, al hacer un commit git no sabría que rama actualizar para apuntar al nuevo commit recién creado. Es necesario tomar precauciones cuando trabajamos de está forma ya que sin una rama los cambios realizados pueden perderse. En nuestro ejemplo anterior podemos ver que HEAD está apuntando a la rama master. Esto simplemente quiere decir que la rama en la cual estamos trabajando actualmente es master y que cualquier cualquier operación que modifique el estado de la rama (como git commit) será sobre master.

Ejemplo:

Ahora mismo nuestro repositorio solo tiene un commit con el identificador de00cee. Nuestro HEAD está apuntado a master que a su vez está apuntando a de00cee. Todo esto lo podemos verificar con los archivos internos de git.



Aquí podemos ver que HEAD apunta a refs/heads/master (una notación más especifica para master) que a su vez apunta a de00cee. Lo podemos ver de una manera gráfica:



Para ejemplificar como es que la rama empieza a tomar forma vamos a agregar unos cuantos commits.

Nuestro primer commit simplemente cambiara el nombre del proyecto en el archivo README.md. En esta ocasión estoy utilizando el programa sed para cambiar el texto "Mi Proyecto" por "Aprendiendo Git", pero ustedes pueden abrir su editor de texto y hacer cambios sobre el README.md.



Y el tercer commit simplemente agregara un archivo nuevo al cual por ahora simplemente llamaremos prueba.txt.



Ahora podemos revisar todos nuestros commits usando git log nuevamente.



Podemos ver nuestros tres commits: 0ba7347, 0f55ed3 y de00cee. Tambien podemos ver como master ya no está apuntando a de00cee sino al último commit 0ba7347. HEAD sigue apuntando a master.



Ahora, para mostrarles que efectivamente el ancestro/padre de 0ba7347 es 0f55ed3 usare un comando especial para mostrarles el contenido del commit:



Usando esta información podemos crear una nueva gráfica:



Recapitulando el estado de este repositorio de prueba:

1) Tenemos tres commits. Cada uno apunta a un padre (con la expceción del primer commit).
2) La rama master apunta al último commit.
3) Nuestro HEAD apunta a master (lo que significa que la rama sobre la cual estamos trabajando es master.

Más adelante pondré ejemplos de como usar multiples ramas para organizar el repositorio.

Epilogo

Al final de la lectura deberán saber que es git, para que se usa git y deberían poder crear sus propios repositorios y agregar información a ellos. Deberían tener una noción básica de lo que es un repositorio, los commits, el índice de git y las ramas.
Páginas: 1 ... 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 [49] 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 ... 448
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines