Git
Chapters ▾ 2nd Edition

6.2 GitHub - Participando en Proyectos

Participando en Proyectos

Una vez que tienes la cuenta configurada, veremos algunos detalles útiles para ayudarte a participar en proyectos existentes.

Bifurcación (fork) de proyectos

Si quieres participar en un proyecto existente, en el que no tengas permisos de escritura, puedes bifurcarlo (hacer un “fork”). Esto consiste en crear una copia completa del repositorio totalmente bajo tu control: se encontrará en tu cuenta y podrás escribir en él sin limitaciones.

Note

Históricamente, el término “fork” podía tener connotaciones algo negativas, ya que significaba que alguien realizaba una copia del código fuente del proyecto y las comenzaba a modificar de forma independiente al proyecto original, tal vez para crear un proyecto competidor y dividir a su comunidad de colaboradores. En GitHub, el “fork” es simplemente una copia del repositorio donde puedas escribir, haciendo públicos tus propios cambios, como una manera abierta de participación.

De esta forma, los proyectos no necesitan añadir colaboradores con acceso de escritura (push). La gente puede bifurcar un proyecto, enviar sus propios cambios a su copia y luego remitir esos cambios al repositorio original para su aprobación, creando lo que se llama un Pull Request, que veremos más adelante. Esto permite abrir una discusión para la revisión del código, donde propietario y participante pueden comunicarse acerca de los cambios y, en última instancia, el propietario original puede aceptarlos e integrarlos en el proyecto original cuando lo considere adecuado.

Para bifurcar un proyecto, visita la página del mismo y pulsa sobre el botón “Fork” del lado superior derecho de la página.

Botón ``Fork''.
Figure 89. Botón “Fork”.

En unos segundos te redireccionarán a una página nueva de proyecto, en tu cuenta y con tu propia copia del código fuente.

El Flujo de Trabajo en GitHub

GitHub está diseñado alrededor de un flujo de trabajo de colaboración específico, centrado en las solicitudes de integración (“pull request”). Este flujo es válido tanto si colaboras con un pequeño equipo en un repositorio compartido como si lo haces en una gran red de participantes con docenas de bifurcaciones particulares. Se centra en el workflow Ramas Puntuales cubierto en Ramificaciones en Git.

El funcionamiento habitual es el siguiente:

  1. Se crea una rama a partir de master.

  2. Se realizan algunos commits hacia esa rama.

  3. Se envía esa rama hacia tu copia (fork) del proyecto.

  4. Abres un Pull Request en GitHub.

  5. Se participa en la discusión asociada y, opcionalmente, se realizan nuevos commits.

  6. El propietario del proyecto original cierra el Pull Request, bien fusionando la rama con tus cambios o bien rechazándolos.

Este es, básicamente, el flujo de trabajo del Responsable de Integración visto en Flujo de Trabajo Administrador-Integración, pero en lugar de usar el correo para comunicarnos y revisar los cambios, lo que se hace es usar las herramientas web de GitHub.

Veamos un ejemplo de cómo proponer un cambio en un proyecto de código abierto hospedado en GitHub, utilizando esta forma de trabajar.

Creación del Pull Request

Tony está buscando código para ejecutar en su microcontrolador Arduino, y ha encontrado un programa interesante en GitHub, en la dirección https://github.com/schacon/blink.

El proyecto en el que queremos participar.
Figure 90. El proyecto en el que queremos participar.

El único problema es que la velocidad de parpadeo es muy rápida, y piensa que es mucho mejor esperar 3 segundos en lugar de 1 entre cada cambio de estado. Luego, nuestra mejora consistirá en cambiar la velocidad y enviar el cambio al proyecto como un cambio propuesto.

Lo primero que se hace, es pulsar en el botón Fork ya conocido para hacer nuestra propia copia del proyecto. Nuestro nombre de usuario es “tonychacon” por lo que la copia del proyecto tendrá como dirección https://github.com/tonychacon/blink, y en esta copia es donde podemos trabajar. La clonaremos localmente, crearemos una rama, realizaremos el cambio sobre el código fuente y finalmente enviaremos esos cambios a GitHub.

$ git clone https://github.com/tonychacon/blink (1)
Cloning into 'blink'...

$ cd blink
$ git checkout -b slow-blink (2)
Switched to a new branch 'slow-blink'

$ sed -i '' 's/1000/3000/' blink.ino (3)

$ git diff --word-diff (4)
diff --git a/blink.ino b/blink.ino
index 15b9911..a6cc5a5 100644
--- a/blink.ino
+++ b/blink.ino
@@ -18,7 +18,7 @@ void setup() {
// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  [-delay(1000);-]{+delay(3000);+}               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  [-delay(1000);-]{+delay(3000);+}               // wait for a second
}

$ git commit -a -m 'three seconds is better' (5)
[slow-blink 5ca509d] three seconds is better
 1 file changed, 2 insertions(+), 2 deletions(-)

$ git push origin slow-blink (6)
Username for 'https://github.com': tonychacon
Password for 'https://tonychacon@github.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tonychacon/blink
 * [new branch]      slow-blink -> slow-blink
  1. Clonar nuestro fork en nuestro equipo

  2. Crear la rama, que sea descriptiva

  3. Realizar nuestros cambios

  4. Comprobar los cambios

  5. Realizar un commit de los cambios en la rama

  6. Enviar nuestra nueva rama de vuelta a nuestro fork

Ahora, si miramos nuestra bifurcación en GitHub, veremos que aparece un aviso de creación de la rama y nos dará la oportunidad de hacer una solicitud de integración con el proyecto original.

También puedes ir a la página “Branches” en https://github.com/<user>/<project>/branches para localizar la rama y abrir el Pull Request desde ahí.

Botón Pull Request
Figure 91. Botón Pull Request

Si pulsamos en el botón verde, veremos una pantalla que permite crear un título y una descripción para darle al propietario original una buena razón para tenerla en cuenta. Normalmente debemos realizar cierto esfuerzo en hacer una buena descripción para que el autor sepa realmente qué estamos aportando y lo valore adecuadamente.

También veremos la lista de commits de la rama que están “por delante” de la rama master (en este caso, la única) y un diff unificado de los cambios que se aplicarían si se fusionasen con el proyecto original.

Página de creación del Pull Request
Figure 92. Página de creación del Pull Request

Cuando seleccionas el botón Create pull request, el propietario del proyecto que has bifurcado recibirá una notificación de que alguien sugiere un cambio junto a un enlace donde está toda la información.

Note

Aunque los Pull Request se utilizan en proyectos públicos como este donde el ayudante tiene un conjunto de cambios completos para enviar, también se utiliza en proyectos internos al principio del ciclo de desarrollo: puedes crear el Pull Request con una rama propia y seguir enviando commits a dicha rama después de crear el Pull Request, siguiendo un modelo iterativo de desarrollo, en lugar de crear la rama cuando ya has finalizado todo el trabajo.

Evolución del Pull Request

En este punto, el propietario puede revisar el cambio sugerido e incorporarlo (merge) al proyecto, o bien rechazarlo o comentarlo. Por ejemplo, si le gusta la idea pero prefiere esperar un poco.

La discusión, en los workflow de Git en entornos distribuidos, tiene lugar por correo electrónico, mientras que en GitHub tiene lugar en línea. El propietario del proyecto puede revisar el diff y dejar un comentario pulsando en cualquier línea del diff.

Comentario de línea del PR
Figure 93. Comentando una línea concreta del diff

Cuando el responsable hace el comentario, la persona que solicitó la integración (y otras personas que hayan configurado sus cuentas para escuchar los cambios del repositorio) recibirán una notificación. Más tarde veremos cómo personalizar esto, pero si las notificaciones están activas, Tony recibiría un correo como este:

Correo de notificación
Figure 94. Comentarios enviados en notificaciones de correo

Cualquiera puede añadir sus propios comentarios. En Página de discusión del Pull Request vemos un ejemplo de propietario de proyecto comentando tanto una línea del código como dejando un comentario general en la sección de discusión. Puedes comprobar que los comentarios del código se insertan igualmente en la conversación.

Página de discusión del PR
Figure 95. Página de discusión del Pull Request

El participante puede ver ahora qué tiene que hacer para ver aceptado su cambio. Con suerte sera poco trabajo. Mientras que con el correo electrónico tendrías que revisar los cambios y reenviarlos a la lista de correo, en GitHub puedes, simplemente, enviar un nuevo commit a la rama y subirla (push).

Si el participante hace esto, el coordinador del proyecto será notificado nuevamente y, cuando visiten la página, verán lo que ha cambiado. De hecho, al ver que un cambio en una línea de código tenía ya un comentario, GitHub se da cuenta y oculta el diff obsoleto.

PR final
Figure 96. Pull Request final

Es interesante notar que si pulsas en “Files Changed” dentro del Pull Request, verás el “diff unificado”, es decir, los cambios que se introducirían en la rama principal si la otra rama fuera fusionada. En términos de git, lo que hace es mostrarte la salida del comando git diff master ... <rama>. Mira en Decidiendo qué introducir para saber más sobre este tipo de diff.

Otra cosa interesante es que GitHub también comprueba si el Pull Request se fusionaría limpiamente (de forma automática) dando entonces un botón para hacerlo. Este botón solo lo veremos si además somos los propietarios del repositorio. Si pulsas este botón, GitHub fusionará sin avance rápido, es decir, que incluso si la fusión pudiera ser de tipo avance-rápido, de todas formas crearía un commit de fusión.

Si quieres, puedes obtener la rama en tu equipo y hacer la fusión localmente. Si fusionas esta rama en la rama master y la subes a GitHub, el Pull Request se cerrará de forma automática.

Este es el flujo de trabajo básico que casi todos los proyectos de GitHub utilizan. Se crean las ramas de trabajo, se crean con ellas los Pull Requests, se genera una discusión, se añade probablemente más trabajo a la rama y finalmente la petición es cerrada (rechazada) o fusionada.

Note
No solo forks

Observa que también puedes abrir un Pull Request entre dos ramas del mismo repositorio. Si estás trabajando en una característica con alguien y ambos tenéis acceso de escritura al repositorio, puedes subir una rama al mismo y abrir un Pull Request con ella de fusión con master para poder formalizar el proceso de revisión de código y discusión. Para esto no se requieren bifurcaciones (forks).

Pull Requests Avanzados

Ahora que sabemos cómo participar de forma básica en un proyecto de GitHub, veamos algunos trucos más acerca de los Pull Requests que ayudarán a usarlos de forma más eficaz.

Pull Requests como parches

Hay que entender que muchos proyectos no tienen la idea de que los Pull Requests sean colas de parches perfectos que se pueden aplicar limpiamente en orden, como sucede con los proyectos basados en listas de correo. Casi todos los proyectos de GitHub consideran las ramas de Pull Requests como conversaciones evolutivas acerca de un cambio propuesto, culminando en un diff unificado que se aplica fusionando.

Esto es importante, ya que normalmente el cambio se sugiere bastante antes de que el código sea suficientemente bueno, lo que lo aleja bastante del modelo basado en parches por lista de correo. Esto facilita una discusión más temprana con los colaboradores, lo que hace que la llegada de la solución correcta sea un esfuerzo de comunidad. Cuando el cambio llega con un Pull Request y los colaboradores o la comunidad sugieren un cambio, normalmente los parches no son directamente alterados, sino que se realiza un nuevo commit en la rama para enviar la diferencia que materializa esas sugerencias, haciendo avanzar la conversación con el contexto del trabajo previo intacto.

Por ejemplo, si miras de nuevo en Pull Request final, verás que el colaborador no reorganiza su commit y envía un nuevo Pull Request. En lugar, lo que hace es añadir nuevos commits y los envía a la misma rama. De este modo, si vuelves a mirar el Pull Request en el futuro, puedes encontrar fácilmente todo el contexto con todas las decisiones tomadas. Al pulsar el botón “Merge”, se crea un commit de fusión que referencia al Pull Request, con lo que es fácil localizar para revisar la conversación original, si es necesario.

Manteniéndonos actualizados

Si el Pull Request se queda anticuado o por cualquier otra razón no puede fusionarse limpiamente, lo normal es corregirlo para que el responsable pueda fusionarlo fácilmente. GitHub comprobará esto y te dirá si cada Pull Request tiene una fusión trivial posible o no.

fallo en fusión de PR
Figure 97. Pull Request que no puede fusionarse limpiamente

Si ves algo parecido a Pull Request que no puede fusionarse limpiamente, seguramente prefieras corregir la rama de forma que se vuelva verde de nuevo y el responsable no tenga trabajo extra con ella.

Tienes dos opciones para hacer esto. Puedes, por un lado, reorganizar (rebase) la rama con el contenido de la rama master (normalmente esta es la rama desde donde se hizo la bifurcación), o bien puedes fusionar la rama objetivo en la tuya.

Muchos desarrolladores eligen la segunda opción, por las mismas razones que que dijimos en la sección anterior. Lo que importa aquí es la historia y la fusión final, por lo que reorganizar no es mucho más que tener una historia más limpia y, sin embargo, es de lejos más complicado de hacer y con más posibilidad de error.

Si quieres fusionar en la rama objetivo para hacer que tu Pull Request sea fusionable, deberías añadir el repositorio original como un nuevo remoto, bajártelo (fetch), fusionar la rama principal en la tuya, corregir los problemas que surjan y finalmente enviarla (push) a la misma rama donde hiciste la solicitud de integración.

Por ejemplo, supongamos que en el ejemplo “tonychacon” que hemos venido usando, el autor original hace un cambio que crea un conflicto con el Pull Request. Seguiremos entonces los siguientes pasos.

$ git remote add upstream https://github.com/schacon/blink (1)

$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From https://github.com/schacon/blink
 * [new branch]      master     -> upstream/master

$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.

$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
    into slower-blink

$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
   ef4725c..3c8d735  slower-blink -> slow-blink
  1. Añadir el repositorio original como un remoto llamado “upstream”

  2. Obtener del remoto lo último enviado al repositorio

  3. Fusionar la rama principal en la nuestra

  4. Corregir el conflicto surgido

  5. Enviar de nuevo los cambios a la rama del Pull Request

Cuando haces esto, el Pull Request se actualiza automáticamente y se re-chequea para ver si es posible un fusionado automático o no.

PR corregido
Figure 98. Ahora el Pull Request ya fusiona bien

Una de las cosas interesantes de Git es que puedes hacer esto continuamente. Si tienes un proyecto con mucha historia, puedes fácilmente fusionarte la rama objetivo (master) una y otra vez cada vez que sea necesario, evitando conflictos y haciendo que el proceso de integración de tus cambios sea muy manejable.

Si finalmente prefieres reorganizar la rama para limpiarla, también puedes hacerlo, pero se recomienda no forzar el push sobre la rama del Pull Request. Si otras personas se la han bajado y hacen más trabajo en ella, provocarás los problemas vistos en Los Peligros de Reorganizar. En su lugar, envía la rama reorganizada a una nueva rama de GitHub y abre con ella un nuevo Pull Request, con referencia al antiguo, cerrando además éste.

Referencias

La siguiente pregunta puede ser “¿cómo hago una referencia a un Pull Request antiguo?”. La respuesta es, de varias formas.

Comencemos con cómo referenciar otro Pull Request o una incidencia (Issue). Todas las incidencias y Pull Requests tienen un número único que los identifica. Este número no se repite dentro de un mismo proyecto. Por ejemplo, dentro de un proyecto solo podemos tener un Pull Request con el número 3, y una incidencia con el número 3. Si quieres hacer referencia al mismo, basta con poner el símbolo # delante del número, en cualquier comentario o descripción del Pull Request o incidencia. También se puede poner referencia tipo usuario#numero para referirnos a un Pull Request o incidencia en una bifurcación que haya creado ese usuario, o incluso puede usarse la forma usuario/repo#num para referirse a una incidencia o Pull Request en otro repositorio diferente.

Veamos un ejemplo. Supongamos que hemos reorganizado la rama del ejemplo anterior, creado un nuevo pull request para ella y ahora queremos hacer una referencia al viejo Pull Request desde el nuevo. También queremos hacer referencia a una incidencia en la bifurcación del repositorio, y una incidencia de un proyecto totalmente distinto. Podemos rellenar la descripción justo como vemos en Referencias cruzadas en un Pull Request..

Referencias a PR
Figure 99. Referencias cruzadas en un Pull Request.

Cuando enviamos este pull request, veremos todo como en Cómo se ven las referencias cruzadas en el Pull Request..

Referencias a PR
Figure 100. Cómo se ven las referencias cruzadas en el Pull Request.

Observa que la URL completa de GitHub que hemos puesto ahí ha sido acortada a la información que necesitamos realmente.

Ahora, si Tony regresa y cierra el Pull Request original, veremos que GitHub crea un evento en la línea de tiempo del Pull Request. Esto significa que cualquier que visite este Pull Request y vea que está cerrado, puede fácilmente enlazarlo al que lo hizo obsoleto. El enlace se mostrará tal como en Cómo se ven las referencias cruzadas en el Pull Request..

PR cerrado
Figure 101. Cómo se ven las referencias cruzadas en el Pull Request.

Además de los números de incidencia, también puedes hacer referencia a un commit específico usando la firma SHA-1. Puedes utilizar la cadena SHA-1 completa (de 40 caracteres) y al detectarla GitHub en un comentario, la convertirá automáticamente en un enlace directo al commit. Nuevamente, puedes hacer referencia a commits en bifurcaciones o en otros repositorios del mismo modo que hicimos con las incidencias.

Markdown

En enlazado a otras incidencias es solo el comienzo de las cosas interesantes que se pueden hacer con cualquier cuadro de texto de GitHub. En las descripciones de las incidencias y los Pull Requests, así como en los comentarios, y otros cuadros de texto, se puede usar lo que se conoce “formato Markdown de GitHub”. El formato Markdown es como escribir en texto plano pero que luego se convierte en texto con formato.

Mira en Ejemplo de texto en Markdown y cómo queda después. un ejemplo de cómo los comentarios o el texto puede escribirse y luego formatearse con Markdown.

Ejemplo de Markdown
Figure 102. Ejemplo de texto en Markdown y cómo queda después.

El formato Markdown de GitHub

En GitHub se añaden algunas cosas a la sintaxis básica del Markdown. Son útiles al tener relación con los Pull Requests o las incidencias.

Listas de tareas

La primera característica añadida, especialmente interesante para los Pull Requests, son las listas de tareas. Una lista de tareas es una lista de cosas con su marcador para indicar que han terminado. En un Pull Requests o una incidencia nos sirven para anotar la lista de cosas pendientes para considerar terminado el trabajo relacionado con esa incidencia.

Puedes crear una lista de tareas así:

- [X] Write the code
- [ ] Write all the tests
- [ ] Document the code

Si incluimos esto en la descripción de nuestra incidencia o Pull Request, lo veremos con el aspecto de Cómo se ven las listas de tareas de Markdown.

Ejemplo de lista de tareas
Figure 103. Cómo se ven las listas de tareas de Markdown.

Esto se suele usar en Pull Requests para indicar qué cosas hay que hacer en la rama antes de considerar que el Pull Request está listo para fusionarse. La parte realmente interesante es que puedes pulsar los marcadores para actualizar el comentario indicando qué tareas se finalizaron, sin necesidad de editar el texto markdown del mismo.

Además, GitHub mostrará esas listas de tareas como metadatos de las páginas que las muestran. Por ejemplo, si tienes un Pull Request con tareas y miras la página resumen de todos los Pull Request, podrás ver cuánto trabajo queda pendiente. Esto ayuda a la gente a dividir los Pull Requests en subtareas y ayuda a otras personas a seguir la evolución de la rama. Se puede ver un ejemplo de esto en Resumen de lista de tareas en la lista de PR..

Ejemplo de lista de tareas
Figure 104. Resumen de lista de tareas en la lista de PR.

Esto es increíblemente útil cuando se abre un Pull Request al principio y se quiere usar para seguir el progreso de desarrollo de la característica.

Fragmentos de código

También se pueden añadir fragmentos de código a los comentarios. Esto resulta útil para mostrar algo que te gustaría probar antes de ponerlo en un commit de tu rama. Esto también se suele usar para añadir ejemplos de código que no funciona u otros asuntos.

Para añadir un fragmento de código, lo tienes que encerrar entre los símbolos del siguiente ejemplo.

```java
for(int i=0 ; i < 5 ; i++)
{
   System.out.println("i is : " + i);
}
```

Si añades junto a los símbolos el nombre de un lenguaje de programación, como hacemos aquí con java, GitHub intentará hacer el resaltado de la sintaxis del lenguaje en el fragmento. En el caso anterior, quedaría con el aspecto de Cómo se ve el fragmento de código..

Fragmento de código
Figure 105. Cómo se ve el fragmento de código.
Citas

Si estás respondiendo a un comentario grande, pero solo a una pequeña parte, puedes seleccionar la parte que te interesa y citarlo, para lo que precedes cada línea citada del símbolo >. Esto es tan útil que hay un atajo de teclado para hacerlo: si seleccionas el texto al que quieres contestar y pulsas la tecla r, creará una cita con ese texto en la caja del comentario.

Un ejemplo de cita:

> Whether 'tis Nobler in the mind to suffer
> The Slings and Arrows of outrageous Fortune,

How big are these slings and in particular, these arrows?

Una vez introducida, el comentario se vería como en Rendered quoting example..

Rendered quoting
Figure 106. Rendered quoting example.
Emojis (emoticonos)

Finalmente, también puedes usar emoji (emoticonos) en tus comentarios. Se utiliza mucho en las discusiones de las incidencias y Pull Requests de GitHub. Incluso tenemos un asistente de emoji: si escribes un comentario y tecleas el carácter :, verás cómo aparecen iconos para ayudarte a completar el que quieras poner.

Auto-completando emoji
Figure 107. Emoji auto-completando emoji.

Los emoticonos son de la forma :nombre: en cualquier punto del comentario. Por ejemplo, podráis escribir algo como esto:

I :eyes: that :bug: and I :cold_sweat:.

:trophy: for :microscope: it.

:+1: and :sparkles: on this :ship:, it's :fire::poop:!

:clap::tada::panda_face:

Al introducir el comentario, se mostraría como Comentando con muchos emoji..

Emoji
Figure 108. Comentando con muchos emoji.

No es que sean especialmente útiles, pero añaden un elemento de gracia y emoción a un medio en el que de otro modo sería mucho más complicado transmitir las emociones.

Note

Actualmente hay bastantes sitios web que usan los emoticonos. Hay una referencia interesante para encontrar el emoji que necesitas en cada momento:

Imágenes

Esto no es técnicamente parte de las mejoras a Markdown de GitHub, pero es increíblemente útil. En adición a añadir enlaces con imágenes en el formato Markdown a los comentarios, GitHub permite arrastrar y soltar imágenes en las áreas de texto para insertarlas.

Arrastrar y soltar imágenes
Figure 109. Arrastrar y soltar imágenes para subirlas.

Si vuelves a Referencias cruzadas en un Pull Request., verás una pequeña nota sobre el área de texto “Parsed as Markdown”. Si pulsas ahí te dará una lista completa de cosas que puedes hacer con el formato Markdown de GitHub.