Trabajando con GitHub de forma efectiva


Ya me han preguntado varias veces cómo trabajar de forma efectiva con GitHub, así que he decidido dedicarle un post.

Primero contaré algunas cosas básicas, para terminar explicando cómo trabajar con forks.

Recomiendo echar un ojo a mi post sobre el Uso básico de Git antes de embarcarse en esta aventura...

Uso básico de Git y GitHub

Crear un repositorio

Lo primero es crear un repositorio. Para ello nos vamos a nuestra cuenta en GitHub, pulsamos sobre new repository e indicamos el nombre, descripción y privacidad.

Opcionalmente podemos indicar cierto contenido a añadir, como el archivo README, .gitignore o la licencia.

Una vez hecho esto, basta con clonarse el repositorio. GitHub nos ofrece instrucciones sobre cómo hacerlo:

git clone git@github.com:USER/REPOSITORY.git

Sin embargo, hay veces que no tenemos acceso a GitHub o bien que ya tenemos el repositorio en otro sitio. Así que podemos añadir nuevos remotes a nuestra configuración local de git:

git remote add origin git@github.com:USER/REPOSITORY.git
git push -u origin master

Voy a explicar esto un poco más en detalle: la primera línea le dice a git que añada un remote llamado origin que apunte a la dirección git@github.com:USER/REPOSITORY.git.

Cada vez que realicemos una operación remota (fetch, push, pull, ...), git necesita un remote para realizarla. Por defecto usará origin si existe. Es decir, que estas dos órdenes son equivalentes:

git fetch
git fetch origin

La segunda línea del código de más arriba, el push, le indica a la copia local de git que quieres que la rama actual se sincronice con la rama master del remote con mombre origin. De esta manera, cuando hagamos commit sobre esta rama git nos permitirá hacer push directamente sobre esa rama.

Hay que recordar que para git las ramas son sólo referencias, y las referencias locales pueden diferir de las referencias remotas. Es más, distintos remotes pueden tener distintas referencias.

Veremos más de esto al final del post.

Actualizar un repositorio

Lo siguiente es actualizar el repositorio. La mejor manera es usar fetch, para descargarse los objetos y referencias remotos. Ojo: sólo se los descarga, pero no actualiza nada en local; es decir: el HEAD seguirá apuntando al mismo sitio y si miramos las branch no habrán cambiado. Sin embargo, tendremos más commits, tags, referencias (show-ref), ... que podremos utilizar.

A continuación podemos realizar distintas acciones locales:

  • actualizar el HEAD si no hay conflictos (conocido como fast-forward), haciendo un rebase o un merge --ff-only.
  • actualizar el HEAD a pesar de los conflictos con rebase.
  • mergear el HEAD con lo descargado, con merge.

Y hasta aquí. Esta parte daría para mucho, así que no me voy a extender.

Enviar cambios

Una vez se han hecho cambios locales, tendremos commits. No voy a entrar aquí en esa parte. El caso es que queremos persistir esos cambios en el repositorio remoto.

Para ello se utiliza push, indicando qué remote se quiere utilizar. Como ya hemos dicho, origin será el remote por defecto.

Uso colaborativo de GitHub

Fork

Eventualmente sentiremos la necesidad de arreglar un problema en un repositorio ajeno. Aquí es donde comienza la gracia.

Lo primero que podemos hacer es irnos a GitHub y pulsar sobre el botón fork. Esto realizará una copia del repositorio en nuestra cuenta.

A continuación nos vamos a nuestra cuenta y obtenemos la URL para poder hacer un clone.

Otra opción es realizar un clone del repositorio principal y trabajar sobre él, aun a sabiendas de que no podemos hacer push. Eso no es importante, ya que después podremos reorganizar los remotes.

Actualizar un Fork

Supongamos que hemos hecho un fork y que nuestro repositorio ha quedado desactualizado, ya que en nuestra cuenta de GitHub tenemos una copia que no es la buena.

En el caso fácil, hicimos un clone de nuestro fork, podemos actualizar usando directamente la URL:

git fetch git@github.com:USER/REPOSITORY.git

Pero claro, nos podemos olvidar, así que es mejor actualizar nuestras referencias locales por si lo necesitamos en el futuro. Para ello, le damos un nombre a la URL (yo suelo utilizar "github", por razones obvias :D):

git remote add github git@github.com:USER/REPOSITORY.git

Y a continuación podemos usar el "alias":

git fetch github

En el caso de que hubiéramos seguido el camino difícil, es decir, nos clonamos directamente el repositorio principal y no nuestro fork, tampoco es un problema, ya que podemos reorganizar los remotes:

git remote rename origin github

Y a continuación añadimos nuestro fork, tal y como se indicó un poco más arriba. El resultado será el mismo.

En cualquier momento podéis ver qué remotes tenéis configurados mediante:

git remote -v

Conclusión

Hace poco que leí el artículo Why I Don't Hate Git: Hidden Consistency, de Armin Ronacher, en el que decía:

Learning the UI vs the Concept

En mi opinión debería ser al revés: "Aprender el Concepto en lugar de la Interfaz". No os quedéis sólo con el comando que hace lo que necesitáis: preguntáos cómo hace lo que hace.

En ese mismo artículo dice también algo muy curioso:

The .git/HEAD was still a symlink to the current branch and a documented way to commit was this dance:

echo "Commit message" | git-commit-tree $(git-write-tree) > .git/HEAD

Only later an alias for commit was added, and even then the tutorial was still … rough I would say.

Puede parecer exagerado, pero queda claro que no hay magia. En vuestras copias locales de git podéis mirar el contenido de .git/HEAD. Es un archivo de texto plano. Y contiene una referencia a la rama actual; al changeset actual si estáis detached.


Comentarios

Comments powered by Disqus