GitLab best practice
Main Page -> Documentation -> GitLab best practice
This page is summarizing guidelines how to use Gitlab within the project. Some general information how to work with GitLab can be found under the following external links:
GitLab is a web service for git, the stupid content tracker (according to its man page). Git implements distributed repositories, git itself does not even make an assumption about a master repository. It is however good to have such a master repository and dedicated strategy how to contribute to it. This is, where GitLab enters the scene. The web service is hosting remote repositories and implements procedures for synchronization and content merging.
In the pCT project we have master repositories for different sub-projects. Check the repositories in the GitLab pCT group.
In this description, we are using the pct-online repository as an example.
Terminology
When working with git/GitLab, you will meet a few terms:
- branch: the tip of a sequence of commits, branches share a common history and develop independently from one point in the history
- merge: two branches are brought together and the result commited with a new commit
- rebase: the history of a branch is applied to another branch resulting into a new history containing the commits of both branches
- fork: repository copy in a user space of GitLab service
- clone: local working copy on a machine
Default branch structure
The main repository has the following branches:
- production: the latest production code, in this branch we have release tags
- master: the latest stable release
- dev: the development branch
Note that we are currently only using the dev branch. The production and master branches will be populated while we are progressing with the development.
In addition to those main branches there can be feature branches where development happens detached from the main branches. A feature branch is based on the dev branch and has a limited lifetime.
Roles
There are two very different use cases, the
- User role: A user wants to download the code, compile it and use it.
- Developer role: A developer contributes to the development.
As a User, you can download an archive from the GitLab web interface, e. g. pct-online, by clicking the download symbol like in this screen shot.
However, even if you think you are fitting best into the user role, you might end up faster than you expect coding and changing the project files. Having content management in place right from the beginning is thus a good idea and it is recommended to use the code by creating a clone of the repository.
See Gitlab Developer FAQ#Create repository clone for instructions.
The developer fork
The developer fork is the main feature in Gitlab's/Github's concept of distributed development, code sharing and code review.
Create developer fork
The fork can be created from the GitLab web interface by simply clicking on the Fork-button, see GitLab Developer FAQ#How do I create a project fork? for details.
To make the development within the project more coherent, you should follow a couple of rules under this link.
Note: If the master repository is not public, also your fork won't be public and you have to allow your colleagues to access it, see in the rules above.
If your account type does not allow for your personal namespace, you can use the common developer fork, see GitLab Developer FAQ#My external user account on GitLab does not give me a namespace for forking.
If you are a very active developer, the common developer fork might not be the right place and we can set up a specific subgroup for you to provide a namespace. Get in contact with the administrators.
Synchronizing developer fork with main repository
This should be done regularly!
Gitlab in general provides repository mirroring for synchronizing different repositories. Mirroring can be done by either Push or Pull. The GitLab version hosted at UiB provides only the option to Push and mirroring can only be set up in the master repository to push updates a fork. If Pull was available, each user would be able to setup mirroring. If you want this for your fork, get in contact with the administrators. More information can be found in Gitlab Master FAQ#Mirror a repository.
Otherwise, synchronization is done in a local clone or via merge requests as described below.
In fact, the matter is not so easy in itself as there can be conflicting developments in the master repository and your development fork.
The primary goal of the update is to move the head of a branch in the development fork forward in the sequence of commits, from one point in the commit history to a another one. If the branch to be updated has already new commits, meaning the commit histories have diverged, all these commits should be applied to the new point in the history. Its important to note, that no additional commits are created in this process. This procedure is called rebase as we put the commits or a branch position to a new base.
Update fork using local clone
A sample sequence for updating a fork of the pct-online repository, assuming the local clone is set up already (see GitLab Developer FAQ#How can I make a clone of a project in my fork?).
- Fetch branch from the master repository
git fetch git@git.app.uib.no:pct/pct-online.git dev
- or if you have configured the upstream
git fetch upstream dev
- Rebase local branch
git rebase FETCH_HEAD dev
- Push to fork
git push origin dev
Update fork using merge request
Another way of updating the developer fork is through a reverse merge request from the master repository to the developer fork, reverse in the sense that the flow of contributions is usually in the opposite direction.
Note: we only want fast-forward merges in this case, this can not be done if the source branch is not a direct continuation of the target branch, i.e. new commits are present. Do the rebase in the local clone in such a case.this case.
- Open the GitLab web interface for the repository
- In the tool bar at the top click .+. and New merge request in the drop-down menu
- Note: you can not go via
Merge Requests
->Create merge request
because this will redirect to your developer fork and you can not select the master repository as source from there.
- Note: you can not go via
- Select the
dev
branch of the master repository as source - Select your developer fork as target and choose branch to be updated
- Press Compare branches and continue
- Set a title, e.g. "Updating dev branch from master repository", check once again that the source is the master repository and the target your developer fork
- Take the occasion to check what has happened since your last update and scroll through the new commits
- Press Submit merge request
- The web interface will redirect to your developer fork, the CI pipeline is started
- Once the CI succeeds, merge by fast-forward - Important DO NOT MERGE BY CREATING MERGE COMMITS!
If the option to merge by fast-forward is not displayed, this needs to be changed in the project settings of the developer fork, see GitLab Developer FAQ#Select Fast-forward merge
Git commits
Changes are added to the local clone by committing. First one has to define changes to be committed by adding files.
git add some_file
Once the files are marked they can be committed
git commit
This will open an editor to allow for entering a commit message. This message will be in the version control alongside with the actual changes.
Log message format
Meaningful commit messages are a powerful resource to keep track and understand the development, and will make it easier to review code changes.
There are some commonly adopted rules
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
Commit strategy
In general it is beneficial to have multiple well described commits with smaller changes instead of one commit with a whole bulk of changes. And there is some advice making it easier on the long run.
- Frequency: Make it to your habit to commit as often as possible, better more then less. Commits can be squashed later when you prepare a merge request. By committing frequently you can keep track of intermediate versions, the latest stable version, an isolated bug, to name a few.
- Atomicity: A commit should include all changes which belong together, e.g. to compile a new executable. Atomicity must be followed when commits are finally prepared for merging to master repository. Every commit in the master repository must represent a compilable snapshot
- Separation: Separate changes not belonging together and not necessary for an atomic commit. E.g. do pure formatting changes separated from functional changes.
Fixup and Squash
If you discovered a little bug or type in a previously committed change it is good to commit this fix right away and isolated from other changes. Use either option --fixup
or --squash
to mark the commit with the intention to squash it into another commit.
git add the_changed_file git commit --fixup HEAD
Instead of HEAD
, any other commit can be specified. The commit is created with an automatic message indicating planned fixup/squash. The difference between the two is that for squash the commit message will not be commented out.
A shortcut if you only have the change to be committed as squash/fixup in the diff:
git commit -a --fixup HEAD
Amending the commit message
Be picky with your commit messages, if you are not happy with the message of the last commit you can amend it
git commit --amend
Re-ordering and squashing commits - interactive rebase
soon to come
Handling merge requests
This section will be updated soon
Git allows for two merging strategies, the normal and fast-forward (ff) merging. The normal merging adds a merge commit. This adds additional information, when something has been merged. In contrast to that, the latter requires to synchronize the branch to be merged via rebasing, and does not need a merge commit. This results in a linear commit history (not necessarily in time). Whether to use the one or the other depends on the specific case and taste. Many people prefer a linear commit history. As a rule of thumb one can say:
- a relatively small and timely update should be done via ff merge
- big developments outside of the main development tree should be merged with merge commits. This allows also for detailed commit messages.
Automatic merges
Gitlab offers the possibility to initiate the merge of a Merge Request (MR) directly from the Web interface. This is very easy and straight forward, however in the Gitlab Community Edition provided by UiB IT, the normal merge procedure is used. As a consequence, every Gitlab Merge Request initiated from the Web interface will include a merge commit.
Fast-forward merges
Fast-forward merges do in contrast to normal git (and Gitlab) merges not produce a separate merge commit. Gitlab Enterprise Edition offers the feature to use fast-forward merges directly from the Gitlab Web interface. UiB IT is providing Gitlab Community Edition, so we do not have this convenient feature right away.
Why fast-forward merges?
Many people consider a linear commit history as an advantage, i.e. not to have merge commits for every merge. A fast-forward merge can be done if only the branch to be merged has additional commits on top of the common commit history. The merge simply means to forward the tip of the target branch.
Workflow
As Gitlab does not provide the functionality, the merge needs to be done outside Gitlab in a local clone from the master repository.
In the workflow below
- adjust the project name wpn
- adjust the user
- adjust the branch name
- you can skip the cloning if you have a already a clone
Workflow for merging the dev brach from a user fork
Recipe going to be updated soon
Gitlab will automatically recognize that the merge request was merged manually and will close it.