How to Contribute Code ---------------------- .. Define the 'shell' role so that one can have syntax highlighting when using inline code as in :shell:`some-command argument...` cf. https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#inline-code-highlighting .. role:: shell(code) :language: bash .. This allows one to use `...` instead of :shell:`...` in this document .. default-role:: shell .. We might want to use “.. highlight:: shell” to make literal blocks use shell syntax highlighting too, however I fear that some occurrences of “::” might then be forgotten or lost during editing, so I'm favoring more verbose but explicit “.. code-block:: shell” directives. .. note:: You'll need to familiarize yourself with setting up a development environment and building FlightGear in order to contribute code. See :doc:`/contributors-guide/guidelines/building` if you do not already have a working setup. Some understanding of `Git`_ will also be necessary; this free, online `book `__ is quite useful. .. _Git: https://git-scm.com/ .. _Git book: https://git-scm.com/book/en/v2 The most efficient way to contribute changes to the FlightGear code base is to file a merge request (this doesn't apply to aircraft or add-ons, which are managed differently). We'll first give an overview of the process including its main prerequisites, then explain each step in more detail. Overview ^^^^^^^^ The basic workflow for first-time contributors to a FlightGear Git repository is: #. **Fork the repository.** Fork the relevant FlightGear repository into your GitLab account. You will need to know which repository contains the code you are trying to change. Familiarizing yourself with the code base and the purpose of each repository is strongly recommended. #. **Set up a local clone.** Prepare a clone of the repository on your hard disk that is convenient for getting updates from the upstream repository and pushing changes to your fork. You'll also use this clone to perform test builds. #. **Build the project.** If you haven't already done so, this will be the time to set up your development environment so that you can build FlightGear. #. **Create a new branch.** Create a branch on your clone to isolate your changes. Use a clear and descriptive name for your branch (e.g., ``fix-menubar-typo`` or ``add-new-joystick-configs``). #. **Make changes.** Make the changes on your branch in small, focused commits. Each commit should be self-contained and represent one logical change. #. **Test and double-check.** Before you submit your changes to the FlightGear team for review, double-check your work. Ensure that your code builds and that tests pass. #. **Rebase and push.** Check if the upstream repository was updated in the meantime. If so, rebase your work on its latest state and perform a final test run to make sure everything still works fine. #. **Submit a merge request.** Open a merge request (MR) to propose your changes for review. Be sure to use the *Merge Request Template* to provide necessary context for reviewers. Feel free to ask on the developer mailing list if you're unsure whom to add as a reviewer. #. **Code review.** Once you have created a merge request, other developers will review your work, give feedback, and sometimes request changes be made. This is a crucial part of the development process, as it helps reduce bugs and improves code quality. #. **Clean up.** If your merge request was applied, pull your changes from the upstream branch and delete the local branch you created for the merge request. In order to present these steps in more detail, we'll now assume that you intend to make your first contribution to the `SimGear repository`_ (and thus, that you haven't already forked it). Except for the target branch name which may differ, the process would be the same for any Git repository of the FlightGear project. .. _SimGear repository: https://gitlab.com/flightgear/simgear .. _FlightGear repository: https://gitlab.com/flightgear/flightgear .. _FGData repository: https://gitlab.com/flightgear/fgdata .. _FlightGear documentation repository: https://gitlab.com/flightgear/documentation Fork the Repository ^^^^^^^^^^^^^^^^^^^ For what follows, you'll need to create a GitLab_ account if you don't already have one. Once you are logged into your account, go to the repository home page (`here `__ in our example) and click on the :guilabel:`Fork` button as shown in :ref:`forking-the-upstream-repo`; after filling some information, this will lead to the creation of a copy of the upstream repository in your own GitLab namespace under ``https://gitlab.com/YourUserName/``. .. _GitLab: https://gitlab.com/ .. figure:: images/01_forking.* :figname: forking-the-upstream-repo :alt: how to find the :guilabel:`Fork` button on the main page of the repository Forking the upstream repository Once you've clicked on the button, a new page is loaded as shown in :ref:`entering-fork-metadata`. The *Project name* will be prominent but it's only a label—choose it as you wish (“My SimGear fork” in the example). For the *Project URL*, you'll want to select your GitLab username after the initial portion ``https://gitlab.com/``. .. figure:: images/02_project_name,_URL,_slug.* :figname: entering-fork-metadata :alt: dialog that allows one to enter the project name, URL and slug for a fork Entering the project name, URL and slug for your fork The *Project slug* is the final part of the base URL for the fork you're about to create; let's choose ``flightgear-simgear`` so that all your forks of repositories of the FlightGear project are named ``flightgear-something``. This way, they will be easy to distinguish from non-FlightGear repositories you might want to publish under ``https://gitlab.com/YourUserName/``. Beware that if you modify the *Project name*, the GitLab user interface automatically modifies the *Project slug*; so, better fill them in this order: *Project name* then *Project slug*. There are a few fields left to enter. If you choose to include only the default branch, you'll save a little amount of space but might have to fetch other upstream branches later. Give your fork public visiblity so that reviews can take place. When ready, click on the :guilabel:`Fork project` button to actually create your fork of the upstream repository. .. _section-Set-up-a-local-clone: Set Up a Local Clone ^^^^^^^^^^^^^^^^^^^^ Since we want to pull updates from the upstream repository, it is convenient to clone it to your hard disk and modify the clone so that pushes go by default to your fork (which is online under ``https://gitlab.com/YourUserName/``). Cloning the upstream SimGear repository can be done with: .. code-block:: shell git clone https://gitlab.com/flightgear/simgear.git .. tip:: If you use `download_and_compile.sh`_ and it manages the repository you want to contribute to (this is obviously the case for SimGear), there is no need to create a new clone: you can simply use the repository clone it created on your disk. .. _download_and_compile.sh: https://gitlab.com/flightgear/fgmeta/-/blob/next/download_and_compile.sh?ref_type=heads Now, let's add a Git remote that points to your fork of the upstream repository. We configure this remote so that pushing to it uses `SSH `__ authentication. We also configure your repository clone so that pushes go to your fork by default. .. _SSH Wikipedia page: https://en.wikipedia.org/wiki/Secure_Shell .. code-block:: shell git remote add flo https://gitlab.com/frougon/flightgear-simgear.git git remote set-url --push flo git@gitlab.com:frougon/flightgear-simgear.git git config remote.pushDefault flo In the above commands, replace ``flo`` (name of the created remote) with an appropriate name of your choice; also replace ``frougon`` with your GitLab username. Build the Project ^^^^^^^^^^^^^^^^^ Make sure you can build FlightGear using the repository clone set up in the previous step and that the resulting executables run normally. This way, you'll be able to properly test any changes you later make in this clone. .. note:: The following steps will have to be repeated for each “self-contained changeset” that you intend to contribute. Create a New Branch ^^^^^^^^^^^^^^^^^^^ Create a branch based on ``origin/next`` that tracks ``origin/next`` and make sure it is up-to-date. Instead of ``new-branch-name``, choose a name that makes it clear what the changes in the created branch are supposed to do. .. code-block:: shell git checkout -b new-branch-name origin/next git pull .. note:: Here, ``origin`` is a remote that points to the upstream repository you cloned from, and ``next`` is the name of the development branch in the `SimGear repository`_ (this is also the case for `FlightGear `__ and `FGData `__). If you were to contribute to a different repository such as `FlightGear documentation `__, the branch to base your work on might have a different name (e.g., ``main``). Make Changes ^^^^^^^^^^^^ Commit the desired changes to the branch you created in the previous step. If you need help with :program:`Git`, this online `book `__ is a great resource. Each commit message should start with a single, not too long summary line (no more than 72—80 characters if possible), then a blank line, then normal explanations. Example commit message: .. code-block:: none HID: allow sending output reports from Nasal - make watched properties only react on a value change - refactor the larger usage enums to their own file Like here, it is useful when the beginning of the first line is short and makes it clear to readers which part of the code (subsystem, class, etc.) is affected by the changes. Test and Double-check ^^^^^^^^^^^^^^^^^^^^^ Rebuild FlightGear with your changes, carefully test. Build the FlightGear test suite and verify that the tests still pass. This can be done by running `make test_suite` or `ninja test_suite` from the FlightGear build directory (not the source repository!). Use commands like: - `git status` to check the state of your working directory and repository; - `git log -p` to proofread your commit diffs and messages; - `git commit --amend` to modify the most recent commit on the branch; - `git rebase -i ⟨ref⟩` to modify commits located further in the ancestry graph (namely, descendants of commit `⟨ref⟩`; see the `git rebase manual page`_ for explanations). .. _git rebase manual page: https://git-scm.com/docs/git-rebase The :program:`gitk` program is not essential but can be nice for examining commits and visualizing the commit graph. If :program:`gitk` appears to see local changes that really aren't there, run `git status` then rerun :program:`gitk`. The `pre-commit`_ and `clang-format`_ programs are useful to run code quality checks and apply the project formatting rules. Some of the configured :program:`pre-commit` hooks will for instance check for common mistakes including typos, mixed whitespace or line endings, and so on. If you've `installed `__ the :program:`pre-commit` hooks in your repository clone, :program:`pre-commit` will be automatically run every time you commit. .. note:: In most cases, the GitLab :abbr:`CI (Continuous Integration)` runs the :program:`pre-commit` and :program:`clang-format` checks as part of the automatic verifications that need to pass, before a merge request can be applied. .. _pre-commit: https://pre-commit.com/ .. _clang-format: https://clang.llvm.org/docs/ClangFormat.html Rebase and Push ^^^^^^^^^^^^^^^ If the upstream branch you started from has changed in the meantime, your work needs to be rebased on top of its latest state. This can be done with the following command: .. code-block:: shell git pull --rebase In case someone was working on the same files as you, this may trigger conflicts (so it's a good idea to first announce what you're going to work on in order to minimize friction and benefit from the insight of experienced people). Conflicts don't happen very often and it's not the end of the world when they do. Again, this `Git book`_ is a very useful resource in such cases. If the rebasing did grab upstream changes, perform a final build-and-test run to be sure everything works fine. Finally, push the branch to your fork: .. code-block:: shell git push With no additional arguments, this pushes the current branch to the remote specified with the ``remote.pushDefault`` setting, i.e. to your clone if you followed the :ref:`above ` instructions. Once the push is successful, :program:`Git` will show a URL; following it will start the next step. Submit a Merge Request ^^^^^^^^^^^^^^^^^^^^^^ The web page opened from the URL output by :program:`Git` when you pushed to your fork allows to create a merge request from the branch that was pushed. From this page, select the proper target branch: for development code in `SimGear `__, that would be ``next``. Select yourself as an *assignee* (meaning that *you* are going to shape the merge request into its final form). Select one or more reviewers to look at it—you can ask on the `flightgear-devel mailing-list`_ if unsure, or just leave the field as *Unassigned*. Don't worry too much about the milestone *(a priori,* ``next`` should do unless the merge request is specifically a fix for a release branch). Select applicable labels, etc. .. _flightgear-devel mailing-list: https://sourceforge.net/p/flightgear/mailman/flightgear-devel/ Regarding the *Delete source branch when merge request is accepted* option, we advise you to make sure it is enabled, otherwise your fork will soon have tons of branches. What this option does is the following: when the branch from your merge request is merged into the upstream branch, GitLab will automatically delete the merge request branch (that you pushed) from your fork. This happens online at GitLab, not in your local clone. Thus, even after this occurs, the branch will still be present locally in your clone, until you delete it yourself (cf. :ref:`below `). Finally, the *Squash commits when merge request is accepted* option can be used in case you pushed several commits but would rather have them coalesced into a single one when the merge request is applied. This is something that can be done locally using :program:`Git` before pushing (in particular, with `git rebase -i`), however the option is still useful in case commits are added to the merge request after the initial push, be it by you or by reviewers. Code Review ^^^^^^^^^^^ After a few minutes, hours or days, you'll receive feedback from the FlightGear developers about your merge request. Reviewing merge requests requires expertise and takes some time, so please carefully read the remarks and suggestions, follow up on the questions and requests for changes. Clean Up ^^^^^^^^ Once the branch is merged, update the local branch of your clone that tracks the upstream branch your merge request went to (this is the ``next`` branch in our SimGear example): .. code-block:: shell git checkout next git pull --rebase (Normally, the ``--rebase`` option shouldn't be necessary here as you shouldn't have any local commits on top of the upstream ones in this branch, however it doesn't hurt and can make things easier if you've been forgetful.) .. _local-branch-removal: You can now delete the local branch you created earlier to avoid cluttering your local clone with legacy, unneeded branches: .. code-block:: shell git branch -d new-branch-name In case this branch hasn't been merged *exactly as is* into the upstream branch (same commit contents and metadata), :program:`Git` will warn and refuse to delete the branch unless you insist using ``-D``. If this happens, you'll have to evaluate yourself (by using `git log -p`, by comparing files...) if deleting your local branch ``new-branch-name`` is really the right thing to do. In the likely case that what was merged is a strict improvement over what you have in ``new-branch-name``, then deleting is the way to go. .. tip:: If you really, really messed up and deleted the branch before realizing that it was a mistake, `git reflog` is likely to save you: among other things, it shows the commit hashes corresponding to previous positions of the tips of various branches. As long as no garbage collection occurred in the meantime (`git gc`), the commit hashes are all you need to restore “lost commits”: simply create a branch or tag from the desired commit (before doing do, you may want to use `git show ⟨hash⟩` to see the commit for a given hash, or `git log ⟨hash⟩` to examine its ancestry). From time to time, you may also want to run `git fetch -p flo` (replace ``flo`` with the name of a remote that points to your fork, cf. :ref:`section-Set-up-a-local-clone`). This removes the ``flo/...`` branches from your local clone that have been deleted from your fork at GitLab due to the *Delete source branch when merge request is accepted* option on the merge request page (note that for instance, ``flo/new-branch-name`` and ``new-branch-name`` are different branches in your clone; they'll both clutter the display when using `git log` or `gitk` until you delete them).