Skip to content

Git Workflow

This page outlines the Git workflow used for the CTA project. By following this workflow, we can ensure a smooth and efficient development process with minimal conflicts and a clean history.

Initial setup

Before you start your development. Git should be configured to use your CERN email and username using the following commands:

git config --global user.email "<YOUR CERN EMAIL>"
git config --global user.name "<YOUR CERN USERNAME>"

Optionally (but recommended), configure which text editor git should be using for any text operations:

git config --global core.editor "vim"

For setting up a development environment, see the section on Development Setup.

Trunk-based Development

For our version control management practice we use trunk-based development. This means we have a single main branch where we frequently merge small features, bug fixes and maintenance updates. It is important that changes are kept small and focussed.

The general workflow for development work is as follows:

  • An issue is created for the feature/bug/maintenance in question. See Development Tracking for how to create a proper issue.
  • Before moving on, it should be discussed with the dev team whether this issue is something to implement in the current/next sprint.
  • The ~"workflow::backlog" label should be assigned if it has been decided that this issue is something to work on for the current sprint.
  • Create a branch from the issue. Do this by clicking on the arrow next to Create merge request. This way the branch name will include the issue number and title so that it is clear what this branch is for. Optionally, create a draft merge request from the created branch into main.
  • The ~"workflow::in progress" label should be assigned when work has started on the issue.
  • The ~"workflow::testing" label should be assigned when the main work has finished and the work is awaiting testing.
  • The ~"workflow::in review" label should be assigned when the work has finished and it is awaiting review.
  • If a review requires changes, move it back to one of the previous states as required.
  • If at any point the issue cannot progress due to another issue it should have the ~"workflow::blocked" label.

Info

Developers should regularly review their open tickets to ensure that the workflow:: tag accurately reflects the state of the issue, and that the issue is closed after it is finished.

On Branches

Branch names should only contain alpha-numeric (lowercase) characters and hyphens (-). If a branch name should include a version number (for whatever reason), using dots (.) is allowed as well. The structure of the branch name should be:

<issue-number>-<issue-title>

This will be done automatically if you create the branch from the issue itself using the GitLab (which is the recommended way). In certain select scenarios, it might be necessary to create a branch without associating it to an issue (there should be a good reason for this). In this case, choose a clear and descriptive name.

If you have a test-branch for a given issue, re-use the branch name above and add a descriptive suffix. Do not create random test branches without a proper name! Ensure any test branches are cleaned up when they are no longer needed.

Integrating Changes into main

The branch main is protected. Therefore, it is not possible to directly push new commits. The only way to merge the changes is with a Merge request (MR). Before a branch is merged into the main branch, it should be reviewed and approved by one other member of the development team.

The developer should:

  • Review the MR title. Usually, the title will become the commit comment on main.
  • Fill the description:
    • Add a short summary of the MR.
    • Clarify if the MR requires any tests on preproduction, before deploying to prod:
    • Clarify if the MR requires any documentation updates before deploying to prod. If so, create an issue with the label ~"type::documentation" and link it in the description.
    • Provide a link to the corresponding issue if not already present. No code changes should be merged without a corresponding issue!
  • Assign a reviewer:
    • Usually, the reviewer is simply the next name on a "round robin" list of CTA developers, so code reviews are spread evenly around the team.
    • The developer can choose a specific reviewer if they want, for example because they want the opinion of someone with a particular expertise.

The reviewer should:

  • Confirm that the MR description has been properly filled.
  • Look at the code changes, in a timely way, and make comments.
    • The developer should resolve any issues raised by the reviewer.
  • When all issues raised by the reviewer have been addressed, the reviewer should approve the MR.

When the MR has been approved, the developer can merge the branch to main. Commits should be squashed when merging, so that only a single commit is merged. This squashed commit should follow the commit structure outlined below.

  • If the MR has been approved and the CI pipeline passes the MR is ready to be merged.
  • When merging the changes back into main, the branch may need to be rebased on top of main (git rebase main). The changes should then be retested, and any conflicts resolved (see git rebase documentation).
    • Where the branch has diverged significantly from main, it may be advisable to do the rebase in a new branch (e.g. when rebasing branch 1020-restore-deleted-files-in-eos, create from it branch rb_1020-restore-deleted-files-in-eos, setup to track the same remote branch, and perform the rebase there).
  • If needed, push the rebased feature branch with git push --force-with-lease. No pushing is allowed directly to main.
  • After the MR has been merged or closed, the issue should be closed as well.

Commit Structure

For the commit title, pay attention to the following:

  • Start with a verb in the imperative mood. If in doubt, just think: "this commit will <commit-title>" Some examples:

    • Fix a typo in the readme
    • Clean up old files from cc7 migration
    • Allow the tape robot to serve tea
    • Delete the outdated Doxygen directory
  • Keep the subject line brief yet descriptive

  • Avoid unnecessary capitalization
  • Double check your spelling
  • Don’t end commit message summaries with punctuation

If useful, you can add additional information to the commit body.

For a commit to end up in the CHANGELOG.md, it is important that it has the correct structure. Commits are added and organised in the changelog based on git trailers. Each commit that is add to main is expected to have these two trailers:

Changelog: <type>
Issue: <ref>

The Changelog trailer type should be one of:

  • feature
  • bug
  • maintenance
  • documentation

If a commit does not fit properly within any of these categories, it is possible to add another type. In this case, be aware that the type will end up verbatim in the CHANGELOG.MD. As such, discuss this with the dev team lead before doing this. The issue <ref> should be a GitLab issue reference such as e.g. #123, CTA#123 or cta/CTA#123.

The full commit issue would then look something like this:

%title

%description (optional)

Changelog: <type>
Issue: <ref>

Squashing Commits

While working on the code, you will be making multiple commits. Before this is merged into main, these should be squashed into a single (or in some cases multiple) well-defined commits. Commits should be focussed; they should be adding and/or fixing one particular thing. This is important for other people inspecting the code or in case of e.g. a rollback.

If you want to squash all commits into a single commit, then this can be done using the GitLab UI in the MR itself. However, only a single issue can be a associated with a given commit. This means that if an MR fixes multiple issues, the squash commit feature provided by GitLab will not be sufficient. In this case you have to do it manually.

Start by identifying the commits in your history:

> git log --oneline
330ccecf5 (HEAD -> main) Add semi-related feature 2
673d6fc95 Last commit, trust me
3f52ec780 Oh no I forgot something
7b505e72d Fix bug to get feature 1 working
25d2b370b Add feature 1
50e19209c (origin/main, origin/HEAD) Changelog versioning update

Identify until which commit you want to squash. In this case, the previous commit from the main branch was 50e19209c (Changelog versioning update). Now you can start an interactive rebase from this commit, which will allow you squash commits starting at this commit. Note that the commit you provide to the rebase command is exclusive; this means that this commit will not be part of the rebase.

git rebase -i 50e19209c

Will open the following in your configured git text editor:

pick 25d2b370b Add feature 1
pick 7b505e72d Fix bug to get feature 1 working
pick 3f52ec780 Oh no I forgot something
pick 673d6fc95 Last commit, trust me
pick 330ccecf5 Add semi-related feature 2

These commits are ordered in reverse chronological order. For the purpose of squashing, we can mark the commits as one of the following:

  • pick keeps the commit as is.
  • squash combines this commit with the previous one and lets you edit.
  • fixup combines this commit with the previous one but discards its commit message (useful if you want to simplify the commit history).

If you want to squash everything into a single commit, we can mark the commits as follows:

pick 25d2b370b Add feature 1
squash 7b505e72d Fix bug to get feature 1 working
squash 3f52ec780 Oh no I forgot something
squash 673d6fc95 Last commit, trust me
squash 330ccecf5 Add semi-related feature 2

After this, you will be able to edit the commit message of the new squashed commit.

If you want to squash this commit history into two separate commits, you can do e.g.:

pick 25d2b370b Add feature 1
squash 7b505e72d Fix bug to get feature 1 working
squash 3f52ec780 Oh no I forgot something
pick 673d6fc95 Last commit, trust me
squash 330ccecf5 Add semi-related feature 2

In this case, we will get two separate commits. The first commit combining 25d2b370b-3f52ec780 and the second commit combining 673d6fc95-330ccecf5.

Once again, you will be able to edit the commit message of both of these new squashed commits. When doing so, ensure that you follow the Git commit template specified above.

Once done with the squashing, we have rewritten history (and they said it couldn't be done). This has the unfortunate side-effect that we cannot directly push to our branch. Instead, we will have to force push (not in the jedi way) to the branch that we are currently on. Force pushing is always dangerous as it overwrites the remote history, potentially erasing commits. As such, if you screw it up there is no way to get it back without a backup. As such, it does not hurt to have a quick backup somewhere of the branch in case something goes wrong. With all these warnings out of the way, execute the following to update the remote branch with the new git history:

git push --force-with-lease

Note that we use --force-with-lease instead of --force. This prevents scenarios in which you are overwriting commits that are on the remote, but were not on your local branch (e.g. because someone else added them and you did not pull the changes). See it as a small extra safeguard.

At this point the added commits should be clean, compliant with the required format and ready to be merged into the main branch.