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!

27 comentarios en “Mantener tu fork al día

  1. Pedro

    Nuevo estado:
    Borrando repositorios clonados y haciendo fork para luego clonarlos.
    Como siempre , claro, conciso y revelador.

    Muchas gracias!!!
    A ver si puedo colaborar en el proyecto 🙂

    1. alfonso

      Hola de nuevo Pedro:

      Gracias a tí por leernos y por tu comentario; me es de mucha ayuda para saber que se entiende bien lo que escribo.

      Y lo de colaborar, decirte que tus recomendaciones por aquí y en twitter es una forma de colaborar 😉 que en los inicios de cualquier proyecto es muy valiosa.

      ¡Seguimos en contacto!

  2. Pancho

    Estoy practicando esto y me he encontrado con una situación que no entiendo. Resulta que el upstream-master es igual a mi origin, en el momento que hago fetch me sale el mismo código y no se porque.
    Siguiendo el manual veo que incluso la dirección del repo origin es diferente a como tu la muestras. Alguna idea de que hago mal?
    Capturas

    Gracias 🙂

  3. admin

    En las capturas que has enviado estás viendo únicamente tu rama activa. Debajo de la barra de herramientas verás un pequeño desplegable que pone «Current Branch». Despliégalo y selecciona «All Branches». Deberías ver todas las ramas.

  4. Pingback: Fork de repositorios en Github | Aprende GIT

  5. Juan

    Hola Alfonso

    Dos post superinstructivos, estos de los fork. Quería hacerte una pregunta: Tengo un fork de un proyecto al que hago mis propias modificaciones, sin animo de incluirlas en el proyecto original, pero si incluyendo sus modificaciones. El caso es que el proyecto del que forkeo tiene tres branches y a mí sólo me interesa una de ellas, y aunque las elimine (el jodío intenta borrarlas del original, menos mal que es de solo lectura para mi), cuando hago fetch me las vuelve a cargar y tengo que volver a eliminarlas. Tengo como remotos mi fork (origin) y el original (upstream), y me interesaría tener como remoto solo el branche que quiero de upstream.
    Se podría indicar de alguna manera (https://github.com/upstream:branch, por ejemplo)?

    1. admin

      Hola Juan:

      Perdona que haya tardado en responder, se me había colado el post en el Spam y acabo de verlo mientras hacía una revisión rutinaria.

      ¿Cómo estás intentando borrar las ramas que no te interesan, qué comando estás utilizando? Sólo podrás borrar las ramas de tu fork (como has indicado en el comentario). Si estás usando sourcetree para borrar las ramas, efectivamente luego te las vuelve a traer al hacer fetch porque te borra la referencia local. Tienes que borrarlas del fork usando este comando:

      git push nombre_remote :nombre_rama

      por ejemplo:

      git push origin :mirama

      Alfonso

      1. Juan

        Gracias por responder, no has tardado tanto 🙂

        Las borro con click derecho -> delete branch en sourcetree. Lo que me interesa realmente es que no me las baje de nuevo al hacer fetch. probé a poner lo que te comenté en la url del remote, pero igual cometí un error de sintaxis, veo que tu dejas un espacio antes de los «:»

        De todas formas creo que me expliqué de mal a fatal. Lo que me interesa es bajarme a local solo una rama al hacer el fetch, de forma que al hacer el push me pueda despreocupar de las otras ramas, que no me interesan.

        Es decir, en mi fork ya eliminé las ramas al crear el fork, directamente desde la página de github, ahora lo que me interesa es que no cometa el error de hacer un merge y que me mergee (que verbo tan horrible) todas las ramas (sobre todo porque en el repositorio original ya van mergeando entre sus ramas cuando lo creen oportuno, así que lo que interesa de esas ramas me viene dado en la rama de mi interés)

        Espero haberme explicado mejor xD

        1. Juan

          Lo leo y no me entero ni yo xD Por si fuera necesario, tengo:

          Origin: mi fork de upstream en github
          Upstram: el repositorio original, al que tengo acceso de solo lectura
          workingcopy: mi copia local.

          Procedimiento de trabajo:
          Varios desarrolladores tienen workingcopy de Origin. Modifican y commitean como si fuera un repositorio normal
          Yo primero hago pull de Origin. Hago mis modificaciones. Hago fetch (de upstream). Elimino ramas innecesarias. Hago merge. Resuelvo conflictos. Hago push para tener Origin con las modificaciones propias y las de upstream.

          1. admin

            Sí, te has explicado. Lo que quieres hacer es posible hacerlo, tienes que jugar con la opción fetch de la definición del remote deltro del .gitconfig Te ha quedado claro ¿verdad? 🙂 Escribo una entrada en el blog al respecto, es una muy buena pregunta.

            Te paso una copia de un trozo de la configuración de uno de mis repositorios en la que sólo me bajo las ramas fast/*, para que veas por dónde van los tiros:


            [remote "origin"]
            url = ssh://gitolite/interno/proyecto1/web.git
            fetch = +refs/heads/fast/*:refs/remotes/origin/fast/*

            Si sólo quieres trabajar con una rama, digamos que se llama «laquemeinteresa», tu fichero .git/config debería incluir una línea parecida a esta:


            [remote "origin"]
            url = ssh://gitolite/clientes/grito/web.git
            fetch = +refs/heads/laquemeinteresa:refs/remotes/origin/laquemeinteresa

            de forma que al hacer git-fetch, sólo se traiga esa rama.

            Mira la página de manual de git-fetch, en particular la sección en la que hablan de lo que es un <refspec>. O dame unos días y lo cuento por aquí un poco más legible ;-). Para evitar historias, si vas a probar, haz una copia de seguridad del repositorio completo antes de trastear.

            Alfonso

  6. Juan

    Ok, me ha quedado claro. Es sólo limitar el fetch y no el remote como intentaba hacer yo. Fenómeno, voy a probarlo, muchísimas gracias 🙂

  7. Juan

    Ha ido perfecto 🙂 De todas formas estaré atento a tu entrada.

    Enhorabuena de nuevo por el blog… no se que haríamos sin blogueros como tú xD

    1. admin

      ¡¡Genial!! Escribiré la entrada de todas formas, será más fácil de encontrar la información que en los comentarios.

      ¡Y gracias por tus palabras!

      Alfonso

  8. Luis

    Gracias, por el artículo.

    Me viene de perlas para hacer mi primera aportación a un plugin de wordpress y practicar lo que vimos en el curso que impartiste.

    A practicar mi inglés. 😉

  9. Wuilmer Bolivar

    Esta excelente la explicación para los que trabajan git a modo gráfico, pero ¿y que pasa con los que trabajamos a modo consola?.

    Hasta ahora lo que he podido hacer es:

    Paso 1:
    git remote add aprendegit [email protected]:aprendegit/fork.git

    Paso 2:
    git pull aprendegit master

    Paso 3:
    git push aprendegit master

    Esta correcto trabajar de esta forma.??
    Que diferencias existe entre pull y fecth que veo en tu explicación.

    1. Pedro A. Castillo

      ¡Me uno a esta pregunta!

      ¿cómo podemos hacer esta parte del tutorial desde la consola, a base de órdenes de terminal?

      ¡Muchas gracias!

      1. alfonso

        Hola Pedro:

        Tendría que buscar un rato para actualizar este tutorial a línea de comandos. Siendo sincero, en este momento estoy un poco liado y no voy a poder hacerlo a corto plazo. Tomo nota de vuestra petición.

        Muchas gracias por tu tiempo.

    2. Gracielita

      Buen artículo , se podría para los que somos de consola por favor aún me cuesta entender como funcionan los procesos y la forma en la que lo explicas con manzanitas es lo que necesito. Gracias

  10. Javier Montesinos

    Hola Alfonso.

    Muchas gracias por tus artículos han sido de gran ayuda para iniciarme en Git. Quería comentarte una cosa a ver si me puedes echar un cable, no sé si es el lugar adecuado.

    Estamos migrando de Subversión a Git tacha !!! Para ponerte en situación tenemos en un repositorio de subversion diferentes proyectos de modo que algunos de ellos son dependencias para otros por ejemplo tengo un proyecto «gestion interna» que puede tener dos subproyectos o módulos «gestion-wm» y «gestion-modelo». Este segundo proyecto gestion-modelo es una dependencia para otro proyecto.

    Estoy planteando la migración de tal forma que:
    gestion-wm vaya a un repositorio con su rama master, develop… para aplicar git-flow
    gestion-modelo irá a otro repositorio con su rama master,develop…

    Como puedo incluir el código de getion-modelo en gestion-wm o culaquier otro proyecto que lo utilice como dependencia? He estado viendo las opciones de submodules y subtree, pero no acaban de quedarme claras. Por lo que he leído en todos sitios recomienda el uso de Subtree.

    Si por ejemplo saco una nueva versión de gestion-wm la 1.1.12 y esa versión implica cambios en gestion-modelo como puedo hacer para que en el tag de la 1.1.12 se haga referencia a la versión concreta de gestion-modelo que se debe usar y de modo que esta sea compartida por todos los proyectos que puedan usar gestion-modelo.

    Espero haberme explicado.

    Una vez más, muchísimas gracias por la ayuda que tu blog me ha prestado.

    Javier Montesinos.

    1. admin

      Hola Javier:

      La primera pregunta que te haría es ¿en qué lenguaje de programación están esos proyectos? El motivo es el siguiente: el problema que necesitas resolver es un problema de gestión de dependencias y para ese problema existen múltiples soluciones que hay que analizar. En los cursos que doy, esta es una pregunta recurrente y en cada caso la discusión y conclusiones a las que llegamos son diferentes. Te voy a dar cuál es el criterio que yo sigo y a poner luego un ejemplo comentamos hace un par de meses en uno de los cursos.

      Mi criterio es que si tu lenguaje de programación o framework tiene un gestor de dependencias, úsalo. Yo programo principalmente en PHP y Ruby/Rails (y objectiveC cuando puedo y me dejan) así que lo que haría sería crear dos repositorios git independientes (uno para gestin-wm y otro para gestion-modelo), crearía un «paquete», «gema» o como quieras llamarlo y los incluiría en el proyecto «gestión interna» utilizando este gestor de dependencias:

      • composer si el proyecto está en PHP o Symfony
      • rubygems si el proyecto está en Ruby o Rails
      • cocoapods si el proyecto está en ObjectiveC
      • – Si usas otros lenguajes como java, perl, python o javascript, todos tienen sus propios gestores de dependencias… maven, pip, npm, bower o el que corresponda en cada caso.

      Siguiendo con mi criterio: si tu lenguaje, infraestructura o plataforma de desarrollo no soporta gestores de dependencias o no puedes utilizarlos (cosa que ya me ha pasado), entonces hay dos opciones: git submodules y/o git subtree merging. En cualquier de los dos casos, tendrías tres repositorios git independientes: uno para el proyecto principal y uno para cada «modulo» de tu proyecto. Tanto submodules como subtree te permiten, así contado en breve, incluir unos repositorios git dentro de otros.

      Una de las principales diferencias con subversion a la hora de organizar repositorios es que la tendencia en git es tener muchos repositorios pequeños en lugar de uno sólo del que cuelgan todos tus proyectos y dependencias.

      También puedes hacerlo a tu manera. Te pongo un ejemplo que estuvimos comentando en uno de los cursos, dentro de un equipo que trabajaban con Java:

      • Cada comoponente o módudo tenía su propio repositorio
      • Las componentes se compilan y el fichero jar/war o lo que usasen en cada caso se copia al repositorio del proyecto que usa esa componente
      • Este fichero binario se introdue en el repositorio con un commit y se versionaba en binario

      Este flujo tiene sus ventanas y sus inconvenientes. En su caso he de decir que era altamente efectivo, aunque quizás académicamente no es la forma más limpia de hacerlo. Dado que los ficheros .jar que generaban apenas pesaban unos cientos de Kb y no cambiaban muy a menudo, con un flujo muy sencillo y sin complicarse la vida con los submodules de git, tenían el problema resuelto en poco tiempo y con muy poco esfuerzo, que es lo importa al fin y al cabo en nuestro día a día.

      Espero haberte ayudado y si tienes cualquier otra pregunta, aquí me tienes.

      Un saludo.

      P.D. Por cierto, he estado viendo tu post sobre git-flow, muchas gracias tanto por los comentarios como por los enlaces y referencias.

      1. Javier Montesinos

        Gracias por tu rapidez en responder Alfonso.

        Te comento, todos los proyectos son Java.

        Nuestra idea pasa por tener un repositorio por componente / proyecto y luego crear las dependencias mediante Maven. Mi duda es ¿en el caso de que usemos esta opción deberíamos tener 3 proyectos o 2? 3 para tener cada componente y otro con el proyecto padre o 2 para tener uno por cada componente e indicar las dependencias en el fichero pom correspondiente.

        Hemos realizado lo siguiente; hemos clonado un proyecto y mediante git subtree hemos añadido una dependencia ubicada en otro repositorio distinto, consiguiendo con esto el merge del código de la dependencia en el proyecto padre (por ejemplo gestion-wm)

        Teniendo el remote del módulo apuntando a su repositorio particular podría publicar mis commits en este repositorio, aunque tengo que ver como diferenciar que el commit va al repositorio de gestion-wm o al de gestion-model.

        Lo que no me acaba de quedar claro es como hacer que en el tag 1.1.3 por ejemplo de gestion-wm se especifique que la versión que se usa de gestion-modelo es la 1.8.1. por cambios que se han publicado en el desarrollo de la última «feature»

        Nuevamente muchas gracias por tu ayuda.

        Javier Montesinos

  11. Yet another programmer

    Esta página está de lujo, estoy aprendiendo un montón gracias a ti 🙂

  12. ricardo

    Muy buen tutorial de git, explicas muy bien!! Tengo una pregunta, en vez de hacer fetch y luego el merge yo pudiera haber hecho un git pull de la rama remota y tendria el mismo resultado?

  13. Apolo

    Buenas.
    Estoy empezando con Git y la verdad es que cada paso que doy es un gustazo esto de control de versiones.
    Vine por información sobre los Forks, y la verdad es que hasta este punto es un gustazo leerte porque lo aclaras todo y lo explicas genial.
    Gracias.

Los comentarios están cerrados.