Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Flujo de trabajo para los Pull request

El llamado "flujo de trabajo de PR" utilizado por Godot es común en muchos proyectos que utilizan Git y debería resultar familiar para los veteranos contribuyentes de software libre. La idea es que solo un pequeño número (si acaso alguno) de contribuyentes hagan cambios directamente en la rama master. En su lugar, los contribuyentes hacen un fork del proyecto (es decir, crean una copia del mismo que pueden modificar a su gusto) y luego utilizan la interfaz de GitHub para solicitar un pull desde una rama de su fork a una rama del repositorio original (generalmente llamado upstream).

El pull request (PR) resultante puede ser revisado por otros contribuyentes, quienes pueden aprobarlo, rechazarlo o, lo más común, solicitar modificaciones. Una vez aprobado, el PR puede ser fusionado por uno de los desarrolladores principales, y sus commits se convertirán en parte de la rama de destino (generalmente la rama master).

Juntos recorreremos un ejemplo para mostrar el flujo de trabajo típico y los comandos de Git asociados. Pero primero, echemos un vistazo rápido a la organización del repositorio Git de Godot.

Repositorio fuente de Git

El repositorio en GitHub es un repositorio de código Git junto con un sistema integrado de seguimiento de problemas y solicitudes de extracción (PR).

Nota

Si está contribuyendo a la documentación, puedes encontrar su repositorio aquí.

El sistema de control de versiones Git es la herramienta utilizada para realizar un seguimiento de las ediciones sucesivas del código fuente. Para contribuir eficientemente a Godot, se recomienda altamente aprender los conceptos básicos de la línea de comandos de Git. Existen algunas interfaces gráficas para Git, pero suelen fomentar malos hábitos en cuanto al flujo de trabajo de Git y PR, por lo que recomendamos no utilizarlas. En particular, aconsejamos no usar el editor en línea de GitHub para contribuciones de código (aunque se tolera para correcciones pequeñas o cambios en la documentación), ya que impone un commit por archivo y por modificación, lo que rápidamente genera PRs con un historial de Git poco legible (especialmente después de la revisión de pares).

Ver también

Las primeras secciones del "Libro" de Git son una buena introducción a la filosofía de la herramienta y a los diversos comandos que necesitas dominar en tu flujo de trabajo diario. Puedes leerlos en línea en el sitio web de Git SCM. También puedes probar la guía interactiva de GitHub.

Las ramas en el repositorio Git están organizadas de la siguiente manera:

  • La rama master es donde se lleva a cabo el desarrollo de la próxima versión principal. Como una rama de desarrollo, puede ser inestable y no está destinada para su uso en producción. Aquí es donde se deben realizar las PR (Pull Requests) en prioridad.

  • Las ramas estables se nombran según su versión, por ejemplo, 3.1 y 2.1. Se utilizan para portar correcciones de errores y mejoras desde la rama master a la versión estable actualmente mantenida (por ejemplo, 3.1.2 o 2.1.6). Como regla general, la última rama estable se mantiene hasta la próxima versión menor (por ejemplo, la rama 3.0 se mantuvo hasta el lanzamiento de Godot 3.1). Si deseas realizar PRs contra una rama estable mantenida, verifica primero si tus cambios también son relevantes para la rama master, y si es así, haz la PR para la rama master en prioridad. Los administradores de lanzamiento pueden luego seleccionar el fix para una rama estable si es relevante.

  • De manera ocasional, puede haber ramas de características (feature branches), generalmente destinadas a fusionarse con la rama master en algún momento.

Forking y clonación

El primer paso es preparar el repositorio godotengine/godot en GitHub. Para hacerlo, necesitarás tener una cuenta de GitHub y estar conectado. En la esquina superior derecha de la página GitHub del repositorio, debería ver el botón "Fork" como se muestra a continuación:

../../_images/github_fork_button.png

Haz clic en él y después de un momento deberías ser redirigido a tu propia copia (fork) del repositorio de Godot, con tu nombre de usuario de GitHub como espacio de nombres:

../../_images/github_fork_url.png

A continuación, puedes clonar tu copia (fork), es decir, crear una copia local del repositorio en línea (en la terminología de Git, el origin remote). Si aún no lo has hecho, descarga Git desde su sitio web si estás usando Windows o macOS, o instálalo a través de tu gestor de paquetes si estás usando Linux.

Nota

Si estás en Windows, abre Git Bash para ingresar los comandos. Los usuarios de macOS y Linux pueden utilizar sus respectivas terminales.

Para clonar tu fork desde GitHub, usa el siguiente comando:

$ git clone https://github.com/USERNAME/godot

Nota

En nuestros ejemplos, el carácter "$" denota el símbolo del prompt de la línea de comandos en las terminales UNIX típicas. No forma parte del comando y no debe ser escrito.

Después de un momento, deberías tener un directorio llamado godot en tu directorio de trabajo actual. Muévete a ese directorio utilizando el comando cd:

$ cd godot

Comenzaremos configurando una referencia al repositorio original del que hicimos el fork:

$ git remote add upstream https://github.com/godotengine/godot
$ git fetch upstream

Esto creará una referencia llamada upstream que apunta al repositorio original godotengine/godot. Esto será útil cuando desees obtener nuevos commits de su rama master para actualizar tu fork. Ya tienes otra referencia remota llamada origin, que apunta a tu fork (USERNAME/godot).

Solo necesitas realizar los pasos anteriores una vez, siempre que mantengas la carpeta local godot (puedes moverla si deseas, los metadatos relevantes están ocultos en su subcarpeta .git).

Nota

Ramifícala, tráela, codifica, agrega al área de preparación, haz commit, súbelo, rebasea... tecnológico.

Esta divertida versión de Technologic de Daft Punk muestra la concepción general que tienen los principiantes en Git sobre su flujo de trabajo: muchas comandos extraños que aprender copiando y pegando, esperando que funcionen como se espera. Y en realidad, no es una mala manera de aprender, siempre y cuando tengas curiosidad y no dudes en cuestionar a tu motor de búsqueda cuando te sientas perdido. Por lo tanto, te daremos los comandos básicos que debes conocer al trabajar con Git.

In the following, we will assume as an example that you want to implement a feature in Godot's Project Manager, which is coded in the editor/project_manager.cpp file.

Ramificación

Por defecto, el comando git clone debería haberte colocado en la rama master de tu fork (origin). Para empezar el desarrollo de tu nueva característica, crearemos una rama de funcionalidad:

# Create the branch based on the current branch (master)
$ git branch better-project-manager

# Change the current branch to the new one
$ git checkout better-project-manager

Este comando es equivalente:

# Change the current branch to a new named one, based on the current branch
$ git checkout -b better-project-manager

Si deseas volver a la rama master, utilizarías:

$ git checkout master

Puedes ver en qué rama te encuentras actualmente con el comando git branch:

$ git branch
  2.1
* better-project-manager
  master

Asegúrate de siempre regresar a la rama master antes de crear una nueva rama, ya que tu rama actual se usará como base para la nueva. Alternativamente, puedes especificar una rama base personalizada después del nombre de la nueva rama:

$ git checkout -b my-new-feature master

Actualizando su rama

Esto no sería necesario la primera vez (justo después de haber bifurcado el repositorio upstream). Sin embargo, la próxima vez que desees trabajar en algo, notarás que la rama master de tu fork está varios commits atrás de la rama master del repositorio upstream: se habrán fusionado solicitudes de extracción de otros colaboradores en el ínterin.

Para asegurarte de que no haya conflictos entre la función que estás desarrollando y la rama master actual del repositorio upstream, deberás actualizar tu rama haciendo pull de la rama upstream.

$ git pull --rebase upstream master

El argumento --rebase se asegurará de que cualquier cambio local que hayas confirmado se reaplique encima de la rama que has extraído, lo cual generalmente es lo que queremos en nuestro flujo de solicitudes de extracción (PR). De esta manera, cuando abras una solicitud de extracción, tus propios commits serán la única diferencia con la rama master upstream.

Durante el rebase, pueden surgir conflictos si tus commits modificaron código que ha sido cambiado en la rama upstream mientras tanto. Si eso sucede, Git se detendrá en el commit conflictivo y te pedirá que resuelvas los conflictos. Puedes hacerlo con cualquier editor de texto, luego agregas los cambios al área de preparación (más sobre eso más adelante) y continúas con git rebase --continue. Repite la operación si hay más commits con conflictos, hasta que se complete la operación de rebase.

Si no estás seguro de lo que está sucediendo durante un rebase y entras en pánico (no te preocupes, todos lo hacemos las primeras veces), puedes abortar el rebase con git rebase --abort. Luego volverás al estado original de tu rama antes de llamar a git pull --rebase.

Nota

Si omites el argumento --rebase, en su lugar, crearás un commit de fusión que le indica a Git cómo combinar las dos ramas distintas. Si se presentan conflictos, se resolverían todos de una vez a través de este commit de fusión.

Si bien este es un flujo de trabajo válido y el comportamiento predeterminado de git pull, los commits de fusión dentro de las solicitudes de extracción (PR) no son bien vistos en nuestro flujo de trabajo de PR. Solo los usamos al fusionar las PR en la rama principal del repositorio original (upstream).

La filosofía es que una PR debe representar la etapa final de los cambios realizados en el código y no nos interesa los errores y correcciones que se hayan hecho en etapas intermedias antes de la fusión. Git nos proporciona excelentes herramientas para "reescribir el historial" y hacer que parezca que hemos hecho las cosas correctamente desde el principio, y estamos encantados de usarlo para asegurarnos de que los cambios sean fáciles de revisar y comprender mucho tiempo después de que se hayan fusionado.

Si ya has creado un commit de fusión sin usar rebase, o has realizado otros cambios que han dado lugar a un historial no deseado, la mejor opción es utilizar un rebase interactivo en la rama del repositorio principal (upstream). Consulta la sección dedicada para obtener instrucciones detalladas.

Truco

Si en algún momento deseas restablecer una rama local a un commit o rama específica, puedes hacerlo con git reset --hard <ID del commit> o git reset --hard <remoto>/<rama> (por ejemplo, git reset --hard upstream/master).

Ten en cuenta que esto eliminará cualquier cambio que hayas realizado en esta rama y que hayas confirmado. Si alguna vez pierdes confirmaciones por error, utiliza el comando git reflog para encontrar el ID del commit del estado anterior que te gustaría restaurar, y úsalo como argumento de git reset --hard para regresar a ese estado.

Realizando cambios

Luego realizarías tus cambios en el archivo editor/project_manager.cpp de nuestro ejemplo con tu entorno de desarrollo habitual (editor de texto, IDE, etc.).

Por defecto, esos cambios están sin preparar (unstaged). El área de preparación es una capa intermedia entre tu directorio de trabajo (donde realizas tus modificaciones) y el repositorio local de Git (los commits y toda la información en la carpeta .git). Para llevar los cambios del directorio de trabajo al repositorio de Git, debes prepararlos (stage) con el comando git add, y luego confirmarlos con el comando git commit.

Hay varios comandos que debes conocer para revisar tu trabajo actual, antes de prepararlo, mientras está preparado y después de haberlo confirmado.

  • git diff te mostrará los cambios actuales sin preparar, es decir, las diferencias entre tu directorio de trabajo y el área de preparación (staging).

  • git checkout -- <archivos> deshará los cambios no preparados en los archivos especificados.

  • git add <archivos> preparará los cambios en los archivos listados.

  • git diff --staged mostrará los cambios actuales preparados, es decir, las diferencias entre el área de preparación y el último commit.

  • git reset HEAD <files> deshará los cambios preparados (staged) en los archivos especificados, devolviéndolos a la versión no preparada (unstaged).

  • git status te mostrará cuáles son las modificaciones actualmente preparadas (staged) y no preparadas (unstaged).

  • git commit realizará la confirmación de los archivos preparados (staged). Abrirá un editor de texto (puedes definir el que prefieras mediante la variable de entorno GIT_EDITOR o la configuración core.editor en Git) para que escribas un mensaje de confirmación. También puedes usar git commit -m "Mensaje de confirmación genial" para escribir directamente el mensaje de confirmación sin abrir el editor.

  • git commit --amend te permite modificar el último commit con los cambios actualmente preparados (añadidos con git add). Esta es la mejor opción si deseas corregir un error en el último commit (error, falta de ortografía, problema de estilo, etc.).

  • git log te mostrará los últimos commits de tu rama actual. Si has realizado commits locales, deberían mostrarse en la parte superior.

  • git show te mostrará los cambios del último commit. También puedes especificar el hash de un commit para ver los cambios de ese commit en particular.

¡Eso es mucho que memorizar! No te preocupes, simplemente consulta esta hoja de referencia cuando necesites hacer cambios y aprende haciéndolo.

Aquí te muestro cómo podría verse el historial del shell en nuestro ejemplo:

# It's nice to know where you're starting from
$ git log

# Do changes to the Project Manager with the nano text editor
$ nano editor/project_manager.cpp

# Find an unrelated bug in Control and fix it
$ nano scene/gui/control.cpp

# Review changes
$ git status
$ git diff

# We'll do two commits for our unrelated changes,
# starting by the Control changes necessary for the PM enhancements
$ git add scene/gui/control.cpp
$ git commit -m "Fix handling of margins in Control"

# Check we did good
$ git log
$ git show
$ git status

# Make our second commit
$ git add editor/project_manager.cpp
$ git commit -m "Add a pretty banner to the Project Manager"
$ git log

Con esto, deberíamos tener dos nuevos commits en nuestra rama better-project-manager que no están en la rama master. Sin embargo, estos cambios son solo locales por ahora, el repositorio remoto no tiene conocimiento de ellos, ni tampoco el repositorio "upstream".

Empujando los cambios a remoto

Ahí es donde entra en juego git push. En Git, un commit siempre se realiza en el repositorio local (a diferencia de Subversion donde un commit modificará directamente el repositorio remoto). Necesitas hacer push de los nuevos commits a una rama remota para compartirlos con el resto del mundo. La sintaxis para hacer esto es:

$ git push <remote> <local branch>[:<remote branch>]

La parte sobre la rama remota se puede omitir si quieres que tenga el mismo nombre que la rama local, que es nuestro caso en este ejemplo, así que haremos lo siguiente:

$ git push origin better-project-manager

Git will ask you for your username and password. For your password, enter your GitHub Personal Access Token (PAT). If you do not have a GitHub Personal Access Token, or do not have one with the correct permissions for your newly forked repository, you will need to create one. Follow this link to create your Personal Access Token: Creating a personal access token.

After you have successfully verified your account using your PAT, the changes will be sent to your remote repository. If you check the fork's page on GitHub, you should see a new branch with your added commits.

Emitiendo una solicitud de extracción

Cuando cargas la rama de tu fork en GitHub, deberías ver una línea que dice "Esta rama está 2 commits adelante de godotengine:master" (y potencialmente algunos commits atrás, si tu rama master estaba fuera de sincronización con la rama master del repositorio original).

../../_images/github_fork_make_pr.png

En esa línea, hay un enlace que dice "Pull request" (Solicitud de extracción). Al hacer clic en él, se abrirá un formulario que te permitirá crear una solicitud de extracción en el repositorio original godotengine/godot. Deberías ver tus dos commits y un mensaje que dice "Able to merge" (Listo para fusionar). Si no ves esto (por ejemplo, si hay muchos más commits o si dice que hay conflictos de fusión), no crees la solicitud de extracción todavía, algo salió mal. Ve a nuestro Godot Contributors Chat y solicita ayuda :)

Utiliza un título explícito para la solicitud de extracción (PR) y agrega los detalles necesarios en el área de comentarios. Puedes arrastrar y soltar capturas de pantalla, GIF o proyectos comprimidos si son relevantes, para mostrar lo que implementaste. Luego, haz clic en "Create a pull request" (Crear una solicitud de extracción), ¡y listo!

Modificando un pull request

Mientras es revisada por otros colaboradores, es probable que necesites realizar cambios en tu solicitud de extracción aún no fusionada, ya sea porque otros colaboradores lo han solicitado o porque encontraste problemas mientras la probabas tú mismo.

La buena noticia es que puedes modificar una solicitud de extracción simplemente actuando en la rama desde la cual creaste la solicitud. Por ejemplo, puedes realizar un nuevo commit en esa rama, hacer push a tu fork y la solicitud de extracción se actualizará automáticamente:

# Check out your branch again if you had changed in the meantime
$ git checkout better-project-manager

# Fix a mistake
$ nano editor/project_manager.cpp
$ git add editor/project_manager.cpp
$ git commit -m "Fix a typo in the banner's title"
$ git push origin better-project-manager

Sin embargo, ten en cuenta que en nuestro flujo de trabajo de solicitudes de extracción, preferimos commits que lleven el código de un estado funcional a otro estado funcional, sin tener commits intermedios que solucionen errores en tu propio código o problemas de estilo. La mayoría de las veces, preferiremos un solo commit en una determinada solicitud de extracción (a menos que haya una buena razón para mantener los cambios separados). En lugar de crear un nuevo commit, considera usar git commit --amend para modificar el commit anterior con tus correcciones. El ejemplo anterior se vería así:

# Check out your branch again if you had changed in the meantime
$ git checkout better-project-manager

# Fix a mistake
$ nano editor/project_manager.cpp
$ git add editor/project_manager.cpp
# --amend will change the previous commit, so you will have the opportunity
# to edit its commit message if relevant.
$ git commit --amend
# As we modified the last commit, it no longer matches the one from your
# remote branch, so we need to force push to overwrite that branch.
$ git push --force origin better-project-manager

El rebase interactivo

If you didn't follow the above steps closely to amend changes into a commit instead of creating fixup commits, or if you authored your changes without being aware of our workflow and Git usage tips, reviewers might request you to rebase your branch to squash some or all of the commits into one.

En efecto, si se han realizado algunos commits después de las revisiones para corregir errores, errores tipográficos, etc. en el commit original, no son relevantes para un futuro lector del registro de cambios que querría saber qué sucedió en el código de Godot, o cuándo y cómo se modificó un archivo en particular.

Para combinar esos commits innecesarios en el principal, tendremos que reescribir la historia. Sí, tenemos ese poder. Puedes leer que es una mala práctica, y es cierto cuando se trata de ramas del repositorio principal. Pero en tu fork, puedes hacer lo que quieras, ¡y todo está permitido para obtener PRs ordenados! :)

Usaremos el rebase interactivo git rebase -i para hacer esto. Este comando toma como argumento un ID de commit o el nombre de una rama, y te permitirá modificar todos los commits entre ese commit/rama y el último en tu rama de trabajo, conocido como HEAD.

Si bien puedes dar cualquier ID de commit a git rebase -i y revisar todo lo que hay en el medio, el flujo de trabajo más común y conveniente implica hacer el rebase en la rama upstream master, lo cual puedes hacer con:

$ git rebase -i upstream/master

Nota

Referenciar ramas en Git es un poco complicado debido a la distinción entre ramas remotas y locales. Aquí, upstream/master (con una barra /) es una rama local que ha sido extraída de la rama master del repositorio remoto llamado upstream.

Las rebases interactivas solo se pueden realizar en ramas locales, por lo que la barra diagonal / es importante aquí. Como el repositorio remoto llamado "upstream" cambia con frecuencia, tu rama local upstream/master puede quedar desactualizada, así que puedes actualizarla con el comando git fetch upstream master. A diferencia de git pull --rebase upstream master, que actualizaría la rama que tienes actualmente en uso, fetch solo actualizará la referencia upstream/master (que es distinta de tu rama local master... sí, puede ser confuso, pero te familiarizarás con esto poco a poco).

Esto abrirá un editor de texto (por defecto, vi; consulta la documentación de Git para configurar tu editor favorito) con algo que puede parecerse a esto:

pick 1b4aad7 Add a pretty banner to the Project Manager
pick e07077e Fix a typo in the banner's title

El editor también mostrará instrucciones sobre cómo actuar en esos commits. En particular, te dirá que "pick" significa usar ese commit (no hacer nada), y que "squash" y "fixup" se pueden utilizar para fundir el commit con su commit padre. La diferencia entre "squash" y "fixup" es que "fixup" descartará el registro del commit fundido. En nuestro ejemplo, no estamos interesados en mantener el registro del commit "Fix a typo", así que usamos:

pick 1b4aad7 Add a pretty banner to the Project Manager
fixup e07077e Fix a typo in the banner's title

Al guardar y salir del editor, se realizará el reorganización interactiva (rebase). El segundo commit se fusionará con el primero, y git log y git show deberían confirmar que ahora tienes solo un commit con los cambios de ambos commits anteriores.

¡Pero cuidado! Has reescrito la historia y ahora tus ramas locales y remotas han divergido. De hecho, el commit 1b4aad7 en el ejemplo anterior habrá cambiado y, por lo tanto, obtendrá un nuevo hash de commit. Si intentas hacer push a tu rama remota, surgirá un error:

$ git push origin better-project-manager
To https://github.com/akien-mga/godot
 ! [rejected]        better-project-manager -> better-project-manager (non-fast-forward)
error: failed to push some refs to 'https://[email protected]/akien-mga/godot'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

This is reasonable behavior, Git will not let you push changes that would override remote content. But that's actually what we want to do here, so we will have to force it:

$ git push --force origin better-project-manager

¡Y tadaa! Git reemplazará sin problemas tu rama remota con lo que tenías localmente (así que asegúrate de que eso es lo que querías, usando git log). Esto también actualizará la solicitud de extracción (PR) en consecuencia.

Rebasing onto another branch

If you have accidentally opened your PR on the wrong branch, or need to target another branch for some reason, you might need to filter out a lot of commits that differ between the old branch (for example 4.2) and the new branch (for example master). This can make rebasing difficult and tedious. Fortunately git has a command just for this situation, git rebase --onto.

If your PR was created from the 4.2 branch and you want to update it to instead start at master the following steps should fix this in one step:

$ git rebase -i --onto master 4.2

This will take all the commits on your branch after the 4.2 branch, and then splice them on top of master, ignoring any commits from the 4.2 branch not on the master branch. You may still need to do some fixing, but this command should save you a lot of tedious work removing commits.

Just like above for the interactive rebase you need to force push your branch to handle the different changes:

$ git push --force origin better-project-manager

Borrando una ramificación de Git

Después de que tu pull request se haya fusionado, hay una última cosa que debes hacer: eliminar la rama de Git de la solicitud de extracción. No habrá problemas si no eliminas tu rama, pero es una buena práctica hacerlo. Deberás hacer esto dos veces, una vez para la rama local y otra para la rama remota en GitHub.

To delete our better Project Manager branch locally, use this command:

$ git branch -d better-project-manager

Alternativamente, si la rama aún no se ha fusionado y queremos eliminarla de todos modos, en lugar de usar -d, usaríamos -D.

A continuación, para eliminar la rama remota en GitHub, usa este comando:

$ git push origin -d better-project-manager

También puedes eliminar la rama remota desde la propia página de solicitud de extracción (PR) en GitHub; un botón debería aparecer una vez que haya sido fusionada o cerrada.