TRABAJANDO CON REPOSITORIOS REMOTOS EN GIT Y GUTHUB

Clonando un repositorio

Un repositorio puede iniciarse copiando (clonando) otro ya existente. En nuestro caso, vamos a clonar un repositorio ya creado en nuestro GitHub y que llamaremos UT05-P03.

Para evitar que nos pida credenciales, vamos a clonarlo usando el protocolo SSH que ya configuramos en una práctica anterior.

La orden para clonar un repositorio remot es bash git clone REPOSITORIO, donde el repositorio es la ruta remota, que dependerá del protocolo usado. En nuestro caso, quedaría algo parecido a esto:

git clone git@github.com:tu_usuario/UT05-P03.git

Al pulsar intro el resultado mostrado debe ser el siguiente:

Cloning into 'UT05-P03'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.

Git usa su propio protocolo "git" para el acceso remoto (también se puede clonar un repositorio local, simplemente indicando el path), pero también soporta otros protocolos como ssh, http, https...

Al contrario que con git init, con git clone no es necesario crear un directorio para el proyecto. Al clonar se creará un directorio con el nombre del proyecto dentro del que te encuentres al llamar a la orden.

Clonar un repositorio significa copiarlo completamente. No solo los archivos, sino todo su historial, cambios realizados, etc. Es decir que en tu repositorio local tendrás exactamente lo mismo que había en el repositorio remoto de donde lo has clonado.

Ahora vamos a entrar en la carpeta del repositorio importado:

cd UT05-P03

IMPORTANTE
En adelante, a menos que se diga lo contrario, todos los comandos y órdenes que se indique se deberán ejecutar en el directorio de nuestro proyecto (o uno de sus subdirectorios, lógicamente). Git reconoce el proyecto con el que está trabajando en función del lugar donde te encuentres al ejecutar los comandos.

Sincronizando repositorios

Como sistema de control de versiones distribuido, una de las principales utilidades de git es poder mantener distintos repositorios sincronizados (es decir, que contengan la misma información), exportando e importando cambios.

Para importar (o exportar) cambios de un repositorio remoto se necesita, lógicamente, tener acceso de lectura a ese repositorio (en sentido estricto, ya hemos importado el estado de un repositorio cuando lo clonamos al hacer git clone).

Para sincronizar con uno o más repositorios remotos, debemos saber qué repositorios remotos son esos. Para ello tenemos remote, que se usa así:

git remote

Y, seguramente, te devolverá algo parecido a origin, lo que nos dice que el repositorio es el que le indicamos como "origen" al hacer el clone y, la verdad, no es mucha información.

Para obtener algo más útil, prueba a hacerlo con el parámetro -v de este modo:

git remote -v

lo que te devolverá algo parecido a esto:

origin	https://github.com/tu_usuario/UT05-P03.git (fetch)
origin	https://github.com/tu_usuario/UT05-P03.git (push)

Esto te dice que hay un repositorio llamado "origin" que se usará tanto para recibir (fetch) como para enviar (push) los cambios. "origin" es el nombre del repositorio remoto por defecto, pero puedes tener muchos más y sincronizar con todos ellos.

Para añadir otro repositorio remoto se hace con la misma instrucción remote de este modo:

git remote add ALIAS_DEL_REPOSITORIO DIRECCION_DEL_REPOSITORIO

Donde ALIAS_DEL_REPOSITORIO es un nombre corto para usar en las instrucciones de git (el equivalente al "origin" que hemos visto) y DIRECCION_DEL_REPOSITORIO la dirección donde se encuentra. Por ejemplo:

git remote add personal \ git://github.com/psicobyte/repo-ejemplo.git

Esto añade un repositorio remoto llamado personal con la dirección que se indica.

Si ahora hacemos un git remote -v, veremos algo como:

personal	git://github.com/tu_usuario/UT05-P03.git (fetch)
personal	git://github.com/tu_usuario/UT05-P03.git (push)
origin	https://github.com/joaquinalbares/UT05-P03.git (fetch)
origin	https://github.com/joaquinalbares/UT05-P03.git (push)

Para eliminar un repositorio tienes:

git remote rm NOMBRE

Y para cambiarle el nombre:

git remote rename NOMBRE_ANTERIOR NOMBRE_ACTUAL

Nota que git no comprueba si realmente existen los repositorios que agregas o si tienes permisos de lectura o escritura en ellos, de forma que el hecho de que estén ahí no significa que vayas a poder usarlos realmente.

Recibiendo cambios

Ha llegado el momento de importar cambios desde un repositorio remoto. Para ello tenemos git pull que se usa así:

git pull REPOSITORIO_REMOTO RAMA

El REPOSITORIO_REMOTO es uno de los nombres de repositorio que hemos visto antes (si no pones ninguno, se supone "origin"). Sobre las ramas se hablará un poco más adelante, pero baste decir que, si no ponemos ninguna, se supone que es la rama "master").

De este modo, la forma más usual de llamar esta orden es, simplemente:

git pull

(que significaría lo mismo que git pull origin master).

Esta instrucción trae del repositorio remoto indicado (o de "origin" si no indicas nada, como hemos visto), todos los cambios que haya respecto al tuyo (lógicamente, no se molesta en traer los que son iguales).

Si el repositorio del que tratas de importar no existe o no tienes permiso de lectura, te dará un mensaje de error advirtiéndote de ello.

Si hay archivos que tú has modificado pero el otro repositorio no, te quedarás con los tuyos. Cuando se trate de archivos que tú no has cambiado pero que sí son distintos en el remoto, actualizarás los tuyos a este último. Pero, si importas archivos que se han modificado en ambos repositorios ¿qué pasa con las diferencias? ¿Sobrescribirá tus archivos? ¿Perderás los del otro repositorio?

Ahí es donde entra la solución de problemas, y lo veremos dentro de poco.

En realidad, git pull es la concatenación de dos acciones distintas, que son git fetch, que trae los cambios remotos creando una nueva rama, y git merge, que une esos cambios con los tuyos. En ocasiones te convendrá más usarlas por separado pero, como aún no hemos visto el manejo de las ramas, dejaremos esto por ahora.

Enviando cambios

Si con pull importamos cambios desde otro repositorio, la instrucción push es la que nos permite enviar cambios a un repositorio remoto.

Se usa de un modo bastante parecido:

git push REPOSITORIO_REMOTO RAMA

Igual que hemos visto con git pull, los valores por defecto son "origin" para el repositorio y "master" para la rama, con lo que se puede poner simplemente:

git push

lo que enviará nuestros cambios al servidor remoto... salvo que algo haya cambiado allí.

Si la versión que hay en el servidor es posterior a la última que sincronizamos (es decir, alguien más ha cambiado algo), git mostrará un error y no nos dejará hacer el push. Antes debemos hacer un pull.

Solo cuando hayamos hecho el pull (y resuelto los conflictos, si es que hubiera alguno), nos dejará hacer el push y enviar nuestra versión.

Al hacer tu push, git te retornará información de los cambios realizados, número de archivos, etc.

El archivo .gitignore

Cuando hacemos git add . o algo parecido, preparamos todos los archivos que hayan sido modificados. Esto es, sin duda, mucho más cómodo que ir añadiendo los archivos uno a uno. Pero muy a menudo hay montones de archivos en tu directorio de trabajo que no quieres que se añadan nunca. Archivos de contraseñas, temporales, borradores, binarios compilados, archivos de configuración local...

Por ejemplo, muchos editores de texto mantienen una copia temporal de los archivos que estás editando, con el mismo nombre pero terminado en el signo "~". Si haces git add ., estos archivos se acabarán añadiendo a tu repositorio, cosa que no tiene demasiada utilidad.

Para evitar este problema y facilitarte el trabajo, git nos permite crear un archivo (varios, en realidad, como veremos enseguida) donde describir qué archivos quieres ignorar.

El archivo en cuestión debe llamarse ".gitignore" (empezando por un punto) y ubicarse en el directorio raíz de tu proyecto.

En este archivo podemos incluir los nombres de archivos que queramos ignorar. Por ejemplo, imaginemos que nuestro .gitignore tiene este (poco útil) contenido:

## Los archivos que se llamen "passwords.txt" serán ignorados
passwords.txt

Las líneas de .gitignore que comienzan con el signo "#" son comentarios (útiles para quien lo lea), y git las ignora.

Gracias a esto, git ignorará cualquier archivo que se llame passwords.txt, y no lo incluirá en tus adds.

Esto es demasiado simple y no nos va a ser muy útil, pero, afortunadamente, .gitignore permite comodines y otras herramientas útiles. Por ejemplo:

## Ignoramos todos los archivos que terminen en "~"
*~

## Ignoramos todos los archivos que terminen en ".temp"
*.temp

## Ignoramos todos los archivos que se llamen 
## "passwords.txt", "passwords.c", "passwords.csv"...
passwords.*

El archivo .gitignore permite hacer cosas mucho más complejas, aunque para la mayoría de los casos con algo como lo visto arriba es suficiente.