Archivo de la etiqueta: github

Uso avanzado de referencias: github y pull requests

El viernes pasado (19 de abril de 2013) Jesús Espino dio una charla titulada “Git internals” en Kaleidos. Lo primero que es digno de mención es que una empresa de una “charla interna” e invite a cualquiera que quiera asistir “de fuera”. Iniciativas como esta deberían estar a la orden del día en empresas en todo nuestro país. Desde aquí animo a cualquier colega de profesión, trabaje donde trabaje (cárnicas, startups o lo que se tercie) a que comparta lo que sabe, bien sea en un blog, dando charlas en grupos locales o imitando este tipo de iniciativas aunque sea internamente dentro de su empresa. “Hay que subir el nivel” como dice Diego Freniche. Y como me gusta repetirme a mi mismo todos los días: “si quieres cambiar las cosas, empieza cambiando tú y tu entorno”.

La charla, que está escrita en TeX, podéis encontrarla en github. Independientemente de los recuerdos que me trajo ver el código fuente (la tesis la escribí en LaTeX) el contenido de la misma no tiene desperdicio. Si os interesa el tema, aunque sea árido, y queréis profundizar en él os recomiendo que leáis el capítulo 9 del libro de Scott Chacon.

Durante la charla se habló bastante de las referencias. Cuando Jesús acabó su magistral intervención, comenté un pequeño truco que aprendí en twitter para poder descargarnos a nuestra máquina los pull requests que recibimos desde github. Llevaba tiempo queriendo sacar un rato para contarlo y Jesús me dio el empujón que me faltaba.

Accediendo a los pull requests

En este artículo usaremos como base el repositorio que utilizamos para la serie de artículos sobre pull requests.

Cuando un usuario hace un pull request desde su fork al repositorio original, github crea en el repositorio orignal una referencia a los commits cuya incorporación se ha solicitado. El repositorio original, el que recibe el pull request, guarda estas referencias en refs/pull/*/head. Recordad que las referencias en git no son más que ficheros que contienen hashes a objetos de git. Estos ficheros están en los servidores de github.

Para poder acceder a esas referencias, y por lo tanto a esos commits, lo único que hay que hacer es decirle a git que se descargue los ficheros con las referencias cuando ejecutamos el comando fetch. Para ello, abrimos el fichero .git/config de nuestro repositorio y modificamos la sección correspondiente al remoto “origin”, añadiendo la línea en cursiva que os pongo a continuación:

[remote "origin"]
 url = https://github.com/aprendegit/fork
 fetch = +refs/heads/*:refs/remotes/origin/*
 fetch = +refs/pull/*/head:refs/remotes/origin/pr/*/head

Antes de ejecutar git-fetch, veamos cómo están las referencias remotas del repositorio en SourceTree:

Referencias remotas antes de importar los pull-requests

Referencias remotas antes de importar los pull-requests

Hagamos un fetch:

$ git fetch
From https://github.com/aprendegit/fork
 * [new ref]         refs/pull/1/head -> origin/pr/1/head
 * [new ref]         refs/pull/2/head -> origin/pr/2/head
 * [new ref]         refs/pull/3/head -> origin/pr/3/head

¡Ahí las tenéis! Si actualizáis la pantalla de SourceTree:

El repositorio después de hacer fetch de los pull requests

El repositorio después de hacer fetch de los pull requests

En la captura podéis ver los dos pull requests que ha recibido el proyecto y cómo ha evolucionado cada uno de ellos.

¿Y esto para qué sirve? Pues para probar el código que estáis recibiendo antes de aceptar el pull request, por ejemplo.

Para terminar, quería dar las gracias de nuevo a Jesús por su charla, a Kaleidos por la iniciativa y a Bert Belder (piscisaureus) por colgar el gist con la información que ha dado pie a este artículo.

¿Qué es un pull request?

En esta entrada, la tercera de una serie de tres, explicamos qué es un pull request y cómo funciona en github.

Recordamos brevemente qué es lo que nuestros usuarios aalbagarcia y aprendegit-user1 hicieron en las dos entradas anteriores:

  • El usuario aalbagarcia crea un repositorio en github: https://github.com/aprendegit/fork
  • El usuario aprendegit-user1 crea un fork (bifurcación) para trabajar en él: https://github.com/aprendegit-user1/fork
  • Los dos usuarios realizan modificaciones cada uno en su repositorio, cada uno de ellos hace un commit y posteriormente un push. Cada uno sube los cambios a su correspondiente repositorio
  • El usuario aprendegit-user1 configura un repositorio remoto adicional (upstream) e incorpora, haciendo un merge, los cambios en el código que hizo aalbagarcia. Después de hacer el merge, aprendegit-user1 hace un push para actualizar su repositorio remoto.

Lo último que nos falta por hacer es que el usuario aalbagarcia, que es el propietario del repositorio inicial (https://github.com/aprendegit/fork), incorpore el trabajo del usuario aprendegit-user1 que se encuentra en el fork https://github.com/aprendegit-user1/fork.

¿Qué es un pull request?

Un pull request es una petición que el propietario de un fork de un repositorio hace al propietario del repositorio original para que este último incorpore los commits que están en el fork. En el caso que nos ocupa, el usuario aprendegit-user1 le enviará la petición a aalbagarcia para que este último incorpore los commits que tiene en su fork.

Lo primero que hará el usuario aprendegit-user1 será crear una rama que apuntará al último commit que ha hecho y que contiene las modificaciones a la página de inicio. El procedimiento es el de siempre:Creando una rama antes de hacer el pull request

  • Se selecciona a la izquierda la rama master y se hace clic sobre el último commit de la rama
  • Se hace click sobre el icono “Branch”
  • Se introduce el nombre de la rama, en este maso mycommits
  • Se hace clic sobre “Create Branch”

Si se usa la línea de comandos, la secuencia sería:

$ git checkout master
Switched to branch 'master'
$ git checkout -b mycommits
Switched to a new branch 'mycommits'

Una vez creada la rama, el usuario aprendegit-user1 hace push de la rama “mycommits” a su fork:

  • Se selecciona la rama mycommits a la izquierda asegurándonos de que esta queda como rama activa haciendo doble clic sobre ella
  • Se selecciona el último commit de la rama
  • Se hace clic sobre el icono Push de la bara de herramientas
  • En el desplegable, se selecciona en la columna “Push?” la rama mycommits y se marca en la columna de la derecha (Track) la rama local como tracking branch.
  • Se hace clic sobre OK.
La secuencia de comandos sería 
$ git checkout mycommits
$ git push -u origin mycommits
Total 0 (delta 0), reused 0 (delta 0)
To [email protected]:aprendegit-user1/fork
    * [new branch] mycommits -> mycommits
Branch mycommits set up to track remote branch mycommits from origin.

Al terminar, el estado del repositorio es el siguiente:

Solicitando el pull request

En este momento, el usuario aprendegit-user1 accede a su cuenta de github y abre en su navegador la página de su fork https://github.com/aprendegit-user1/fork.

Una vez hemos accedido, cambiamos a la rama mycommits como se indica en la siguiente captura:Acceder a github y cambiar a la rama mycommits

Una vez se ha cambiado a la rama mycommits, aprendegit-user1 hace clic sobre el botón “Pull Request”El botón pull request

Esta es la pantalla que se presenta a aprendegit-user1:Pantalla para enviar el pull request

En la parte superior, aprendegit-user1 selecciona la rama que contiene los commits que aalbagarcia (el destinatario del pull request) debe incorporar en su repositorio. En la parte de abajo se ven tres pestañas en las que se puede:

  • Poner un título y descripción al pull request
  • Ver los commits que aalbagarcia incorporará al repositorio si acepta el pull request (pestaña Commits)
  • Ver un diff que los cambios que se incluyen en los commits (pestaña Files Changed)

Es una buena práctica poner un título y descripción que reflejen el motivo del commit. Estos pull requests los ven todos los colaboradores del proyecto (si el proyecto es público lo ve todo el mundo) Así que aunque aalbagarcia y aprendegit-user1 hayan hablado por teléfono acerca de este pull request, el resto del mundo seguramente no ha escuchado la conversación. Pensando en el resto del equipo, aprendegit-user1 incluye un mensaje descriptivo de qué contiene el pull request. Cuando termina, hace clic sobre el botón “Send Pull Request”. Esta es la pantalla a la que aprendegit-user1 llega después de enviarlo:Pantalla del pull request que ve aprendegit-user1

La captura es bastante sencilla de entender. Vemos el título y descripción introducidos por aprendegit-user1, los commits y una interfaz para intercambiar comentarios con el equipo.

Aunque en pequeño, se nos indica qué es lo que tendríamos que hacer para añadir commits a este pull request: enviar más commits a la rama “mycommits” del fork de aprendegit-user1.

¿Porqué podemos necesitar añadir más commits? Imagínate que el equipo revisa el pull request y se determina que todavía queda trabajo pendiente de hacer. ¿Cómo se procede? aprendegit-user1 continúa el trabajo en la rama rama “mycommits” de su fork y cuando termina lo sube a su repositorio remoto. El pull request se actualiza y todo el equipo puede ver el nuevo código. Esto lo veremos en detalle en otro artículo, de momento, seguimos con el proceso.

Aceptando el pull request

Cuando aprendegit-user1 envía el pull request, Github envía al propietario un email avisándole de que tiene un pull request listo para revisar. En este caso, el usuario aalbagarcia recibe el email, accede a su cuenta de Github y va a su repositorio https://github.com/aprendegit/fork:Página del propietario del repositorio después del pull request

En esta pantalla, seleccionamos la pestaña “Pull Requests” y accedemos a una interfaz que nos muestra todos los que tenemos. Dado que este es el primero, en este listado seleccionamos el único que hay:Listado de pull requests del repositorio

Cuando aalbagarcia selecciona “Añadiendo logotipo”, accede a una pantalla muy parecida a la que hemos visto con el usuario aprendegit-user1. En la siguiente captura indico cuáles son las diferencias:La página del pull request vista por el propietario

Las diferencias son:

  • Existe un botón “Merge pull request” que el propietario aalbagarcia utilizará para incorporar los commits al repositorio
  • Existe un botón “Close” que el propietario utiliza para cerrar el pull request sin incorporar los cambios

Una vez revisado el código y confirmado que todo está bien, aalbagarcia hace clic sobre el botón “Merge Pull Request”. En ese momento Github le pedirá que introduzca un comentario para el merge commit. Una vez introducido, aalbagarcia hace clic sobre el botón “Commit merge” y ya está, Github automáticamente cierra el pull request. También envía un email a los dos interesados (aalbagarcia y aprendegit-user1) avisándoles de que se han incorporado los cambios en el repositorio.

Si volvéis a la página de commits del repositorio en github (https://github.com/aprendegit/fork/commits/master) veréis que tras hacer el pull request, aparece un nuevo commit en el repositorio:Merge commit tras el pull request

¿Cómo quedan los repositorios de aalbagarcia y aprendegit-user1?

En este momento, el repositorio remoto de aalbagarcia ya contiene el merge commit que se ha creado a través del pull request. La pregunta es ¿el de aprendegit-user1 también? ¿verán estos nuevos commits los dos usuarios?

…la respuesta es que, efectivamente, no lo verán hasta que hagan un fetch en sus respetivos repositorios. Tras hacer los dos usuarios un fetch de todos sus repositorios remotos (recuerda que aprendegit-user1 tiene 2 remotos,  origin y upstream) así quedan sus respectivas copias de trabajo:

Copias de trabajo después del pull request y el fetch

Para terminar:

  • aalbagarcia tiene que hacer un merge de la rama origin/master a la rama master
  • aprendegit-user1 tiene que hacer un merge de la rama upstream/master a la rama master y después hacer un push de su rama master

Una vez hecho esto, los repositorios quedan de la siguiente manera:

Copias de trabajo después de macer merge de las ramas remotas

Y como podéis ver, están sincronizados.

¿Quieres hace un pull request?

Si quieres hacer tú mismo el proceso para ver cómo funciona, ve al repositorio https://github.com/aprendegit/fork, haz un fork y envíanos las modificaciones siguiendo los pasos que hemos seguido en esta serie de artículos. Si tienes alguna duda no dudes en preguntarnos.

Seguiremos hablando de pull requests, aunque eso ya será en otros artículos.

Mantener tu fork al día

En la entrada anterior vimos qué significa hacer un fork y cómo hacerlo desde github. Resumo brevemente los pasos que dimos:

  1. El usuario aprendegit-user1 hizo un fork del repositorio https://github.com/aprendegit/fork
  2. El usuario aprendegit-user1 modificó la página de inicio del proyecto (ficticio) que estamos usando e hizo un push de sus modificaciones a su fork (https://github.com/aprendegit-user1/fork)
  3. Vimos cómo las modificaciones que aprendegit-user1 hizo a su fork no se “sincronizan” automáticamente con el repositorio original

A la vez que prendegit-user1 ha avanzado con su tarea, nuestro primer usuario (aalbagarcia) y administrador del repositorio original (https://github.com/aprendegit/fork), ha continuado también trabajando. A la vez que el usuario aprendegit-user1 ha cambiado la página de inicio, aalbagarcia ha añadido un nuevo campo a la clase Post de nuestro blog. Cuando aalbagarcia ha acabado su trabajo, lo sube al repositorio original (https://github.com/aprendegit/fork) haciendo un push.

El estado de ambos repositorios es el siguiente:

Situación de los repositorios de aalbagarcia y aprendegit-user1

En la parte superior de la imagen tenemos una captura del estado del repositorio de aprendegit-user1 (fork) y en la parte inferior una captura del repositorio de aalbagarcia. Vemos que cada uno tiene en su repositorio su trabajo. Ha llegado la hora de sincronizarlos, cosa que haremos en dos pasos:

  • Paso 1: aprendegit-usuario1 traerá a su fork el trabajo de aalbagarcia (en este caso el commit c3e3f5b que podéis ver arriba)
  • Paso 2: aalbagarcia incluirá en el repositorio original el trabajo de aprendegit a través de un pull request.
¡Empezamos por el Paso 1!

Trabajando con múltiples repositorios remotos

Lo primero que aprendgit-user1 tiene que hacer es añadir el repositorio original a su lista de remotes. El usuario aprendegit-user1 abre SourceTree y va a Repository->Repository settings:

Preferencias del repositorio

Dentro de la pantalla de “Repository settings”, selecciona la pestaña “Remotes”. En esta pantalla está su fork (origin). Se hace clic sobre el botón “Add” para añadir el repositorio original:

Seleccionando nombre y URL de nuevo remote

En la ventana emergente, se introduce “upstream” como nombre del remote (upstream es una sugerencia, podéis poner otro si lo preferís) y se pone la URL del repositorio original (https://github.com/aprendegit/fork). Rellenado el formulario, se hace clic en OK y de nuevo clic en OK en la ventana de preferencias. Una vez cerradas todas las ventanas de diálogo, el repositorio de aprendegit-user1 tendrá este aspecto:

Aspecto del repositorio tras añadir upstream

En la sección “REMOTES” aparece junto a origin el nuevo origen upstream, pero si se despliega el contenido veremos que está vacío. Pulsando sobre “Fetch” descargaremos el contenido del repositorio remoto “upstream”:

Fetch

…y cuando el fetch finaliza, el repositorio queda de la siguiente manera:

Situación del fork después de hacer fetch de upstream

El fork de aprendegit-user1 ya muestra las ramas del repositorio original ¿véis el commit c3e3f5b? ¡ya lo tenemos ahí!. ¿Qué nos falta? Pues tan sólo hacer un merge de la rama upstream/master a la rama master para “sincronizar” el fork con el original. Aprendegit-user1 selecciona el commit correspondiente a nuestra rama master (en la captura anterior ya está seleccionado) y hace clic sobre el icono merge en la barra de herramientas:

Incorporando (merge) del repositorio original al fork

En la pantalla de merge, se selecciona el commit correspondiente a la rama upstream/master y se hace clic sobre OK. Cuando el merge termina, así queda el repositorio de aprendegit-user1:

Estado del fork tras incorporar los cambios de aalbagarcia

¡Ya está! aprendegit-user1 ya tiene su rama sincronizada con el repositorio original. Ahora sólo queda hacer un push; como se puede ver el propio SourceTree nos avisa de que la rama master de aprendegit-user1 está dos commits por delante de la rama remota origin/master. Basta con hacer clic sobre el icono push para que aprendegit-user1 actualice su repositorio remoto.

Cuando aprendegit-user1 termina, así quedan su repositorio (fork) y el de aalbagarcia (original):

Estado del fork y repositorio original tras el merge

¡aalbagarcia todavía no tiene en su repositorio el trabajo de aprendegit-user1! Para eso, aprendegit-user1 hará un pull request… en la siguiente entrada.

¡Happy gitting!

Crear ficheros en repositorios github a través de la web

Recientemente, github ha añadido una nueva funcionalidad a sus usuarios través de la web: la posibilidad de añadir ficheros directamente al repositorio a través del navegador. ¿Significa esto que podremos programar nuestra próxima aplicación usando la web de github? Pues no, aunque para gustos los colores.

Bromas aparte, la funcionalidad puede parecer una tontería, pero bien utilizada es bastante útil. A modo de ejemplo, la vamos a utilizar en un caso real para crear un fichero README.md en un repositorio que no lo tiene.

Si lo tuviese que hacer por el procedimiento normal, tendría que hacer lo siguiente:

  • Crear el fichero README.md en mi copia local
  • git add README.md para añadir el fichero al índice
  • git commit -m’Añadiendo README.md’ para hacer el commit
  • git pull para bajarme a mi copia local las últimas modificaciones
  • Si en el último paso ha habido algún conflicto, tendríamos que resolverlo (si habéis usado bien las ramas, esto no pasará)
  • git push para subir los cambios

La verdad es que es bastante trabajo para subir un fichero de texto ¿verdad? Bueno, pues vamos a ver cómo lo hacemos desde la web.

En primer lugar, vamos a la página de github, accedemos con nuestro usuario y contraseña y vamos a la página de nuestro repositorio:

Creando ficheros desde github

En la captura anterior veis dónde está el botón. Hacemos clic sobre él y se nos abrirá la pantalla para crear el fichero:

Pantalla para crear un fichero en github

La pantalla no tiene mucho misterio, como podéis ver en la captura:

  • Seleccionamos en qué rama lo queremos crear
  • Introducimos el nombre y extensión del fichero
  • Ponemos el contenido
  • Rellenamos el mensaje para el commit (el resumen es obligatorio)

Una vez tenemos el fichero escrito, hacemos clic sobre “Commit New File” y si no se produce ningún error, volveremos a la pantalla del repositorio con algunos cambios:

  • El fichero ya lo tenemos en el repositorio
  • Hay un commit más en el repositorio

Tras subir el fichero

Pues ya está. Para obtener el fichero en nuestra copia local, hacemos un pull de la rama en la que hemos subido el fichero. Como veis, es un poquito más cómodo que el método normal y nos puede ahorrar tiempo en ciertas tareas.

¿Cuándo conviene usar esta funcionalidad de github?

Como pone en la propia entrada del blog en github, es un método pensado para ficheros como el README, LICENSE, el .gitignore o escribir documentación. Usa el sentido común y sé práctico. Yo, por ejemplo, lo he usado para crear un fichero sencillo con datos de prueba en json y pasárselo a mis compañeros desde el iPad en el sofá o para crear un fichero sencillo con traducciones en formato CSV.

Creando .gitignore desde plantillas

Los chicos de github están en todo, así que para ahorrarnos un poco de trabajo han creado una serie de plantillas para crear ficheros .gitignore. Para verlas y seleccionarlas, creamos un nuevo fichero como hemos visto antes y en el nombre del mismo escribimos “.gitignore”:

Creando un .gitignore desde plantilla

Cuando lo hagamos, veremos a la derecha un desplegable para seleccionar la plantilla. La seleccionamos y ya podemos editar el fichero, quitando reglas o añadiendo las que puedan faltar. Al terminar, no nos olvidamos de hacer clic sobre “Commit New File”.

¿Y si el repositorio no es mío?

Si intentas crea un fichero sobre un repositorio sobre el que no tienes permiso, verás el siguiente mensaje:

Creando fichero sobre repositorio sin permiso de escritura

Lo que nos está diciendo es que antes de crear el fichero va a hacer un fork del repositorio en nuestra cuenta y que el fichero será creado en el nuevo repositorio. Una vez creado, podemos hacer un pull request al administrador del repositorio original para que incorpore el nuevo fichero en su repositorio.